Merge "Revert "Revert "[lint] treat non-user getter calls as lint errors with baselines to exempt existing failures"""
diff --git a/Android.bp b/Android.bp
index b8c060d..9127966f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -124,7 +124,6 @@
":libbluetooth-binder-aidl",
":libcamera_client_aidl",
":libcamera_client_framework_aidl",
- ":packagemanager_aidl",
":libupdate_engine_aidl",
":resourcemanager_aidl",
":storaged_aidl",
@@ -228,6 +227,7 @@
name: "framework-internal-utils",
static_libs: [
"apex_aidl_interface-java",
+ "packagemanager_aidl-java",
"framework-protos",
"updatable-driver-protos",
"ota_metadata_proto_java",
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
index 8305d3f..a9f720a 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
@@ -44,6 +44,11 @@
private Throwable mFirstFailure = null;
+ /**
+ * Starts a new run. Also responsible for finalising the calculations from the previous run,
+ * if there was one; therefore, any previous run must not be {@link #pauseTiming() paused} when
+ * this is called.
+ */
public boolean keepRunning() {
switch (mState) {
case NOT_STARTED:
@@ -88,7 +93,31 @@
mState = PAUSED;
}
+ /**
+ * Resumes the timing after a previous {@link #pauseTiming()}.
+ * First waits for the system to be idle prior to resuming.
+ *
+ * If this is called at the end of the run (so that no further timing is actually desired before
+ * {@link #keepRunning()} is called anyway), use {@link #resumeTimingForNextIteration()} instead
+ * to avoid unnecessary waiting.
+ */
public void resumeTiming() {
+ ShellHelper.runShellCommand("am wait-for-broadcast-idle");
+ resumeTimer();
+ }
+
+ /**
+ * Resume timing in preparation for a possible next run (rather than to continue timing the
+ * current run).
+ *
+ * It is equivalent to {@link #resumeTiming()} except that it skips steps that
+ * are unnecessary at the end of a trial (namely, waiting for the system to idle).
+ */
+ public void resumeTimingForNextIteration() {
+ resumeTimer();
+ }
+
+ private void resumeTimer() {
if (mState != PAUSED) {
throw new IllegalStateException("Unable to resume the runner: already running");
}
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 9971cd42..6e2742c 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -88,6 +88,10 @@
public class UserLifecycleTests {
private static final String TAG = UserLifecycleTests.class.getSimpleName();
+ /** Max runtime for each test (including all runs within that test). */
+ // No point exceeding 10 minutes, since device would likely be considered non-responsive anyway.
+ private static final long TIMEOUT_MAX_TEST_TIME_MS = 9 * 60_000;
+
private static final int TIMEOUT_IN_SECOND = 30;
private static final int CHECK_USER_REMOVED_INTERVAL_MS = 200;
@@ -144,7 +148,7 @@
}
/** Tests creating a new user. */
- @Test
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void createUser() {
while (mRunner.keepRunning()) {
Log.i(TAG, "Starting timer");
@@ -153,12 +157,12 @@
mRunner.pauseTiming();
Log.i(TAG, "Stopping timer");
removeUser(userId);
- mRunner.resumeTiming();
+ mRunner.resumeTimingForNextIteration();
}
}
/** Tests creating and starting a new user. */
- @Test
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void createAndStartUser() throws RemoteException {
while (mRunner.keepRunning()) {
Log.i(TAG, "Starting timer");
@@ -174,7 +178,7 @@
mRunner.pauseTiming();
Log.i(TAG, "Stopping timer");
removeUser(userId);
- mRunner.resumeTiming();
+ mRunner.resumeTimingForNextIteration();
}
}
@@ -182,15 +186,15 @@
* Tests starting an uninitialized user.
* Measures the time until ACTION_USER_STARTED is received.
*/
- @Test
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void startUser() throws RemoteException {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createUserNoFlags();
final CountDownLatch latch = new CountDownLatch(1);
registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userId);
- Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
+ Log.i(TAG, "Starting timer");
mIam.startUserInBackground(userId);
waitForLatch("Failed to achieve ACTION_USER_STARTED for user " + userId, latch);
@@ -198,7 +202,7 @@
mRunner.pauseTiming();
Log.i(TAG, "Stopping timer");
removeUser(userId);
- mRunner.resumeTiming();
+ mRunner.resumeTimingForNextIteration();
}
}
@@ -206,13 +210,13 @@
* Tests starting & unlocking an uninitialized user.
* Measures the time until unlock listener is triggered and user is unlocked.
*/
- @Test
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void startAndUnlockUser() {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createUserNoFlags();
- Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
+ Log.i(TAG, "Starting timer");
// Waits for UserState.mUnlockProgress.finish().
startUserInBackgroundAndWaitForUnlock(userId);
@@ -220,19 +224,19 @@
mRunner.pauseTiming();
Log.i(TAG, "Stopping timer");
removeUser(userId);
- mRunner.resumeTiming();
+ mRunner.resumeTimingForNextIteration();
}
}
/** Tests switching to an uninitialized user. */
- @Test
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void switchUser() throws RemoteException {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int startUser = mAm.getCurrentUser();
final int userId = createUserNoFlags();
- Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
+ Log.i(TAG, "Starting timer");
switchUser(userId);
@@ -240,12 +244,12 @@
Log.i(TAG, "Stopping timer");
switchUserNoCheck(startUser);
removeUser(userId);
- mRunner.resumeTiming();
+ mRunner.resumeTimingForNextIteration();
}
}
/** Tests switching to a previously-started, but no-longer-running, user. */
- @Test
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void switchUser_stopped() throws RemoteException {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
@@ -253,8 +257,8 @@
final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ true);
final CountDownLatch latch = new CountDownLatch(1);
registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch, testUser);
- Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
+ Log.i(TAG, "Starting timer");
mAm.switchUser(testUser);
waitForLatch("Failed to achieve 2nd ACTION_USER_UNLOCKED for user " + testUser, latch);
@@ -264,19 +268,19 @@
Log.i(TAG, "Stopping timer");
switchUserNoCheck(startUser);
removeUser(testUser);
- mRunner.resumeTiming();
+ mRunner.resumeTimingForNextIteration();
}
}
/** Tests switching to an already-created already-running non-owner background user. */
- @Test
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void switchUser_running() throws RemoteException {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int startUser = mAm.getCurrentUser();
final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ false);
- Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
+ Log.i(TAG, "Starting timer");
switchUser(testUser);
@@ -284,12 +288,12 @@
Log.i(TAG, "Stopping timer");
switchUserNoCheck(startUser);
removeUser(testUser);
- mRunner.resumeTiming();
+ mRunner.resumeTimingForNextIteration();
}
}
/** Tests stopping a background user. */
- @Test
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void stopUser() throws RemoteException {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
@@ -301,20 +305,20 @@
mIam.startUserInBackground(userId);
waitForLatch("Failed to achieve ACTION_USER_STARTED for user " + userId, latch1);
waitForLatch("Failed to achieve ACTION_MEDIA_MOUNTED for user " + userId, latch2);
- Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
+ Log.i(TAG, "Starting timer");
stopUser(userId, false);
mRunner.pauseTiming();
Log.i(TAG, "Stopping timer");
removeUser(userId);
- mRunner.resumeTiming();
+ mRunner.resumeTimingForNextIteration();
}
}
/** Tests reaching LOOKED_BOOT_COMPLETE when switching to uninitialized user. */
- @Test
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void lockedBootCompleted() throws RemoteException {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
@@ -322,8 +326,8 @@
final int userId = createUserNoFlags();
final CountDownLatch latch = new CountDownLatch(1);
registerUserSwitchObserver(null, latch, userId);
- Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
+ Log.i(TAG, "Starting timer");
mAm.switchUser(userId);
waitForLatch("Failed to achieve onLockedBootComplete for user " + userId, latch);
@@ -332,12 +336,12 @@
Log.i(TAG, "Stopping timer");
switchUserNoCheck(startUser);
removeUser(userId);
- mRunner.resumeTiming();
+ mRunner.resumeTimingForNextIteration();
}
}
/** Tests stopping an ephemeral foreground user. */
- @Test
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void ephemeralUserStopped() throws RemoteException {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
@@ -359,8 +363,8 @@
}, new IntentFilter(Intent.ACTION_USER_STOPPED));
final CountDownLatch switchLatch = new CountDownLatch(1);
registerUserSwitchObserver(switchLatch, null, startUser);
- Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
+ Log.i(TAG, "Starting timer");
mAm.switchUser(startUser);
waitForLatch("Failed to achieve ACTION_USER_STOPPED for user " + userId, latch);
@@ -373,12 +377,12 @@
Log.e(TAG, "Thread interrupted unexpectedly while waiting for switch.", e);
}
removeUser(userId);
- mRunner.resumeTiming();
+ mRunner.resumeTimingForNextIteration();
}
}
/** Tests creating a new profile. */
- @Test
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void managedProfileCreate() {
assumeTrue(mHasManagedUserFeature);
@@ -390,32 +394,32 @@
Log.i(TAG, "Stopping timer");
attestTrue("Failed creating profile " + userId, mUm.isManagedProfile(userId));
removeUser(userId);
- mRunner.resumeTiming();
+ mRunner.resumeTimingForNextIteration();
}
}
/** Tests starting (unlocking) an uninitialized profile. */
- @Test
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void managedProfileUnlock() {
assumeTrue(mHasManagedUserFeature);
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createManagedProfile();
- Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
+ Log.i(TAG, "Starting timer");
startUserInBackgroundAndWaitForUnlock(userId);
mRunner.pauseTiming();
Log.i(TAG, "Stopping timer");
removeUser(userId);
- mRunner.resumeTiming();
+ mRunner.resumeTimingForNextIteration();
}
}
/** Tests starting (unlocking) a previously-started, but no-longer-running, profile. */
- @Test
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void managedProfileUnlock_stopped() throws RemoteException {
assumeTrue(mHasManagedUserFeature);
@@ -425,22 +429,22 @@
// Start the profile initially, then stop it. Similar to setQuietModeEnabled.
startUserInBackgroundAndWaitForUnlock(userId);
stopUser(userId, true);
- Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
+ Log.i(TAG, "Starting timer");
startUserInBackgroundAndWaitForUnlock(userId);
mRunner.pauseTiming();
Log.i(TAG, "Stopping timer");
removeUser(userId);
- mRunner.resumeTiming();
+ mRunner.resumeTimingForNextIteration();
}
}
/**
* Tests starting (unlocking) & launching an already-installed app in an uninitialized profile.
*/
- @Test
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void managedProfileUnlockAndLaunchApp() throws RemoteException {
assumeTrue(mHasManagedUserFeature);
@@ -449,8 +453,8 @@
final int userId = createManagedProfile();
WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null);
installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
- Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
+ Log.i(TAG, "Starting timer");
startUserInBackgroundAndWaitForUnlock(userId);
startApp(userId, DUMMY_PACKAGE_NAME);
@@ -458,7 +462,7 @@
mRunner.pauseTiming();
Log.i(TAG, "Stopping timer");
removeUser(userId);
- mRunner.resumeTiming();
+ mRunner.resumeTimingForNextIteration();
}
}
@@ -468,7 +472,7 @@
* A sort of combination of {@link #managedProfileUnlockAndLaunchApp} and
* {@link #managedProfileUnlock_stopped}}.
*/
- @Test
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void managedProfileUnlockAndLaunchApp_stopped() throws RemoteException {
assumeTrue(mHasManagedUserFeature);
@@ -481,8 +485,8 @@
startApp(userId, DUMMY_PACKAGE_NAME);
stopUser(userId, true);
SystemClock.sleep(1_000); // 1 second cool-down before re-starting profile.
- Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
+ Log.i(TAG, "Starting timer");
startUserInBackgroundAndWaitForUnlock(userId);
startApp(userId, DUMMY_PACKAGE_NAME);
@@ -490,27 +494,27 @@
mRunner.pauseTiming();
Log.i(TAG, "Stopping timer");
removeUser(userId);
- mRunner.resumeTiming();
+ mRunner.resumeTimingForNextIteration();
}
}
/** Tests installing a pre-existing app in an uninitialized profile. */
- @Test
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void managedProfileInstall() throws RemoteException {
assumeTrue(mHasManagedUserFeature);
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createManagedProfile();
- Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
+ Log.i(TAG, "Starting timer");
installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
mRunner.pauseTiming();
Log.i(TAG, "Stopping timer");
removeUser(userId);
- mRunner.resumeTiming();
+ mRunner.resumeTimingForNextIteration();
}
}
@@ -518,15 +522,15 @@
* Tests creating a new profile, starting (unlocking) it, installing an app,
* and launching that app in it.
*/
- @Test
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void managedProfileCreateUnlockInstallAndLaunchApp() throws RemoteException {
assumeTrue(mHasManagedUserFeature);
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null);
- Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
+ Log.i(TAG, "Starting timer");
final int userId = createManagedProfile();
startUserInBackgroundAndWaitForUnlock(userId);
@@ -536,12 +540,12 @@
mRunner.pauseTiming();
Log.i(TAG, "Stopping timer");
removeUser(userId);
- mRunner.resumeTiming();
+ mRunner.resumeTimingForNextIteration();
}
}
/** Tests stopping a profile. */
- @Test
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void managedProfileStopped() throws RemoteException {
assumeTrue(mHasManagedUserFeature);
@@ -552,21 +556,21 @@
registerMediaBroadcastReceiver(prelatch, userId);
startUserInBackgroundAndWaitForUnlock(userId);
waitForLatch("Failed to achieve ACTION_MEDIA_MOUNTED for user " + userId, prelatch);
- Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
+ Log.i(TAG, "Starting timer");
stopUser(userId, true);
mRunner.pauseTiming();
Log.i(TAG, "Stopping timer");
removeUser(userId);
- mRunner.resumeTiming();
+ mRunner.resumeTimingForNextIteration();
}
}
// TODO: This is just a POC. Do this properly and add more.
/** Tests starting (unlocking) a newly-created profile using the user-type-pkg-whitelist. */
- @Test
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void managedProfileUnlock_usingWhitelist() {
assumeTrue(mHasManagedUserFeature);
final int origMode = getUserTypePackageWhitelistMode();
@@ -577,22 +581,22 @@
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createManagedProfile();
- Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
+ Log.i(TAG, "Starting timer");
startUserInBackgroundAndWaitForUnlock(userId);
mRunner.pauseTiming();
Log.i(TAG, "Stopping timer");
removeUser(userId);
- mRunner.resumeTiming();
+ mRunner.resumeTimingForNextIteration();
}
} finally {
setUserTypePackageWhitelistMode(origMode);
}
}
/** Tests starting (unlocking) a newly-created profile NOT using the user-type-pkg-whitelist. */
- @Test
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void managedProfileUnlock_notUsingWhitelist() {
assumeTrue(mHasManagedUserFeature);
final int origMode = getUserTypePackageWhitelistMode();
@@ -602,15 +606,15 @@
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createManagedProfile();
- Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
+ Log.i(TAG, "Starting timer");
startUserInBackgroundAndWaitForUnlock(userId);
mRunner.pauseTiming();
Log.i(TAG, "Stopping timer");
removeUser(userId);
- mRunner.resumeTiming();
+ mRunner.resumeTimingForNextIteration();
}
} finally {
setUserTypePackageWhitelistMode(origMode);
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
index d493a1c..272e12d 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
@@ -96,6 +96,17 @@
return Collections.unmodifiableMap(mAll);
}
+ /**
+ * Asserts that this {@link AppSearchBatchResult} has no failures.
+ *
+ * @hide
+ */
+ public void checkSuccess() {
+ if (!isSuccess()) {
+ throw new IllegalStateException("AppSearchBatchResult has failures: " + this);
+ }
+ }
+
@Override
@NonNull
public String toString() {
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java
index 10e014b..d6d5315 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java
@@ -71,7 +71,7 @@
/**
* Checks the setSchema() call won't delete any types or has incompatible types after all {@link
- * Migrator} has been triggered..
+ * Migrator} has been triggered.
*/
public static void checkDeletedAndIncompatibleAfterMigration(
@NonNull SetSchemaResponse setSchemaResponse, @NonNull Set<String> activeMigrators)
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index c33d5ec..d4e3239 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -363,6 +363,7 @@
schemasVisibleToPackages.put(entry.getKey(), packageIdentifiers);
}
instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
+ // TODO(b/173532925): Implement logging for statsBuilder
SetSchemaResponse setSchemaResponse = instance.getAppSearchImpl().setSchema(
packageName,
databaseName,
@@ -371,7 +372,8 @@
schemasNotDisplayedBySystem,
schemasVisibleToPackages,
forceOverride,
- schemaVersion);
+ schemaVersion,
+ /*setSchemaStatsBuilder=*/ null);
++operationSuccessCount;
invokeCallbackOnResult(callback,
AppSearchResult.newSuccessfulResult(setSchemaResponse.getBundle()));
@@ -816,8 +818,10 @@
AppSearchUserInstance instance =
mAppSearchUserInstanceManager.getUserInstance(targetUser);
+ // TODO(b/173532925): Implement logging for statsBuilder
SearchResultPage searchResultPage =
- instance.getAppSearchImpl().getNextPage(packageName, nextPageToken);
+ instance.getAppSearchImpl().getNextPage(
+ packageName, nextPageToken, /*statsBuilder=*/ null);
invokeCallbackOnResult(
callback,
AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
@@ -898,8 +902,11 @@
outputStream, searchResultPage.getResults().get(i)
.getGenericDocument().getBundle());
}
+ // TODO(b/173532925): Implement logging for statsBuilder
searchResultPage = instance.getAppSearchImpl().getNextPage(
- packageName, searchResultPage.getNextPageToken());
+ packageName,
+ searchResultPage.getNextPageToken(),
+ /*statsBuilder=*/ null);
}
}
invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
index 15916cc..324163f 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
@@ -59,6 +59,7 @@
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
import com.android.server.appsearch.external.localstorage.stats.SearchStats;
+import com.android.server.appsearch.external.localstorage.stats.SetSchemaStats;
import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore;
import com.google.android.icing.IcingSearchEngine;
@@ -393,6 +394,7 @@
* @param forceOverride Whether to force-apply the schema even if it is incompatible. Documents
* which do not comply with the new schema will be deleted.
* @param version The overall version number of the request.
+ * @param setSchemaStatsBuilder Builder for {@link SetSchemaStats} to hold stats for setSchema
* @return The response contains deleted schema types and incompatible schema types of this
* call.
* @throws AppSearchException On IcingSearchEngine error. If the status code is
@@ -408,7 +410,8 @@
@NonNull List<String> schemasNotDisplayedBySystem,
@NonNull Map<String, List<PackageIdentifier>> schemasVisibleToPackages,
boolean forceOverride,
- int version)
+ int version,
+ @Nullable SetSchemaStats.Builder setSchemaStatsBuilder)
throws AppSearchException {
mReadWriteLock.writeLock().lock();
try {
@@ -438,6 +441,12 @@
mLogUtil.piiTrace(
"setSchema, response", setSchemaResultProto.getStatus(), setSchemaResultProto);
+ if (setSchemaStatsBuilder != null) {
+ setSchemaStatsBuilder.setStatusCode(
+ statusProtoToResultCode(setSchemaResultProto.getStatus()));
+ AppSearchLoggerHelper.copyNativeStats(setSchemaResultProto, setSchemaStatsBuilder);
+ }
+
// Determine whether it succeeded.
try {
checkSuccess(setSchemaResultProto.getStatus());
@@ -1127,8 +1136,13 @@
* @throws AppSearchException on IcingSearchEngine error or if can't advance on nextPageToken.
*/
@NonNull
- public SearchResultPage getNextPage(@NonNull String packageName, long nextPageToken)
+ public SearchResultPage getNextPage(
+ @NonNull String packageName,
+ long nextPageToken,
+ @Nullable SearchStats.Builder statsBuilder)
throws AppSearchException {
+ long totalLatencyStartMillis = SystemClock.elapsedRealtime();
+
mReadWriteLock.readLock().lock();
try {
throwIfClosedLocked();
@@ -1137,6 +1151,13 @@
checkNextPageToken(packageName, nextPageToken);
SearchResultProto searchResultProto =
mIcingSearchEngineLocked.getNextPage(nextPageToken);
+
+ if (statsBuilder != null) {
+ statsBuilder.setStatusCode(statusProtoToResultCode(searchResultProto.getStatus()));
+ AppSearchLoggerHelper.copyNativeStats(
+ searchResultProto.getQueryStats(), statsBuilder);
+ }
+
mLogUtil.piiTrace(
"getNextPage, response",
searchResultProto.getResultsCount(),
@@ -1152,9 +1173,22 @@
mNextPageTokensLocked.get(packageName).remove(nextPageToken);
}
}
- return rewriteSearchResultProto(searchResultProto, mSchemaMapLocked);
+ long rewriteSearchResultLatencyStartMillis = SystemClock.elapsedRealtime();
+ SearchResultPage resultPage =
+ rewriteSearchResultProto(searchResultProto, mSchemaMapLocked);
+ if (statsBuilder != null) {
+ statsBuilder.setRewriteSearchResultLatencyMillis(
+ (int)
+ (SystemClock.elapsedRealtime()
+ - rewriteSearchResultLatencyStartMillis));
+ }
+ return resultPage;
} finally {
mReadWriteLock.readLock().unlock();
+ if (statsBuilder != null) {
+ statsBuilder.setTotalLatencyMillis(
+ (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis));
+ }
}
}
@@ -1334,7 +1368,7 @@
statusProtoToResultCode(deleteResultProto.getStatus()));
// TODO(b/187206766) also log query stats here once IcingLib returns it
AppSearchLoggerHelper.copyNativeStats(
- deleteResultProto.getDeleteStats(), removeStatsBuilder);
+ deleteResultProto.getDeleteByQueryStats(), removeStatsBuilder);
}
// It seems that the caller wants to get success if the data matching the query is
@@ -1343,7 +1377,8 @@
deleteResultProto.getStatus(), StatusProto.Code.OK, StatusProto.Code.NOT_FOUND);
// Update derived maps
- int numDocumentsDeleted = deleteResultProto.getDeleteStats().getNumDocumentsDeleted();
+ int numDocumentsDeleted =
+ deleteResultProto.getDeleteByQueryStats().getNumDocumentsDeleted();
updateDocumentCountAfterRemovalLocked(packageName, numDocumentsDeleted);
} finally {
mReadWriteLock.writeLock().unlock();
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java
index 98cedc7..1f7d44e 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java
@@ -24,6 +24,7 @@
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
import com.android.server.appsearch.external.localstorage.stats.SearchStats;
+import com.android.server.appsearch.external.localstorage.stats.SetSchemaStats;
/**
* An interface for implementing client-defined logging AppSearch operations stats.
@@ -54,5 +55,8 @@
/** Logs {@link OptimizeStats} */
void logStats(@NonNull OptimizeStats stats);
+ /** Logs {@link SetSchemaStats} */
+ void logStats(@NonNull SetSchemaStats stats);
+
// TODO(b/173532925) Add remaining logStats once we add all the stats.
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java
index cd653e5..c19ba14 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java
@@ -23,12 +23,15 @@
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
import com.android.server.appsearch.external.localstorage.stats.SearchStats;
+import com.android.server.appsearch.external.localstorage.stats.SetSchemaStats;
+import com.google.android.icing.proto.DeleteByQueryStatsProto;
import com.google.android.icing.proto.DeleteStatsProto;
import com.google.android.icing.proto.InitializeStatsProto;
import com.google.android.icing.proto.OptimizeStatsProto;
import com.google.android.icing.proto.PutDocumentStatsProto;
import com.google.android.icing.proto.QueryStatsProto;
+import com.google.android.icing.proto.SetSchemaResultProto;
import java.util.Objects;
@@ -142,6 +145,26 @@
}
/**
+ * Copies native DeleteByQuery stats to builder.
+ *
+ * @param fromNativeStats Stats copied from.
+ * @param toStatsBuilder Stats copied to.
+ */
+ static void copyNativeStats(
+ @NonNull DeleteByQueryStatsProto fromNativeStats,
+ @NonNull RemoveStats.Builder toStatsBuilder) {
+ Objects.requireNonNull(fromNativeStats);
+ Objects.requireNonNull(toStatsBuilder);
+
+ @SuppressWarnings("deprecation")
+ int deleteType = DeleteStatsProto.DeleteType.Code.DEPRECATED_QUERY.getNumber();
+ toStatsBuilder
+ .setNativeLatencyMillis(fromNativeStats.getLatencyMs())
+ .setDeleteType(deleteType)
+ .setDeletedDocumentCount(fromNativeStats.getNumDocumentsDeleted());
+ }
+
+ /**
* Copies native {@link OptimizeStatsProto} to builder.
*
* @param fromNativeStats Stats copied from.
@@ -164,4 +187,25 @@
.setStorageSizeAfterBytes(fromNativeStats.getStorageSizeAfter())
.setTimeSinceLastOptimizeMillis(fromNativeStats.getTimeSinceLastOptimizeMs());
}
+
+ /*
+ * Copy SetSchema result stats to builder.
+ *
+ * @param fromProto Stats copied from.
+ * @param toStatsBuilder Stats copied to.
+ */
+ static void copyNativeStats(
+ @NonNull SetSchemaResultProto fromProto,
+ @NonNull SetSchemaStats.Builder toStatsBuilder) {
+ Objects.requireNonNull(fromProto);
+ Objects.requireNonNull(toStatsBuilder);
+ toStatsBuilder
+ .setNewTypeCount(fromProto.getNewSchemaTypesCount())
+ .setDeletedTypeCount(fromProto.getDeletedSchemaTypesCount())
+ .setCompatibleTypeChangeCount(fromProto.getFullyCompatibleChangedSchemaTypesCount())
+ .setIndexIncompatibleTypeChangeCount(
+ fromProto.getIndexIncompatibleChangedSchemaTypesCount())
+ .setBackwardsIncompatibleTypeChangeCount(
+ fromProto.getIncompatibleSchemaTypesCount());
+ }
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SearchStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SearchStats.java
index d7904f3..75ae2d0a 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SearchStats.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SearchStats.java
@@ -40,6 +40,7 @@
VISIBILITY_SCOPE_LOCAL,
// Searches the global documents. Including platform surfaceable and 3p-access.
VISIBILITY_SCOPE_GLOBAL,
+ VISIBILITY_SCOPE_UNKNOWN,
// TODO(b/173532925) Add THIRD_PARTY_ACCESS once we can distinguish platform
// surfaceable from 3p access(right both of them are categorized as
// VISIBILITY_SCOPE_GLOBAL)
@@ -51,6 +52,7 @@
public static final int VISIBILITY_SCOPE_LOCAL = 1;
// Searches the global documents. Including platform surfaceable and 3p-access.
public static final int VISIBILITY_SCOPE_GLOBAL = 2;
+ public static final int VISIBILITY_SCOPE_UNKNOWN = 3;
// TODO(b/173532925): Add a field searchType to indicate where the search is used(normal
// query vs in removeByQuery vs during migration)
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SetSchemaStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SetSchemaStats.java
index 9d789a8..3e5a80f 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SetSchemaStats.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SetSchemaStats.java
@@ -47,9 +47,6 @@
private final int mTotalLatencyMillis;
- /** Overall time used for the native function call. */
- private final int mNativeLatencyMillis;
-
/** Number of newly added schema types. */
private final int mNewTypeCount;
@@ -72,7 +69,6 @@
mStatusCode = builder.mStatusCode;
mSchemaMigrationStats = builder.mSchemaMigrationStats;
mTotalLatencyMillis = builder.mTotalLatencyMillis;
- mNativeLatencyMillis = builder.mNativeLatencyMillis;
mNewTypeCount = builder.mNewTypeCount;
mDeletedTypeCount = builder.mDeletedTypeCount;
mCompatibleTypeChangeCount = builder.mCompatibleTypeChangeCount;
@@ -112,11 +108,6 @@
return mTotalLatencyMillis;
}
- /** Returns overall time used for the native function call. */
- public int getNativeLatencyMillis() {
- return mNativeLatencyMillis;
- }
-
/** Returns number of newly added schema types. */
public int getNewTypeCount() {
return mNewTypeCount;
@@ -159,7 +150,6 @@
@AppSearchResult.ResultCode int mStatusCode;
@Nullable SchemaMigrationStats mSchemaMigrationStats;
int mTotalLatencyMillis;
- int mNativeLatencyMillis;
int mNewTypeCount;
int mDeletedTypeCount;
int mCompatibleTypeChangeCount;
@@ -193,13 +183,6 @@
return this;
}
- /** Sets native latency in milliseconds. */
- @NonNull
- public Builder setNativeLatencyMillis(int nativeLatencyMillis) {
- mNativeLatencyMillis = nativeLatencyMillis;
- return this;
- }
-
/** Sets number of new types. */
@NonNull
public Builder setNewTypeCount(int newTypeCount) {
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
index fdf6a00..4c29ece 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
@@ -36,6 +36,7 @@
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
import com.android.server.appsearch.external.localstorage.stats.SearchStats;
+import com.android.server.appsearch.external.localstorage.stats.SetSchemaStats;
import com.android.server.appsearch.util.PackageUtil;
import java.io.UnsupportedEncodingException;
@@ -180,6 +181,11 @@
}
}
+ @Override
+ public void logStats(@NonNull SetSchemaStats stats) {
+ // TODO(b/173532925): Log stats
+ }
+
/**
* Removes cached UID for package.
*
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStoreImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStoreImpl.java
index ce142d6..c4d1016 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStoreImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStoreImpl.java
@@ -126,7 +126,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ SCHEMA_VERSION);
+ /*version=*/ SCHEMA_VERSION,
+ /*setSchemaStatsBuilder=*/ null);
}
// Populate visibility settings set
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index a81d7d80..4db8355 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-Ie04f1ecc033faae8085afcb51eb9e40a298998d5
+bd53b062816070b64feb992c2bf58f3afa3d420e
diff --git a/apex/appsearch/testing/Android.bp b/apex/appsearch/testing/Android.bp
index 5407cb4..f78d98a 100644
--- a/apex/appsearch/testing/Android.bp
+++ b/apex/appsearch/testing/Android.bp
@@ -28,6 +28,7 @@
"framework",
"framework-appsearch",
"guava",
+ "service-appsearch",
"truth-prebuilt",
],
visibility: [
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
index ec9a42ea..4d8e8e9 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
@@ -19,6 +19,8 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.appsearch.AppSearchBatchResult;
import android.app.appsearch.AppSearchSessionShim;
import android.app.appsearch.GenericDocument;
@@ -26,12 +28,66 @@
import android.app.appsearch.SearchResult;
import android.app.appsearch.SearchResultsShim;
+import com.android.server.appsearch.external.localstorage.AppSearchLogger;
+import com.android.server.appsearch.external.localstorage.stats.CallStats;
+import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
+import com.android.server.appsearch.external.localstorage.stats.OptimizeStats;
+import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
+import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
+import com.android.server.appsearch.external.localstorage.stats.SearchStats;
+import com.android.server.appsearch.external.localstorage.stats.SetSchemaStats;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Future;
public class AppSearchTestUtils {
+ // Non-thread-safe logger implementation for testing
+ public static class TestLogger implements AppSearchLogger {
+ @Nullable public CallStats mCallStats;
+ @Nullable public PutDocumentStats mPutDocumentStats;
+ @Nullable public InitializeStats mInitializeStats;
+ @Nullable public SearchStats mSearchStats;
+ @Nullable public RemoveStats mRemoveStats;
+ @Nullable public OptimizeStats mOptimizeStats;
+ @Nullable public SetSchemaStats mSetSchemaStats;
+
+ @Override
+ public void logStats(@NonNull CallStats stats) {
+ mCallStats = stats;
+ }
+
+ @Override
+ public void logStats(@NonNull PutDocumentStats stats) {
+ mPutDocumentStats = stats;
+ }
+
+ @Override
+ public void logStats(@NonNull InitializeStats stats) {
+ mInitializeStats = stats;
+ }
+
+ @Override
+ public void logStats(@NonNull SearchStats stats) {
+ mSearchStats = stats;
+ }
+
+ @Override
+ public void logStats(@NonNull RemoveStats stats) {
+ mRemoveStats = stats;
+ }
+
+ @Override
+ public void logStats(@NonNull OptimizeStats stats) {
+ mOptimizeStats = stats;
+ }
+
+ @Override
+ public void logStats(@NonNull SetSchemaStats stats) {
+ mSetSchemaStats = stats;
+ }
+ }
public static <K, V> AppSearchBatchResult<K, V> checkIsBatchResultSuccess(
Future<AppSearchBatchResult<K, V>> future) throws Exception {
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
index 2ecc0ed..5d29515 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -28,6 +28,7 @@
import static com.android.server.tare.EconomicPolicy.getEventType;
import static com.android.server.tare.TareUtils.appToString;
import static com.android.server.tare.TareUtils.getCurrentTimeMillis;
+import static com.android.server.tare.TareUtils.narcToString;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -460,8 +461,9 @@
Math.min(ongoingEvent.reward.maxDailyReward - rewardSum, computedDelta));
}
+ @VisibleForTesting
@GuardedBy("mLock")
- private void recordTransactionLocked(final int userId, @NonNull final String pkgName,
+ void recordTransactionLocked(final int userId, @NonNull final String pkgName,
@NonNull Ledger ledger, @NonNull Ledger.Transaction transaction,
final boolean notifyOnAffordabilityChange) {
if (transaction.delta == 0) {
@@ -476,12 +478,14 @@
final long maxCirculationAllowed = mIrs.getMaxCirculationLocked();
final long newArcsInCirculation = mCurrentNarcsInCirculation + transaction.delta;
if (transaction.delta > 0 && newArcsInCirculation > maxCirculationAllowed) {
- final long newDelta = maxCirculationAllowed - mCurrentNarcsInCirculation;
+ // Set lower bound at 0 so we don't accidentally take away credits when we were trying
+ // to _give_ the app credits.
+ final long newDelta = Math.max(0, maxCirculationAllowed - mCurrentNarcsInCirculation);
Slog.i(TAG, "Would result in too many credits in circulation. Decreasing transaction "
+ eventToString(transaction.eventId)
+ (transaction.tag == null ? "" : ":" + transaction.tag)
+ " for " + appToString(userId, pkgName)
- + " by " + (transaction.delta - newDelta));
+ + " by " + narcToString(transaction.delta - newDelta));
transaction = new Ledger.Transaction(
transaction.startTimeMs, transaction.endTimeMs,
transaction.eventId, transaction.tag, newDelta);
@@ -490,12 +494,15 @@
if (transaction.delta > 0
&& originalBalance + transaction.delta
> mCompleteEconomicPolicy.getMaxSatiatedBalance()) {
- final long newDelta = mCompleteEconomicPolicy.getMaxSatiatedBalance() - originalBalance;
+ // Set lower bound at 0 so we don't accidentally take away credits when we were trying
+ // to _give_ the app credits.
+ final long newDelta =
+ Math.max(0, mCompleteEconomicPolicy.getMaxSatiatedBalance() - originalBalance);
Slog.i(TAG, "Would result in becoming too rich. Decreasing transaction "
+ eventToString(transaction.eventId)
+ (transaction.tag == null ? "" : ":" + transaction.tag)
+ " for " + appToString(userId, pkgName)
- + " by " + (transaction.delta - newDelta));
+ + " by " + narcToString(transaction.delta - newDelta));
transaction = new Ledger.Transaction(
transaction.startTimeMs, transaction.endTimeMs,
transaction.eventId, transaction.tag, newDelta);
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 2fa10f0..096211b 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -264,6 +264,11 @@
// get the cached value
private static final long NETWORK_SCORER_CACHE_DURATION_MILLIS = 5000L;
+ // Cache the device provisioning package queried from resource config_deviceProvisioningPackage.
+ // Note that there is no synchronization on this method which is okay since in the worst case
+ // scenario, they might be a few extra reads from resources.
+ private String mCachedDeviceProvisioningPackage = null;
+
// Messages for the handler
static final int MSG_INFORM_LISTENERS = 3;
static final int MSG_FORCE_IDLE_STATE = 4;
@@ -1641,9 +1646,11 @@
* returns {@code false}.
*/
private boolean isDeviceProvisioningPackage(String packageName) {
- String deviceProvisioningPackage = mContext.getResources().getString(
- com.android.internal.R.string.config_deviceProvisioningPackage);
- return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName);
+ if (mCachedDeviceProvisioningPackage == null) {
+ mCachedDeviceProvisioningPackage = mContext.getResources().getString(
+ com.android.internal.R.string.config_deviceProvisioningPackage);
+ }
+ return mCachedDeviceProvisioningPackage.equals(packageName);
}
private boolean isCarrierApp(String packageName) {
diff --git a/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl b/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl
index fb3172b..e1c89e9 100644
--- a/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl
+++ b/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl
@@ -18,6 +18,7 @@
import android.media.Session2Token;
import android.media.IMediaCommunicationServiceCallback;
import android.media.MediaParceledListSlice;
+import android.view.KeyEvent;
/** {@hide} */
interface IMediaCommunicationService {
@@ -25,6 +26,8 @@
boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid);
MediaParceledListSlice getSession2Tokens(int userId);
+ void dispatchMediaKeyEvent(String packageName, in KeyEvent keyEvent, boolean asSystemService);
+
void registerCallback(IMediaCommunicationServiceCallback callback, String packageName);
void unregisterCallback(IMediaCommunicationServiceCallback callback);
}
diff --git a/apex/media/framework/api/module-lib-current.txt b/apex/media/framework/api/module-lib-current.txt
index eb6397a1..7317f14 100644
--- a/apex/media/framework/api/module-lib-current.txt
+++ b/apex/media/framework/api/module-lib-current.txt
@@ -2,6 +2,7 @@
package android.media {
public class MediaCommunicationManager {
+ method public void dispatchMediaKeyEvent(@NonNull android.view.KeyEvent, boolean);
method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void registerSessionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaCommunicationManager.SessionCallback);
method public void unregisterSessionCallback(@NonNull android.media.MediaCommunicationManager.SessionCallback);
}
diff --git a/apex/media/framework/java/android/media/MediaCommunicationManager.java b/apex/media/framework/java/android/media/MediaCommunicationManager.java
index f39bcfb..b2528cd 100644
--- a/apex/media/framework/java/android/media/MediaCommunicationManager.java
+++ b/apex/media/framework/java/android/media/MediaCommunicationManager.java
@@ -32,6 +32,7 @@
import android.os.UserHandle;
import android.service.media.MediaBrowserService;
import android.util.Log;
+import android.view.KeyEvent;
import com.android.internal.annotations.GuardedBy;
import com.android.modules.annotation.MinSdk;
@@ -63,7 +64,8 @@
private static final int CURRENT_VERSION = VERSION_1;
private final Context mContext;
- private final IMediaCommunicationService mService;
+ // Do not access directly use getService().
+ private IMediaCommunicationService mService;
private final Object mLock = new Object();
private final CopyOnWriteArrayList<SessionCallbackRecord> mTokenCallbackRecords =
@@ -80,10 +82,6 @@
throw new UnsupportedOperationException("Android version must be S or greater.");
}
mContext = context;
- mService = IMediaCommunicationService.Stub.asInterface(
- MediaFrameworkInitializer.getMediaServiceManager()
- .getMediaCommunicationServiceRegisterer()
- .get());
}
/**
@@ -105,7 +103,7 @@
throw new IllegalArgumentException("token's type should be TYPE_SESSION");
}
try {
- mService.notifySession2Created(token);
+ getService().notifySession2Created(token);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
@@ -130,7 +128,7 @@
return false;
}
try {
- return mService.isTrusted(
+ return getService().isTrusted(
userInfo.getPackageName(), userInfo.getPid(), userInfo.getUid());
} catch (RemoteException e) {
Log.w(TAG, "Cannot communicate with the service.", e);
@@ -182,7 +180,7 @@
MediaCommunicationServiceCallbackStub callbackStub =
new MediaCommunicationServiceCallbackStub();
try {
- mService.registerCallback(callbackStub, mContext.getPackageName());
+ getService().registerCallback(callbackStub, mContext.getPackageName());
mCallbackStub = callbackStub;
} catch (RemoteException ex) {
Log.e(TAG, "Failed to register callback.", ex);
@@ -205,7 +203,7 @@
synchronized (mLock) {
if (mCallbackStub != null && mTokenCallbackRecords.isEmpty()) {
try {
- mService.unregisterCallback(mCallbackStub);
+ getService().unregisterCallback(mCallbackStub);
} catch (RemoteException ex) {
Log.e(TAG, "Failed to unregister callback.", ex);
}
@@ -214,9 +212,19 @@
}
}
+ private IMediaCommunicationService getService() {
+ if (mService == null) {
+ mService = IMediaCommunicationService.Stub.asInterface(
+ MediaFrameworkInitializer.getMediaServiceManager()
+ .getMediaCommunicationServiceRegisterer()
+ .get());
+ }
+ return mService;
+ }
+
private List<Session2Token> getSession2Tokens(int userId) {
try {
- MediaParceledListSlice slice = mService.getSession2Tokens(userId);
+ MediaParceledListSlice slice = getService().getSession2Tokens(userId);
return slice == null ? Collections.emptyList() : slice.getList();
} catch (RemoteException e) {
Log.e(TAG, "Failed to get session tokens", e);
@@ -225,6 +233,25 @@
}
/**
+ * Sends a media key event. The receiver will be selected automatically.
+ *
+ * @param keyEvent the key event to send
+ * @param asSystemService if {@code true}, the event sent to the session as if it was come from
+ * the system service instead of the app process.
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public void dispatchMediaKeyEvent(@NonNull KeyEvent keyEvent, boolean asSystemService) {
+ Objects.requireNonNull(keyEvent, "keyEvent shouldn't be null");
+ try {
+ getService().dispatchMediaKeyEvent(mContext.getPackageName(),
+ keyEvent, asSystemService);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send key event.", e);
+ }
+ }
+
+ /**
* Callback for listening to changes to the sessions.
* @see #registerSessionCallback(Executor, SessionCallback)
* @hide
diff --git a/apex/media/framework/java/android/media/MediaTranscodingManager.java b/apex/media/framework/java/android/media/MediaTranscodingManager.java
index 93d58d0..7e4799c 100644
--- a/apex/media/framework/java/android/media/MediaTranscodingManager.java
+++ b/apex/media/framework/java/android/media/MediaTranscodingManager.java
@@ -22,7 +22,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
@@ -121,7 +120,6 @@
private final String mPackageName;
private final int mPid;
private final int mUid;
- private final boolean mIsLowRamDevice;
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
private final HashMap<Integer, TranscodingSession> mPendingTranscodingSessions = new HashMap();
private final Object mLock = new Object();
@@ -208,10 +206,7 @@
if (!SdkLevel.isAtLeastS()) {
return null;
}
- // Do not try to get the service on AndroidGo (low-ram) devices.
- if (mIsLowRamDevice) {
- return null;
- }
+
int retryCount = !retry ? 1 : CONNECT_SERVICE_RETRY_COUNT;
Log.i(TAG, "get service with retry " + retryCount);
for (int count = 1; count <= retryCount; count++) {
@@ -429,7 +424,6 @@
mPackageName = mContext.getPackageName();
mUid = Os.getuid();
mPid = Os.getpid();
- mIsLowRamDevice = mContext.getSystemService(ActivityManager.class).isLowRamDevice();
}
/**
diff --git a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
index ed31aa3..03a2372 100644
--- a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
+++ b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
@@ -31,6 +31,7 @@
import android.media.MediaParceledListSlice;
import android.media.Session2CommandGroup;
import android.media.Session2Token;
+import android.media.session.MediaSessionManager;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -42,6 +43,7 @@
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import android.view.KeyEvent;
import com.android.internal.annotations.GuardedBy;
import com.android.server.SystemService;
@@ -60,7 +62,7 @@
* @hide
*/
public class MediaCommunicationService extends SystemService {
- private static final String TAG = "MediaCommunicationService";
+ private static final String TAG = "MediaCommunicationSrv";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
final Context mContext;
@@ -77,6 +79,7 @@
@GuardedBy("mLock")
final List<CallbackRecord> mCallbackRecords = new ArrayList<>();
final NotificationManager mNotificationManager;
+ MediaSessionManager mSessionManager;
public MediaCommunicationService(Context context) {
super(context);
@@ -91,6 +94,17 @@
}
@Override
+ public void onBootPhase(int phase) {
+ super.onBootPhase(phase);
+ switch (phase) {
+ // This ensures MediaSessionService is started
+ case PHASE_BOOT_COMPLETED:
+ mSessionManager = mContext.getSystemService(MediaSessionManager.class);
+ break;
+ }
+ }
+
+ @Override
public void onUserStarting(@NonNull TargetUser user) {
if (DEBUG) Log.d(TAG, "onUserStarting: " + user);
updateUser();
@@ -267,6 +281,24 @@
session.close();
}
+ static boolean isMediaSessionKey(int keyCode) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_MEDIA_PLAY:
+ case KeyEvent.KEYCODE_MEDIA_PAUSE:
+ case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+ case KeyEvent.KEYCODE_MUTE:
+ case KeyEvent.KEYCODE_HEADSETHOOK:
+ case KeyEvent.KEYCODE_MEDIA_STOP:
+ case KeyEvent.KEYCODE_MEDIA_NEXT:
+ case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+ case KeyEvent.KEYCODE_MEDIA_REWIND:
+ case KeyEvent.KEYCODE_MEDIA_RECORD:
+ case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+ return true;
+ }
+ return false;
+ }
+
private class Stub extends IMediaCommunicationService.Stub {
@Override
public void notifySession2Created(Session2Token sessionToken) {
@@ -351,6 +383,29 @@
}
@Override
+ public void dispatchMediaKeyEvent(String packageName, KeyEvent keyEvent,
+ boolean asSystemService) {
+ if (keyEvent == null || !isMediaSessionKey(keyEvent.getKeyCode())) {
+ Log.w(TAG, "Attempted to dispatch null or non-media key event.");
+ return;
+ }
+
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ //TODO: Dispatch key event to media session 2 if required
+ if (asSystemService) {
+ mSessionManager.dispatchMediaKeyEventAsSystemService(keyEvent);
+ } else {
+ mSessionManager.dispatchMediaKeyEvent(keyEvent, false);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void registerCallback(IMediaCommunicationServiceCallback callback,
String packageName) throws RemoteException {
Objects.requireNonNull(callback, "callback should not be null");
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 90a5d49..54fd430 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -127,6 +127,7 @@
})";
static const char IMAGE_FRAG_DYNAMIC_COLORING_SHADER_SOURCE[] = R"(
precision mediump float;
+ const float cWhiteMaskThreshold = 0.05f;
uniform sampler2D uTexture;
uniform float uFade;
uniform float uColorProgress;
@@ -141,10 +142,20 @@
varying highp vec2 vUv;
void main() {
vec4 mask = texture2D(uTexture, vUv);
- vec4 color = mask.r * mix(uStartColor0, uEndColor0, uColorProgress)
- + mask.g * mix(uStartColor1, uEndColor1, uColorProgress)
- + mask.b * mix(uStartColor2, uEndColor2, uColorProgress)
- + mask.a * mix(uStartColor3, uEndColor3, uColorProgress);
+ float r = mask.r;
+ float g = mask.g;
+ float b = mask.b;
+ float a = mask.a;
+ // If all channels have values, render pixel as a shade of white.
+ float useWhiteMask = step(cWhiteMaskThreshold, r)
+ * step(cWhiteMaskThreshold, g)
+ * step(cWhiteMaskThreshold, b)
+ * step(cWhiteMaskThreshold, a);
+ vec4 color = r * mix(uStartColor0, uEndColor0, uColorProgress)
+ + g * mix(uStartColor1, uEndColor1, uColorProgress)
+ + b * mix(uStartColor2, uEndColor2, uColorProgress)
+ + a * mix(uStartColor3, uEndColor3, uColorProgress);
+ color = mix(color, vec4(vec3((r + g + b + a) * 0.25f), 1.0), useWhiteMask);
gl_FragColor = vec4(color.x, color.y, color.z, (1.0 - uFade)) * color.a;
})";
static const char IMAGE_FRAG_SHADER_SOURCE[] = R"(
@@ -1076,6 +1087,8 @@
int pause = 0;
int progress = 0;
int framesToFadeCount = 0;
+ int colorTransitionStart = 0;
+ int colorTransitionEnd = 0;
char path[ANIM_ENTRY_NAME_MAX];
char color[7] = "000000"; // default to black if unspecified
char clockPos1[TEXT_POS_LEN_MAX + 1] = "";
@@ -1101,14 +1114,17 @@
} else {
animation.progressEnabled = false;
}
- } else if (sscanf(l, "dynamic_colors %" STRTO(ANIM_PATH_MAX) "s #%6s #%6s #%6s #%6s",
+ } else if (sscanf(l, "dynamic_colors %" STRTO(ANIM_PATH_MAX) "s #%6s #%6s #%6s #%6s %d %d",
dynamicColoringPartNameBuffer,
- start_color_0, start_color_1, start_color_2, start_color_3)) {
+ start_color_0, start_color_1, start_color_2, start_color_3,
+ &colorTransitionStart, &colorTransitionEnd)) {
animation.dynamicColoringEnabled = true;
parseColor(start_color_0, animation.startColors[0]);
parseColor(start_color_1, animation.startColors[1]);
parseColor(start_color_2, animation.startColors[2]);
parseColor(start_color_3, animation.startColors[3]);
+ animation.colorTransitionStart = colorTransitionStart;
+ animation.colorTransitionEnd = colorTransitionEnd;
dynamicColoringPartName = std::string(dynamicColoringPartNameBuffer);
} else if (sscanf(l, "%c %d %d %" STRTO(ANIM_PATH_MAX) "s%n",
&pathType, &count, &pause, path, &nextReadPos) >= 4) {
@@ -1460,11 +1476,16 @@
if (shouldStopPlayingPart(part, fadedFramesCount, lastDisplayedProgress)) break;
// Color progress is
- // - the normalized animation progress between [0, 1] for the dynamic coloring part,
+ // - the animation progress, normalized from
+ // [colorTransitionStart,colorTransitionEnd] to [0, 1] for the dynamic coloring
+ // part.
// - 0 for parts that come before,
// - 1 for parts that come after.
float colorProgress = part.useDynamicColoring
- ? (float)j / fcount
+ ? fmin(fmax(
+ ((float)j - animation.colorTransitionStart) /
+ fmax(animation.colorTransitionEnd -
+ animation.colorTransitionStart, 1.0f), 0.0f), 1.0f)
: (part.postDynamicColoring ? 1 : 0);
processDisplayEvents();
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 0e29621..7a597da 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -111,6 +111,8 @@
Font progressFont;
// Controls if dynamic coloring is enabled for the whole animation.
bool dynamicColoringEnabled = false;
+ int colorTransitionStart = 0; // Start frame of dynamic color transition.
+ int colorTransitionEnd = 0; // End frame of dynamic color transition.
float startColors[4][3]; // Start colors of dynamic color transition.
float endColors[4][3]; // End colors of dynamic color transition.
};
diff --git a/core/api/current.txt b/core/api/current.txt
index 2f01265..f67c338 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1194,6 +1194,7 @@
field public static final int requiredFeature = 16844116; // 0x1010554
field public static final int requiredForAllUsers = 16843728; // 0x10103d0
field public static final int requiredNotFeature = 16844117; // 0x1010555
+ field public static final int requiredSplitTypes;
field public static final int requiresFadingEdge = 16843685; // 0x10103a5
field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364
field public static final int resizeClip = 16843983; // 0x10104cf
@@ -1329,6 +1330,7 @@
field public static final int splitMotionEvents = 16843503; // 0x10102ef
field public static final int splitName = 16844105; // 0x1010549
field public static final int splitTrack = 16843852; // 0x101044c
+ field public static final int splitTypes;
field public static final int spotShadowAlpha = 16843967; // 0x10104bf
field public static final int src = 16843033; // 0x1010119
field public static final int ssp = 16843747; // 0x10103e3
@@ -9455,6 +9457,7 @@
method public int getConnectionState(android.bluetooth.BluetoothDevice);
method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
field public static final int A2DP = 2; // 0x2
+ field public static final int CSIP_SET_COORDINATOR = 25; // 0x19
field public static final String EXTRA_PREVIOUS_STATE = "android.bluetooth.profile.extra.PREVIOUS_STATE";
field public static final String EXTRA_STATE = "android.bluetooth.profile.extra.STATE";
field public static final int GATT = 7; // 0x7
@@ -19927,29 +19930,32 @@
}
public class Location implements android.os.Parcelable {
- ctor public Location(String);
- ctor public Location(android.location.Location);
- method public float bearingTo(android.location.Location);
- method public static String convert(double, int);
- method public static double convert(String);
+ ctor public Location(@Nullable String);
+ ctor public Location(@NonNull android.location.Location);
+ method public float bearingTo(@NonNull android.location.Location);
+ method @NonNull public static String convert(@FloatRange double, int);
+ method @FloatRange public static double convert(@NonNull String);
method public int describeContents();
- method public static void distanceBetween(double, double, double, double, float[]);
- method public float distanceTo(android.location.Location);
- method public void dump(android.util.Printer, String);
- method public float getAccuracy();
- method public double getAltitude();
- method public float getBearing();
- method public float getBearingAccuracyDegrees();
- method public long getElapsedRealtimeNanos();
- method public double getElapsedRealtimeUncertaintyNanos();
- method public android.os.Bundle getExtras();
- method public double getLatitude();
- method public double getLongitude();
- method public String getProvider();
- method public float getSpeed();
- method public float getSpeedAccuracyMetersPerSecond();
- method public long getTime();
- method public float getVerticalAccuracyMeters();
+ method public static void distanceBetween(@FloatRange double, @FloatRange double, @FloatRange double, @FloatRange double, float[]);
+ method @FloatRange public float distanceTo(@NonNull android.location.Location);
+ method public void dump(@NonNull android.util.Printer, @Nullable String);
+ method @FloatRange public float getAccuracy();
+ method @FloatRange public double getAltitude();
+ method @FloatRange(from=0.0f, to=360.0f, toInclusive=false) public float getBearing();
+ method @FloatRange public float getBearingAccuracyDegrees();
+ method @IntRange public long getElapsedRealtimeAgeMillis();
+ method @IntRange public long getElapsedRealtimeAgeMillis(@IntRange long);
+ method @IntRange public long getElapsedRealtimeMillis();
+ method @IntRange public long getElapsedRealtimeNanos();
+ method @FloatRange public double getElapsedRealtimeUncertaintyNanos();
+ method @Nullable public android.os.Bundle getExtras();
+ method @FloatRange public double getLatitude();
+ method @FloatRange public double getLongitude();
+ method @Nullable public String getProvider();
+ method @FloatRange public float getSpeed();
+ method @FloatRange public float getSpeedAccuracyMetersPerSecond();
+ method @IntRange public long getTime();
+ method @FloatRange public float getVerticalAccuracyMeters();
method public boolean hasAccuracy();
method public boolean hasAltitude();
method public boolean hasBearing();
@@ -19958,30 +19964,35 @@
method public boolean hasSpeed();
method public boolean hasSpeedAccuracy();
method public boolean hasVerticalAccuracy();
+ method public boolean isComplete();
method @Deprecated public boolean isFromMockProvider();
method public boolean isMock();
- method @Deprecated public void removeAccuracy();
- method @Deprecated public void removeAltitude();
- method @Deprecated public void removeBearing();
- method @Deprecated public void removeSpeed();
+ method public void removeAccuracy();
+ method public void removeAltitude();
+ method public void removeBearing();
+ method public void removeBearingAccuracy();
+ method public void removeElapsedRealtimeUncertaintyNanos();
+ method public void removeSpeed();
+ method public void removeSpeedAccuracy();
+ method public void removeVerticalAccuracy();
method public void reset();
- method public void set(android.location.Location);
- method public void setAccuracy(float);
- method public void setAltitude(double);
- method public void setBearing(float);
- method public void setBearingAccuracyDegrees(float);
- method public void setElapsedRealtimeNanos(long);
- method public void setElapsedRealtimeUncertaintyNanos(double);
+ method public void set(@NonNull android.location.Location);
+ method public void setAccuracy(@FloatRange float);
+ method public void setAltitude(@FloatRange double);
+ method public void setBearing(@FloatRange(fromInclusive=false, toInclusive=false) float);
+ method public void setBearingAccuracyDegrees(@FloatRange float);
+ method public void setElapsedRealtimeNanos(@IntRange long);
+ method public void setElapsedRealtimeUncertaintyNanos(@FloatRange double);
method public void setExtras(@Nullable android.os.Bundle);
- method public void setLatitude(double);
- method public void setLongitude(double);
+ method public void setLatitude(@FloatRange double);
+ method public void setLongitude(@FloatRange double);
method public void setMock(boolean);
- method public void setProvider(String);
- method public void setSpeed(float);
- method public void setSpeedAccuracyMetersPerSecond(float);
- method public void setTime(long);
- method public void setVerticalAccuracyMeters(float);
- method public void writeToParcel(android.os.Parcel, int);
+ method public void setProvider(@Nullable String);
+ method public void setSpeed(@FloatRange float);
+ method public void setSpeedAccuracyMetersPerSecond(@FloatRange float);
+ method public void setTime(@IntRange long);
+ method public void setVerticalAccuracyMeters(@FloatRange float);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.Location> CREATOR;
field public static final int FORMAT_DEGREES = 0; // 0x0
field public static final int FORMAT_MINUTES = 1; // 0x1
@@ -20236,9 +20247,9 @@
field public static final int USAGE_GAME = 14; // 0xe
field public static final int USAGE_MEDIA = 1; // 0x1
field public static final int USAGE_NOTIFICATION = 5; // 0x5
- field public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9; // 0x9
- field public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8; // 0x8
- field public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7; // 0x7
+ field @Deprecated public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9; // 0x9
+ field @Deprecated public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8; // 0x8
+ field @Deprecated public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7; // 0x7
field public static final int USAGE_NOTIFICATION_EVENT = 10; // 0xa
field public static final int USAGE_NOTIFICATION_RINGTONE = 6; // 0x6
field public static final int USAGE_UNKNOWN = 0; // 0x0
@@ -23895,14 +23906,16 @@
}
public class Spatializer {
- method public void addOnSpatializerEnabledChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnSpatializerEnabledChangedListener);
+ method public void addOnSpatializerStateChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnSpatializerStateChangedListener);
method public boolean canBeSpatialized(@NonNull android.media.AudioAttributes, @NonNull android.media.AudioFormat);
+ method public boolean isAvailable();
method public boolean isEnabled();
- method public void removeOnSpatializerEnabledChangedListener(@NonNull android.media.Spatializer.OnSpatializerEnabledChangedListener);
+ method public void removeOnSpatializerStateChangedListener(@NonNull android.media.Spatializer.OnSpatializerStateChangedListener);
}
- public static interface Spatializer.OnSpatializerEnabledChangedListener {
- method public void onSpatializerEnabledChanged(boolean);
+ public static interface Spatializer.OnSpatializerStateChangedListener {
+ method public void onSpatializerAvailableChanged(@NonNull android.media.Spatializer, boolean);
+ method public void onSpatializerEnabledChanged(@NonNull android.media.Spatializer, boolean);
}
public final class SubtitleData {
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 5b72b02..b47c9cf 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -327,6 +327,11 @@
method public static int getNumSignalStrengthLevels();
}
+ public class SubscriptionManager {
+ method public void addSubscriptionInfoRecord(@NonNull String, @Nullable String, int, int);
+ method public void removeSubscriptionInfoRecord(@NonNull String, int);
+ }
+
public class TelephonyManager {
method @NonNull public static int[] getAllNetworkTypes();
}
diff --git a/core/api/removed.txt b/core/api/removed.txt
index bf86422..235de26 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -213,16 +213,6 @@
}
-package android.location {
-
- public class Location implements android.os.Parcelable {
- method @Deprecated public void removeBearingAccuracy();
- method @Deprecated public void removeSpeedAccuracy();
- method @Deprecated public void removeVerticalAccuracy();
- }
-
-}
-
package android.media {
public final class AudioFormat implements android.os.Parcelable {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 6727031..37bbebf 100755
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2107,6 +2107,7 @@
field @NonNull public static final android.os.ParcelUuid AVRCP_TARGET;
field @NonNull public static final android.os.ParcelUuid BASE_UUID;
field @NonNull public static final android.os.ParcelUuid BNEP;
+ field @NonNull public static final android.os.ParcelUuid COORDINATED_SET;
field @NonNull public static final android.os.ParcelUuid DIP;
field @NonNull public static final android.os.ParcelUuid GENERIC_MEDIA_CONTROL;
field @NonNull public static final android.os.ParcelUuid HEARING_AID;
@@ -4960,7 +4961,6 @@
}
public class Location implements android.os.Parcelable {
- method public boolean isComplete();
method public void makeComplete();
method @Deprecated public void setIsFromMockProvider(boolean);
field @Deprecated public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
@@ -5413,9 +5413,10 @@
}
public class Spatializer {
- method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addCompatibleAudioDevice(@NonNull android.media.AudioDeviceAttributes);
- method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getCompatibleAudioDevices();
- method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeCompatibleAudioDevice(@NonNull android.media.AudioDeviceAttributes);
+ method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void addCompatibleAudioDevice(@NonNull android.media.AudioDeviceAttributes);
+ method @NonNull @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public java.util.List<android.media.AudioDeviceAttributes> getCompatibleAudioDevices();
+ method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void removeCompatibleAudioDevice(@NonNull android.media.AudioDeviceAttributes);
+ method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setEnabled(boolean);
}
}
@@ -6197,7 +6198,7 @@
method public void close();
method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration);
method public int flush();
- method public int getId();
+ method @Deprecated public int getId();
method public long getIdLong();
method public int read(@NonNull byte[], long, long);
method public int setDataSource(@Nullable android.media.tv.tuner.filter.Filter);
@@ -6556,7 +6557,8 @@
public static class AnalogFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setAftFlag(int);
- method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setFrequency(int);
+ method @Deprecated @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setFrequency(int);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setFrequencyLong(long);
method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setSifStandard(int);
method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setSignalType(int);
}
@@ -6624,7 +6626,8 @@
method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setBandwidth(int);
method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setDemodOutputFormat(int);
- method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setFrequency(int);
+ method @Deprecated @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setFrequency(int);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setFrequencyLong(long);
method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setPlpSettings(@NonNull android.media.tv.tuner.frontend.Atsc3PlpSettings[]);
}
@@ -6667,7 +6670,8 @@
public static class AtscFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings build();
- method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings.Builder setFrequency(int);
+ method @Deprecated @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings.Builder setFrequency(int);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings.Builder setFrequencyLong(long);
method @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings.Builder setModulation(int);
}
@@ -6727,7 +6731,8 @@
method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setBandwidth(int);
method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setCodeRate(int);
- method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setFrequency(int);
+ method @Deprecated @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setFrequency(int);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setFrequencyLong(long);
method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setGuardInterval(int);
method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setModulation(int);
method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setTimeInterleaveMode(int);
@@ -6791,7 +6796,8 @@
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setAnnex(int);
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setBandwidth(int);
- method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setFrequency(int);
+ method @Deprecated @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setFrequency(int);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setFrequencyLong(long);
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setInnerFec(long);
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setModulation(int);
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setOuterFec(int);
@@ -6879,7 +6885,8 @@
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setCanHandleDiseqcRxMessage(boolean);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setCodeRate(@Nullable android.media.tv.tuner.frontend.DvbsCodeRate);
- method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setFrequency(int);
+ method @Deprecated @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setFrequency(int);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setFrequencyLong(long);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setInputStreamId(int);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setModulation(int);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setPilot(int);
@@ -6988,7 +6995,8 @@
method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setBandwidth(int);
method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setConstellation(int);
- method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setFrequency(int);
+ method @Deprecated @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setFrequency(int);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setFrequencyLong(long);
method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setGuardInterval(int);
method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setHierarchy(int);
method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setHighPriority(boolean);
@@ -7007,9 +7015,11 @@
}
public class FrontendInfo {
- method public int getAcquireRange();
+ method @Deprecated public int getAcquireRange();
+ method public long getAcquireRangeLong();
method public int getExclusiveGroupId();
- method @NonNull public android.util.Range<java.lang.Integer> getFrequencyRange();
+ method @Deprecated @NonNull public android.util.Range<java.lang.Integer> getFrequencyRange();
+ method @NonNull public android.util.Range<java.lang.Long> getFrequencyRangeLong();
method @NonNull public android.media.tv.tuner.frontend.FrontendCapabilities getFrontendCapabilities();
method public int getId();
method @NonNull public int[] getStatusCapabilities();
@@ -7018,11 +7028,14 @@
}
public abstract class FrontendSettings {
- method @IntRange(from=1) public int getEndFrequency();
- method public int getFrequency();
+ method @Deprecated @IntRange(from=1) public int getEndFrequency();
+ method @IntRange(from=1) public long getEndFrequencyLong();
+ method @Deprecated public int getFrequency();
+ method public long getFrequencyLong();
method public int getFrontendSpectralInversion();
method public abstract int getType();
- method @IntRange(from=1) public void setEndFrequency(int);
+ method @Deprecated @IntRange(from=1) public void setEndFrequency(int);
+ method @IntRange(from=1) public void setEndFrequencyLong(long);
method public void setSpectralInversion(int);
field public static final long FEC_11_15 = 4194304L; // 0x400000L
field public static final long FEC_11_20 = 8388608L; // 0x800000L
@@ -7085,7 +7098,8 @@
method @NonNull public int[] getBers();
method @NonNull public int[] getCodeRates();
method @NonNull public int[] getExtendedModulations();
- method public int getFreqOffset();
+ method @Deprecated public int getFreqOffset();
+ method public long getFreqOffsetLong();
method public int getGuardInterval();
method public int getHierarchy();
method public long getInnerFec();
@@ -7201,7 +7215,8 @@
public static class Isdbs3FrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setCodeRate(int);
- method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setFrequency(int);
+ method @Deprecated @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setFrequency(int);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setFrequencyLong(long);
method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setModulation(int);
method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setRolloff(int);
method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setStreamId(int);
@@ -7244,7 +7259,8 @@
public static class IsdbsFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setCodeRate(int);
- method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setFrequency(int);
+ method @Deprecated @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setFrequency(int);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setFrequencyLong(long);
method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setModulation(int);
method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setRolloff(int);
method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setStreamId(int);
@@ -7291,7 +7307,8 @@
method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setBandwidth(int);
method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setCodeRate(int);
- method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setFrequency(int);
+ method @Deprecated @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setFrequency(int);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setFrequencyLong(long);
method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setGuardInterval(int);
method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setMode(int);
method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setModulation(int);
@@ -7311,7 +7328,8 @@
method public default void onDvbcAnnexReported(int);
method public void onDvbsStandardReported(int);
method public void onDvbtStandardReported(int);
- method public void onFrequenciesReported(@NonNull int[]);
+ method public default void onFrequenciesLongReported(@NonNull long[]);
+ method @Deprecated public void onFrequenciesReported(@NonNull int[]);
method public void onGroupIdsReported(@NonNull int[]);
method public void onHierarchyReported(int);
method public void onInputStreamIdsReported(@NonNull int[]);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index b29349e..365493de 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -771,6 +771,11 @@
return procState >= PROCESS_STATE_TRANSIENT_BACKGROUND;
}
+ /** @hide Should this process state be considered in the cache? */
+ public static final boolean isProcStateCached(int procState) {
+ return procState >= PROCESS_STATE_CACHED_ACTIVITY;
+ }
+
/** @hide Is this a foreground service type? */
public static boolean isForegroundService(int procState) {
return procState == PROCESS_STATE_FOREGROUND_SERVICE;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index bff1e57..2e22b92 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -466,11 +466,7 @@
@Override
public int hashCode() {
- return hashCode(authority, userId);
- }
-
- public static int hashCode(final String auth, final int userIdent) {
- return ((auth != null) ? auth.hashCode() : 0) ^ userIdent;
+ return ((authority != null) ? authority.hashCode() : 0) ^ userId;
}
}
@@ -492,7 +488,7 @@
// Note we never removes items from this map but that's okay because there are only so many
// users and so many authorities.
@GuardedBy("mGetProviderKeys")
- final SparseArray<ProviderKey> mGetProviderKeys = new SparseArray<>();
+ final ArrayMap<ProviderKey, ProviderKey> mGetProviderKeys = new ArrayMap<>();
final ArrayMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
= new ArrayMap<Activity, ArrayList<OnActivityPausedListener>>();
@@ -1105,17 +1101,18 @@
IUiAutomationConnection instrumentationUiConnection, int debugMode,
boolean enableBinderTracking, boolean trackAllocation,
boolean isRestrictedBackupMode, boolean persistent, Configuration config,
- CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings,
+ CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
String buildSerial, AutofillOptions autofillOptions,
ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges,
SharedMemory serializedSystemFontMap) {
if (services != null) {
if (false) {
// Test code to make sure the app could see the passed-in services.
- for (String name : services.keySet()) {
- if (services.get(name) == null) {
+ for (Object oname : services.keySet()) {
+ if (services.get(oname) == null) {
continue; // AM just passed in a null service.
}
+ String name = (String) oname;
// See b/79378449 about the following exemption.
switch (name) {
@@ -6991,11 +6988,11 @@
}
private ProviderKey getGetProviderKey(String auth, int userId) {
- final int key = ProviderKey.hashCode(auth, userId);
+ final ProviderKey key = new ProviderKey(auth, userId);
synchronized (mGetProviderKeys) {
ProviderKey lock = mGetProviderKeys.get(key);
if (lock == null) {
- lock = new ProviderKey(auth, userId);
+ lock = key;
mGetProviderKeys.put(key, lock);
}
return lock;
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 2afd98e..d6ff6d3 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -77,7 +77,7 @@
IInstrumentationWatcher testWatcher, IUiAutomationConnection uiAutomationConnection,
int debugMode, boolean enableBinderTracking, boolean trackAllocation,
boolean restrictedBackupMode, boolean persistent, in Configuration config,
- in CompatibilityInfo compatInfo, in Map<String, IBinder> services,
+ in CompatibilityInfo compatInfo, in Map services,
in Bundle coreSettings, in String buildSerial, in AutofillOptions autofillOptions,
in ContentCaptureOptions contentCaptureOptions, in long[] disabledCompatChanges,
in SharedMemory serializedSystemFontMap);
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 1837fb8..6553b61 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -260,8 +260,6 @@
private boolean mDemoted = false;
private boolean mImportantConvo = false;
private long mDeletedTime = DEFAULT_DELETION_TIME_MS;
- // If the sound for this channel is missing, e.g. after restore.
- private boolean mIsSoundMissing;
/**
* Creates a notification channel.
@@ -717,13 +715,6 @@
}
/**
- * @hide
- */
- public boolean isSoundMissing() {
- return mIsSoundMissing;
- }
-
- /**
* Returns the audio attributes for sound played by notifications posted to this channel.
*/
public AudioAttributes getAudioAttributes() {
@@ -1007,9 +998,8 @@
// according to the docs because canonicalize method has to handle canonical uris as well.
Uri canonicalizedUri = contentResolver.canonicalize(uri);
if (canonicalizedUri == null) {
- // We got a null because the uri in the backup does not exist here.
- mIsSoundMissing = true;
- return null;
+ // We got a null because the uri in the backup does not exist here, so we return default
+ return Settings.System.DEFAULT_NOTIFICATION_URI;
}
return contentResolver.uncanonicalize(canonicalizedUri);
}
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 23fc6f3..31c81be 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -115,21 +115,6 @@
"file_patterns": ["(/|^)VoiceInteract[^/]*"]
},
{
- "name": "CtsContentTestCases",
- "options": [
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "include-filter": "android.content.wm.cts"
- }
- ],
- "file_patterns": ["(/|^)ContextImpl.java"]
- },
- {
"name": "CtsOsTestCases",
"options": [
{
@@ -163,6 +148,23 @@
"file_patterns": ["(/|^)ContextImpl.java"]
}
],
+ "presubmit-large": [
+ {
+ "name": "CtsContentTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "include-filter": "android.content.wm.cts"
+ }
+ ],
+ "file_patterns": ["(/|^)ContextImpl.java"]
+ }
+ ],
"postsubmit": [
{
"file_patterns": ["(/|^)ActivityThreadClientTest.java"],
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 1221148..eee981d 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1331,7 +1331,10 @@
if (alias == null) {
return getName();
}
- return alias;
+ return alias
+ .replace('\t', ' ')
+ .replace('\n', ' ')
+ .replace('\r', ' ');
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 5b67a75..0cf9f9f 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -227,12 +227,18 @@
int MCP_SERVER = 24;
/**
+ * Coordinated Set Identification Profile set coordinator
+ *
+ */
+ int CSIP_SET_COORDINATOR = 25;
+
+ /**
* Max profile ID. This value should be updated whenever a new profile is added to match
* the largest value assigned to a profile.
*
* @hide
*/
- int MAX_PROFILE_ID = 24;
+ int MAX_PROFILE_ID = 25;
/**
* Default priority for devices that we try to auto-connect to and
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index ff250e6..67b7252 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -184,6 +184,11 @@
/** @hide */
@NonNull
@SystemApi
+ public static final ParcelUuid COORDINATED_SET =
+ ParcelUuid.fromString("00001846-0000-1000-8000-00805F9B34FB");
+ /** @hide */
+ @NonNull
+ @SystemApi
public static final ParcelUuid BASE_UUID =
ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index f5a0c43..9ff13a47 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3215,7 +3215,7 @@
* apps targeting SDK Version {@link android.os.Build.VERSION_CODES#O}
* or higher are not allowed to start background services from the background.
* See
- * <a href="{@docRoot}/about/versions/oreo/background">
+ * <a href="/about/versions/oreo/background">
* Background Execution Limits</a>
* for more details.
*
@@ -3224,7 +3224,7 @@
* apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S}
* or higher are not allowed to start foreground services from the background.
* See
- * <a href="{@docRoot}/about/versions/12/behavior-changes-12">
+ * <a href="/about/versions/12/behavior-changes-12">
* Behavior changes: Apps targeting Android 12
* </a>
* for more details.
@@ -3278,7 +3278,7 @@
* apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S}
* or higher are not allowed to start foreground services from the background.
* See
- * <a href="{@docRoot}/about/versions/12/behavior-changes-12">
+ * <a href="/about/versions/12/behavior-changes-12">
* Behavior changes: Apps targeting Android 12
* </a>
* for more details.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 546abf8..335e703 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2739,6 +2739,22 @@
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGES_UNSUSPENDED = "android.intent.action.PACKAGES_UNSUSPENDED";
+ /**
+ * Broadcast Action: One of the suspend conditions have been modified for the packages.
+ * <p>Includes the following extras:
+ * <ul>
+ * <li> {@link #EXTRA_CHANGED_PACKAGE_LIST} is the set of packages which have been modified
+ * <li> {@link #EXTRA_CHANGED_UID_LIST} is the set of uids which have been modified
+ * </ul>
+ *
+ * <p class="note">This is a protected intent that can only be sent
+ * by the system. It is only sent to registered receivers.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PACKAGES_SUSPENSION_CHANGED =
+ "android.intent.action.PACKAGES_SUSPENSION_CHANGED";
/**
* Broadcast Action: Distracting packages have been changed.
@@ -4918,6 +4934,12 @@
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_CAR_LAUNCHER = "android.intent.category.CAR_LAUNCHER";
/**
+ * Used to indicate that the activity can be used in communal mode.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+ public static final String CATEGORY_COMMUNAL_MODE = "android.intent.category.COMMUNAL_MODE";
+ /**
* Indicates a Leanback settings activity to be displayed in the Leanback launcher.
* @hide
*/
diff --git a/core/java/android/content/TEST_MAPPING b/core/java/android/content/TEST_MAPPING
index 614143e..7f1d0d1 100644
--- a/core/java/android/content/TEST_MAPPING
+++ b/core/java/android/content/TEST_MAPPING
@@ -1,21 +1,6 @@
{
"presubmit": [
{
- "name": "CtsContentTestCases",
- "options": [
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "include-filter": "android.content.wm.cts"
- }
- ],
- "file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java"]
- },
- {
"name": "CtsOsTestCases",
"options": [
{
@@ -51,5 +36,22 @@
],
"file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java", "(/|^)ComponentCallbacksController.java"]
}
+ ],
+ "presubmit-large": [
+ {
+ "name": "CtsContentTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "include-filter": "android.content.wm.cts"
+ }
+ ],
+ "file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java"]
+ }
]
}
\ No newline at end of file
diff --git a/core/java/android/content/om/TEST_MAPPING b/core/java/android/content/om/TEST_MAPPING
index d8f8854..6185cf6 100644
--- a/core/java/android/content/om/TEST_MAPPING
+++ b/core/java/android/content/om/TEST_MAPPING
@@ -21,7 +21,9 @@
"include-filter": "android.appsecurity.cts.OverlayHostTest"
}
]
- },
+ }
+ ],
+ "presubmit-large": [
{
"name": "CtsContentTestCases",
"options": [
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 95c5612..172a51a 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.TestApi;
import android.app.Activity;
@@ -1368,8 +1369,8 @@
}
/** @hide */
- public void setMaxAspectRatio(float maxAspectRatio) {
- this.mMaxAspectRatio = maxAspectRatio;
+ public void setMaxAspectRatio(@FloatRange(from = 0f) float maxAspectRatio) {
+ this.mMaxAspectRatio = maxAspectRatio >= 0f ? maxAspectRatio : 0f;
}
/** @hide */
@@ -1378,8 +1379,8 @@
}
/** @hide */
- public void setMinAspectRatio(float minAspectRatio) {
- this.mMinAspectRatio = minAspectRatio;
+ public void setMinAspectRatio(@FloatRange(from = 0f) float minAspectRatio) {
+ this.mMinAspectRatio = minAspectRatio >= 0f ? minAspectRatio : 0f;
}
/**
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index 8bc3734..0a69413 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -19,6 +19,16 @@
"name": "CarrierAppIntegrationTestCases"
},
{
+ "name": "CtsIncrementalInstallHostTestCases",
+ "options": [
+ {
+ "include-filter": "android.incrementalinstall.cts.IncrementalFeatureTest"
+ }
+ ]
+ }
+ ],
+ "presubmit-large": [
+ {
"name": "CtsContentTestCases",
"options": [
{
@@ -31,14 +41,6 @@
"include-filter": "android.content.pm.cts"
}
]
- },
- {
- "name": "CtsIncrementalInstallHostTestCases",
- "options": [
- {
- "include-filter": "android.incrementalinstall.cts.IncrementalFeatureTest"
- }
- ]
}
],
"postsubmit": [
diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java
index 024c18c..49d4137 100644
--- a/core/java/android/content/pm/parsing/ApkLite.java
+++ b/core/java/android/content/pm/parsing/ApkLite.java
@@ -19,12 +19,15 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.content.pm.SigningDetails;
import android.content.pm.VerifierInfo;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DataClass;
import java.util.List;
+import java.util.Set;
/**
* Lightweight parsed details about a single APK file.
@@ -44,6 +47,10 @@
private final @Nullable String mUsesSplitName;
/** Name of the split APK that this APK is a configuration for */
private final @Nullable String mConfigForSplit;
+ /** Indicate the types of the required split are necessary for this package to run */
+ private final @Nullable Set<String> mRequiredSplitTypes;
+ /** Split types of this APK */
+ private final @Nullable Set<String> mSplitTypes;
/** Major version number of this package */
private final int mVersionCodeMajor;
@@ -105,9 +112,9 @@
/**
* Indicate the policy to deal with user data when rollback is committed
*
- * @see {@link android.content.pm.PackageManager.RollbackDataPolicy#RESTORE}
- * @see {@link android.content.pm.PackageManager.RollbackDataPolicy#WIPE}
- * @see {@link android.content.pm.PackageManager.RollbackDataPolicy#RETAIN}
+ * @see {@link PackageManager#ROLLBACK_DATA_POLICY_RESTORE}
+ * @see {@link PackageManager#ROLLBACK_DATA_POLICY_WIPE}
+ * @see {@link PackageManager#ROLLBACK_DATA_POLICY_RETAIN}
*/
private final int mRollbackDataPolicy;
@@ -118,14 +125,17 @@
boolean debuggable, boolean profileableByShell, boolean multiArch, boolean use32bitAbi,
boolean useEmbeddedDex, boolean extractNativeLibs, boolean isolatedSplits,
String targetPackageName, boolean overlayIsStatic, int overlayPriority,
- int minSdkVersion, int targetSdkVersion, int rollbackDataPolicy) {
+ int minSdkVersion, int targetSdkVersion, int rollbackDataPolicy,
+ Set<String> requiredSplitTypes, Set<String> splitTypes) {
mPath = path;
mPackageName = packageName;
mSplitName = splitName;
+ mSplitTypes = splitTypes;
mFeatureSplit = isFeatureSplit;
mConfigForSplit = configForSplit;
mUsesSplitName = usesSplitName;
- mSplitRequired = isSplitRequired;
+ mRequiredSplitTypes = requiredSplitTypes;
+ mSplitRequired = (isSplitRequired || hasAnyRequiredSplitTypes());
mVersionCode = versionCode;
mVersionCodeMajor = versionCodeMajor;
mRevisionCode = revisionCode;
@@ -156,9 +166,16 @@
return PackageInfo.composeLongVersionCode(mVersionCodeMajor, mVersionCode);
}
+ /**
+ * Return if requiredSplitTypes presents.
+ */
+ private boolean hasAnyRequiredSplitTypes() {
+ return !CollectionUtils.isEmpty(mRequiredSplitTypes);
+ }
- // Code below generated by codegen v1.0.22.
+
+ // Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -212,6 +229,22 @@
}
/**
+ * Indicate the types of the required split are necessary for this package to run
+ */
+ @DataClass.Generated.Member
+ public @Nullable Set<String> getRequiredSplitTypes() {
+ return mRequiredSplitTypes;
+ }
+
+ /**
+ * Split types of this APK
+ */
+ @DataClass.Generated.Member
+ public @Nullable Set<String> getSplitTypes() {
+ return mSplitTypes;
+ }
+
+ /**
* Major version number of this package
*/
@DataClass.Generated.Member
@@ -388,9 +421,9 @@
/**
* Indicate the policy to deal with user data when rollback is committed
*
- * @see {@link android.content.pm.PackageManager.RollbackDataPolicy#RESTORE}
- * @see {@link android.content.pm.PackageManager.RollbackDataPolicy#WIPE}
- * @see {@link android.content.pm.PackageManager.RollbackDataPolicy#RETAIN}
+ * @see {@link PackageManager#ROLLBACK_DATA_POLICY_RESTORE}
+ * @see {@link PackageManager#ROLLBACK_DATA_POLICY_WIPE}
+ * @see {@link PackageManager#ROLLBACK_DATA_POLICY_RETAIN}
*/
@DataClass.Generated.Member
public int getRollbackDataPolicy() {
@@ -398,10 +431,10 @@
}
@DataClass.Generated(
- time = 1616985847981L,
- codegenVersion = "1.0.22",
+ time = 1628562554893L,
+ codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ApkLite.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final int mRollbackDataPolicy\npublic long getLongVersionCode()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final int mRollbackDataPolicy\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index f727a48..d3b6f2b 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -38,6 +38,7 @@
import android.os.Trace;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Pair;
import android.util.Slog;
@@ -58,6 +59,7 @@
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/** @hide */
public class ApkLiteParseUtils {
@@ -104,8 +106,11 @@
final ApkLite baseApk = result.getResult();
final String packagePath = packageFile.getAbsolutePath();
return input.success(
- new PackageLite(packagePath, baseApk.getPath(), baseApk, null,
- null, null, null, null, null, baseApk.getTargetSdkVersion()));
+ new PackageLite(packagePath, baseApk.getPath(), baseApk, null /* splitNames */,
+ null /* isFeatureSplits */, null /* usesSplitNames */,
+ null /* configForSplit */, null /* splitApkPaths */,
+ null /* splitRevisionCodes */, baseApk.getTargetSdkVersion(),
+ null /* requiredSplitTypes */, null /* splitTypes */));
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -209,6 +214,8 @@
final int size = ArrayUtils.size(splitApks);
String[] splitNames = null;
+ Set<String>[] requiredSplitTypes = null;
+ Set<String>[] splitTypes = null;
boolean[] isFeatureSplits = null;
String[] usesSplitNames = null;
String[] configForSplits = null;
@@ -216,6 +223,8 @@
int[] splitRevisionCodes = null;
if (size > 0) {
splitNames = new String[size];
+ requiredSplitTypes = new Set[size];
+ splitTypes = new Set[size];
isFeatureSplits = new boolean[size];
usesSplitNames = new String[size];
configForSplits = new String[size];
@@ -227,6 +236,8 @@
for (int i = 0; i < size; i++) {
final ApkLite apk = splitApks.get(splitNames[i]);
+ requiredSplitTypes[i] = apk.getRequiredSplitTypes();
+ splitTypes[i] = apk.getSplitTypes();
usesSplitNames[i] = apk.getUsesSplitName();
isFeatureSplits[i] = apk.isFeatureSplit();
configForSplits[i] = apk.getConfigForSplit();
@@ -242,7 +253,7 @@
return input.success(
new PackageLite(codePath, baseCodePath, baseApk, splitNames, isFeatureSplits,
usesSplitNames, configForSplits, splitCodePaths, splitRevisionCodes,
- baseApk.getTargetSdkVersion()));
+ baseApk.getTargetSdkVersion(), requiredSplitTypes, splitTypes));
}
/**
@@ -345,9 +356,15 @@
if (result.isError()) {
return input.error(result);
}
-
Pair<String, String> packageSplit = result.getResult();
+ final ParseResult<Pair<Set<String>, Set<String>>> requiredSplitTypesResult =
+ parseRequiredSplitTypes(input, parser);
+ if (requiredSplitTypesResult.isError()) {
+ return input.error(result);
+ }
+ Pair<Set<String>, Set<String>> requiredSplitTypes = requiredSplitTypesResult.getResult();
+
int installLocation = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE,
"installLocation", PARSE_DEFAULT_INSTALL_LOCATION);
int versionCode = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE, "versionCode", 0);
@@ -522,7 +539,7 @@
coreApp, debuggable, profilableByShell, multiArch, use32bitAbi,
useEmbeddedDex, extractNativeLibs, isolatedSplits, targetPackage,
overlayIsStatic, overlayPriority, minSdkVersion, targetSdkVersion,
- rollbackDataPolicy));
+ rollbackDataPolicy, requiredSplitTypes.first, requiredSplitTypes.second));
}
public static ParseResult<Pair<String, String>> parsePackageSplitNames(ParseInput input,
@@ -567,6 +584,54 @@
(splitName != null) ? splitName.intern() : splitName));
}
+ /**
+ * Utility method that parses attributes android:requiredSplitTypes and android:splitTypes.
+ */
+ public static ParseResult<Pair<Set<String>, Set<String>>> parseRequiredSplitTypes(
+ ParseInput input, XmlResourceParser parser) {
+ Set<String> requiredSplitTypes = null;
+ Set<String> splitTypes = null;
+ String value = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "requiredSplitTypes");
+ if (!TextUtils.isEmpty(value)) {
+ final ParseResult<Set<String>> result = separateAndValidateSplitTypes(input, value);
+ if (result.isError()) {
+ return input.error(result);
+ }
+ requiredSplitTypes = result.getResult();
+ }
+
+ value = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "splitTypes");
+ if (!TextUtils.isEmpty(value)) {
+ final ParseResult<Set<String>> result = separateAndValidateSplitTypes(input, value);
+ if (result.isError()) {
+ return input.error(result);
+ }
+ splitTypes = result.getResult();
+ }
+
+ return input.success(Pair.create(requiredSplitTypes, splitTypes));
+ }
+
+ private static ParseResult<Set<String>> separateAndValidateSplitTypes(ParseInput input,
+ String values) {
+ final Set<String> ret = new ArraySet<>();
+ for (String value : values.trim().split(",")) {
+ final String type = value.trim();
+ // Using requireFilename as true because it limits length of the name to the
+ // {@link #MAX_FILE_NAME_SIZE}.
+ final ParseResult<?> nameResult = validateName(input, type,
+ false /* requireSeparator */, true /* requireFilename */);
+ if (nameResult.isError()) {
+ return input.error(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "Invalid manifest split types: " + nameResult.getErrorMessage());
+ }
+ if (!ret.add(type)) {
+ Slog.w(TAG, type + " was defined multiple times");
+ }
+ }
+ return input.success(ret);
+ }
+
public static VerifierInfo parseVerifier(AttributeSet attrs) {
String packageName = attrs.getAttributeValue(ANDROID_RES_NAMESPACE, "name");
String encodedPublicKey = attrs.getAttributeValue(ANDROID_RES_NAMESPACE, "publicKey");
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index f2a6a5c..dfc1886a 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -531,10 +531,8 @@
ai.lockTaskLaunchMode = a.getLockTaskLaunchMode();
ai.screenOrientation = a.getScreenOrientation();
ai.resizeMode = a.getResizeMode();
- Float maxAspectRatio = a.getMaxAspectRatio();
- ai.setMaxAspectRatio(maxAspectRatio != null ? maxAspectRatio : 0f);
- Float minAspectRatio = a.getMinAspectRatio();
- ai.setMinAspectRatio(minAspectRatio != null ? minAspectRatio : 0f);
+ ai.setMaxAspectRatio(a.getMaxAspectRatio());
+ ai.setMinAspectRatio(a.getMinAspectRatio());
ai.supportsSizeChanges = a.isSupportsSizeChanges();
ai.requestedVrComponent = a.getRequestedVrComponent();
ai.rotationAnimation = a.getRotationAnimation();
diff --git a/core/java/android/content/pm/parsing/PackageLite.java b/core/java/android/content/pm/parsing/PackageLite.java
index 9172555..5f5e812 100644
--- a/core/java/android/content/pm/parsing/PackageLite.java
+++ b/core/java/android/content/pm/parsing/PackageLite.java
@@ -22,11 +22,13 @@
import android.content.pm.VerifierInfo;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DataClass;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
/**
* Lightweight parsed details about a single package.
@@ -52,6 +54,12 @@
/** Dependencies of any split APKs, ordered by parsed splitName */
private final @Nullable String[] mUsesSplitNames;
private final @Nullable String[] mConfigForSplit;
+ /** Indicate the types of the required split are necessary for base APK to run */
+ private final @Nullable Set<String> mBaseRequiredSplitTypes;
+ /** Indicate the types of the required split are necessary for split APKs to run */
+ private final @Nullable Set<String>[] mRequiredSplitTypes;
+ /** Split type of any split APKs, ordered by parsed splitName */
+ private final @Nullable Set<String>[] mSplitTypes;
/** Major and minor version number of this package */
private final int mVersionCodeMajor;
private final int mVersionCode;
@@ -101,7 +109,7 @@
public PackageLite(String path, String baseApkPath, ApkLite baseApk,
String[] splitNames, boolean[] isFeatureSplits, String[] usesSplitNames,
String[] configForSplit, String[] splitApkPaths, int[] splitRevisionCodes,
- int targetSdk) {
+ int targetSdk, Set<String>[] requiredSplitTypes, Set<String>[] splitTypes) {
// The following paths may be different from the path in ApkLite because we
// move or rename the APK files. Use parameters to indicate the correct paths.
mPath = path;
@@ -119,9 +127,12 @@
mExtractNativeLibs = baseApk.isExtractNativeLibs();
mIsolatedSplits = baseApk.isIsolatedSplits();
mUseEmbeddedDex = baseApk.isUseEmbeddedDex();
- mSplitRequired = baseApk.isSplitRequired();
+ mBaseRequiredSplitTypes = baseApk.getRequiredSplitTypes();
+ mRequiredSplitTypes = requiredSplitTypes;
+ mSplitRequired = (baseApk.isSplitRequired() || hasAnyRequiredSplitTypes());
mProfileableByShell = baseApk.isProfileableByShell();
mSplitNames = splitNames;
+ mSplitTypes = splitTypes;
mIsFeatureSplits = isFeatureSplits;
mUsesSplitNames = usesSplitNames;
mConfigForSplit = configForSplit;
@@ -150,9 +161,19 @@
return PackageInfo.composeLongVersionCode(mVersionCodeMajor, mVersionCode);
}
+ /**
+ * Return if requiredSplitTypes presents in the package.
+ */
+ private boolean hasAnyRequiredSplitTypes() {
+ if (!CollectionUtils.isEmpty(mBaseRequiredSplitTypes)) {
+ return true;
+ }
+ return ArrayUtils.find(mRequiredSplitTypes, r -> !CollectionUtils.isEmpty(r)) != null;
+ }
- // Code below generated by codegen v1.0.22.
+
+ // Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -221,6 +242,30 @@
}
/**
+ * Indicate the types of the required split are necessary for base APK to run
+ */
+ @DataClass.Generated.Member
+ public @Nullable Set<String> getBaseRequiredSplitTypes() {
+ return mBaseRequiredSplitTypes;
+ }
+
+ /**
+ * Indicate the types of the required split are necessary for split APKs to run
+ */
+ @DataClass.Generated.Member
+ public @Nullable Set<String>[] getRequiredSplitTypes() {
+ return mRequiredSplitTypes;
+ }
+
+ /**
+ * Split type of any split APKs, ordered by parsed splitName
+ */
+ @DataClass.Generated.Member
+ public @Nullable Set<String>[] getSplitTypes() {
+ return mSplitTypes;
+ }
+
+ /**
* Major and minor version number of this package
*/
@DataClass.Generated.Member
@@ -357,10 +402,10 @@
}
@DataClass.Generated(
- time = 1615914120261L,
- codegenVersion = "1.0.22",
+ time = 1628562559343L,
+ codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/PackageLite.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\npublic java.util.List<java.lang.String> getAllApkPaths()\npublic long getLongVersionCode()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\npublic java.util.List<java.lang.String> getAllApkPaths()\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 80befcd..115ba50 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -150,6 +150,7 @@
public static final boolean DEBUG_JAR = false;
public static final boolean DEBUG_BACKUP = false;
public static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f;
+ public static final float ASPECT_RATIO_NOT_SET = -1f;
/** File name in an APK for the Android manifest. */
public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
@@ -234,7 +235,7 @@
* For those names would be used as a part of the file name. Limits size to 223 and reserves 32
* for the OS.
*/
- private static final int MAX_FILE_NAME_SIZE = 223;
+ static final int MAX_FILE_NAME_SIZE = 223;
/**
* @see #parseDefault(ParseInput, File, int, List, boolean)
@@ -2096,7 +2097,7 @@
pkg.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestApplication_gwpAsanMode, -1));
pkg.setMemtagMode(sa.getInt(R.styleable.AndroidManifestApplication_memtagMode, -1));
if (sa.hasValue(R.styleable.AndroidManifestApplication_nativeHeapZeroInitialized)) {
- Boolean v = sa.getBoolean(
+ final boolean v = sa.getBoolean(
R.styleable.AndroidManifestApplication_nativeHeapZeroInitialized, false);
pkg.setNativeHeapZeroInitialized(
v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED);
@@ -2677,7 +2678,7 @@
for (int index = 0; index < activitiesSize; index++) {
ParsedActivity activity = activities.get(index);
// If the max aspect ratio for the activity has already been set, skip.
- if (activity.getMaxAspectRatio() != null) {
+ if (activity.getMaxAspectRatio() != ASPECT_RATIO_NOT_SET) {
continue;
}
@@ -2706,7 +2707,7 @@
int activitiesSize = activities.size();
for (int index = 0; index < activitiesSize; index++) {
ParsedActivity activity = activities.get(index);
- if (activity.getMinAspectRatio() == null) {
+ if (activity.getMinAspectRatio() == ASPECT_RATIO_NOT_SET) {
activity.setMinAspectRatio(activity.getResizeMode(), minAspectRatio);
}
}
diff --git a/core/java/android/content/pm/parsing/ParsingUtils.java b/core/java/android/content/pm/parsing/ParsingUtils.java
index 289716a..f3a1740 100644
--- a/core/java/android/content/pm/parsing/ParsingUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingUtils.java
@@ -41,6 +41,8 @@
public static final int DEFAULT_MIN_SDK_VERSION = 1;
public static final int DEFAULT_TARGET_SDK_VERSION = 0;
+ public static final int NOT_SET = -1;
+
@Nullable
public static String buildClassName(String pkg, CharSequence clsSeq) {
if (clsSeq == null || clsSeq.length() <= 0) {
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivity.java b/core/java/android/content/pm/parsing/component/ParsedActivity.java
index adb6b76..8ca86f1 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivity.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivity.java
@@ -21,6 +21,7 @@
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
+import static android.content.pm.parsing.ParsingPackageUtils.ASPECT_RATIO_NOT_SET;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
import android.annotation.NonNull;
@@ -67,11 +68,8 @@
private int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
private int resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
- @Nullable
- private Float maxAspectRatio;
-
- @Nullable
- private Float minAspectRatio;
+ private float maxAspectRatio = ASPECT_RATIO_NOT_SET;
+ private float minAspectRatio = ASPECT_RATIO_NOT_SET;
private boolean supportsSizeChanges;
@@ -234,7 +232,7 @@
return this;
}
- public ParsedActivity setMaxAspectRatio(Float maxAspectRatio) {
+ public ParsedActivity setMaxAspectRatio(float maxAspectRatio) {
this.maxAspectRatio = maxAspectRatio;
return this;
}
@@ -260,7 +258,7 @@
return this;
}
- public ParsedActivity setMinAspectRatio(Float minAspectRatio) {
+ public ParsedActivity setMinAspectRatio(float minAspectRatio) {
this.minAspectRatio = minAspectRatio;
return this;
}
@@ -375,8 +373,8 @@
dest.writeInt(this.lockTaskLaunchMode);
dest.writeInt(this.screenOrientation);
dest.writeInt(this.resizeMode);
- dest.writeValue(this.maxAspectRatio);
- dest.writeValue(this.minAspectRatio);
+ dest.writeFloat(this.maxAspectRatio);
+ dest.writeFloat(this.minAspectRatio);
dest.writeBoolean(this.supportsSizeChanges);
dest.writeString(this.requestedVrComponent);
dest.writeInt(this.rotationAnimation);
@@ -412,8 +410,8 @@
this.lockTaskLaunchMode = in.readInt();
this.screenOrientation = in.readInt();
this.resizeMode = in.readInt();
- this.maxAspectRatio = (Float) in.readValue(Float.class.getClassLoader());
- this.minAspectRatio = (Float) in.readValue(Float.class.getClassLoader());
+ this.maxAspectRatio = in.readFloat();
+ this.minAspectRatio = in.readFloat();
this.supportsSizeChanges = in.readBoolean();
this.requestedVrComponent = in.readString();
this.rotationAnimation = in.readInt();
@@ -505,13 +503,11 @@
return resizeMode;
}
- @Nullable
- public Float getMaxAspectRatio() {
+ public float getMaxAspectRatio() {
return maxAspectRatio;
}
- @Nullable
- public Float getMinAspectRatio() {
+ public float getMinAspectRatio() {
return minAspectRatio;
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index ac6bcd0..dc7eb80 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -18,6 +18,7 @@
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE_PER_TASK;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.parsing.ParsingUtils.NOT_SET;
import static android.content.pm.parsing.component.ComponentParseUtils.flag;
import android.annotation.NonNull;
@@ -269,15 +270,15 @@
activity, tag, null, pkg, sa, 0, useRoundIcon, input,
R.styleable.AndroidManifestActivityAlias_banner,
R.styleable.AndroidManifestActivityAlias_description,
- null /*directBootAwareAttr*/,
+ NOT_SET /*directBootAwareAttr*/,
R.styleable.AndroidManifestActivityAlias_enabled,
R.styleable.AndroidManifestActivityAlias_icon,
R.styleable.AndroidManifestActivityAlias_label,
R.styleable.AndroidManifestActivityAlias_logo,
R.styleable.AndroidManifestActivityAlias_name,
- null /*processAttr*/,
+ NOT_SET /*processAttr*/,
R.styleable.AndroidManifestActivityAlias_roundIcon,
- null /*splitNameAttr*/,
+ NOT_SET /*splitNameAttr*/,
R.styleable.AndroidManifestActivityAlias_attributionTags);
if (result.isError()) {
return result;
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
index ab596d3..6d798fd 100644
--- a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
@@ -16,8 +16,9 @@
package android.content.pm.parsing.component;
+import static android.content.pm.parsing.ParsingUtils.NOT_SET;
+
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.Property;
import android.content.pm.parsing.ParsingPackage;
@@ -41,9 +42,8 @@
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
static <Component extends ParsedComponent> ParseResult<Component> parseComponent(
Component component, String tag, ParsingPackage pkg, TypedArray array,
- boolean useRoundIcon, ParseInput input, int bannerAttr,
- @Nullable Integer descriptionAttr, int iconAttr, int labelAttr, int logoAttr,
- int nameAttr, int roundIconAttr) {
+ boolean useRoundIcon, ParseInput input, int bannerAttr, int descriptionAttr,
+ int iconAttr, int labelAttr, int logoAttr, int nameAttr, int roundIconAttr) {
String name = array.getNonConfigurationString(nameAttr, 0);
if (TextUtils.isEmpty(name)) {
return input.error(tag + " does not specify android:name");
@@ -81,7 +81,7 @@
component.setBanner(bannerVal);
}
- if (descriptionAttr != null) {
+ if (descriptionAttr != NOT_SET) {
component.setDescriptionRes(array.getResourceId(descriptionAttr, 0));
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java b/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java
index 5977c83..f122fd6 100644
--- a/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java
@@ -16,15 +16,17 @@
package android.content.pm.parsing.component;
+import static android.content.pm.parsing.ParsingUtils.NOT_SET;
+
import android.annotation.NonNull;
import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import com.android.internal.R;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseResult;
import org.xmlpull.v1.XmlPullParserException;
@@ -46,7 +48,7 @@
ParseResult<ParsedInstrumentation> result = ParsedComponentUtils.parseComponent(
instrumentation, tag, pkg, sa, useRoundIcon, input,
R.styleable.AndroidManifestInstrumentation_banner,
- null /*descriptionAttr*/,
+ NOT_SET /*descriptionAttr*/,
R.styleable.AndroidManifestInstrumentation_icon,
R.styleable.AndroidManifestInstrumentation_label,
R.styleable.AndroidManifestInstrumentation_logo,
diff --git a/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
index 7ccca93..869e81c 100644
--- a/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
@@ -16,8 +16,9 @@
package android.content.pm.parsing.component;
+import static android.content.pm.parsing.ParsingUtils.NOT_SET;
+
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.IntentFilter;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.ParsingUtils;
@@ -45,11 +46,10 @@
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
static <Component extends ParsedMainComponent> ParseResult<Component> parseMainComponent(
Component component, String tag, String[] separateProcesses, ParsingPackage pkg,
- TypedArray array, int flags, boolean useRoundIcon, ParseInput input,
- int bannerAttr, int descriptionAttr, @Nullable Integer directBootAwareAttr,
- @Nullable Integer enabledAttr, int iconAttr, int labelAttr, int logoAttr, int nameAttr,
- @Nullable Integer processAttr, int roundIconAttr, @Nullable Integer splitNameAttr,
- @Nullable Integer attributionTagsAttr) {
+ TypedArray array, int flags, boolean useRoundIcon, ParseInput input, int bannerAttr,
+ int descriptionAttr, int directBootAwareAttr, int enabledAttr, int iconAttr,
+ int labelAttr, int logoAttr, int nameAttr, int processAttr, int roundIconAttr,
+ int splitNameAttr, int attributionTagsAttr) {
ParseResult<Component> result = ParsedComponentUtils.parseComponent(component, tag, pkg,
array, useRoundIcon, input, bannerAttr, descriptionAttr, iconAttr, labelAttr,
logoAttr, nameAttr, roundIconAttr);
@@ -57,18 +57,18 @@
return result;
}
- if (directBootAwareAttr != null) {
+ if (directBootAwareAttr != NOT_SET) {
component.setDirectBootAware(array.getBoolean(directBootAwareAttr, false));
if (component.isDirectBootAware()) {
pkg.setPartiallyDirectBootAware(true);
}
}
- if (enabledAttr != null) {
+ if (enabledAttr != NOT_SET) {
component.setEnabled(array.getBoolean(enabledAttr, true));
}
- if (processAttr != null) {
+ if (processAttr != NOT_SET) {
CharSequence processName;
if (pkg.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) {
processName = array.getNonConfigurationString(processAttr,
@@ -91,11 +91,11 @@
component.setProcessName(processNameResult.getResult());
}
- if (splitNameAttr != null) {
+ if (splitNameAttr != NOT_SET) {
component.setSplitName(array.getNonConfigurationString(splitNameAttr, 0));
}
- if (attributionTagsAttr != null) {
+ if (attributionTagsAttr != NOT_SET) {
final String attributionTags = array.getNonConfigurationString(attributionTagsAttr, 0);
if (attributionTags != null) {
component.setAttributionTags(attributionTags.split("\\|"));
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
index eec333c..5a7a5ef 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
@@ -16,6 +16,8 @@
package android.content.pm.parsing.component;
+import static android.content.pm.parsing.ParsingUtils.NOT_SET;
+
import android.annotation.NonNull;
import android.content.pm.PermissionInfo;
import android.content.pm.parsing.ParsingPackage;
@@ -163,7 +165,7 @@
result = ParsedComponentUtils.parseComponent(
permission, tag, pkg, sa, useRoundIcon, input,
R.styleable.AndroidManifestPermissionTree_banner,
- null /*descriptionAttr*/,
+ NOT_SET /*descriptionAttr*/,
R.styleable.AndroidManifestPermissionTree_icon,
R.styleable.AndroidManifestPermissionTree_label,
R.styleable.AndroidManifestPermissionTree_logo,
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
index d4e19af..54dd295 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
@@ -101,7 +101,7 @@
proc.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestProcess_gwpAsanMode, -1));
proc.setMemtagMode(sa.getInt(R.styleable.AndroidManifestProcess_memtagMode, -1));
if (sa.hasValue(R.styleable.AndroidManifestProcess_nativeHeapZeroInitialized)) {
- Boolean v = sa.getBoolean(
+ final boolean v = sa.getBoolean(
R.styleable.AndroidManifestProcess_nativeHeapZeroInitialized, false);
proc.setNativeHeapZeroInitialized(
v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED);
diff --git a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
index 324612d..1a3fc85 100644
--- a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
+++ b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
@@ -16,6 +16,8 @@
package android.content.pm.parsing.result;
+import static android.content.pm.parsing.ParsingUtils.NOT_SET;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -62,7 +64,7 @@
private ArrayMap<Long, String> mDeferredErrors = null;
private String mPackageName;
- private Integer mTargetSdkVersion;
+ private int mTargetSdkVersion = NOT_SET;
/**
* Specifically for {@link PackageManager#getPackageArchiveInfo(String, int)} where
@@ -119,7 +121,7 @@
// how many APKs they're going through.
mDeferredErrors.erase();
}
- mTargetSdkVersion = null;
+ mTargetSdkVersion = NOT_SET;
return this;
}
@@ -139,7 +141,7 @@
if (DEBUG_THROW_ALL_ERRORS) {
return error(parseError);
}
- if (mTargetSdkVersion != null) {
+ if (mTargetSdkVersion != NOT_SET) {
if (mDeferredErrors != null && mDeferredErrors.containsKey(deferredError)) {
// If the map already contains the key, that means it's already been checked and
// found to be disabled. Otherwise it would've failed when mTargetSdkVersion was
diff --git a/core/java/android/content/res/TEST_MAPPING b/core/java/android/content/res/TEST_MAPPING
index c02af59..535afd36 100644
--- a/core/java/android/content/res/TEST_MAPPING
+++ b/core/java/android/content/res/TEST_MAPPING
@@ -2,7 +2,9 @@
"presubmit": [
{
"name": "CtsResourcesLoaderTests"
- },
+ }
+ ],
+ "presubmit-large": [
{
"name": "CtsContentTestCases",
"options": [
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 3f3db29..c8c122d 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -438,9 +438,16 @@
}
private class OnAuthenticationCancelListener implements CancellationSignal.OnCancelListener {
+ private final long mAuthRequestId;
+
+ OnAuthenticationCancelListener(long id) {
+ mAuthRequestId = id;
+ }
+
@Override
public void onCancel() {
- cancelAuthentication();
+ Log.d(TAG, "Cancel BP authentication requested for: " + mAuthRequestId);
+ cancelAuthentication(mAuthRequestId);
}
}
@@ -853,10 +860,12 @@
* @param userId The user to authenticate
* @param operationId The keystore operation associated with authentication
*
+ * @return A requestId that can be used to cancel this operation.
+ *
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
- public void authenticateUserForOperation(
+ public long authenticateUserForOperation(
@NonNull CancellationSignal cancel,
@NonNull @CallbackExecutor Executor executor,
@NonNull AuthenticationCallback callback,
@@ -871,7 +880,8 @@
if (callback == null) {
throw new IllegalArgumentException("Must supply a callback");
}
- authenticateInternal(operationId, cancel, executor, callback, userId);
+
+ return authenticateInternal(operationId, cancel, executor, callback, userId);
}
/**
@@ -1002,10 +1012,10 @@
authenticateInternal(null /* crypto */, cancel, executor, callback, mContext.getUserId());
}
- private void cancelAuthentication() {
+ private void cancelAuthentication(long requestId) {
if (mService != null) {
try {
- mService.cancelAuthentication(mToken, mContext.getOpPackageName());
+ mService.cancelAuthentication(mToken, mContext.getOpPackageName(), requestId);
} catch (RemoteException e) {
Log.e(TAG, "Unable to cancel authentication", e);
}
@@ -1024,7 +1034,7 @@
authenticateInternal(operationId, cancel, executor, callback, userId);
}
- private void authenticateInternal(
+ private long authenticateInternal(
long operationId,
@NonNull CancellationSignal cancel,
@NonNull @CallbackExecutor Executor executor,
@@ -1040,9 +1050,7 @@
try {
if (cancel.isCanceled()) {
Log.w(TAG, "Authentication already canceled");
- return;
- } else {
- cancel.setOnCancelListener(new OnAuthenticationCancelListener());
+ return -1;
}
mExecutor = executor;
@@ -1065,14 +1073,16 @@
promptInfo = mPromptInfo;
}
- mService.authenticate(mToken, operationId, userId, mBiometricServiceReceiver,
- mContext.getOpPackageName(), promptInfo);
-
+ final long authId = mService.authenticate(mToken, operationId, userId,
+ mBiometricServiceReceiver, mContext.getOpPackageName(), promptInfo);
+ cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId));
+ return authId;
} catch (RemoteException e) {
Log.e(TAG, "Remote exception while authenticating", e);
mExecutor.execute(() -> callback.onAuthenticationError(
BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE,
mContext.getString(R.string.biometric_error_hw_unavailable)));
+ return -1;
}
}
diff --git a/core/java/android/hardware/biometrics/IAuthService.aidl b/core/java/android/hardware/biometrics/IAuthService.aidl
index 4c2a9ae..91f794c 100644
--- a/core/java/android/hardware/biometrics/IAuthService.aidl
+++ b/core/java/android/hardware/biometrics/IAuthService.aidl
@@ -41,13 +41,14 @@
// Retrieve the package where BIometricOrompt's UI is implemented
String getUiPackage();
- // Requests authentication. The service choose the appropriate biometric to use, and show
- // the corresponding BiometricDialog.
- void authenticate(IBinder token, long sessionId, int userId,
+ // Requests authentication. The service chooses the appropriate biometric to use, and shows
+ // the corresponding BiometricDialog. A requestId is returned that can be used to cancel
+ // this operation.
+ long authenticate(IBinder token, long sessionId, int userId,
IBiometricServiceReceiver receiver, String opPackageName, in PromptInfo promptInfo);
- // Cancel authentication for the given sessionId
- void cancelAuthentication(IBinder token, String opPackageName);
+ // Cancel authentication for the given requestId.
+ void cancelAuthentication(IBinder token, String opPackageName, long requestId);
// TODO(b/141025588): Make userId the first arg to be consistent with hasEnrolledBiometrics.
// Checks if biometrics can be used.
diff --git a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
index 876513f..addd622 100644
--- a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
@@ -48,13 +48,13 @@
// startPreparedClient().
void prepareForAuthentication(boolean requireConfirmation, IBinder token, long operationId,
int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName,
- int cookie, boolean allowBackgroundAuthentication);
+ long requestId, int cookie, boolean allowBackgroundAuthentication);
// Starts authentication with the previously prepared client.
void startPreparedClient(int cookie);
- // Cancels authentication.
- void cancelAuthenticationFromService(IBinder token, String opPackageName);
+ // Cancels authentication for the given requestId.
+ void cancelAuthenticationFromService(IBinder token, String opPackageName, long requestId);
// Determine if HAL is loaded and ready
boolean isHardwareDetected(String opPackageName);
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index 64b5118..2c3c8c3 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -36,13 +36,14 @@
// Retrieve static sensor properties for all biometric sensors
List<SensorPropertiesInternal> getSensorProperties(String opPackageName);
- // Requests authentication. The service choose the appropriate biometric to use, and show
- // the corresponding BiometricDialog.
- void authenticate(IBinder token, long operationId, int userId,
+ // Requests authentication. The service chooses the appropriate biometric to use, and shows
+ // the corresponding BiometricDialog. A requestId is returned that can be used to cancel
+ // this operation.
+ long authenticate(IBinder token, long operationId, int userId,
IBiometricServiceReceiver receiver, String opPackageName, in PromptInfo promptInfo);
- // Cancel authentication for the given session.
- void cancelAuthentication(IBinder token, String opPackageName);
+ // Cancel authentication for the given requestId.
+ void cancelAuthentication(IBinder token, String opPackageName, long requestId);
// Checks if biometrics can be used.
int canAuthenticate(String opPackageName, int userId, int callingUserId, int authenticators);
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index b55a1cb..eab9d8b 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1022,7 +1022,8 @@
* <p>which define a transform from input sensor colors, <code>P_in = [ r g b ]</code>,
* to output linear sRGB, <code>P_out = [ r' g' b' ]</code>,</p>
* <p>with colors as follows:</p>
- * <pre><code>r' = I0r + I1g + I2b
+ * <pre><code>
+ * r' = I0r + I1g + I2b
* g' = I3r + I4g + I5b
* b' = I6r + I7g + I8b
* </code></pre>
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index fc728a2..4708f3e 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -104,6 +104,9 @@
private SparseArray<CaptureCallbackHolder> mCaptureCallbackMap =
new SparseArray<CaptureCallbackHolder>();
+ /** map request IDs which have batchedOutputs to requestCount*/
+ private HashMap<Integer, Integer> mBatchOutputMap = new HashMap<>();
+
private int mRepeatingRequestId = REQUEST_ID_NONE;
// Latest repeating request list's types
private int[] mRepeatingRequestTypes;
@@ -973,6 +976,7 @@
mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(REQUEST_ID_NONE, null);
mIdle = true;
mCaptureCallbackMap = new SparseArray<CaptureCallbackHolder>();
+ mBatchOutputMap = new HashMap<>();
mFrameNumberTracker = new FrameNumberTracker();
mCurrentSession.closeWithoutDraining();
@@ -1179,6 +1183,41 @@
return requestTypes;
}
+ private boolean hasBatchedOutputs(List<CaptureRequest> requestList) {
+ boolean hasBatchedOutputs = true;
+ for (int i = 0; i < requestList.size(); i++) {
+ CaptureRequest request = requestList.get(i);
+ if (!request.isPartOfCRequestList()) {
+ hasBatchedOutputs = false;
+ break;
+ }
+ if (i == 0) {
+ Collection<Surface> targets = request.getTargets();
+ if (targets.size() != 2) {
+ hasBatchedOutputs = false;
+ break;
+ }
+ }
+ }
+ return hasBatchedOutputs;
+ }
+
+ private void updateTracker(int requestId, long frameNumber,
+ int requestType, CaptureResult result, boolean isPartialResult) {
+ int requestCount = 1;
+ // If the request has batchedOutputs update each frame within the batch.
+ if (mBatchOutputMap.containsKey(requestId)) {
+ requestCount = mBatchOutputMap.get(requestId);
+ for (int i = 0; i < requestCount; i++) {
+ mFrameNumberTracker.updateTracker(frameNumber - (requestCount - 1 - i),
+ result, isPartialResult, requestType);
+ }
+ } else {
+ mFrameNumberTracker.updateTracker(frameNumber, result,
+ isPartialResult, requestType);
+ }
+ }
+
private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback,
Executor executor, boolean repeating) throws CameraAccessException {
@@ -1224,6 +1263,14 @@
request.recoverStreamIdToSurface();
}
+ // If the request has batched outputs, then store the
+ // requestCount and requestId in the map.
+ boolean hasBatchedOutputs = hasBatchedOutputs(requestList);
+ if (hasBatchedOutputs) {
+ int requestCount = requestList.size();
+ mBatchOutputMap.put(requestInfo.getRequestId(), requestCount);
+ }
+
if (callback != null) {
mCaptureCallbackMap.put(requestInfo.getRequestId(),
new CaptureCallbackHolder(
@@ -1839,8 +1886,18 @@
if (DEBUG) {
Log.v(TAG, String.format("got error frame %d", frameNumber));
}
- mFrameNumberTracker.updateTracker(frameNumber,
- /*error*/true, request.getRequestType());
+
+ // Update FrameNumberTracker for every frame during HFR mode.
+ if (mBatchOutputMap.containsKey(requestId)) {
+ for (int i = 0; i < mBatchOutputMap.get(requestId); i++) {
+ mFrameNumberTracker.updateTracker(frameNumber - (subsequenceId - i),
+ /*error*/true, request.getRequestType());
+ }
+ } else {
+ mFrameNumberTracker.updateTracker(frameNumber,
+ /*error*/true, request.getRequestType());
+ }
+
checkAndFireSequenceComplete();
// Dispatch the failure callback
@@ -2023,7 +2080,6 @@
public void onResultReceived(CameraMetadataNative result,
CaptureResultExtras resultExtras, PhysicalCaptureResultInfo physicalResults[])
throws RemoteException {
-
int requestId = resultExtras.getRequestId();
long frameNumber = resultExtras.getFrameNumber();
@@ -2064,8 +2120,8 @@
+ frameNumber);
}
- mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult,
- requestType);
+ updateTracker(requestId, frameNumber, requestType, /*result*/null,
+ isPartialResult);
return;
}
@@ -2077,8 +2133,9 @@
+ frameNumber);
}
- mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult,
- requestType);
+ updateTracker(requestId, frameNumber, requestType, /*result*/null,
+ isPartialResult);
+
return;
}
@@ -2184,9 +2241,7 @@
Binder.restoreCallingIdentity(ident);
}
- // Collect the partials for a total result; or mark the frame as totally completed
- mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult,
- requestType);
+ updateTracker(requestId, frameNumber, requestType, finalResult, isPartialResult);
// Fire onCaptureSequenceCompleted
if (!isPartialResult) {
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 3e15c0ee..2025f40 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -361,6 +361,11 @@
for (int i = 0; i < numListeners; i++) {
mask |= mDisplayListeners.get(i).mEventsMask;
}
+ if (mDispatchNativeCallbacks) {
+ mask |= DisplayManager.EVENT_FLAG_DISPLAY_ADDED
+ | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED;
+ }
return mask;
}
@@ -1075,12 +1080,17 @@
private static native void nSignalNativeCallbacks(float refreshRate);
- // Called from AChoreographer via JNI.
- // Registers AChoreographer so that refresh rate callbacks can be dispatched from DMS.
- private void registerNativeChoreographerForRefreshRateCallbacks() {
+ /**
+ * Called from AChoreographer via JNI.
+ * Registers AChoreographer so that refresh rate callbacks can be dispatched from DMS.
+ * Public for unit testing to be able to call this method.
+ */
+ @VisibleForTesting
+ public void registerNativeChoreographerForRefreshRateCallbacks() {
synchronized (mLock) {
- registerCallbackIfNeededLocked();
mDispatchNativeCallbacks = true;
+ registerCallbackIfNeededLocked();
+ updateCallbackIfNeededLocked();
DisplayInfo display = getDisplayInfoLocked(Display.DEFAULT_DISPLAY);
if (display != null) {
// We need to tell AChoreographer instances the current refresh rate so that apps
@@ -1091,11 +1101,16 @@
}
}
- // Called from AChoreographer via JNI.
- // Unregisters AChoreographer from receiving refresh rate callbacks.
- private void unregisterNativeChoreographerForRefreshRateCallbacks() {
+ /**
+ * Called from AChoreographer via JNI.
+ * Unregisters AChoreographer from receiving refresh rate callbacks.
+ * Public for unit testing to be able to call this method.
+ */
+ @VisibleForTesting
+ public void unregisterNativeChoreographerForRefreshRateCallbacks() {
synchronized (mLock) {
mDispatchNativeCallbacks = false;
+ updateCallbackIfNeededLocked();
}
}
}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 385ad2d..56f8142 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -58,7 +58,7 @@
public class FaceManager implements BiometricAuthenticator, BiometricFaceConstants {
private static final String TAG = "FaceManager";
- private static final boolean DEBUG = true;
+
private static final int MSG_ENROLL_RESULT = 100;
private static final int MSG_ACQUIRED = 101;
private static final int MSG_AUTHENTICATION_SUCCEEDED = 102;
@@ -207,13 +207,9 @@
throw new IllegalArgumentException("Must supply an authentication callback");
}
- if (cancel != null) {
- if (cancel.isCanceled()) {
- Slog.w(TAG, "authentication already canceled");
- return;
- } else {
- cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
- }
+ if (cancel != null && cancel.isCanceled()) {
+ Slog.w(TAG, "authentication already canceled");
+ return;
}
if (mService != null) {
@@ -223,17 +219,18 @@
mCryptoObject = crypto;
final long operationId = crypto != null ? crypto.getOpId() : 0;
Trace.beginSection("FaceManager#authenticate");
- mService.authenticate(mToken, operationId, userId, mServiceReceiver,
- mContext.getOpPackageName(), isKeyguardBypassEnabled);
+ final long authId = mService.authenticate(mToken, operationId, userId,
+ mServiceReceiver, mContext.getOpPackageName(), isKeyguardBypassEnabled);
+ if (cancel != null) {
+ cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId));
+ }
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception while authenticating: ", e);
- if (callback != null) {
- // Though this may not be a hardware issue, it will cause apps to give up or
- // try again later.
- callback.onAuthenticationError(FACE_ERROR_HW_UNAVAILABLE,
- getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
- 0 /* vendorCode */));
- }
+ // Though this may not be a hardware issue, it will cause apps to give up or
+ // try again later.
+ callback.onAuthenticationError(FACE_ERROR_HW_UNAVAILABLE,
+ getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */));
} finally {
Trace.endSection();
}
@@ -255,14 +252,14 @@
if (cancel.isCanceled()) {
Slog.w(TAG, "Detection already cancelled");
return;
- } else {
- cancel.setOnCancelListener(new OnFaceDetectionCancelListener());
}
mFaceDetectionCallback = callback;
try {
- mService.detectFace(mToken, userId, mServiceReceiver, mContext.getOpPackageName());
+ final long authId = mService.detectFace(
+ mToken, userId, mServiceReceiver, mContext.getOpPackageName());
+ cancel.setOnCancelListener(new OnFaceDetectionCancelListener(authId));
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception when requesting finger detect", e);
}
@@ -726,23 +723,23 @@
}
}
- private void cancelAuthentication(CryptoObject cryptoObject) {
+ private void cancelAuthentication(long requestId) {
if (mService != null) {
try {
- mService.cancelAuthentication(mToken, mContext.getOpPackageName());
+ mService.cancelAuthentication(mToken, mContext.getOpPackageName(), requestId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
- private void cancelFaceDetect() {
+ private void cancelFaceDetect(long requestId) {
if (mService == null) {
return;
}
try {
- mService.cancelFaceDetect(mToken, mContext.getOpPackageName());
+ mService.cancelFaceDetect(mToken, mContext.getOpPackageName(), requestId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -794,9 +791,9 @@
// This is used as a last resort in case a vendor string is missing
// It should not happen for anything other than FACE_ERROR_VENDOR, but
// warn and use the default if all else fails.
- // TODO(b/196639965): update string
Slog.w(TAG, "Invalid error message: " + errMsg + ", " + vendorCode);
- return "";
+ return context.getString(
+ com.android.internal.R.string.face_error_vendor_unknown);
}
/**
@@ -1110,22 +1107,30 @@
}
private class OnAuthenticationCancelListener implements OnCancelListener {
- private final CryptoObject mCrypto;
+ private final long mAuthRequestId;
- OnAuthenticationCancelListener(CryptoObject crypto) {
- mCrypto = crypto;
+ OnAuthenticationCancelListener(long id) {
+ mAuthRequestId = id;
}
@Override
public void onCancel() {
- cancelAuthentication(mCrypto);
+ Slog.d(TAG, "Cancel face authentication requested for: " + mAuthRequestId);
+ cancelAuthentication(mAuthRequestId);
}
}
private class OnFaceDetectionCancelListener implements OnCancelListener {
+ private final long mAuthRequestId;
+
+ OnFaceDetectionCancelListener(long id) {
+ mAuthRequestId = id;
+ }
+
@Override
public void onCancel() {
- cancelFaceDetect();
+ Slog.d(TAG, "Cancel face detect requested for: " + mAuthRequestId);
+ cancelFaceDetect(mAuthRequestId);
}
}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index db02a0ef..e919824 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -44,34 +44,36 @@
// Retrieve static sensor properties for the specified sensor
FaceSensorPropertiesInternal getSensorProperties(int sensorId, String opPackageName);
- // Authenticate the given sessionId with a face
- void authenticate(IBinder token, long operationId, int userId, IFaceServiceReceiver receiver,
+ // Authenticate with a face. A requestId is returned that can be used to cancel this operation.
+ long authenticate(IBinder token, long operationId, int userId, IFaceServiceReceiver receiver,
String opPackageName, boolean isKeyguardBypassEnabled);
// Uses the face hardware to detect for the presence of a face, without giving details
- // about accept/reject/lockout.
- void detectFace(IBinder token, int userId, IFaceServiceReceiver receiver, String opPackageName);
+ // about accept/reject/lockout. A requestId is returned that can be used to cancel this
+ // operation.
+ long detectFace(IBinder token, int userId, IFaceServiceReceiver receiver, String opPackageName);
// This method prepares the service to start authenticating, but doesn't start authentication.
// This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be
// called from BiometricService. The additional uid, pid, userId arguments should be determined
// by BiometricService. To start authentication after the clients are ready, use
// startPreparedClient().
- void prepareForAuthentication(int sensorId, boolean requireConfirmation, IBinder token, long operationId,
- int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName,
- int cookie, boolean allowBackgroundAuthentication);
+ void prepareForAuthentication(int sensorId, boolean requireConfirmation, IBinder token,
+ long operationId, int userId, IBiometricSensorReceiver sensorReceiver,
+ String opPackageName, long requestId, int cookie,
+ boolean allowBackgroundAuthentication);
// Starts authentication with the previously prepared client.
void startPreparedClient(int sensorId, int cookie);
- // Cancel authentication for the given sessionId
- void cancelAuthentication(IBinder token, String opPackageName);
+ // Cancel authentication for the given requestId.
+ void cancelAuthentication(IBinder token, String opPackageName, long requestId);
- // Cancel face detection
- void cancelFaceDetect(IBinder token, String opPackageName);
+ // Cancel face detection for the given requestId.
+ void cancelFaceDetect(IBinder token, String opPackageName, long requestId);
// Same as above, with extra arguments.
- void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName);
+ void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName, long requestId);
// Start face enrollment
void enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 87d45b9..7c42dc0 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -189,22 +189,30 @@
}
private class OnAuthenticationCancelListener implements OnCancelListener {
- private android.hardware.biometrics.CryptoObject mCrypto;
+ private final long mAuthRequestId;
- public OnAuthenticationCancelListener(android.hardware.biometrics.CryptoObject crypto) {
- mCrypto = crypto;
+ OnAuthenticationCancelListener(long id) {
+ mAuthRequestId = id;
}
@Override
public void onCancel() {
- cancelAuthentication(mCrypto);
+ Slog.d(TAG, "Cancel fingerprint authentication requested for: " + mAuthRequestId);
+ cancelAuthentication(mAuthRequestId);
}
}
private class OnFingerprintDetectionCancelListener implements OnCancelListener {
+ private final long mAuthRequestId;
+
+ OnFingerprintDetectionCancelListener(long id) {
+ mAuthRequestId = id;
+ }
+
@Override
public void onCancel() {
- cancelFingerprintDetect();
+ Slog.d(TAG, "Cancel fingerprint detect requested for: " + mAuthRequestId);
+ cancelFingerprintDetect(mAuthRequestId);
}
}
@@ -552,13 +560,9 @@
throw new IllegalArgumentException("Must supply an authentication callback");
}
- if (cancel != null) {
- if (cancel.isCanceled()) {
- Slog.w(TAG, "authentication already canceled");
- return;
- } else {
- cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
- }
+ if (cancel != null && cancel.isCanceled()) {
+ Slog.w(TAG, "authentication already canceled");
+ return;
}
if (mService != null) {
@@ -567,8 +571,11 @@
mAuthenticationCallback = callback;
mCryptoObject = crypto;
final long operationId = crypto != null ? crypto.getOpId() : 0;
- mService.authenticate(mToken, operationId, sensorId, userId, mServiceReceiver,
- mContext.getOpPackageName());
+ final long authId = mService.authenticate(mToken, operationId, sensorId, userId,
+ mServiceReceiver, mContext.getOpPackageName());
+ if (cancel != null) {
+ cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId));
+ }
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception while authenticating: ", e);
// Though this may not be a hardware issue, it will cause apps to give up or try
@@ -595,15 +602,14 @@
if (cancel.isCanceled()) {
Slog.w(TAG, "Detection already cancelled");
return;
- } else {
- cancel.setOnCancelListener(new OnFingerprintDetectionCancelListener());
}
mFingerprintDetectionCallback = callback;
try {
- mService.detectFingerprint(mToken, userId, mServiceReceiver,
+ final long authId = mService.detectFingerprint(mToken, userId, mServiceReceiver,
mContext.getOpPackageName());
+ cancel.setOnCancelListener(new OnFingerprintDetectionCancelListener(authId));
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception when requesting finger detect", e);
}
@@ -1320,21 +1326,21 @@
}
}
- private void cancelAuthentication(android.hardware.biometrics.CryptoObject cryptoObject) {
+ private void cancelAuthentication(long requestId) {
if (mService != null) try {
- mService.cancelAuthentication(mToken, mContext.getOpPackageName());
+ mService.cancelAuthentication(mToken, mContext.getOpPackageName(), requestId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- private void cancelFingerprintDetect() {
+ private void cancelFingerprintDetect(long requestId) {
if (mService == null) {
return;
}
try {
- mService.cancelFingerprintDetect(mToken, mContext.getOpPackageName());
+ mService.cancelFingerprintDetect(mToken, mContext.getOpPackageName(), requestId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1390,9 +1396,9 @@
// This is used as a last resort in case a vendor string is missing
// It should not happen for anything other than FINGERPRINT_ERROR_VENDOR, but
// warn and use the default if all else fails.
- // TODO(b/196639965): update string
Slog.w(TAG, "Invalid error message: " + errMsg + ", " + vendorCode);
- return "";
+ return context.getString(
+ com.android.internal.R.string.fingerprint_error_vendor_unknown);
}
/**
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 3979afe..4774827 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -48,15 +48,16 @@
// Retrieve static sensor properties for the specified sensor
FingerprintSensorPropertiesInternal getSensorProperties(int sensorId, String opPackageName);
- // Authenticate the given sessionId with a fingerprint. This is protected by
- // USE_FINGERPRINT/USE_BIOMETRIC permission. This is effectively deprecated, since it only comes
- // through FingerprintManager now.
- void authenticate(IBinder token, long operationId, int sensorId, int userId,
+ // Authenticate with a fingerprint. This is protected by USE_FINGERPRINT/USE_BIOMETRIC
+ // permission. This is effectively deprecated, since it only comes through FingerprintManager
+ // now. A requestId is returned that can be used to cancel this operation.
+ long authenticate(IBinder token, long operationId, int sensorId, int userId,
IFingerprintServiceReceiver receiver, String opPackageName);
// Uses the fingerprint hardware to detect for the presence of a finger, without giving details
- // about accept/reject/lockout.
- void detectFingerprint(IBinder token, int userId, IFingerprintServiceReceiver receiver,
+ // about accept/reject/lockout. A requestId is returned that can be used to cancel this
+ // operation.
+ long detectFingerprint(IBinder token, int userId, IFingerprintServiceReceiver receiver,
String opPackageName);
// This method prepares the service to start authenticating, but doesn't start authentication.
@@ -65,21 +66,21 @@
// by BiometricService. To start authentication after the clients are ready, use
// startPreparedClient().
void prepareForAuthentication(int sensorId, IBinder token, long operationId, int userId,
- IBiometricSensorReceiver sensorReceiver, String opPackageName, int cookie,
- boolean allowBackgroundAuthentication);
+ IBiometricSensorReceiver sensorReceiver, String opPackageName, long requestId,
+ int cookie, boolean allowBackgroundAuthentication);
// Starts authentication with the previously prepared client.
void startPreparedClient(int sensorId, int cookie);
- // Cancel authentication for the given sessionId
- void cancelAuthentication(IBinder token, String opPackageName);
+ // Cancel authentication for the given requestId.
+ void cancelAuthentication(IBinder token, String opPackageName, long requestId);
- // Cancel finger detection
- void cancelFingerprintDetect(IBinder token, String opPackageName);
+ // Cancel finger detection for the given requestId.
+ void cancelFingerprintDetect(IBinder token, String opPackageName, long requestId);
// Same as above, except this is protected by the MANAGE_BIOMETRIC signature permission. Takes
// an additional uid, pid, userid.
- void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName);
+ void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName, long requestId);
// Start fingerprint enrollment
void enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver,
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index dadea67..4255d88 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1731,12 +1731,12 @@
if (config.orientation != Configuration.ORIENTATION_LANDSCAPE) {
return false;
}
- if ((mInputEditorInfo != null
- && (mInputEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0)
+ if (mInputEditorInfo != null
+ && ((mInputEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0
// If app window has portrait orientation, regardless of what display orientation
// is, IME shouldn't use fullscreen-mode.
|| (mInputEditorInfo.internalImeOptions
- & EditorInfo.IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT) != 0) {
+ & EditorInfo.IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT) != 0)) {
return false;
}
return true;
diff --git a/core/java/android/inputmethodservice/RemoteInputConnection.java b/core/java/android/inputmethodservice/RemoteInputConnection.java
index ec5bcf1..464b421 100644
--- a/core/java/android/inputmethodservice/RemoteInputConnection.java
+++ b/core/java/android/inputmethodservice/RemoteInputConnection.java
@@ -34,7 +34,7 @@
import android.view.inputmethod.SurroundingText;
import com.android.internal.inputmethod.CancellationGroup;
-import com.android.internal.inputmethod.Completable;
+import com.android.internal.inputmethod.CompletableFutureUtil;
import com.android.internal.inputmethod.IInputContextInvoker;
import com.android.internal.inputmethod.ImeTracing;
import com.android.internal.inputmethod.InputConnectionProtoDumper;
@@ -42,6 +42,7 @@
import com.android.internal.view.IInputMethod;
import java.lang.ref.WeakReference;
+import java.util.concurrent.CompletableFuture;
/**
* Takes care of remote method invocations of {@link InputConnection} in the IME side.
@@ -96,8 +97,8 @@
return null;
}
- final Completable.CharSequence value = mInvoker.getTextAfterCursor(length, flags);
- final CharSequence result = Completable.getResultOrNull(
+ final CompletableFuture<CharSequence> value = mInvoker.getTextAfterCursor(length, flags);
+ final CharSequence result = CompletableFutureUtil.getResultOrNull(
value, TAG, "getTextAfterCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
@@ -120,8 +121,8 @@
return null;
}
- final Completable.CharSequence value = mInvoker.getTextBeforeCursor(length, flags);
- final CharSequence result = Completable.getResultOrNull(
+ final CompletableFuture<CharSequence> value = mInvoker.getTextBeforeCursor(length, flags);
+ final CharSequence result = CompletableFutureUtil.getResultOrNull(
value, TAG, "getTextBeforeCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
@@ -144,8 +145,8 @@
// This method is not implemented.
return null;
}
- final Completable.CharSequence value = mInvoker.getSelectedText(flags);
- final CharSequence result = Completable.getResultOrNull(
+ final CompletableFuture<CharSequence> value = mInvoker.getSelectedText(flags);
+ final CharSequence result = CompletableFutureUtil.getResultOrNull(
value, TAG, "getSelectedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
@@ -181,9 +182,9 @@
// This method is not implemented.
return null;
}
- final Completable.SurroundingText value = mInvoker.getSurroundingText(beforeLength,
+ final CompletableFuture<SurroundingText> value = mInvoker.getSurroundingText(beforeLength,
afterLength, flags);
- final SurroundingText result = Completable.getResultOrNull(
+ final SurroundingText result = CompletableFutureUtil.getResultOrNull(
value, TAG, "getSurroundingText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
@@ -202,8 +203,8 @@
return 0;
}
- final Completable.Int value = mInvoker.getCursorCapsMode(reqModes);
- final int result = Completable.getResultOrZero(
+ final CompletableFuture<Integer> value = mInvoker.getCursorCapsMode(reqModes);
+ final int result = CompletableFutureUtil.getResultOrZero(
value, TAG, "getCursorCapsMode()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
@@ -222,8 +223,8 @@
return null;
}
- final Completable.ExtractedText value = mInvoker.getExtractedText(request, flags);
- final ExtractedText result = Completable.getResultOrNull(
+ final CompletableFuture<ExtractedText> value = mInvoker.getExtractedText(request, flags);
+ final ExtractedText result = CompletableFutureUtil.getResultOrNull(
value, TAG, "getExtractedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
@@ -371,8 +372,8 @@
// This method is not implemented.
return false;
}
- final Completable.Boolean value = mInvoker.requestCursorUpdates(cursorUpdateMode);
- return Completable.getResultOrFalse(value, TAG, "requestCursorUpdates()",
+ final CompletableFuture<Boolean> value = mInvoker.requestCursorUpdates(cursorUpdateMode);
+ return CompletableFutureUtil.getResultOrFalse(value, TAG, "requestCursorUpdates()",
mCancellationGroup, MAX_WAIT_TIME_MILLIS);
}
@@ -407,8 +408,9 @@
inputMethodService.exposeContent(inputContentInfo, this);
}
- final Completable.Boolean value = mInvoker.commitContent(inputContentInfo, flags, opts);
- return Completable.getResultOrFalse(
+ final CompletableFuture<Boolean> value =
+ mInvoker.commitContent(inputContentInfo, flags, opts);
+ return CompletableFutureUtil.getResultOrFalse(
value, TAG, "commitContent()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
}
diff --git a/core/java/android/net/PacProxyManager.java b/core/java/android/net/PacProxyManager.java
index 8f7ad8c..da79634 100644
--- a/core/java/android/net/PacProxyManager.java
+++ b/core/java/android/net/PacProxyManager.java
@@ -94,7 +94,7 @@
}
/**
- * Updates the PAC Proxy Installer with current Proxy information.
+ * Updates the PAC Proxy Service with current Proxy information.
*/
@RequiresPermission(anyOf = {
android.Manifest.permission.NETWORK_STACK,
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 1692921f..6da02f5 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -31,6 +31,7 @@
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Set;
+import java.util.function.Supplier;
/**
* A mapping from String keys to values of various types. In most cases, you
@@ -38,7 +39,8 @@
* {@link PersistableBundle} subclass.
*/
public class BaseBundle {
- private static final String TAG = "Bundle";
+ /** @hide */
+ protected static final String TAG = "Bundle";
static final boolean DEBUG = false;
// Keep them in sync with frameworks/native/libs/binder/PersistableBundle.cpp.
@@ -95,7 +97,7 @@
Parcel mParcelledData = null;
/**
- * Whether {@link #mParcelledData} was generated by native coed or not.
+ * Whether {@link #mParcelledData} was generated by native code or not.
*/
private boolean mParcelledByNative;
@@ -198,7 +200,7 @@
if (size == 0) {
return null;
}
- Object o = mMap.valueAt(0);
+ Object o = getValueAt(0);
try {
return (String) o;
} catch (ClassCastException e) {
@@ -229,7 +231,12 @@
* using the currently assigned class loader.
*/
@UnsupportedAppUsage
- /* package */ void unparcel() {
+ final void unparcel() {
+ unparcel(/* itemwise */ false);
+ }
+
+ /** Deserializes the underlying data and each item if {@code itemwise} is true. */
+ final void unparcel(boolean itemwise) {
synchronized (this) {
final Parcel source = mParcelledData;
if (source != null) {
@@ -241,9 +248,42 @@
+ ": no parcelled data");
}
}
+ if (itemwise) {
+ for (int i = 0, n = mMap.size(); i < n; i++) {
+ // Triggers deserialization of i-th item, if needed
+ getValueAt(i);
+ }
+ }
}
}
+ /**
+ * Returns the value for key {@code key}.
+ *
+ * @hide
+ */
+ final Object getValue(String key) {
+ int i = mMap.indexOfKey(key);
+ return (i >= 0) ? getValueAt(i) : null;
+ }
+
+ /**
+ * Returns the value for a certain position in the array map.
+ *
+ * @hide
+ */
+ final Object getValueAt(int i) {
+ Object object = mMap.valueAt(i);
+ if (object instanceof Supplier<?>) {
+ Supplier<?> supplier = (Supplier<?>) object;
+ synchronized (this) {
+ object = supplier.get();
+ }
+ mMap.setValueAt(i, object);
+ }
+ return object;
+ }
+
private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel,
boolean parcelledByNative) {
if (LOG_DEFUSABLE && sShouldDefuse && (mFlags & FLAG_DEFUSABLE) == 0) {
@@ -282,15 +322,8 @@
map.ensureCapacity(count);
}
try {
- if (parcelledByNative) {
- // If it was parcelled by native code, then the array map keys aren't sorted
- // by their hash codes, so use the safe (slow) one.
- parcelledData.readArrayMapSafelyInternal(map, count, mClassLoader);
- } else {
- // If parcelled by Java, we know the contents are sorted properly,
- // so we can use ArrayMap.append().
- parcelledData.readArrayMapInternal(map, count, mClassLoader);
- }
+ recycleParcel &= parcelledData.readArrayMap(map, count, !parcelledByNative,
+ /* lazy */ true, mClassLoader);
} catch (BadParcelableException e) {
if (sShouldDefuse) {
Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
@@ -342,7 +375,7 @@
/** @hide */
ArrayMap<String, Object> getMap() {
- unparcel();
+ unparcel(/* itemwise */ true);
return mMap;
}
@@ -400,7 +433,12 @@
}
/**
- * @hide This kind-of does an equality comparison. Kind-of.
+ * Performs a loose equality check, which means there can be false negatives but if the method
+ * returns true than both objects are guaranteed to be equal.
+ *
+ * The point is that this method is a light-weight check in performance terms.
+ *
+ * @hide
*/
public boolean kindofEquals(BaseBundle other) {
if (other == null) {
@@ -415,6 +453,12 @@
} else if (isParcelled()) {
return mParcelledData.compareData(other.mParcelledData) == 0;
} else {
+ // Following semantic above of failing in case we get a serialized value vs a
+ // deserialized one, we'll compare the map. If a certain element hasn't been
+ // deserialized yet, it's a Supplier (or more specifically a LazyValue, but let's
+ // pretend we don't know that here :P), we'll use that element's equality comparison as
+ // map naturally does. That will takes care of comparing the payload if needed (see
+ // Parcel.readLazyValue() for details).
return mMap.equals(other.mMap);
}
}
@@ -453,7 +497,7 @@
final int N = fromMap.size();
mMap = new ArrayMap<>(N);
for (int i = 0; i < N; i++) {
- mMap.append(fromMap.keyAt(i), deepCopyValue(fromMap.valueAt(i)));
+ mMap.append(fromMap.keyAt(i), deepCopyValue(from.getValueAt(i)));
}
}
} else {
@@ -526,7 +570,7 @@
@Nullable
public Object get(String key) {
unparcel();
- return mMap.get(key);
+ return getValue(key);
}
/**
@@ -1001,7 +1045,7 @@
*/
char getChar(String key, char defaultValue) {
unparcel();
- Object o = mMap.get(key);
+ Object o = getValue(key);
if (o == null) {
return defaultValue;
}
@@ -1266,7 +1310,7 @@
@Nullable
Serializable getSerializable(@Nullable String key) {
unparcel();
- Object o = mMap.get(key);
+ Object o = getValue(key);
if (o == null) {
return null;
}
@@ -1289,7 +1333,7 @@
@Nullable
ArrayList<Integer> getIntegerArrayList(@Nullable String key) {
unparcel();
- Object o = mMap.get(key);
+ Object o = getValue(key);
if (o == null) {
return null;
}
@@ -1312,7 +1356,7 @@
@Nullable
ArrayList<String> getStringArrayList(@Nullable String key) {
unparcel();
- Object o = mMap.get(key);
+ Object o = getValue(key);
if (o == null) {
return null;
}
@@ -1335,7 +1379,7 @@
@Nullable
ArrayList<CharSequence> getCharSequenceArrayList(@Nullable String key) {
unparcel();
- Object o = mMap.get(key);
+ Object o = getValue(key);
if (o == null) {
return null;
}
@@ -1404,7 +1448,7 @@
@Nullable
short[] getShortArray(@Nullable String key) {
unparcel();
- Object o = mMap.get(key);
+ Object o = getValue(key);
if (o == null) {
return null;
}
@@ -1427,7 +1471,7 @@
@Nullable
char[] getCharArray(@Nullable String key) {
unparcel();
- Object o = mMap.get(key);
+ Object o = getValue(key);
if (o == null) {
return null;
}
@@ -1496,7 +1540,7 @@
@Nullable
float[] getFloatArray(@Nullable String key) {
unparcel();
- Object o = mMap.get(key);
+ Object o = getValue(key);
if (o == null) {
return null;
}
@@ -1585,7 +1629,7 @@
void writeToParcelInner(Parcel parcel, int flags) {
// If the parcel has a read-write helper, we can't just copy the blob, so unparcel it first.
if (parcel.hasReadWriteHelper()) {
- unparcel();
+ unparcel(/* itemwise */ true);
}
// Keep implementation in sync with writeToParcel() in
// frameworks/native/libs/binder/PersistableBundle.cpp.
@@ -1660,10 +1704,13 @@
}
if (parcel.hasReadWriteHelper()) {
- // If the parcel has a read-write helper, then we can't lazily-unparcel it, so just
- // unparcel right away.
+ // If the parcel has a read-write helper, it's better to deserialize immediately
+ // otherwise the helper would have to either maintain valid state long after the bundle
+ // had been constructed with parcel or to make sure they trigger deserialization of the
+ // bundle immediately; neither of which is obvious.
synchronized (this) {
initializeFromParcelLocked(parcel, /*recycleParcel=*/ false, isNativeBundle);
+ unparcel(/* itemwise */ true);
}
return;
}
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 102f525..b6163f1 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -331,47 +331,9 @@
// It's been unparcelled, so we need to walk the map
for (int i=mMap.size()-1; i>=0; i--) {
Object obj = mMap.valueAt(i);
- if (obj instanceof Parcelable) {
- if ((((Parcelable)obj).describeContents()
- & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
- fdFound = true;
- break;
- }
- } else if (obj instanceof Parcelable[]) {
- Parcelable[] array = (Parcelable[]) obj;
- for (int n = array.length - 1; n >= 0; n--) {
- Parcelable p = array[n];
- if (p != null && ((p.describeContents()
- & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0)) {
- fdFound = true;
- break;
- }
- }
- } else if (obj instanceof SparseArray) {
- SparseArray<? extends Parcelable> array =
- (SparseArray<? extends Parcelable>) obj;
- for (int n = array.size() - 1; n >= 0; n--) {
- Parcelable p = array.valueAt(n);
- if (p != null && (p.describeContents()
- & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
- fdFound = true;
- break;
- }
- }
- } else if (obj instanceof ArrayList) {
- ArrayList array = (ArrayList) obj;
- // an ArrayList here might contain either Strings or
- // Parcelables; only look inside for Parcelables
- if (!array.isEmpty() && (array.get(0) instanceof Parcelable)) {
- for (int n = array.size() - 1; n >= 0; n--) {
- Parcelable p = (Parcelable) array.get(n);
- if (p != null && ((p.describeContents()
- & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0)) {
- fdFound = true;
- break;
- }
- }
- }
+ if (Parcel.hasFileDescriptors(obj)) {
+ fdFound = true;
+ break;
}
}
}
@@ -392,7 +354,7 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public Bundle filterValues() {
- unparcel();
+ unparcel(/* itemwise */ true);
Bundle bundle = this;
if (mMap != null) {
ArrayMap<String, Object> map = mMap;
@@ -973,7 +935,7 @@
@Nullable
public Bundle getBundle(@Nullable String key) {
unparcel();
- Object o = mMap.get(key);
+ Object o = getValue(key);
if (o == null) {
return null;
}
@@ -1000,7 +962,7 @@
@Nullable
public <T extends Parcelable> T getParcelable(@Nullable String key) {
unparcel();
- Object o = mMap.get(key);
+ Object o = getValue(key);
if (o == null) {
return null;
}
@@ -1027,7 +989,7 @@
@Nullable
public Parcelable[] getParcelableArray(@Nullable String key) {
unparcel();
- Object o = mMap.get(key);
+ Object o = getValue(key);
if (o == null) {
return null;
}
@@ -1054,7 +1016,7 @@
@Nullable
public <T extends Parcelable> ArrayList<T> getParcelableArrayList(@Nullable String key) {
unparcel();
- Object o = mMap.get(key);
+ Object o = getValue(key);
if (o == null) {
return null;
}
@@ -1078,7 +1040,7 @@
@Nullable
public <T extends Parcelable> SparseArray<T> getSparseParcelableArray(@Nullable String key) {
unparcel();
- Object o = mMap.get(key);
+ Object o = getValue(key);
if (o == null) {
return null;
}
@@ -1301,7 +1263,7 @@
public void writeToParcel(Parcel parcel, int flags) {
final boolean oldAllowFds = parcel.pushAllowFds((mFlags & FLAG_ALLOW_FDS) != 0);
try {
- super.writeToParcelInner(parcel, flags);
+ writeToParcelInner(parcel, flags);
} finally {
parcel.restoreAllowFds(oldAllowFds);
}
@@ -1313,7 +1275,7 @@
* @param parcel The parcel to overwrite this bundle from.
*/
public void readFromParcel(Parcel parcel) {
- super.readFromParcelInner(parcel);
+ readFromParcelInner(parcel);
mFlags = FLAG_ALLOW_FDS;
maybePrefillHasFds();
}
diff --git a/core/java/android/os/ExternalVibration.java b/core/java/android/os/ExternalVibration.java
index 7fd02116..0686dd6 100644
--- a/core/java/android/os/ExternalVibration.java
+++ b/core/java/android/os/ExternalVibration.java
@@ -42,25 +42,38 @@
// boundaries.
@NonNull
private IBinder mToken;
-
public ExternalVibration(int uid, @NonNull String pkg, @NonNull AudioAttributes attrs,
@NonNull IExternalVibrationController controller) {
+ this(uid, pkg, attrs, controller, new Binder());
+ }
+
+ private ExternalVibration(int uid, @NonNull String pkg, @NonNull AudioAttributes attrs,
+ @NonNull IExternalVibrationController controller, @NonNull IBinder token) {
mUid = uid;
mPkg = Preconditions.checkNotNull(pkg);
mAttrs = Preconditions.checkNotNull(attrs);
mController = Preconditions.checkNotNull(controller);
- mToken = new Binder();
+ mToken = Preconditions.checkNotNull(token);
+
+ // IExternalVibrationController is a hidden AIDL interface with implementation provided by
+ // the audio framework to allow mute/unmute control over the external vibration.
+ //
+ // Transactions are locked in audioflinger, and should be blocking to avoid racing
+ // conditions on multiple audio playback.
+ //
+ // They can also be triggered before starting a new external vibration in
+ // IExternalVibratorService, as the ongoing external vibration needs to be muted before the
+ // new one can start, which also requires blocking calls to mute.
+ Binder.allowBlocking(mController.asBinder());
}
private ExternalVibration(Parcel in) {
- mUid = in.readInt();
- mPkg = in.readString();
- mAttrs = readAudioAttributes(in);
- mController = IExternalVibrationController.Stub.asInterface(in.readStrongBinder());
- mToken = in.readStrongBinder();
+ this(in.readInt(), in.readString(), readAudioAttributes(in),
+ IExternalVibrationController.Stub.asInterface(in.readStrongBinder()),
+ in.readStrongBinder());
}
- private AudioAttributes readAudioAttributes(Parcel in) {
+ private static AudioAttributes readAudioAttributes(Parcel in) {
int usage = in.readInt();
int contentType = in.readInt();
int capturePreset = in.readInt();
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index e06e7b6..c42c1d9 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -26,6 +26,7 @@
import android.util.ArraySet;
import android.util.ExceptionUtils;
import android.util.Log;
+import android.util.MathUtils;
import android.util.Size;
import android.util.SizeF;
import android.util.Slog;
@@ -56,7 +57,9 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
+import java.util.function.Supplier;
/**
* Container for a message (data and object references) that can
@@ -679,6 +682,65 @@
}
/**
+ * Check if the object used in {@link #readValue(ClassLoader)} / {@link #writeValue(Object)}
+ * has file descriptors.
+ *
+ * <p>For most cases, it will use the self-reported {@link Parcelable#describeContents()} method
+ * for that.
+ *
+ * @throws IllegalArgumentException if you provide any object not supported by above methods.
+ * Most notably, if you pass {@link Parcel}, this method will throw, for that check
+ * {@link Parcel#hasFileDescriptors()}
+ *
+ * @hide
+ */
+ public static boolean hasFileDescriptors(Object value) {
+ getValueType(value); // Will throw if value is not supported
+ if (value instanceof LazyValue) {
+ return ((LazyValue) value).hasFileDescriptors();
+ } else if (value instanceof Parcelable) {
+ if ((((Parcelable) value).describeContents()
+ & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
+ return true;
+ }
+ } else if (value instanceof Parcelable[]) {
+ Parcelable[] array = (Parcelable[]) value;
+ for (int n = array.length - 1; n >= 0; n--) {
+ Parcelable p = array[n];
+ if (p != null && ((p.describeContents()
+ & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0)) {
+ return true;
+ }
+ }
+ } else if (value instanceof SparseArray<?>) {
+ SparseArray<?> array = (SparseArray<?>) value;
+ for (int n = array.size() - 1; n >= 0; n--) {
+ Object object = array.valueAt(n);
+ if (object instanceof Parcelable) {
+ Parcelable p = (Parcelable) object;
+ if (p != null && (p.describeContents()
+ & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
+ return true;
+ }
+ }
+ }
+ } else if (value instanceof ArrayList<?>) {
+ ArrayList<?> array = (ArrayList<?>) value;
+ for (int n = array.size() - 1; n >= 0; n--) {
+ Object object = array.get(n);
+ if (object instanceof Parcelable) {
+ Parcelable p = (Parcelable) object;
+ if (p != null && ((p.describeContents()
+ & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
* Store or read an IBinder interface token in the parcel at the current
* {@link #dataPosition}. This is used to validate that the marshalled
* transaction is intended for the target interface. This is typically written
@@ -1832,108 +1894,206 @@
* should be used).</p>
*/
public final void writeValue(@Nullable Object v) {
+ if (v instanceof LazyValue) {
+ LazyValue value = (LazyValue) v;
+ value.writeToParcel(this);
+ return;
+ }
+ int type = getValueType(v);
+ writeInt(type);
+ if (isLengthPrefixed(type)) {
+ // Length
+ int length = dataPosition();
+ writeInt(-1); // Placeholder
+ // Object
+ int start = dataPosition();
+ writeValue(type, v);
+ int end = dataPosition();
+ // Backpatch length
+ setDataPosition(length);
+ writeInt(end - start);
+ setDataPosition(end);
+ } else {
+ writeValue(type, v);
+ }
+ }
+
+ /** @hide */
+ public static int getValueType(@Nullable Object v) {
if (v == null) {
- writeInt(VAL_NULL);
+ return VAL_NULL;
} else if (v instanceof String) {
- writeInt(VAL_STRING);
- writeString((String) v);
+ return VAL_STRING;
} else if (v instanceof Integer) {
- writeInt(VAL_INTEGER);
- writeInt((Integer) v);
+ return VAL_INTEGER;
} else if (v instanceof Map) {
- writeInt(VAL_MAP);
- writeMap((Map) v);
+ return VAL_MAP;
} else if (v instanceof Bundle) {
// Must be before Parcelable
- writeInt(VAL_BUNDLE);
- writeBundle((Bundle) v);
+ return VAL_BUNDLE;
} else if (v instanceof PersistableBundle) {
- writeInt(VAL_PERSISTABLEBUNDLE);
- writePersistableBundle((PersistableBundle) v);
+ // Must be before Parcelable
+ return VAL_PERSISTABLEBUNDLE;
+ } else if (v instanceof SizeF) {
+ // Must be before Parcelable
+ return VAL_SIZEF;
} else if (v instanceof Parcelable) {
// IMPOTANT: cases for classes that implement Parcelable must
- // come before the Parcelable case, so that their specific VAL_*
+ // come before the Parcelable case, so that their speci fic VAL_*
// types will be written.
- writeInt(VAL_PARCELABLE);
- writeParcelable((Parcelable) v, 0);
+ return VAL_PARCELABLE;
} else if (v instanceof Short) {
- writeInt(VAL_SHORT);
- writeInt(((Short) v).intValue());
+ return VAL_SHORT;
} else if (v instanceof Long) {
- writeInt(VAL_LONG);
- writeLong((Long) v);
+ return VAL_LONG;
} else if (v instanceof Float) {
- writeInt(VAL_FLOAT);
- writeFloat((Float) v);
+ return VAL_FLOAT;
} else if (v instanceof Double) {
- writeInt(VAL_DOUBLE);
- writeDouble((Double) v);
+ return VAL_DOUBLE;
} else if (v instanceof Boolean) {
- writeInt(VAL_BOOLEAN);
- writeInt((Boolean) v ? 1 : 0);
+ return VAL_BOOLEAN;
} else if (v instanceof CharSequence) {
// Must be after String
- writeInt(VAL_CHARSEQUENCE);
- writeCharSequence((CharSequence) v);
+ return VAL_CHARSEQUENCE;
} else if (v instanceof List) {
- writeInt(VAL_LIST);
- writeList((List) v);
+ return VAL_LIST;
} else if (v instanceof SparseArray) {
- writeInt(VAL_SPARSEARRAY);
- writeSparseArray((SparseArray) v);
+ return VAL_SPARSEARRAY;
} else if (v instanceof boolean[]) {
- writeInt(VAL_BOOLEANARRAY);
- writeBooleanArray((boolean[]) v);
+ return VAL_BOOLEANARRAY;
} else if (v instanceof byte[]) {
- writeInt(VAL_BYTEARRAY);
- writeByteArray((byte[]) v);
+ return VAL_BYTEARRAY;
} else if (v instanceof String[]) {
- writeInt(VAL_STRINGARRAY);
- writeStringArray((String[]) v);
+ return VAL_STRINGARRAY;
} else if (v instanceof CharSequence[]) {
// Must be after String[] and before Object[]
- writeInt(VAL_CHARSEQUENCEARRAY);
- writeCharSequenceArray((CharSequence[]) v);
+ return VAL_CHARSEQUENCEARRAY;
} else if (v instanceof IBinder) {
- writeInt(VAL_IBINDER);
- writeStrongBinder((IBinder) v);
+ return VAL_IBINDER;
} else if (v instanceof Parcelable[]) {
- writeInt(VAL_PARCELABLEARRAY);
- writeParcelableArray((Parcelable[]) v, 0);
+ return VAL_PARCELABLEARRAY;
} else if (v instanceof int[]) {
- writeInt(VAL_INTARRAY);
- writeIntArray((int[]) v);
+ return VAL_INTARRAY;
} else if (v instanceof long[]) {
- writeInt(VAL_LONGARRAY);
- writeLongArray((long[]) v);
+ return VAL_LONGARRAY;
} else if (v instanceof Byte) {
- writeInt(VAL_BYTE);
- writeInt((Byte) v);
+ return VAL_BYTE;
} else if (v instanceof Size) {
- writeInt(VAL_SIZE);
- writeSize((Size) v);
- } else if (v instanceof SizeF) {
- writeInt(VAL_SIZEF);
- writeSizeF((SizeF) v);
+ return VAL_SIZE;
} else if (v instanceof double[]) {
- writeInt(VAL_DOUBLEARRAY);
- writeDoubleArray((double[]) v);
+ return VAL_DOUBLEARRAY;
} else {
Class<?> clazz = v.getClass();
if (clazz.isArray() && clazz.getComponentType() == Object.class) {
// Only pure Object[] are written here, Other arrays of non-primitive types are
// handled by serialization as this does not record the component type.
- writeInt(VAL_OBJECTARRAY);
- writeArray((Object[]) v);
+ return VAL_OBJECTARRAY;
} else if (v instanceof Serializable) {
// Must be last
- writeInt(VAL_SERIALIZABLE);
- writeSerializable((Serializable) v);
+ return VAL_SERIALIZABLE;
} else {
- throw new RuntimeException("Parcel: unable to marshal value " + v);
+ throw new IllegalArgumentException("Parcel: unknown type for value " + v);
}
}
}
+ /**
+ * Writes value {@code v} in the parcel. This does NOT write the int representing the type
+ * first.
+ *
+ * @hide
+ */
+ public void writeValue(int type, @Nullable Object v) {
+ switch (type) {
+ case VAL_NULL:
+ break;
+ case VAL_STRING:
+ writeString((String) v);
+ break;
+ case VAL_INTEGER:
+ writeInt((Integer) v);
+ break;
+ case VAL_MAP:
+ writeMap((Map) v);
+ break;
+ case VAL_BUNDLE:
+ writeBundle((Bundle) v);
+ break;
+ case VAL_PERSISTABLEBUNDLE:
+ writePersistableBundle((PersistableBundle) v);
+ break;
+ case VAL_PARCELABLE:
+ writeParcelable((Parcelable) v, 0);
+ break;
+ case VAL_SHORT:
+ writeInt(((Short) v).intValue());
+ break;
+ case VAL_LONG:
+ writeLong((Long) v);
+ break;
+ case VAL_FLOAT:
+ writeFloat((Float) v);
+ break;
+ case VAL_DOUBLE:
+ writeDouble((Double) v);
+ break;
+ case VAL_BOOLEAN:
+ writeInt((Boolean) v ? 1 : 0);
+ break;
+ case VAL_CHARSEQUENCE:
+ writeCharSequence((CharSequence) v);
+ break;
+ case VAL_LIST:
+ writeList((List) v);
+ break;
+ case VAL_SPARSEARRAY:
+ writeSparseArray((SparseArray) v);
+ break;
+ case VAL_BOOLEANARRAY:
+ writeBooleanArray((boolean[]) v);
+ break;
+ case VAL_BYTEARRAY:
+ writeByteArray((byte[]) v);
+ break;
+ case VAL_STRINGARRAY:
+ writeStringArray((String[]) v);
+ break;
+ case VAL_CHARSEQUENCEARRAY:
+ writeCharSequenceArray((CharSequence[]) v);
+ break;
+ case VAL_IBINDER:
+ writeStrongBinder((IBinder) v);
+ break;
+ case VAL_PARCELABLEARRAY:
+ writeParcelableArray((Parcelable[]) v, 0);
+ break;
+ case VAL_INTARRAY:
+ writeIntArray((int[]) v);
+ break;
+ case VAL_LONGARRAY:
+ writeLongArray((long[]) v);
+ break;
+ case VAL_BYTE:
+ writeInt((Byte) v);
+ break;
+ case VAL_SIZE:
+ writeSize((Size) v);
+ break;
+ case VAL_SIZEF:
+ writeSizeF((SizeF) v);
+ break;
+ case VAL_DOUBLEARRAY:
+ writeDoubleArray((double[]) v);
+ break;
+ case VAL_OBJECTARRAY:
+ writeArray((Object[]) v);
+ break;
+ case VAL_SERIALIZABLE:
+ writeSerializable((Serializable) v);
+ break;
+ default:
+ throw new RuntimeException("Parcel: unable to marshal value " + v);
+ }
+ }
/**
* Flatten the name of the class of the Parcelable and its contents
@@ -3208,7 +3368,180 @@
@Nullable
public final Object readValue(@Nullable ClassLoader loader) {
int type = readInt();
+ final Object object;
+ if (isLengthPrefixed(type)) {
+ int length = readInt();
+ int start = dataPosition();
+ object = readValue(type, loader);
+ int actual = dataPosition() - start;
+ if (actual != length) {
+ Log.w(TAG,
+ "Unparcelling of " + object + " of type " + Parcel.valueTypeToString(type)
+ + " consumed " + actual + " bytes, but " + length + " expected.");
+ }
+ } else {
+ object = readValue(type, loader);
+ }
+ return object;
+ }
+ /**
+ * This will return a {@link Supplier} for length-prefixed types that deserializes the object
+ * when {@link Supplier#get()} is called, for other types it will return the object itself.
+ *
+ * <p>After calling {@link Supplier#get()} the parcel cursor will not change. Note that you
+ * shouldn't recycle the parcel, not at least until all objects have been retrieved. No
+ * synchronization attempts are made.
+ *
+ * </p>The supplier returned implements {@link #equals(Object)} and {@link #hashCode()}. Two
+ * suppliers are equal if either of the following is true:
+ * <ul>
+ * <li>{@link Supplier#get()} has been called on both and both objects returned are equal.
+ * <li>{@link Supplier#get()} hasn't been called on either one and everything below is true:
+ * <ul>
+ * <li>The {@code loader} parameters used to retrieve each are equal.
+ * <li>They both have the same type.
+ * <li>They have the same payload length.
+ * <li>Their binary content is the same.
+ * </ul>
+ * </ul>
+ *
+ * @hide
+ */
+ @Nullable
+ public Object readLazyValue(@Nullable ClassLoader loader) {
+ int start = dataPosition();
+ int type = readInt();
+ if (isLengthPrefixed(type)) {
+ int length = readInt();
+ setDataPosition(MathUtils.addOrThrow(dataPosition(), length));
+ return new LazyValue(this, start, length, type, loader);
+ } else {
+ return readValue(type, loader);
+ }
+ }
+
+ private static final class LazyValue implements Supplier<Object> {
+ private final int mPosition;
+ private final int mLength;
+ private final int mType;
+ @Nullable private final ClassLoader mLoader;
+ @Nullable private Parcel mSource;
+ @Nullable private Object mObject;
+ @Nullable private Parcel mValueParcel;
+
+ LazyValue(Parcel source, int position, int length, int type, @Nullable ClassLoader loader) {
+ mSource = source;
+ mPosition = position;
+ mLength = length;
+ mType = type;
+ mLoader = loader;
+ }
+
+ @Override
+ public Object get() {
+ if (mObject == null) {
+ int restore = mSource.dataPosition();
+ try {
+ mSource.setDataPosition(mPosition);
+ mObject = mSource.readValue(mLoader);
+ } finally {
+ mSource.setDataPosition(restore);
+ }
+ mSource = null;
+ if (mValueParcel != null) {
+ mValueParcel.recycle();
+ mValueParcel = null;
+ }
+ }
+ return mObject;
+ }
+
+ public void writeToParcel(Parcel out) {
+ if (mObject == null) {
+ int restore = mSource.dataPosition();
+ try {
+ mSource.setDataPosition(mPosition);
+ out.writeInt(mSource.readInt()); // Type
+ out.writeInt(mSource.readInt()); // Length
+ out.appendFrom(mSource, mSource.dataPosition(), mLength);
+ } finally {
+ mSource.setDataPosition(restore);
+ }
+ } else {
+ out.writeValue(mObject);
+ }
+ }
+
+ public boolean hasFileDescriptors() {
+ return getValueParcel().hasFileDescriptors();
+ }
+
+ @Override
+ public String toString() {
+ return mObject == null
+ ? "Supplier{" + valueTypeToString(mType) + "@" + mPosition + "+" + mLength + '}'
+ : "Supplier{" + mObject + "}";
+ }
+
+ /**
+ * We're checking if the *lazy value* is equal to another one, not if the *object*
+ * represented by the lazy value is equal to the other one. So, if there are two lazy values
+ * and one of them has been deserialized but the other hasn't this will always return false.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof LazyValue)) {
+ return false;
+ }
+ LazyValue value = (LazyValue) other;
+ // Check if they are either both serialized or both deserialized
+ if ((mObject == null) != (value.mObject == null)) {
+ return false;
+ }
+ // If both are deserialized, compare the live objects
+ if (mObject != null) {
+ return mObject.equals(value.mObject);
+ }
+ // Better safely fail here since this could mean we get different objects
+ if (!Objects.equals(mLoader, value.mLoader)) {
+ return false;
+ }
+ // Otherwise compare metadata prior to comparing payload
+ if (mType != value.mType || mLength != value.mLength) {
+ return false;
+ }
+ // Finally we compare the payload
+ return getValueParcel().compareData(value.getValueParcel()) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mObject, mLoader, mType, mLength);
+ }
+
+ /** This extracts the parcel section responsible for the object and returns it. */
+ private Parcel getValueParcel() {
+ if (mValueParcel == null) {
+ mValueParcel = Parcel.obtain();
+ // mLength is the length of object representation, excluding the type and length.
+ // mPosition is the position of the entire value container, right before the type.
+ // So, we add 4 bytes for the type + 4 bytes for the length written.
+ mValueParcel.appendFrom(mSource, mPosition, mLength + 8);
+ }
+ return mValueParcel;
+ }
+ }
+
+ /**
+ * Reads a value from the parcel of type {@code type}. Does NOT read the int representing the
+ * type first.
+ */
+ @Nullable
+ private Object readValue(int type, @Nullable ClassLoader loader) {
switch (type) {
case VAL_NULL:
return null;
@@ -3307,6 +3640,20 @@
}
}
+ private boolean isLengthPrefixed(int type) {
+ switch (type) {
+ case VAL_PARCELABLE:
+ case VAL_PARCELABLEARRAY:
+ case VAL_LIST:
+ case VAL_SPARSEARRAY:
+ case VAL_BUNDLE:
+ case VAL_SERIALIZABLE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
/**
* Read and return a new Parcelable from the parcel. The given class loader
* will be used to load any enclosed Parcelables. If it is null, the default
@@ -3609,49 +3956,49 @@
}
}
- /* package */ void readArrayMapInternal(@NonNull ArrayMap outVal, int N,
- @Nullable ClassLoader loader) {
- if (DEBUG_ARRAY_MAP) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Log.d(TAG, "Reading " + N + " ArrayMap entries", here);
- }
- int startPos;
- while (N > 0) {
- if (DEBUG_ARRAY_MAP) startPos = dataPosition();
- String key = readString();
- Object value = readValue(loader);
- if (DEBUG_ARRAY_MAP) Log.d(TAG, " Read #" + (N-1) + " "
- + (dataPosition()-startPos) + " bytes: key=0x"
- + Integer.toHexString((key != null ? key.hashCode() : 0)) + " " + key);
- outVal.append(key, value);
- N--;
- }
- outVal.validate();
+ /* package */ void readArrayMapInternal(@NonNull ArrayMap<? super String, Object> outVal,
+ int size, @Nullable ClassLoader loader) {
+ readArrayMap(outVal, size, /* sorted */ true, /* lazy */ false, loader);
}
- /* package */ void readArrayMapSafelyInternal(@NonNull ArrayMap outVal, int N,
- @Nullable ClassLoader loader) {
- if (DEBUG_ARRAY_MAP) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Log.d(TAG, "Reading safely " + N + " ArrayMap entries", here);
- }
- while (N > 0) {
+ /**
+ * Reads a map into {@code map}.
+ *
+ * @param sorted Whether the keys are sorted by their hashes, if so we use an optimized path.
+ * @param lazy Whether to populate the map with lazy {@link Supplier} objects for
+ * length-prefixed values. See {@link Parcel#readLazyValue(ClassLoader)} for more
+ * details.
+ * @return whether the parcel can be recycled or not.
+ * @hide
+ */
+ boolean readArrayMap(ArrayMap<? super String, Object> map, int size, boolean sorted,
+ boolean lazy, @Nullable ClassLoader loader) {
+ boolean recycle = true;
+ while (size > 0) {
String key = readString();
- if (DEBUG_ARRAY_MAP) Log.d(TAG, " Read safe #" + (N-1) + ": key=0x"
- + (key != null ? key.hashCode() : 0) + " " + key);
- Object value = readValue(loader);
- outVal.put(key, value);
- N--;
+ Object value = (lazy) ? readLazyValue(loader) : readValue(loader);
+ if (value instanceof LazyValue) {
+ recycle = false;
+ }
+ if (sorted) {
+ map.append(key, value);
+ } else {
+ map.put(key, value);
+ }
+ size--;
}
+ if (sorted) {
+ map.validate();
+ }
+ return recycle;
}
/**
* @hide For testing only.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public void readArrayMap(@NonNull ArrayMap outVal, @Nullable ClassLoader loader) {
+ public void readArrayMap(@NonNull ArrayMap<? super String, Object> outVal,
+ @Nullable ClassLoader loader) {
final int N = readInt();
if (N < 0) {
return;
@@ -3736,4 +4083,38 @@
public long getBlobAshmemSize() {
return nativeGetBlobAshmemSize(mNativePtr);
}
+
+ private static String valueTypeToString(int type) {
+ switch (type) {
+ case VAL_NULL: return "VAL_NULL";
+ case VAL_INTEGER: return "VAL_INTEGER";
+ case VAL_MAP: return "VAL_MAP";
+ case VAL_BUNDLE: return "VAL_BUNDLE";
+ case VAL_PERSISTABLEBUNDLE: return "VAL_PERSISTABLEBUNDLE";
+ case VAL_PARCELABLE: return "VAL_PARCELABLE";
+ case VAL_SHORT: return "VAL_SHORT";
+ case VAL_LONG: return "VAL_LONG";
+ case VAL_FLOAT: return "VAL_FLOAT";
+ case VAL_DOUBLE: return "VAL_DOUBLE";
+ case VAL_BOOLEAN: return "VAL_BOOLEAN";
+ case VAL_CHARSEQUENCE: return "VAL_CHARSEQUENCE";
+ case VAL_LIST: return "VAL_LIST";
+ case VAL_SPARSEARRAY: return "VAL_SPARSEARRAY";
+ case VAL_BOOLEANARRAY: return "VAL_BOOLEANARRAY";
+ case VAL_BYTEARRAY: return "VAL_BYTEARRAY";
+ case VAL_STRINGARRAY: return "VAL_STRINGARRAY";
+ case VAL_CHARSEQUENCEARRAY: return "VAL_CHARSEQUENCEARRAY";
+ case VAL_IBINDER: return "VAL_IBINDER";
+ case VAL_PARCELABLEARRAY: return "VAL_PARCELABLEARRAY";
+ case VAL_INTARRAY: return "VAL_INTARRAY";
+ case VAL_LONGARRAY: return "VAL_LONGARRAY";
+ case VAL_BYTE: return "VAL_BYTE";
+ case VAL_SIZE: return "VAL_SIZE";
+ case VAL_SIZEF: return "VAL_SIZEF";
+ case VAL_DOUBLEARRAY: return "VAL_DOUBLEARRAY";
+ case VAL_OBJECTARRAY: return "VAL_OBJECTARRAY";
+ case VAL_SERIALIZABLE: return "VAL_SERIALIZABLE";
+ default: return "UNKNOWN(" + type + ")";
+ }
+ }
}
diff --git a/services/core/java/com/android/server/utils/DeviceConfigInterface.java b/core/java/android/provider/DeviceConfigInterface.java
similarity index 63%
rename from services/core/java/com/android/server/utils/DeviceConfigInterface.java
rename to core/java/android/provider/DeviceConfigInterface.java
index ff60903..0a888f4 100644
--- a/services/core/java/com/android/server/utils/DeviceConfigInterface.java
+++ b/core/java/android/provider/DeviceConfigInterface.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,57 +14,103 @@
* limitations under the License.
*/
-package com.android.server.utils;
+package android.provider;
+
+import static android.provider.Settings.ResetMode;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.BadConfigException;
+import android.provider.DeviceConfig.Properties;
import java.util.concurrent.Executor;
/**
* Abstraction around {@link DeviceConfig} to allow faking device configuration in tests.
+ *
+ * @hide
*/
public interface DeviceConfigInterface {
+
/**
+ * @hide
* @see DeviceConfig#getProperty
*/
@Nullable
String getProperty(@NonNull String namespace, @NonNull String name);
/**
+ * @hide
+ * @see DeviceConfig#getProperties
+ */
+ @NonNull
+ Properties getProperties(@NonNull String namespace, @NonNull String... names);
+
+ /**
+ * @hide
+ * @see DeviceConfig#setProperty
+ */
+ boolean setProperty(@NonNull String namespace, @NonNull String name, @Nullable String value,
+ boolean makeDefault);
+
+ /**
+ * @hide
+ * @see DeviceConfig#setProperties
+ */
+ boolean setProperties(@NonNull Properties properties) throws BadConfigException;
+
+ /**
+ * @hide
+ * @see DeviceConfig#deleteProperty
+ */
+ boolean deleteProperty(@NonNull String namespace, @NonNull String name);
+
+ /**
+ * @hide
+ * @see DeviceConfig#resetToDefaults
+ */
+ void resetToDefaults(@ResetMode int resetMode, @Nullable String namespace);
+
+ /**
+ * @hide
* @see DeviceConfig#getString
*/
@NonNull
String getString(@NonNull String namespace, @NonNull String name, @NonNull String defaultValue);
/**
+ * @hide
* @see DeviceConfig#getInt
*/
int getInt(@NonNull String namespace, @NonNull String name, int defaultValue);
/**
+ * @hide
* @see DeviceConfig#getLong
*/
long getLong(@NonNull String namespace, @NonNull String name, long defaultValue);
/**
+ * @hide
* @see DeviceConfig#getBoolean
*/
boolean getBoolean(@NonNull String namespace, @NonNull String name, boolean defaultValue);
/**
+ * @hide
* @see DeviceConfig#getFloat
*/
float getFloat(@NonNull String namespace, @NonNull String name, float defaultValue);
/**
+ * @hide
* @see DeviceConfig#addOnPropertiesChangedListener
*/
void addOnPropertiesChangedListener(@NonNull String namespace, @NonNull Executor executor,
@NonNull DeviceConfig.OnPropertiesChangedListener listener);
/**
+ * @hide
* @see DeviceConfig#removeOnPropertiesChangedListener
*/
void removeOnPropertiesChangedListener(
@@ -72,7 +118,10 @@
/**
* Calls through to the real {@link DeviceConfig}.
+ *
+ * @hide
*/
+ @NonNull
DeviceConfigInterface REAL = new DeviceConfigInterface() {
@Override
public String getProperty(String namespace, String name) {
@@ -80,6 +129,36 @@
}
@Override
+ public DeviceConfig.Properties getProperties(@NonNull String namespace,
+ @NonNull String... names) {
+ return DeviceConfig.getProperties(namespace, names);
+ }
+
+ @Override
+ public boolean setProperty(@NonNull String namespace,
+ @NonNull String name,
+ @Nullable String value, boolean makeDefault) {
+ return DeviceConfig.setProperty(namespace, name, value, makeDefault);
+ }
+
+ @Override
+ public boolean setProperties(@NonNull Properties properties)
+ throws BadConfigException {
+ return DeviceConfig.setProperties(properties);
+ }
+
+ @Override
+ public boolean deleteProperty(@NonNull String namespace,
+ @NonNull String name) {
+ return DeviceConfig.deleteProperty(namespace, name);
+ }
+
+ @Override
+ public void resetToDefaults(int resetMode, @Nullable String namespace) {
+ DeviceConfig.resetToDefaults(resetMode, namespace);
+ }
+
+ @Override
public String getString(String namespace, String name, String defaultValue) {
return DeviceConfig.getString(namespace, name, defaultValue);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7a2898a..79a3c03 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11975,6 +11975,13 @@
public static final String WIFI_MIGRATION_COMPLETED = "wifi_migration_completed";
/**
+ * Whether UWB should be enabled.
+ * @hide
+ */
+ @Readable
+ public static final String UWB_ENABLED = "uwb_enabled";
+
+ /**
* Value to specify whether network quality scores and badging should be shown in the UI.
*
* Type: int (0 for false, 1 for true)
diff --git a/core/java/android/util/apk/TEST_MAPPING b/core/java/android/util/apk/TEST_MAPPING
index 4598b4f..e182521 100644
--- a/core/java/android/util/apk/TEST_MAPPING
+++ b/core/java/android/util/apk/TEST_MAPPING
@@ -1,6 +1,16 @@
{
"presubmit": [
{
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.util.apk.SourceStampVerifierTest"
+ }
+ ]
+ }
+ ],
+ "presubmit-large": [
+ {
"name": "CtsContentTestCases",
"options": [
{
@@ -10,14 +20,6 @@
"include-filter": "android.content.pm.cts.PackageManagerShellCommandTest"
}
]
- },
- {
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.util.apk.SourceStampVerifierTest"
- }
- ]
}
]
}
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index d23200b..656fdbd 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -151,7 +151,8 @@
if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId
&& mHandler.hasAccessibilityCallback(message)) {
AccessibilityInteractionClient.getInstanceForThread(
- interrogatingTid).setSameThreadMessage(message);
+ interrogatingTid, /* initializeCache= */true)
+ .setSameThreadMessage(message);
} else {
// For messages without callback of interrogating client, just handle the
// message immediately if this is UI thread.
diff --git a/core/java/android/view/IDisplayWindowListener.aidl b/core/java/android/view/IDisplayWindowListener.aidl
index 610e0f8..f95d6b3 100644
--- a/core/java/android/view/IDisplayWindowListener.aidl
+++ b/core/java/android/view/IDisplayWindowListener.aidl
@@ -32,7 +32,8 @@
oneway interface IDisplayWindowListener {
/**
- * Called when a display is added to the WM hierarchy.
+ * Called when a new display is added to the WM hierarchy. The existing display ids are returned
+ * when this listener is registered with WM via {@link #registerDisplayWindowListener}.
*/
void onDisplayAdded(int displayId);
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 8c2348c..371d3d0 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -521,9 +521,10 @@
void unregisterDisplayFoldListener(IDisplayFoldListener listener);
/**
- * Registers an IDisplayContainerListener
+ * Registers an IDisplayContainerListener, and returns the set of existing display ids. The
+ * listener's onDisplayAdded() will not be called for the displays returned.
*/
- void registerDisplayWindowListener(IDisplayWindowListener listener);
+ int[] registerDisplayWindowListener(IDisplayWindowListener listener);
/**
* Unregisters an IDisplayContainerListener.
diff --git a/core/java/android/view/InsetsAnimationControlCallbacks.java b/core/java/android/view/InsetsAnimationControlCallbacks.java
index 3431c3e..7696bbb 100644
--- a/core/java/android/view/InsetsAnimationControlCallbacks.java
+++ b/core/java/android/view/InsetsAnimationControlCallbacks.java
@@ -20,7 +20,8 @@
import android.view.WindowInsetsAnimation.Bounds;
/**
- * Provide an interface to let InsetsAnimationControlImpl call back into its owner.
+ * Provide an interface to let InsetsAnimationControlImpl and InsetsResizeAnimationRunner call back
+ * into its owner.
* @hide
*/
public interface InsetsAnimationControlCallbacks {
@@ -34,10 +35,9 @@
* <li>Dispatch {@link WindowInsetsAnimationControlListener#onReady}</li>
* </ul>
*/
- void startAnimation(InsetsAnimationControlImpl controller,
- WindowInsetsAnimationControlListener listener, int types,
- WindowInsetsAnimation animation,
- Bounds bounds);
+ <T extends InsetsAnimationControlRunner & WindowInsetsAnimationController>
+ void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types,
+ WindowInsetsAnimation animation, Bounds bounds);
/**
* Schedule the apply by posting the animation callback.
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 17b3020..b92d24f 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -105,7 +105,7 @@
private float mCurrentAlpha = 1.0f;
private float mPendingAlpha = 1.0f;
@VisibleForTesting(visibility = PACKAGE)
- public boolean mReadyDispatched;
+ private boolean mReadyDispatched;
private Boolean mPerceptible;
@VisibleForTesting
@@ -170,6 +170,11 @@
}
@Override
+ public void setReadyDispatched(boolean dispatched) {
+ mReadyDispatched = dispatched;
+ }
+
+ @Override
public Insets getHiddenStateInsets() {
return mHiddenInsets;
}
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index 691e638..c11bde8 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -54,8 +54,8 @@
@Override
@UiThread
- public void startAnimation(InsetsAnimationControlImpl controller,
- WindowInsetsAnimationControlListener listener, int types,
+ public <T extends InsetsAnimationControlRunner & WindowInsetsAnimationController>
+ void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types,
WindowInsetsAnimation animation, Bounds bounds) {
// Animation will be started in constructor already.
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 2ed705a..5c56baa 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -205,6 +205,9 @@
private static final int ANIMATION_DURATION_FADE_IN_MS = 500;
private static final int ANIMATION_DURATION_FADE_OUT_MS = 1500;
+ /** Visible for WindowManagerWrapper */
+ public static final int ANIMATION_DURATION_RESIZE = 300;
+
private static final int ANIMATION_DELAY_DIM_MS = 500;
private static final int ANIMATION_DURATION_SYNC_IME_MS = 285;
@@ -235,6 +238,9 @@
private static final Interpolator FAST_OUT_LINEAR_IN_INTERPOLATOR =
new PathInterpolator(0.4f, 0f, 1f, 1f);
+ /** Visible for WindowManagerWrapper */
+ public static final Interpolator RESIZE_INTERPOLATOR = new LinearInterpolator();
+
/** The amount IME will move up/down when animating in floating mode. */
private static final int FLOATING_IME_BOTTOM_INSET_DP = -80;
@@ -288,9 +294,13 @@
@VisibleForTesting
public static final int ANIMATION_TYPE_USER = 2;
+ /** Running animation will resize insets */
+ @VisibleForTesting
+ public static final int ANIMATION_TYPE_RESIZE = 3;
+
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {ANIMATION_TYPE_NONE, ANIMATION_TYPE_SHOW, ANIMATION_TYPE_HIDE,
- ANIMATION_TYPE_USER})
+ ANIMATION_TYPE_USER, ANIMATION_TYPE_RESIZE})
@interface AnimationType {
}
@@ -320,7 +330,7 @@
private final boolean mDisable;
private final int mFloatingImeBottomInset;
- private ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal =
+ private final ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal =
new ThreadLocal<AnimationHandler>() {
@Override
protected AnimationHandler initialValue() {
@@ -550,7 +560,6 @@
private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
private final ArrayList<RunningAnimation> mRunningAnimations = new ArrayList<>();
- private final ArrayList<InsetsAnimationControlImpl> mTmpFinishedControls = new ArrayList<>();
private final ArraySet<InsetsSourceConsumer> mRequestedVisibilityChanged = new ArraySet<>();
private WindowInsets mLastInsets;
@@ -570,7 +579,7 @@
private int mCaptionInsetsHeight = 0;
private boolean mAnimationsDisabled;
- private Runnable mPendingControlTimeout = this::abortPendingImeControlRequest;
+ private final Runnable mPendingControlTimeout = this::abortPendingImeControlRequest;
private final ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners
= new ArrayList<>();
@@ -580,7 +589,7 @@
/** Set of inset types which cannot be controlled by the user animation */
private @InsetsType int mDisabledUserAnimationInsetsTypes;
- private Runnable mInvokeControllableInsetsChangedListeners =
+ private final Runnable mInvokeControllableInsetsChangedListeners =
this::invokeControllableInsetsChangedListeners;
public InsetsController(Host host) {
@@ -608,23 +617,23 @@
}
final List<WindowInsetsAnimation> runningAnimations = new ArrayList<>();
+ final List<WindowInsetsAnimation> finishedAnimations = new ArrayList<>();
final InsetsState state = new InsetsState(mState, true /* copySources */);
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
RunningAnimation runningAnimation = mRunningAnimations.get(i);
if (DEBUG) Log.d(TAG, "Running animation type: " + runningAnimation.type);
- InsetsAnimationControlRunner runner = runningAnimation.runner;
- if (runner instanceof InsetsAnimationControlImpl) {
- InsetsAnimationControlImpl control = (InsetsAnimationControlImpl) runner;
+ final InsetsAnimationControlRunner runner = runningAnimation.runner;
+ if (runner instanceof WindowInsetsAnimationController) {
// Keep track of running animation to be dispatched. Aggregate it here such that
// if it gets finished within applyChangeInsets we still dispatch it to
// onProgress.
if (runningAnimation.startDispatched) {
- runningAnimations.add(control.getAnimation());
+ runningAnimations.add(runner.getAnimation());
}
- if (control.applyChangeInsets(state)) {
- mTmpFinishedControls.add(control);
+ if (((WindowInsetsAnimationController) runner).applyChangeInsets(state)) {
+ finishedAnimations.add(runner.getAnimation());
}
}
}
@@ -642,10 +651,9 @@
}
}
- for (int i = mTmpFinishedControls.size() - 1; i >= 0; i--) {
- dispatchAnimationEnd(mTmpFinishedControls.get(i).getAnimation());
+ for (int i = finishedAnimations.size() - 1; i >= 0; i--) {
+ dispatchAnimationEnd(finishedAnimations.get(i));
}
- mTmpFinishedControls.clear();
};
}
@@ -691,15 +699,13 @@
true /* excludeInvisibleIme */)) {
if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged");
mHost.notifyInsetsChanged();
+ startResizingAnimationIfNeeded(lastState);
}
return true;
}
private void updateState(InsetsState newState) {
- mState.setDisplayFrame(newState.getDisplayFrame());
- mState.setDisplayCutout(newState.getDisplayCutout());
- mState.setRoundedCorners(newState.getRoundedCorners());
- mState.setPrivacyIndicatorBounds(newState.getPrivacyIndicatorBounds());
+ mState.set(newState, 0 /* types */);
@InsetsType int disabledUserAnimationTypes = 0;
@InsetsType int[] cancelledUserAnimationTypes = {0};
for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
@@ -764,6 +770,39 @@
return false;
}
+ private void startResizingAnimationIfNeeded(InsetsState fromState) {
+ if (!fromState.getDisplayFrame().equals(mState.getDisplayFrame())) {
+ return;
+ }
+ @InsetsType int types = 0;
+ InsetsState toState = null;
+ final ArraySet<Integer> internalTypes = InsetsState.toInternalType(Type.systemBars());
+ for (int i = internalTypes.size() - 1; i >= 0; i--) {
+ final @InternalInsetsType int type = internalTypes.valueAt(i);
+ final InsetsSource fromSource = fromState.peekSource(type);
+ final InsetsSource toSource = mState.peekSource(type);
+ if (fromSource != null && toSource != null
+ && fromSource.isVisible() && toSource.isVisible()
+ && !fromSource.getFrame().equals(toSource.getFrame())
+ && (Rect.intersects(mFrame, fromSource.getFrame())
+ || Rect.intersects(mFrame, toSource.getFrame()))) {
+ types |= InsetsState.toPublicType(toSource.getType());
+ if (toState == null) {
+ toState = new InsetsState();
+ }
+ toState.addSource(new InsetsSource(toSource));
+ }
+ }
+ if (types == 0) {
+ return;
+ }
+ cancelExistingControllers(types);
+ final InsetsAnimationControlRunner runner = new InsetsResizeAnimationRunner(
+ mFrame, fromState, toState, RESIZE_INTERPOLATOR, ANIMATION_DURATION_RESIZE, types,
+ this);
+ mRunningAnimations.add(new RunningAnimation(runner, runner.getAnimationType()));
+ }
+
/**
* @see InsetsState#calculateInsets
*/
@@ -785,7 +824,7 @@
/**
* @see InsetsState#calculateVisibleInsets(Rect, int)
*/
- public Rect calculateVisibleInsets(@SoftInputModeFlags int softInputMode) {
+ public Insets calculateVisibleInsets(@SoftInputModeFlags int softInputMode) {
return mState.calculateVisibleInsets(mFrame, softInputMode);
}
@@ -1474,12 +1513,12 @@
@VisibleForTesting
@Override
- public void startAnimation(InsetsAnimationControlImpl controller,
- WindowInsetsAnimationControlListener listener, int types,
+ public <T extends InsetsAnimationControlRunner & WindowInsetsAnimationController>
+ void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types,
WindowInsetsAnimation animation, Bounds bounds) {
mHost.dispatchWindowInsetsAnimationPrepare(animation);
mHost.addOnPreDrawRunnable(() -> {
- if (controller.isCancelled()) {
+ if (runner.isCancelled()) {
if (WARN) Log.w(TAG, "startAnimation canceled before preDraw");
return;
}
@@ -1487,15 +1526,15 @@
"InsetsAnimation: " + WindowInsets.Type.toString(types), types);
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
RunningAnimation runningAnimation = mRunningAnimations.get(i);
- if (runningAnimation.runner == controller) {
+ if (runningAnimation.runner == runner) {
runningAnimation.startDispatched = true;
}
}
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.pendingAnim", 0);
mHost.dispatchWindowInsetsAnimationStart(animation, bounds);
mStartingAnimation = true;
- controller.mReadyDispatched = true;
- listener.onReady(controller, types);
+ runner.setReadyDispatched(true);
+ listener.onReady(runner, types);
mStartingAnimation = false;
});
}
diff --git a/core/java/android/view/InsetsResizeAnimationRunner.java b/core/java/android/view/InsetsResizeAnimationRunner.java
new file mode 100644
index 0000000..83f51aa
--- /dev/null
+++ b/core/java/android/view/InsetsResizeAnimationRunner.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.InsetsAnimationControlImplProto.CURRENT_ALPHA;
+import static android.view.InsetsAnimationControlImplProto.IS_CANCELLED;
+import static android.view.InsetsAnimationControlImplProto.IS_FINISHED;
+import static android.view.InsetsAnimationControlImplProto.PENDING_ALPHA;
+import static android.view.InsetsAnimationControlImplProto.PENDING_FRACTION;
+import static android.view.InsetsAnimationControlImplProto.PENDING_INSETS;
+import static android.view.InsetsAnimationControlImplProto.SHOWN_ON_FINISH;
+import static android.view.InsetsAnimationControlImplProto.TMP_MATRIX;
+import static android.view.InsetsController.ANIMATION_TYPE_RESIZE;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
+import android.view.InsetsState.InternalInsetsType;
+import android.view.WindowInsets.Type.InsetsType;
+import android.view.WindowInsetsAnimation.Bounds;
+import android.view.animation.Interpolator;
+
+/**
+ * Runs a fake animation of resizing insets to produce insets animation callbacks.
+ * @hide
+ */
+public class InsetsResizeAnimationRunner implements InsetsAnimationControlRunner,
+ WindowInsetsAnimationController, WindowInsetsAnimationControlListener {
+
+ private final InsetsState mFromState;
+ private final InsetsState mToState;
+ private final @InsetsType int mTypes;
+ private final WindowInsetsAnimation mAnimation;
+ private final InsetsAnimationControlCallbacks mController;
+ private ValueAnimator mAnimator;
+ private boolean mCancelled;
+ private boolean mFinished;
+
+ public InsetsResizeAnimationRunner(Rect frame, InsetsState fromState, InsetsState toState,
+ Interpolator interpolator, long duration, @InsetsType int types,
+ InsetsAnimationControlCallbacks controller) {
+ mFromState = fromState;
+ mToState = toState;
+ mTypes = types;
+ mController = controller;
+ mAnimation = new WindowInsetsAnimation(types, interpolator, duration);
+ mAnimation.setAlpha(1f);
+ final Insets fromInsets = fromState.calculateInsets(
+ frame, types, false /* ignoreVisibility */);
+ final Insets toInsets = toState.calculateInsets(
+ frame, types, false /* ignoreVisibility */);
+ controller.startAnimation(this, this, types, mAnimation,
+ new Bounds(Insets.min(fromInsets, toInsets), Insets.max(fromInsets, toInsets)));
+ }
+
+ @Override
+ public int getTypes() {
+ return mTypes;
+ }
+
+ @Override
+ public int getControllingTypes() {
+ return mTypes;
+ }
+
+ @Override
+ public WindowInsetsAnimation getAnimation() {
+ return mAnimation;
+ }
+
+ @Override
+ public int getAnimationType() {
+ return ANIMATION_TYPE_RESIZE;
+ }
+
+ @Override
+ public void cancel() {
+ if (mCancelled || mFinished) {
+ return;
+ }
+ mCancelled = true;
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ }
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return mCancelled;
+ }
+
+ @Override
+ public void onReady(WindowInsetsAnimationController controller, int types) {
+ if (mCancelled) {
+ return;
+ }
+ mAnimator = ValueAnimator.ofFloat(0f, 1f);
+ mAnimator.setDuration(mAnimation.getDurationMillis());
+ mAnimator.addUpdateListener(animation -> {
+ mAnimation.setFraction(animation.getAnimatedFraction());
+ mController.scheduleApplyChangeInsets(InsetsResizeAnimationRunner.this);
+ });
+ mAnimator.addListener(new AnimatorListenerAdapter() {
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mFinished = true;
+ mController.scheduleApplyChangeInsets(InsetsResizeAnimationRunner.this);
+ }
+ });
+ mAnimator.start();
+ }
+
+ @Override
+ public boolean applyChangeInsets(InsetsState outState) {
+ final float fraction = mAnimation.getInterpolatedFraction();
+ for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
+ final InsetsSource fromSource = mFromState.peekSource(type);
+ final InsetsSource toSource = mToState.peekSource(type);
+ if (fromSource == null || toSource == null) {
+ continue;
+ }
+ final Rect fromFrame = fromSource.getFrame();
+ final Rect toFrame = toSource.getFrame();
+ final Rect frame = new Rect(
+ (int) (fromFrame.left + fraction * (toFrame.left - fromFrame.left)),
+ (int) (fromFrame.top + fraction * (toFrame.top - fromFrame.top)),
+ (int) (fromFrame.right + fraction * (toFrame.right - fromFrame.right)),
+ (int) (fromFrame.bottom + fraction * (toFrame.bottom - fromFrame.bottom)));
+ final InsetsSource source = new InsetsSource(type);
+ source.setFrame(frame);
+ source.setVisible(toSource.isVisible());
+ outState.addSource(source);
+ }
+ if (mFinished) {
+ mController.notifyFinished(this, true /* shown */);
+ }
+ return mFinished;
+ }
+
+ @Override
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(IS_CANCELLED, mCancelled);
+ proto.write(IS_FINISHED, mFinished);
+ proto.write(TMP_MATRIX, "null");
+ proto.write(PENDING_INSETS, "null");
+ proto.write(PENDING_FRACTION, mAnimation.getInterpolatedFraction());
+ proto.write(SHOWN_ON_FINISH, true);
+ proto.write(CURRENT_ALPHA, 1f);
+ proto.write(PENDING_ALPHA, 1f);
+ proto.end(token);
+ }
+
+ @Override
+ public Insets getHiddenStateInsets() {
+ return Insets.NONE;
+ }
+
+ @Override
+ public Insets getShownStateInsets() {
+ return Insets.NONE;
+ }
+
+ @Override
+ public Insets getCurrentInsets() {
+ return Insets.NONE;
+ }
+
+ @Override
+ public float getCurrentFraction() {
+ return 0;
+ }
+
+ @Override
+ public float getCurrentAlpha() {
+ return 0;
+ }
+
+ @Override
+ public void setInsetsAndAlpha(Insets insets, float alpha, float fraction) {
+ }
+
+ @Override
+ public void finish(boolean shown) {
+ }
+
+ @Override
+ public boolean isFinished() {
+ return false;
+ }
+
+ @Override
+ public void notifyControlRevoked(int types) {
+ }
+
+ @Override
+ public void updateSurfacePosition(SparseArray<InsetsSourceControl> controls) {
+ }
+
+ @Override
+ public boolean hasZeroInsetsIme() {
+ return false;
+ }
+
+ @Override
+ public void setReadyDispatched(boolean dispatched) {
+ }
+
+ @Override
+ public void onFinished(WindowInsetsAnimationController controller) {
+ }
+
+ @Override
+ public void onCancelled(WindowInsetsAnimationController controller) {
+ }
+}
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index 1506ee4..9d98a3e 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -185,6 +185,15 @@
return result;
}
+ @Override
+ public String toString() {
+ return "InsetsSourceControl: {"
+ + "type=" + InsetsState.typeToString(mType)
+ + ", mSurfacePosition=" + mSurfacePosition
+ + ", mInsetsHint=" + mInsetsHint
+ + "}";
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix);
pw.print("InsetsSourceControl type="); pw.print(InsetsState.typeToString(mType));
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index f4444a1..75b69cb 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -301,7 +301,7 @@
return mPrivacyIndicatorBounds.inset(insetLeft, insetTop, insetRight, insetBottom);
}
- public Rect calculateInsets(Rect frame, @InsetsType int types, boolean ignoreVisibility) {
+ public Insets calculateInsets(Rect frame, @InsetsType int types, boolean ignoreVisibility) {
Insets insets = Insets.NONE;
for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
InsetsSource source = mSources[type];
@@ -314,10 +314,10 @@
}
insets = Insets.max(source.calculateInsets(frame, ignoreVisibility), insets);
}
- return insets.toRect();
+ return insets;
}
- public Rect calculateVisibleInsets(Rect frame, @SoftInputModeFlags int softInputMode) {
+ public Insets calculateVisibleInsets(Rect frame, @SoftInputModeFlags int softInputMode) {
Insets insets = Insets.NONE;
for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
InsetsSource source = mSources[type];
@@ -332,7 +332,7 @@
}
insets = Insets.max(source.calculateVisibleInsets(frame), insets);
}
- return insets.toRect();
+ return insets;
}
/**
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index d6186d7..9972eba 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -23,6 +23,7 @@
import static android.graphics.Matrix.MTRANS_X;
import static android.graphics.Matrix.MTRANS_Y;
import static android.view.SurfaceControlProto.HASH_CODE;
+import static android.view.SurfaceControlProto.LAYER_ID;
import static android.view.SurfaceControlProto.NAME;
import android.annotation.FloatRange;
@@ -239,6 +240,7 @@
private static native int nativeGetGPUContextPriority();
private static native void nativeSetTransformHint(long nativeObject, int transformHint);
private static native int nativeGetTransformHint(long nativeObject);
+ private static native int nativeGetLayerId(long nativeObject);
@Nullable
@GuardedBy("mLock")
@@ -354,8 +356,6 @@
@GuardedBy("mLock")
private int mHeight;
- private int mTransformHint;
-
private WeakReference<View> mLocalOwnerView;
static GlobalTransactionWrapper sGlobalTransaction;
@@ -1538,6 +1538,7 @@
final long token = proto.start(fieldId);
proto.write(HASH_CODE, System.identityHashCode(this));
proto.write(NAME, mName);
+ proto.write(LAYER_ID, getLayerId());
proto.end(token);
}
@@ -3652,4 +3653,15 @@
public void setTransformHint(@Surface.Rotation int transformHint) {
nativeSetTransformHint(mNativeObject, transformHint);
}
+
+ /**
+ * @hide
+ */
+ public int getLayerId() {
+ if (mNativeObject != 0) {
+ return nativeGetLayerId(mNativeObject);
+ }
+
+ return -1;
+ }
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index a4e7a43..643c1bc 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1889,10 +1889,12 @@
public void setChildSurfacePackage(@NonNull SurfaceControlViewHost.SurfacePackage p) {
final SurfaceControl lastSc = mSurfacePackage != null ?
mSurfacePackage.getSurfaceControl() : null;
- if (mSurfaceControl != null && lastSc != null) {
- mTmpTransaction.reparent(lastSc, null).apply();
- mSurfacePackage.release();
- } else if (mSurfaceControl != null) {
+ if (mSurfaceControl != null) {
+ if (lastSc != null) {
+ mTmpTransaction.reparent(lastSc, null);
+ mSurfacePackage.release();
+ }
+
reparentSurfacePackage(mTmpTransaction, p);
mTmpTransaction.apply();
}
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 495edab..9ed42f3 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -433,9 +433,8 @@
mAmbiguousGestureMultiplier = Math.max(1.0f, multiplierValue.getFloat());
// Size of the screen in bytes, in ARGB_8888 format
- final WindowManager windowManager = context.getSystemService(WindowManager.class);
- final Rect maxWindowBounds = windowManager.getMaximumWindowMetrics().getBounds();
- mMaximumDrawingCacheSize = 4 * maxWindowBounds.width() * maxWindowBounds.height();
+ final Rect maxBounds = config.windowConfiguration.getMaxBounds();
+ mMaximumDrawingCacheSize = 4 * maxBounds.width() * maxBounds.height();
mOverscrollDistance = (int) (sizeAndDensity * OVERSCROLL_DISTANCE + 0.5f);
mOverflingDistance = (int) (sizeAndDensity * OVERFLING_DISTANCE + 0.5f);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0280be4..a8b21fb 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2428,7 +2428,7 @@
mAttachInfo.mContentInsets.set(mLastWindowInsets.getSystemWindowInsets().toRect());
mAttachInfo.mStableInsets.set(mLastWindowInsets.getStableInsets().toRect());
mAttachInfo.mVisibleInsets.set(mInsetsController.calculateVisibleInsets(
- mWindowAttributes.softInputMode));
+ mWindowAttributes.softInputMode).toRect());
}
return mLastWindowInsets;
}
diff --git a/core/java/android/view/WindowInsetsAnimationController.java b/core/java/android/view/WindowInsetsAnimationController.java
index 6578e9b..bdfeb74 100644
--- a/core/java/android/view/WindowInsetsAnimationController.java
+++ b/core/java/android/view/WindowInsetsAnimationController.java
@@ -188,4 +188,20 @@
* fullscreen or non-overlapping).
*/
boolean hasZeroInsetsIme();
+
+ /**
+ * Flags whether {@link WindowInsetsAnimationControlListener#onReady(
+ * WindowInsetsAnimationController, int)} has been invoked.
+ * @hide
+ */
+ void setReadyDispatched(boolean dispatched);
+
+ /**
+ * Returns the {@link InsetsState} based on the current animation progress.
+ *
+ * @param outState the insets state which matches the current animation progress.
+ * @return {@code true} if the animation has been finished; {@code false} otherwise.
+ * @hide
+ */
+ boolean applyChangeInsets(InsetsState outState);
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index eb6bc58..98947d2 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -333,6 +333,12 @@
int TRANSIT_OLD_TASK_FRAGMENT_CLOSE = 29;
/**
+ * A window of task fragment is changing bounds.
+ * @hide
+ */
+ int TRANSIT_OLD_TASK_FRAGMENT_CHANGE = 30;
+
+ /**
* @hide
*/
@IntDef(prefix = { "TRANSIT_OLD_" }, value = {
@@ -359,7 +365,8 @@
TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE,
TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE,
TRANSIT_OLD_TASK_FRAGMENT_OPEN,
- TRANSIT_OLD_TASK_FRAGMENT_CLOSE
+ TRANSIT_OLD_TASK_FRAGMENT_CLOSE,
+ TRANSIT_OLD_TASK_FRAGMENT_CHANGE
})
@Retention(RetentionPolicy.SOURCE)
@interface TransitionOldType {}
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index a2d3e34..7bd156b 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
@@ -300,7 +301,6 @@
}
}
- // TODO(b/150095967): Set window type to LayoutParams
private WindowInsets computeWindowInsets(Rect bounds) {
// Initialize params which used for obtaining all system insets.
final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
@@ -322,10 +322,10 @@
.getWindowInsets(attrs, mContext.getDisplayId(), insetsState);
final Configuration config = mContext.getResources().getConfiguration();
final boolean isScreenRound = config.isScreenRound();
- final int windowingMode = config.windowConfiguration.getWindowingMode();
return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState*/,
isScreenRound, alwaysConsumeSystemBars, SOFT_INPUT_ADJUST_NOTHING, attrs.flags,
- SYSTEM_UI_FLAG_VISIBLE, attrs.type, windowingMode, null /* typeSideMap */);
+ SYSTEM_UI_FLAG_VISIBLE, attrs.type, WINDOWING_MODE_FULLSCREEN,
+ null /* typeSideMap */);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
index d14dc6e..dc01990 100644
--- a/core/java/android/view/accessibility/AccessibilityCache.java
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -16,6 +16,9 @@
package android.view.accessibility;
+
+import static android.view.accessibility.AccessibilityNodeInfo.FOCUS_ACCESSIBILITY;
+
import android.os.Build;
import android.util.ArraySet;
import android.util.Log;
@@ -70,6 +73,7 @@
private long mInputFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
private int mAccessibilityFocusedWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+ private int mInputFocusWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
private boolean mIsAllWindowsCached;
@@ -190,6 +194,7 @@
removeCachedNodeLocked(event.getWindowId(), mInputFocus);
}
mInputFocus = event.getSourceNodeId();
+ mInputFocusWindow = event.getWindowId();
nodeToRefresh = removeCachedNodeLocked(event.getWindowId(), mInputFocus);
} break;
@@ -439,6 +444,7 @@
}
if (clone.isFocused()) {
mInputFocus = sourceId;
+ mInputFocusWindow = windowId;
}
}
}
@@ -462,6 +468,7 @@
mInputFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
mAccessibilityFocusedWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+ mInputFocusWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
}
}
@@ -486,6 +493,87 @@
}
/**
+ * Gets a cached {@link AccessibilityNodeInfo} with focus according to focus type.
+ *
+ * Note: {@link android.view.accessibility.AccessibilityWindowInfo#ACTIVE_WINDOW_ID} will return
+ * null.
+ *
+ * @param focusType The focus type.
+ * @param windowId A unique window id.
+ * @param initialNodeId A unique view id or virtual descendant id from where to start the
+ * search.
+ * @return The cached {@link AccessibilityNodeInfo} if it has a11y focus or null if such not
+ * found.
+ */
+ public AccessibilityNodeInfo getFocus(int focusType, long initialNodeId, int windowId) {
+ synchronized (mLock) {
+ int currentFocusWindowId;
+ long currentFocusId;
+ if (focusType == FOCUS_ACCESSIBILITY) {
+ currentFocusWindowId = mAccessibilityFocusedWindow;
+ currentFocusId = mAccessibilityFocus;
+ } else {
+ currentFocusWindowId = mInputFocusWindow;
+ currentFocusId = mInputFocus;
+ }
+
+ if (currentFocusWindowId == AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
+ return null;
+ }
+
+ if (windowId != AccessibilityWindowInfo.ANY_WINDOW_ID
+ && windowId != currentFocusWindowId) {
+ return null;
+ }
+
+ LongSparseArray<AccessibilityNodeInfo> nodes =
+ mNodeCache.get(currentFocusWindowId);
+ if (nodes == null) {
+ return null;
+ }
+
+ final AccessibilityNodeInfo currentFocusedNode = nodes.get(currentFocusId);
+ if (currentFocusedNode == null) {
+ return null;
+ }
+
+ if (initialNodeId == currentFocusId || (isCachedNodeOrDescendantLocked(
+ currentFocusedNode.getParentNodeId(), initialNodeId, nodes))) {
+ if (VERBOSE) {
+ Log.i(LOG_TAG, "getFocus(0x" + Long.toHexString(currentFocusId) + ") = "
+ + currentFocusedNode + " with type: "
+ + (focusType == FOCUS_ACCESSIBILITY
+ ? "FOCUS_ACCESSIBILITY"
+ : "FOCUS_INPUT"));
+ }
+ // Return a copy since the client calls to AccessibilityNodeInfo#recycle()
+ // will wipe the data of the cached info.
+ return new AccessibilityNodeInfo(currentFocusedNode);
+ }
+
+ if (VERBOSE) {
+ Log.i(LOG_TAG, "getFocus is null with type: "
+ + (focusType == FOCUS_ACCESSIBILITY
+ ? "FOCUS_ACCESSIBILITY"
+ : "FOCUS_INPUT"));
+ }
+ return null;
+ }
+ }
+
+ private boolean isCachedNodeOrDescendantLocked(long nodeId, long ancestorId,
+ LongSparseArray<AccessibilityNodeInfo> nodes) {
+ if (ancestorId == nodeId) {
+ return true;
+ }
+ AccessibilityNodeInfo node = nodes.get(nodeId);
+ if (node == null) {
+ return false;
+ }
+ return isCachedNodeOrDescendantLocked(node.getParentNodeId(), ancestorId, nodes);
+ }
+
+ /**
* Clears nodes for the window with the given id
*/
private void clearNodesForWindowLocked(int windowId) {
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 80ef6cf..6975bb2 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -22,6 +22,7 @@
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Binder;
@@ -114,8 +115,7 @@
from a window, mapping from windowId -> timestamp. */
private static final SparseLongArray sScrollingWindows = new SparseLongArray();
- private static AccessibilityCache sAccessibilityCache =
- new AccessibilityCache(new AccessibilityCache.AccessibilityNodeRefresher());
+ private static AccessibilityCache sAccessibilityCache;
private final AtomicInteger mInteractionIdCounter = new AtomicInteger();
@@ -150,7 +150,7 @@
@UnsupportedAppUsage()
public static AccessibilityInteractionClient getInstance() {
final long threadId = Thread.currentThread().getId();
- return getInstanceForThread(threadId);
+ return getInstanceForThread(threadId, true);
}
/**
@@ -161,11 +161,17 @@
*
* @return The client for a given <code>threadId</code>.
*/
- public static AccessibilityInteractionClient getInstanceForThread(long threadId) {
+ public static AccessibilityInteractionClient getInstanceForThread(long threadId,
+ boolean initializeCache) {
synchronized (sStaticLock) {
AccessibilityInteractionClient client = sClients.get(threadId);
if (client == null) {
- client = new AccessibilityInteractionClient();
+ if (Binder.getCallingUid() == Process.SYSTEM_UID) {
+ // Don't initialize a cache for the system process
+ client = new AccessibilityInteractionClient(false);
+ } else {
+ client = new AccessibilityInteractionClient(initializeCache);
+ }
sClients.put(threadId, client);
}
return client;
@@ -176,11 +182,20 @@
* @return The client for the current thread.
*/
public static AccessibilityInteractionClient getInstance(Context context) {
+ return getInstance(/* initializeCache= */true, context);
+ }
+
+ /**
+ * @param initializeCache whether to initialize the cache in a new client instance
+ * @return The client for the current thread.
+ */
+ public static AccessibilityInteractionClient getInstance(boolean initializeCache,
+ Context context) {
final long threadId = Thread.currentThread().getId();
if (context != null) {
- return getInstanceForThread(threadId, context);
+ return getInstanceForThread(threadId, initializeCache, context);
}
- return getInstanceForThread(threadId);
+ return getInstanceForThread(threadId, initializeCache);
}
/**
@@ -189,14 +204,19 @@
* We do not have a thread local variable since other threads should be able to
* look up the correct client knowing a thread id. See ViewRootImpl for details.
*
+ * @param initializeCache whether to initialize the cache in a new client instance
* @return The client for a given <code>threadId</code>.
*/
public static AccessibilityInteractionClient getInstanceForThread(
- long threadId, Context context) {
+ long threadId, boolean initializeCache, Context context) {
synchronized (sStaticLock) {
AccessibilityInteractionClient client = sClients.get(threadId);
if (client == null) {
- client = new AccessibilityInteractionClient(context);
+ if (Binder.getCallingUid() == Process.SYSTEM_UID) {
+ client = new AccessibilityInteractionClient(false, context);
+ } else {
+ client = new AccessibilityInteractionClient(initializeCache, context);
+ }
sClients.put(threadId, client);
}
return client;
@@ -249,13 +269,26 @@
private AccessibilityInteractionClient() {
/* reducing constructor visibility */
+ this(true);
+ }
+
+ private AccessibilityInteractionClient(boolean initializeCache) {
+ initializeCache(initializeCache);
mAccessibilityManager = null;
}
- private AccessibilityInteractionClient(Context context) {
+ private AccessibilityInteractionClient(boolean initializeCache, Context context) {
+ initializeCache(initializeCache);
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
}
+ private static void initializeCache(boolean initialize) {
+ if (initialize && sAccessibilityCache == null) {
+ sAccessibilityCache = new AccessibilityCache(
+ new AccessibilityCache.AccessibilityNodeRefresher());
+ }
+ }
+
/**
* Sets the message to be processed if the interacted view hierarchy
* and the interacting client are running in the same thread.
@@ -311,7 +344,7 @@
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
AccessibilityWindowInfo window;
- if (!bypassCache) {
+ if (!bypassCache && sAccessibilityCache != null) {
window = sAccessibilityCache.getWindow(accessibilityWindowId);
if (window != null) {
if (DEBUG) {
@@ -341,7 +374,7 @@
+ bypassCache);
}
- if (window != null) {
+ if (window != null && sAccessibilityCache != null) {
if (!bypassCache) {
sAccessibilityCache.addWindow(window);
}
@@ -384,21 +417,24 @@
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
- SparseArray<List<AccessibilityWindowInfo>> windows =
- sAccessibilityCache.getWindowsOnAllDisplays();
- if (windows != null) {
+ SparseArray<List<AccessibilityWindowInfo>> windows;
+ if (sAccessibilityCache != null) {
+ windows = sAccessibilityCache.getWindowsOnAllDisplays();
+ if (windows != null) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Windows cache hit");
+ }
+ if (shouldTraceClient()) {
+ logTraceClient(
+ connection, "getWindows cache", "connectionId=" + connectionId);
+ }
+ return windows;
+ }
if (DEBUG) {
- Log.i(LOG_TAG, "Windows cache hit");
+ Log.i(LOG_TAG, "Windows cache miss");
}
- if (shouldTraceClient()) {
- logTraceClient(
- connection, "getWindows cache", "connectionId=" + connectionId);
- }
- return windows;
}
- if (DEBUG) {
- Log.i(LOG_TAG, "Windows cache miss");
- }
+
final long identityToken = Binder.clearCallingIdentity();
try {
windows = connection.getWindows();
@@ -409,7 +445,9 @@
logTraceClient(connection, "getWindows", "connectionId=" + connectionId);
}
if (windows != null) {
- sAccessibilityCache.setWindowsOnAllDisplays(windows);
+ if (sAccessibilityCache != null) {
+ sAccessibilityCache.setWindowsOnAllDisplays(windows);
+ }
return windows;
}
} else {
@@ -493,7 +531,7 @@
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
- if (!bypassCache) {
+ if (!bypassCache && sAccessibilityCache != null) {
AccessibilityNodeInfo cachedInfo = sAccessibilityCache.getNode(
accessibilityWindowId, accessibilityNodeId);
if (cachedInfo != null) {
@@ -725,7 +763,9 @@
* @param connectionId The id of a connection for interacting with the system.
* @param accessibilityWindowId A unique window id. Use
* {@link android.view.accessibility.AccessibilityWindowInfo#ACTIVE_WINDOW_ID}
- * to query the currently active window.
+ * to query the currently active window. Use
+ * {@link android.view.accessibility.AccessibilityWindowInfo#ANY_WINDOW_ID} to query all
+ * windows
* @param accessibilityNodeId A unique view id or virtual descendant id from
* where to start the search. Use
* {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
@@ -733,11 +773,28 @@
* @param focusType The focus type.
* @return The accessibility focused {@link AccessibilityNodeInfo}.
*/
+ @SuppressLint("LongLogTag")
public AccessibilityNodeInfo findFocus(int connectionId, int accessibilityWindowId,
long accessibilityNodeId, int focusType) {
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
+ if (sAccessibilityCache != null) {
+ AccessibilityNodeInfo cachedInfo = sAccessibilityCache.getFocus(focusType,
+ accessibilityNodeId, accessibilityWindowId);
+ if (cachedInfo != null) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Focused node cache hit retrieved"
+ + idToString(cachedInfo.getWindowId(),
+ cachedInfo.getSourceNodeId()));
+ }
+ return cachedInfo;
+ }
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Focused node cache miss with "
+ + idToString(accessibilityWindowId, accessibilityNodeId));
+ }
+ }
final int interactionId = mInteractionIdCounter.getAndIncrement();
if (shouldTraceClient()) {
logTraceClient(connection, "findFocus",
@@ -901,7 +958,9 @@
*/
@UnsupportedAppUsage()
public void clearCache() {
- sAccessibilityCache.clear();
+ if (sAccessibilityCache != null) {
+ sAccessibilityCache.clear();
+ }
}
public void onAccessibilityEvent(AccessibilityEvent event) {
@@ -917,7 +976,9 @@
default:
break;
}
- sAccessibilityCache.onAccessibilityEvent(event);
+ if (sAccessibilityCache != null) {
+ sAccessibilityCache.onAccessibilityEvent(event);
+ }
}
/**
@@ -1153,7 +1214,7 @@
}
}
info.setSealed(true);
- if (!bypassCache) {
+ if (!bypassCache && sAccessibilityCache != null) {
sAccessibilityCache.add(info);
}
}
diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl
index 0ca8a86..1ad0452 100644
--- a/core/java/android/window/ITaskFragmentOrganizerController.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizerController.aidl
@@ -16,6 +16,7 @@
package android.window;
+import android.view.RemoteAnimationDefinition;
import android.window.ITaskFragmentOrganizer;
/** @hide */
@@ -30,4 +31,17 @@
* Unregisters a previously registered TaskFragmentOrganizer.
*/
void unregisterOrganizer(in ITaskFragmentOrganizer organizer);
+
+ /**
+ * Registers remote animations per transition type for the organizer. It will override the
+ * animations if the transition only contains windows that belong to the organized
+ * TaskFragments.
+ */
+ void registerRemoteAnimations(in ITaskFragmentOrganizer organizer,
+ in RemoteAnimationDefinition definition);
+
+ /**
+ * Unregisters remote animations per transition type for the organizer.
+ */
+ void unregisterRemoteAnimations(in ITaskFragmentOrganizer organizer);
}
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index f22f0b2..337c5a1 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -23,6 +23,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.view.RemoteAnimationDefinition;
import java.util.concurrent.Executor;
@@ -90,6 +91,34 @@
}
}
+ /**
+ * Registers remote animations per transition type for the organizer. It will override the
+ * animations if the transition only contains windows that belong to the organized
+ * TaskFragments.
+ * @hide
+ */
+ @CallSuper
+ public void registerRemoteAnimations(@NonNull RemoteAnimationDefinition definition) {
+ try {
+ getController().registerRemoteAnimations(mInterface, definition);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unregisters remote animations per transition type for the organizer.
+ * @hide
+ */
+ @CallSuper
+ public void unregisterRemoteAnimations() {
+ try {
+ getController().unregisterRemoteAnimations(mInterface);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** Called when a TaskFragment is created and organized by this organizer. */
public void onTaskFragmentAppeared(
@NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {}
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
index 179ac8b..a611d65d 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
@@ -39,6 +39,9 @@
import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_FULL_SCREEN;
import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_UNKNOWN_MODE;
import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_WINDOW;
+import static com.android.internal.util.FrameworkStatsLog.NON_A11Y_TOOL_SERVICE_WARNING_REPORTED__STATUS__WARNING_CLICKED;
+import static com.android.internal.util.FrameworkStatsLog.NON_A11Y_TOOL_SERVICE_WARNING_REPORTED__STATUS__WARNING_SERVICE_DISABLED;
+import static com.android.internal.util.FrameworkStatsLog.NON_A11Y_TOOL_SERVICE_WARNING_REPORTED__STATUS__WARNING_SHOWN;
import android.content.ComponentName;
import android.content.Context;
@@ -50,6 +53,16 @@
/** Methods for logging accessibility states. */
public final class AccessibilityStatsLogUtils {
+ /** The status represents an accessibility privacy warning has been shown. */
+ public static int ACCESSIBILITY_PRIVACY_WARNING_STATUS_SHOWN =
+ NON_A11Y_TOOL_SERVICE_WARNING_REPORTED__STATUS__WARNING_SHOWN;
+ /** The status represents an accessibility privacy warning has been clicked to review. */
+ public static int ACCESSIBILITY_PRIVACY_WARNING_STATUS_CLICKED =
+ NON_A11Y_TOOL_SERVICE_WARNING_REPORTED__STATUS__WARNING_CLICKED;
+ /** The status represents an accessibility privacy warning service has been disabled. */
+ public static int ACCESSIBILITY_PRIVACY_WARNING_STATUS_SERVICE_DISABLED =
+ NON_A11Y_TOOL_SERVICE_WARNING_REPORTED__STATUS__WARNING_SERVICE_DISABLED;
+
private static final int UNKNOWN_STATUS =
ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__UNKNOWN;
@@ -154,6 +167,23 @@
convertToLoggingMagnificationMode(mode));
}
+ /**
+ * Logs the warning status of the non-a11yTool service. Calls this when the warning status is
+ * changed.
+ *
+ * @param packageName The package name of the non-a11yTool service
+ * @param status The warning status of the non-a11yTool service, it should be one of
+ * {@code ACCESSIBILITY_PRIVACY_WARNING_STATUS_SHOWN},{@code
+ * ACCESSIBILITY_PRIVACY_WARNING_STATUS_CLICKED} and {@code
+ * ACCESSIBILITY_PRIVACY_WARNING_STATUS_SERVICE_DISABLED}
+ * @param durationMillis The duration in milliseconds between current and previous status
+ */
+ public static void logNonA11yToolServiceWarningReported(String packageName, int status,
+ long durationMillis) {
+ FrameworkStatsLog.write(FrameworkStatsLog.NON_A11Y_TOOL_SERVICE_WARNING_REPORT,
+ packageName, status, durationMillis);
+ }
+
private static boolean isAccessibilityFloatingMenuEnabled(Context context) {
return Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, /* def= */ -1)
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 35d25ca..4d0f4c2 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -45,7 +45,6 @@
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
-import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -71,11 +70,9 @@
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
-import android.os.IBinder;
import android.os.Message;
import android.os.Parcelable;
import android.os.PatternMatcher;
-import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.UserHandle;
import android.os.UserManager;
@@ -86,14 +83,10 @@
import android.provider.OpenableColumns;
import android.provider.Settings;
import android.service.chooser.ChooserTarget;
-import android.service.chooser.ChooserTargetService;
-import android.service.chooser.IChooserTargetResult;
-import android.service.chooser.IChooserTargetService;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.HashedStringCache;
import android.util.Log;
-import android.util.Pair;
import android.util.Size;
import android.util.Slog;
import android.view.LayoutInflater;
@@ -145,10 +138,8 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Set;
/**
* The Chooser Activity handles intent resolution specifically for sharing intents -
@@ -206,7 +197,6 @@
private boolean mIsAppPredictorComponentAvailable;
private Map<ChooserTarget, AppTarget> mDirectShareAppTargetCache;
private Map<ChooserTarget, ShortcutInfo> mDirectShareShortcutInfoCache;
- private Map<ComponentName, ComponentName> mChooserTargetComponentNameCache;
public static final int TARGET_TYPE_DEFAULT = 0;
public static final int TARGET_TYPE_CHOOSER_TARGET = 1;
@@ -227,8 +217,6 @@
// statsd logger wrapper
protected ChooserActivityLogger mChooserActivityLogger;
- private static final boolean USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS = true;
-
@IntDef(flag = false, prefix = { "TARGET_TYPE_" }, value = {
TARGET_TYPE_DEFAULT,
TARGET_TYPE_CHOOSER_TARGET,
@@ -268,7 +256,6 @@
private long mChooserShownTime;
protected boolean mIsSuccessfullySelected;
- private long mQueriedTargetServicesTimeMs;
private long mQueriedSharingShortcutsTimeMs;
private int mChooserRowServiceSpacing;
@@ -278,9 +265,6 @@
private static final String TARGET_DETAILS_FRAGMENT_TAG = "targetDetailsFragment";
- private final List<ChooserTargetServiceConnection> mServiceConnections = new ArrayList<>();
- private final Set<Pair<ComponentName, UserHandle>> mServicesRequested = new HashSet<>();
-
private static final int MAX_LOG_RANK_POSITION = 12;
private static final int MAX_EXTRA_INITIAL_INTENTS = 2;
@@ -457,16 +441,12 @@
private final ChooserHandler mChooserHandler = new ChooserHandler();
private class ChooserHandler extends Handler {
- private static final int CHOOSER_TARGET_SERVICE_RESULT = 1;
private static final int SHORTCUT_MANAGER_SHARE_TARGET_RESULT = 4;
private static final int SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED = 5;
private static final int LIST_VIEW_UPDATE_MESSAGE = 6;
- private boolean mReceivedDirectShareTargets = false;
-
private void removeAllMessages() {
removeMessages(LIST_VIEW_UPDATE_MESSAGE);
- removeMessages(CHOOSER_TARGET_SERVICE_RESULT);
removeMessages(SHORTCUT_MANAGER_SHARE_TARGET_RESULT);
removeMessages(SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED);
}
@@ -478,36 +458,6 @@
}
switch (msg.what) {
- case CHOOSER_TARGET_SERVICE_RESULT:
- if (DEBUG) Log.d(TAG, "CHOOSER_TARGET_SERVICE_RESULT");
- final ServiceResultInfo sri = (ServiceResultInfo) msg.obj;
- if (!mServiceConnections.contains(sri.connection)) {
- Log.w(TAG, "ChooserTargetServiceConnection " + sri.connection
- + sri.originalTarget.getResolveInfo().activityInfo.packageName
- + " returned after being removed from active connections."
- + " Have you considered returning results faster?");
- break;
- }
- if (sri.resultTargets != null) {
- ChooserListAdapter adapterForUserHandle =
- mChooserMultiProfilePagerAdapter.getListAdapterForUserHandle(
- sri.userHandle);
- if (adapterForUserHandle != null) {
- adapterForUserHandle.addServiceResults(sri.originalTarget,
- sri.resultTargets, TARGET_TYPE_CHOOSER_TARGET,
- /* directShareShortcutInfoCache */ null);
- if (!sri.resultTargets.isEmpty() && sri.originalTarget != null) {
- mChooserTargetComponentNameCache.put(
- sri.resultTargets.get(0).getComponentName(),
- sri.originalTarget.getResolvedComponentName());
- }
- }
- }
- unbindService(sri.connection);
- sri.connection.destroy();
- mServiceConnections.remove(sri.connection);
- break;
-
case LIST_VIEW_UPDATE_MESSAGE:
if (DEBUG) {
Log.d(TAG, "LIST_VIEW_UPDATE_MESSAGE; ");
@@ -752,7 +702,6 @@
target.getAction()
);
mDirectShareShortcutInfoCache = new HashMap<>();
- mChooserTargetComponentNameCache = new HashMap<>();
setEnterSharedElementCallback(new SharedElementCallback() {
@Override
@@ -1592,7 +1541,6 @@
mRefinementResultReceiver.destroy();
mRefinementResultReceiver = null;
}
- unbindRemainingServices();
mChooserHandler.removeAllMessages();
if (mPreviewCoord != null) mPreviewCoord.cancelLoads();
@@ -1887,96 +1835,6 @@
}
}
- @VisibleForTesting
- protected void queryTargetServices(ChooserListAdapter adapter) {
-
- mQueriedTargetServicesTimeMs = System.currentTimeMillis();
-
- Context selectedProfileContext = createContextAsUser(
- adapter.getUserHandle(), 0 /* flags */);
- final PackageManager pm = selectedProfileContext.getPackageManager();
- ShortcutManager sm = selectedProfileContext.getSystemService(ShortcutManager.class);
- int targetsToQuery = 0;
-
- for (int i = 0, N = adapter.getDisplayResolveInfoCount(); i < N; i++) {
- final DisplayResolveInfo dri = adapter.getDisplayResolveInfo(i);
- if (adapter.getScore(dri) == 0) {
- // A score of 0 means the app hasn't been used in some time;
- // don't query it as it's not likely to be relevant.
- continue;
- }
- final ActivityInfo ai = dri.getResolveInfo().activityInfo;
- if (sm.hasShareTargets(ai.packageName)) {
- // Share targets will be queried from ShortcutManager
- continue;
- }
- final Bundle md = ai.metaData;
- final String serviceName = md != null ? convertServiceName(ai.packageName,
- md.getString(ChooserTargetService.META_DATA_NAME)) : null;
- if (serviceName != null && ChooserFlags.USE_SERVICE_TARGETS_FOR_DIRECT_TARGETS) {
- final ComponentName serviceComponent = new ComponentName(
- ai.packageName, serviceName);
-
- UserHandle userHandle = adapter.getUserHandle();
- Pair<ComponentName, UserHandle> requestedItem =
- new Pair<>(serviceComponent, userHandle);
- if (mServicesRequested.contains(requestedItem)) {
- continue;
- }
- mServicesRequested.add(requestedItem);
-
- final Intent serviceIntent = new Intent(ChooserTargetService.SERVICE_INTERFACE)
- .setComponent(serviceComponent);
-
- if (DEBUG) {
- Log.d(TAG, "queryTargets found target with service " + serviceComponent);
- }
-
- try {
- final String perm = pm.getServiceInfo(serviceComponent, 0).permission;
- if (!ChooserTargetService.BIND_PERMISSION.equals(perm)) {
- Log.w(TAG, "ChooserTargetService " + serviceComponent + " does not require"
- + " permission " + ChooserTargetService.BIND_PERMISSION
- + " - this service will not be queried for ChooserTargets."
- + " add android:permission=\""
- + ChooserTargetService.BIND_PERMISSION + "\""
- + " to the <service> tag for " + serviceComponent
- + " in the manifest.");
- continue;
- }
- } catch (NameNotFoundException e) {
- Log.e(TAG, "Could not look up service " + serviceComponent
- + "; component name not found");
- continue;
- }
-
- final ChooserTargetServiceConnection conn =
- new ChooserTargetServiceConnection(this, dri,
- adapter.getUserHandle());
-
- // Explicitly specify the user handle instead of calling bindService
- // to avoid the warning from calling from the system process without an explicit
- // user handle
- if (bindServiceAsUser(serviceIntent, conn, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND,
- adapter.getUserHandle())) {
- if (DEBUG) {
- Log.d(TAG, "Binding service connection for target " + dri
- + " intent " + serviceIntent);
- }
- mServiceConnections.add(conn);
- targetsToQuery++;
- }
- }
- if (targetsToQuery >= QUERY_TARGET_SERVICE_LIMIT) {
- if (DEBUG) {
- Log.d(TAG, "queryTargets hit query target limit "
- + QUERY_TARGET_SERVICE_LIMIT);
- }
- break;
- }
- }
- }
-
private IntentFilter getTargetIntentFilter() {
try {
final Intent intent = getTargetIntent();
@@ -2107,7 +1965,7 @@
final Message msg = Message.obtain();
msg.what = ChooserHandler.SHORTCUT_MANAGER_SHARE_TARGET_RESULT;
msg.obj = new ServiceResultInfo(chooserListAdapter.getDisplayResolveInfo(i),
- chooserTargets, null, userHandle);
+ chooserTargets, userHandle);
msg.arg1 = shortcutType;
mChooserHandler.sendMessage(msg);
}
@@ -2226,25 +2084,8 @@
return fullName;
}
- void unbindRemainingServices() {
- if (DEBUG) {
- Log.d(TAG, "unbindRemainingServices, " + mServiceConnections.size() + " left");
- }
- for (int i = 0, N = mServiceConnections.size(); i < N; i++) {
- final ChooserTargetServiceConnection conn = mServiceConnections.get(i);
- if (DEBUG) Log.d(TAG, "unbinding " + conn);
- unbindService(conn);
- conn.destroy();
- }
- mServicesRequested.clear();
- mServiceConnections.clear();
- }
-
private void logDirectShareTargetReceived(int logCategory) {
- final long queryTime =
- logCategory == MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_SHORTCUT_MANAGER
- ? mQueriedSharingShortcutsTimeMs : mQueriedTargetServicesTimeMs;
- final int apiLatency = (int) (System.currentTimeMillis() - queryTime);
+ final int apiLatency = (int) (System.currentTimeMillis() - mQueriedSharingShortcutsTimeMs);
getMetricsLogger().write(new LogMaker(logCategory).setSubtype(apiLatency));
}
@@ -2290,8 +2131,7 @@
List<AppTargetId> targetIds = new ArrayList<>();
for (ChooserTargetInfo chooserTargetInfo : surfacedTargetInfo) {
ChooserTarget chooserTarget = chooserTargetInfo.getChooserTarget();
- ComponentName componentName = mChooserTargetComponentNameCache.getOrDefault(
- chooserTarget.getComponentName(), chooserTarget.getComponentName());
+ ComponentName componentName = chooserTarget.getComponentName();
if (mDirectShareShortcutInfoCache.containsKey(chooserTarget)) {
String shortcutId = mDirectShareShortcutInfoCache.get(chooserTarget).getId();
targetIds.add(new AppTargetId(
@@ -2782,7 +2622,6 @@
@Override // ResolverListCommunicator
public void onHandlePackagesChanged(ResolverListAdapter listAdapter) {
- mServicesRequested.clear();
mChooserMultiProfilePagerAdapter.getActiveListAdapter().notifyDataSetChanged();
super.onHandlePackagesChanged(listAdapter);
}
@@ -2850,13 +2689,6 @@
queryDirectShareTargets(chooserListAdapter, false);
}
- if (USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS) {
- if (DEBUG) {
- Log.d(TAG, "List built querying services");
- }
-
- queryTargetServices(chooserListAdapter);
- }
getChooserActivityLogger().logSharesheetAppLoadComplete();
}
@@ -3897,116 +3729,15 @@
}
}
- static class ChooserTargetServiceConnection implements ServiceConnection {
- private DisplayResolveInfo mOriginalTarget;
- private ComponentName mConnectedComponent;
- private ChooserActivity mChooserActivity;
- private final UserHandle mUserHandle;
- private final Object mLock = new Object();
-
- private final IChooserTargetResult mChooserTargetResult = new IChooserTargetResult.Stub() {
- @Override
- public void sendResult(List<ChooserTarget> targets) throws RemoteException {
- synchronized (mLock) {
- if (mChooserActivity == null) {
- Log.e(TAG, "destroyed ChooserTargetServiceConnection received result from "
- + mConnectedComponent + "; ignoring...");
- return;
- }
- Context contextAsUser =
- mChooserActivity.createContextAsUser(mUserHandle, 0 /* flags */);
- mChooserActivity.filterServiceTargets(contextAsUser,
- mOriginalTarget.getResolveInfo().activityInfo.packageName, targets);
- final Message msg = Message.obtain();
- msg.what = ChooserHandler.CHOOSER_TARGET_SERVICE_RESULT;
- msg.obj = new ServiceResultInfo(mOriginalTarget, targets,
- ChooserTargetServiceConnection.this, mUserHandle);
- mChooserActivity.mChooserHandler.sendMessage(msg);
- }
- }
- };
-
- public ChooserTargetServiceConnection(ChooserActivity chooserActivity,
- DisplayResolveInfo dri, UserHandle userHandle) {
- mChooserActivity = chooserActivity;
- mOriginalTarget = dri;
- mUserHandle = userHandle;
- }
-
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- if (DEBUG) Log.d(TAG, "onServiceConnected: " + name);
- synchronized (mLock) {
- if (mChooserActivity == null) {
- Log.e(TAG, "destroyed ChooserTargetServiceConnection got onServiceConnected");
- return;
- }
-
- final IChooserTargetService icts = IChooserTargetService.Stub.asInterface(service);
- try {
- icts.getChooserTargets(mOriginalTarget.getResolvedComponentName(),
- mOriginalTarget.getResolveInfo().filter, mChooserTargetResult);
- } catch (RemoteException e) {
- Log.e(TAG, "Querying ChooserTargetService " + name + " failed.", e);
- mChooserActivity.unbindService(this);
- mChooserActivity.mServiceConnections.remove(this);
- destroy();
- }
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- if (DEBUG) Log.d(TAG, "onServiceDisconnected: " + name);
- synchronized (mLock) {
- if (mChooserActivity == null) {
- Log.e(TAG,
- "destroyed ChooserTargetServiceConnection got onServiceDisconnected");
- return;
- }
-
- mChooserActivity.unbindService(this);
- mChooserActivity.mServiceConnections.remove(this);
- if (mChooserActivity.mServiceConnections.isEmpty()) {
- mChooserActivity.sendVoiceChoicesIfNeeded();
- }
- mConnectedComponent = null;
- destroy();
- }
- }
-
- public void destroy() {
- synchronized (mLock) {
- mChooserActivity = null;
- mOriginalTarget = null;
- }
- }
-
- @Override
- public String toString() {
- return "ChooserTargetServiceConnection{service="
- + mConnectedComponent + ", activity="
- + (mOriginalTarget != null
- ? mOriginalTarget.getResolveInfo().activityInfo.toString()
- : "<connection destroyed>") + "}";
- }
-
- public ComponentName getComponentName() {
- return mOriginalTarget.getResolveInfo().activityInfo.getComponentName();
- }
- }
-
static class ServiceResultInfo {
public final DisplayResolveInfo originalTarget;
public final List<ChooserTarget> resultTargets;
- public final ChooserTargetServiceConnection connection;
public final UserHandle userHandle;
public ServiceResultInfo(DisplayResolveInfo ot, List<ChooserTarget> rt,
- ChooserTargetServiceConnection c, UserHandle userHandle) {
+ UserHandle userHandle) {
originalTarget = ot;
resultTargets = rt;
- connection = c;
this.userHandle = userHandle;
}
}
diff --git a/core/java/com/android/internal/app/ChooserFlags.java b/core/java/com/android/internal/app/ChooserFlags.java
index 1a93f1b..ab6b0f8 100644
--- a/core/java/com/android/internal/app/ChooserFlags.java
+++ b/core/java/com/android/internal/app/ChooserFlags.java
@@ -17,9 +17,6 @@
package com.android.internal.app;
import android.app.prediction.AppPredictionManager;
-import android.provider.DeviceConfig;
-
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
/**
* Common flags for {@link ChooserListAdapter} and {@link ChooserActivity}.
@@ -27,15 +24,6 @@
public class ChooserFlags {
/**
- * Whether to use the deprecated {@link android.service.chooser.ChooserTargetService} API for
- * direct share targets. If true, both CTS and Shortcuts will be used to find Direct Share
- * targets. If false, only Shortcuts will be used.
- */
- public static final boolean USE_SERVICE_TARGETS_FOR_DIRECT_TARGETS =
- DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SHARE_USE_SERVICE_TARGETS, false);
-
- /**
* Whether to use {@link AppPredictionManager} to query for direct share targets (as opposed to
* talking directly to {@link android.content.pm.ShortcutManager}.
*/
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java
index 84354d9..ec224e5 100644
--- a/core/java/com/android/internal/app/SuspendedAppActivity.java
+++ b/core/java/com/android/internal/app/SuspendedAppActivity.java
@@ -72,17 +72,19 @@
private Resources mSuspendingAppResources;
private SuspendDialogInfo mSuppliedDialogInfo;
private Bundle mOptions;
- private BroadcastReceiver mUnsuspendReceiver = new BroadcastReceiver() {
+ private BroadcastReceiver mSuspendModifiedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_PACKAGES_UNSUSPENDED.equals(intent.getAction())) {
- final String[] unsuspended = intent.getStringArrayExtra(
+ if (Intent.ACTION_PACKAGES_SUSPENSION_CHANGED.equals(intent.getAction())) {
+ // Suspension conditions were modified, dismiss any related visible dialogs.
+ final String[] modified = intent.getStringArrayExtra(
Intent.EXTRA_CHANGED_PACKAGE_LIST);
- if (ArrayUtils.contains(unsuspended, mSuspendedPackage)) {
+ if (ArrayUtils.contains(modified, mSuspendedPackage)) {
if (!isFinishing()) {
- Slog.w(TAG, "Package " + mSuspendedPackage
- + " got unsuspended while the dialog was visible. Finishing.");
+ Slog.w(TAG, "Package " + mSuspendedPackage + " has modified"
+ + " suspension conditions while dialog was visible. Finishing.");
SuspendedAppActivity.this.finish();
+ // TODO (b/198201994): reload the suspend dialog to show most relevant info
}
}
}
@@ -245,15 +247,16 @@
setupAlert();
- final IntentFilter unsuspendFilter = new IntentFilter(Intent.ACTION_PACKAGES_UNSUSPENDED);
- registerReceiverAsUser(mUnsuspendReceiver, UserHandle.of(mUserId), unsuspendFilter, null,
- null);
+ final IntentFilter suspendModifiedFilter =
+ new IntentFilter(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED);
+ registerReceiverAsUser(mSuspendModifiedReceiver, UserHandle.of(mUserId),
+ suspendModifiedFilter, null, null);
}
@Override
protected void onDestroy() {
super.onDestroy();
- unregisterReceiver(mUnsuspendReceiver);
+ unregisterReceiver(mSuspendModifiedReceiver);
}
private void requestDismissKeyguardIfNeeded(CharSequence dismissMessage) {
diff --git a/core/java/com/android/internal/inputmethod/CallbackUtils.java b/core/java/com/android/internal/inputmethod/CallbackUtils.java
deleted file mode 100644
index aca761d..0000000
--- a/core/java/com/android/internal/inputmethod/CallbackUtils.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.inputmethod;
-
-import android.annotation.NonNull;
-import android.os.RemoteException;
-
-import java.util.function.BooleanSupplier;
-import java.util.function.IntSupplier;
-import java.util.function.Supplier;
-
-/**
- * Defines a set of helper methods to callback corresponding results in {@link ResultCallbacks}.
- */
-public final class CallbackUtils {
-
- /**
- * Not intended to be instantiated.
- */
- private CallbackUtils() {
- }
-
- /**
- * A utility method using given {@link IBooleanResultCallback} to callback the result.
- *
- * @param callback {@link IBooleanResultCallback} to be called back.
- * @param resultSupplier the supplier from which the result is provided.
- */
- public static void onResult(@NonNull IBooleanResultCallback callback,
- @NonNull BooleanSupplier resultSupplier) {
- boolean result = false;
- Throwable exception = null;
-
- try {
- result = resultSupplier.getAsBoolean();
- } catch (Throwable throwable) {
- exception = throwable;
- }
-
- try {
- if (exception != null) {
- callback.onError(ThrowableHolder.of(exception));
- return;
- }
- callback.onResult(result);
- } catch (RemoteException ignored) { }
- }
-
- /**
- * A utility method using given {@link IIntResultCallback} to callback the result.
- *
- * @param callback {@link IIntResultCallback} to be called back.
- * @param resultSupplier the supplier from which the result is provided.
- */
- public static void onResult(@NonNull IIntResultCallback callback,
- @NonNull IntSupplier resultSupplier) {
- int result = 0;
- Throwable exception = null;
-
- try {
- result = resultSupplier.getAsInt();
- } catch (Throwable throwable) {
- exception = throwable;
- }
-
- try {
- if (exception != null) {
- callback.onError(ThrowableHolder.of(exception));
- return;
- }
- callback.onResult(result);
- } catch (RemoteException ignored) { }
- }
-
- /**
- * A utility method using given {@link IVoidResultCallback} to callback the result.
- *
- * @param callback {@link IVoidResultCallback} to be called back.
- * @param runnable to execute the given method
- */
- public static void onResult(@NonNull IVoidResultCallback callback,
- @NonNull Runnable runnable) {
- Throwable exception = null;
-
- try {
- runnable.run();
- } catch (Throwable throwable) {
- exception = throwable;
- }
-
- try {
- if (exception != null) {
- callback.onError(ThrowableHolder.of(exception));
- return;
- }
- callback.onResult();
- } catch (RemoteException ignored) { }
- }
-
- /**
- * A utility method using given {@link IInputContentUriTokenResultCallback} to callback the
- * result.
- *
- * @param callback {@link IInputContentUriTokenResultCallback} to be called back.
- * @param resultSupplier the supplier from which the result is provided.
- */
- public static void onResult(@NonNull IInputContentUriTokenResultCallback callback,
- @NonNull Supplier<IInputContentUriToken> resultSupplier) {
- IInputContentUriToken result = null;
- Throwable exception = null;
-
- try {
- result = resultSupplier.get();
- } catch (Throwable throwable) {
- exception = throwable;
- }
-
- try {
- if (exception != null) {
- callback.onError(ThrowableHolder.of(exception));
- return;
- }
- callback.onResult(result);
- } catch (RemoteException ignored) { }
- }
-}
diff --git a/core/java/com/android/internal/inputmethod/CancellationGroup.java b/core/java/com/android/internal/inputmethod/CancellationGroup.java
index aef9e3b..3b2e1cd 100644
--- a/core/java/com/android/internal/inputmethod/CancellationGroup.java
+++ b/core/java/com/android/internal/inputmethod/CancellationGroup.java
@@ -23,49 +23,66 @@
import com.android.internal.annotations.GuardedBy;
import java.util.ArrayList;
-import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CompletableFuture;
/**
* A utility class, which works as both a factory class of a cancellation signal to cancel
* all the completable objects.
+ *
+ * <p>TODO: Make this lock-free.</p>
*/
public final class CancellationGroup {
private final Object mLock = new Object();
/**
- * List of {@link CountDownLatch}, which can be used to propagate {@link #cancelAll()} to
+ * List of {@link CompletableFuture}, which can be used to propagate {@link #cancelAll()} to
* completable objects.
*
* <p>This will be lazily instantiated to avoid unnecessary object allocations.</p>
*/
@Nullable
@GuardedBy("mLock")
- private ArrayList<CountDownLatch> mLatchList = null;
+ private ArrayList<CompletableFuture<?>> mFutureList = null;
@GuardedBy("mLock")
private boolean mCanceled = false;
+ /**
+ * Tries to register the given {@link CompletableFuture} into the callback list if this
+ * {@link CancellationGroup} is not yet cancelled.
+ *
+ * <p>If this {@link CancellationGroup} is already cancelled, then this method will immediately
+ * call {@link CompletableFuture#cancel(boolean)} then return {@code false}.</p>
+ *
+ * <p>When this method returns {@code true}, call {@link #unregisterFuture(CompletableFuture)}
+ * to remove the unnecessary object reference.</p>
+ *
+ * @param future {@link CompletableFuture} to be added to the cancellation callback list.
+ * @return {@code true} if the given {@code future} is added to the callback list.
+ * {@code false} otherwise.
+ */
@AnyThread
- boolean registerLatch(@NonNull CountDownLatch latch) {
+ boolean tryRegisterFutureOrCancelImmediately(@NonNull CompletableFuture<?> future) {
synchronized (mLock) {
if (mCanceled) {
+ future.cancel(false);
return false;
}
- if (mLatchList == null) {
+ if (mFutureList == null) {
// Set the initial capacity to 1 with an assumption that usually there is up to 1
// on-going operation.
- mLatchList = new ArrayList<>(1);
+ mFutureList = new ArrayList<>(1);
}
- mLatchList.add(latch);
+ mFutureList.add(future);
return true;
}
}
@AnyThread
- void unregisterLatch(@NonNull CountDownLatch latch) {
+ void unregisterFuture(@NonNull CompletableFuture<?> future) {
synchronized (mLock) {
- if (mLatchList != null) {
- mLatchList.remove(latch);
+ if (mFutureList != null) {
+ mFutureList.remove(future);
}
}
}
@@ -80,10 +97,10 @@
synchronized (mLock) {
if (!mCanceled) {
mCanceled = true;
- if (mLatchList != null) {
- mLatchList.forEach(CountDownLatch::countDown);
- mLatchList.clear();
- mLatchList = null;
+ if (mFutureList != null) {
+ mFutureList.forEach(future -> future.cancel(false));
+ mFutureList.clear();
+ mFutureList = null;
}
}
}
diff --git a/core/java/com/android/internal/inputmethod/Completable.java b/core/java/com/android/internal/inputmethod/Completable.java
deleted file mode 100644
index 132272c..0000000
--- a/core/java/com/android/internal/inputmethod/Completable.java
+++ /dev/null
@@ -1,558 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.inputmethod;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.annotation.AnyThread;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.lang.annotation.Retention;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * An class to consolidate completable object types supported by
- * {@link CancellationGroup}.
- */
-public final class Completable {
-
- /**
- * Not intended to be instantiated.
- */
- private Completable() {
- }
-
- /**
- * Base class of all the completable types supported by {@link CancellationGroup}.
- */
- protected static class ValueBase {
- /**
- * {@link CountDownLatch} to be signaled to unblock
- * {@link #await(int, TimeUnit, CancellationGroup)}.
- */
- private final CountDownLatch mLatch = new CountDownLatch(1);
-
- /**
- * Lock {@link Object} to guard complete operations within this class.
- */
- protected final Object mStateLock = new Object();
-
- /**
- * Indicates the completion state of this object.
- */
- @GuardedBy("mStateLock")
- @CompletionState
- protected int mState = CompletionState.NOT_COMPLETED;
-
- /**
- * {@link Throwable} message passed to {@link #onError(ThrowableHolder)}.
- *
- * <p>This is not {@code null} only when {@link #mState} is
- * {@link CompletionState#COMPLETED_WITH_ERROR}.</p>
- */
- @GuardedBy("mStateLock")
- @Nullable
- protected String mMessage = null;
-
- @Retention(SOURCE)
- @IntDef({
- CompletionState.NOT_COMPLETED,
- CompletionState.COMPLETED_WITH_VALUE,
- CompletionState.COMPLETED_WITH_ERROR})
- protected @interface CompletionState {
- /**
- * This object is not completed yet.
- */
- int NOT_COMPLETED = 0;
- /**
- * This object is already completed with a value.
- */
- int COMPLETED_WITH_VALUE = 1;
- /**
- * This object is already completed with an error.
- */
- int COMPLETED_WITH_ERROR = 2;
- }
-
- /**
- * Converts the given {@link CompletionState} into a human-readable string.
- *
- * @param state {@link CompletionState} to be converted.
- * @return a human-readable {@link String} for the given {@code state}.
- */
- @AnyThread
- protected static String stateToString(@CompletionState int state) {
- switch (state) {
- case CompletionState.NOT_COMPLETED:
- return "NOT_COMPLETED";
- case CompletionState.COMPLETED_WITH_VALUE:
- return "COMPLETED_WITH_VALUE";
- case CompletionState.COMPLETED_WITH_ERROR:
- return "COMPLETED_WITH_ERROR";
- default:
- return "Unknown(value=" + state + ")";
- }
- }
-
- /**
- * @return {@code true} if {@link #onComplete()} gets called and {@link #mState} is
- * {@link CompletionState#COMPLETED_WITH_VALUE}.
- */
- @AnyThread
- public boolean hasValue() {
- synchronized (mStateLock) {
- return mState == CompletionState.COMPLETED_WITH_VALUE;
- }
- }
-
- /**
- * Provides the base implementation of {@code getValue()} for derived classes.
- *
- * <p>Must be called after acquiring {@link #mStateLock}.</p>
- *
- * @throws RuntimeException when {@link #mState} is
- * {@link CompletionState#COMPLETED_WITH_ERROR}.
- * @throws UnsupportedOperationException when {@link #mState} is not
- * {@link CompletionState#COMPLETED_WITH_VALUE} and
- * {@link CompletionState#COMPLETED_WITH_ERROR}.
- */
- @GuardedBy("mStateLock")
- protected void enforceGetValueLocked() {
- switch (mState) {
- case CompletionState.NOT_COMPLETED:
- throw new UnsupportedOperationException(
- "getValue() is allowed only if hasValue() returns true");
- case CompletionState.COMPLETED_WITH_VALUE:
- return;
- case CompletionState.COMPLETED_WITH_ERROR:
- throw new RuntimeException(mMessage);
- default:
- throw new UnsupportedOperationException(
- "getValue() is not allowed on state=" + stateToString(mState));
- }
- }
-
- /**
- * Called by subclasses to signale {@link #mLatch}.
- */
- @AnyThread
- protected void onComplete() {
- mLatch.countDown();
- }
-
- /**
- * Notify when exception happened.
- *
- * @param throwableHolder contains the {@link Throwable} object when exception happened.
- */
- @AnyThread
- protected void onError(ThrowableHolder throwableHolder) {
- synchronized (mStateLock) {
- switch (mState) {
- case CompletionState.NOT_COMPLETED:
- mMessage = throwableHolder.getMessage();
- mState = CompletionState.COMPLETED_WITH_ERROR;
- break;
- default:
- throw new UnsupportedOperationException(
- "onError() is not allowed on state=" + stateToString(mState));
- }
- }
- onComplete();
- }
-
- /**
- * Blocks the calling thread until at least one of the following conditions is met.
- *
- * <p>
- * <ol>
- * <li>This object becomes ready to return the value.</li>
- * <li>{@link CancellationGroup#cancelAll()} gets called.</li>
- * <li>The given timeout period has passed.</li>
- * </ol>
- * </p>
- *
- * <p>The caller can distinguish the case 1 and case 2 by calling {@link #hasValue()}.
- * Note that the return value of {@link #hasValue()} can change from {@code false} to
- * {@code true} at any time, even after this methods finishes with returning
- * {@code true}.</p>
- *
- * @param timeout length of the timeout.
- * @param timeUnit unit of {@code timeout}.
- * @param cancellationGroup {@link CancellationGroup} to cancel completable objects.
- * @return {@code false} if and only if the given timeout period has passed. Otherwise
- * {@code true}.
- */
- @AnyThread
- public boolean await(int timeout, @NonNull TimeUnit timeUnit,
- @Nullable CancellationGroup cancellationGroup) {
- if (cancellationGroup == null) {
- return awaitInner(timeout, timeUnit);
- }
-
- if (!cancellationGroup.registerLatch(mLatch)) {
- // Already canceled when this method gets called.
- return false;
- }
- try {
- return awaitInner(timeout, timeUnit);
- } finally {
- cancellationGroup.unregisterLatch(mLatch);
- }
- }
-
- private boolean awaitInner(int timeout, @NonNull TimeUnit timeUnit) {
- try {
- return mLatch.await(timeout, timeUnit);
- } catch (InterruptedException e) {
- return true;
- }
- }
-
- /**
- * Blocks the calling thread until this object becomes ready to return the value, even if
- * {@link InterruptedException} is thrown.
- */
- @AnyThread
- public void await() {
- boolean interrupted = false;
- while (true) {
- try {
- mLatch.await();
- break;
- } catch (InterruptedException ignored) {
- interrupted = true;
- }
- }
-
- if (interrupted) {
- // Try to preserve the interrupt bit on this thread.
- Thread.currentThread().interrupt();
- }
- }
- }
-
- /**
- * Completable object of integer primitive.
- */
- public static final class Int extends ValueBase {
- @GuardedBy("mStateLock")
- private int mValue = 0;
-
- /**
- * Notify when a value is set to this completable object.
- *
- * @param value value to be set.
- */
- @AnyThread
- void onComplete(int value) {
- synchronized (mStateLock) {
- switch (mState) {
- case CompletionState.NOT_COMPLETED:
- mValue = value;
- mState = CompletionState.COMPLETED_WITH_VALUE;
- break;
- default:
- throw new UnsupportedOperationException(
- "onComplete() is not allowed on state=" + stateToString(mState));
- }
- }
- onComplete();
- }
-
- /**
- * @return value associated with this object.
- * @throws RuntimeException when called while {@link #onError} happened.
- * @throws UnsupportedOperationException when called while {@link #hasValue()} returns
- * {@code false}.
- */
- @AnyThread
- public int getValue() {
- synchronized (mStateLock) {
- enforceGetValueLocked();
- return mValue;
- }
- }
- }
-
- /**
- * Completable object of {@link java.lang.Void}.
- */
- public static final class Void extends ValueBase {
- /**
- * Notify when this completable object callback.
- */
- @AnyThread
- @Override
- protected void onComplete() {
- synchronized (mStateLock) {
- switch (mState) {
- case CompletionState.NOT_COMPLETED:
- mState = CompletionState.COMPLETED_WITH_VALUE;
- break;
- default:
- throw new UnsupportedOperationException(
- "onComplete() is not allowed on state=" + stateToString(mState));
- }
- }
- super.onComplete();
- }
-
- /**
- * @throws RuntimeException when called while {@link #onError} happened.
- * @throws UnsupportedOperationException when called while {@link #hasValue()} returns
- * {@code false}.
- */
- @AnyThread
- public void getValue() {
- synchronized (mStateLock) {
- enforceGetValueLocked();
- }
- }
- }
-
- /**
- * Base class of completable object types.
- *
- * @param <T> type associated with this completable object.
- */
- public static class Values<T> extends ValueBase {
- @GuardedBy("mStateLock")
- @Nullable
- private T mValue = null;
-
- /**
- * Notify when a value is set to this completable value object.
- *
- * @param value value to be set.
- */
- @AnyThread
- void onComplete(@Nullable T value) {
- synchronized (mStateLock) {
- switch (mState) {
- case CompletionState.NOT_COMPLETED:
- mValue = value;
- mState = CompletionState.COMPLETED_WITH_VALUE;
- break;
- default:
- throw new UnsupportedOperationException(
- "onComplete() is not allowed on state=" + stateToString(mState));
- }
- }
- onComplete();
- }
-
- /**
- * @return value associated with this object.
- * @throws RuntimeException when called while {@link #onError} happened
- * @throws UnsupportedOperationException when called while {@link #hasValue()} returns
- * {@code false}.
- */
- @AnyThread
- @Nullable
- public T getValue() {
- synchronized (mStateLock) {
- enforceGetValueLocked();
- return mValue;
- }
- }
- }
-
- /**
- * @return an instance of {@link Completable.Int}.
- */
- public static Completable.Int createInt() {
- return new Completable.Int();
- }
-
- /**
- * @return an instance of {@link Completable.Boolean}.
- */
- public static Completable.Boolean createBoolean() {
- return new Completable.Boolean();
- }
-
- /**
- * @return an instance of {@link Completable.CharSequence}.
- */
- public static Completable.CharSequence createCharSequence() {
- return new Completable.CharSequence();
- }
-
- /**
- * @return an instance of {@link Completable.ExtractedText}.
- */
- public static Completable.ExtractedText createExtractedText() {
- return new Completable.ExtractedText();
- }
-
- /**
- * @return an instance of {@link Completable.SurroundingText}.
- */
- public static Completable.SurroundingText createSurroundingText() {
- return new Completable.SurroundingText();
- }
-
- /**
- * @return an instance of {@link Completable.IInputContentUriToken}.
- */
- public static Completable.IInputContentUriToken createIInputContentUriToken() {
- return new Completable.IInputContentUriToken();
- }
-
- /**
- * @return an instance of {@link Completable.Void}.
- */
- public static Completable.Void createVoid() {
- return new Completable.Void();
- }
-
- /**
- * Completable object of {@link java.lang.Boolean}.
- */
- public static final class Boolean extends Values<java.lang.Boolean> { }
-
- /**
- * Completable object of {@link java.lang.CharSequence}.
- */
- public static final class CharSequence extends Values<java.lang.CharSequence> { }
-
- /**
- * Completable object of {@link android.view.inputmethod.ExtractedText}.
- */
- public static final class ExtractedText
- extends Values<android.view.inputmethod.ExtractedText> { }
-
- /**
- * Completable object of {@link android.view.inputmethod.SurroundingText}.
- */
- public static final class SurroundingText
- extends Values<android.view.inputmethod.SurroundingText> { }
-
- /**
- * Completable object of {@link IInputContentUriToken>}.
- */
- public static final class IInputContentUriToken
- extends Values<com.android.internal.inputmethod.IInputContentUriToken> { }
-
- /**
- * Await the result by the {@link Completable.Values}.
- *
- * @return the result once {@link ValueBase#onComplete()}.
- */
- @AnyThread
- @Nullable
- public static <T> T getResult(@NonNull Completable.Values<T> value) {
- value.await();
- return value.getValue();
- }
-
- /**
- * Await the int result by the {@link Completable.Int}.
- *
- * @return the result once {@link ValueBase#onComplete()}.
- */
- @AnyThread
- public static int getIntResult(@NonNull Completable.Int value) {
- value.await();
- return value.getValue();
- }
-
- /**
- * Await the result by the {@link Completable.Void}.
- *
- * Check the result once {@link ValueBase#onComplete()}
- */
- @AnyThread
- public static void getResult(@NonNull Completable.Void value) {
- value.await();
- value.getValue();
- }
-
- /**
- * Await the result by the {@link Completable.Boolean}, and log it if there is no result after
- * given timeout.
- *
- * @return the result once {@link ValueBase#onComplete()}
- */
- @AnyThread
- public static boolean getResultOrFalse(@NonNull Completable.Boolean value, String tag,
- @NonNull String methodName, @Nullable CancellationGroup cancellationGroup,
- int maxWaitTime) {
- final boolean timedOut = value.await(maxWaitTime, TimeUnit.MILLISECONDS, cancellationGroup);
- if (value.hasValue()) {
- return value.getValue();
- }
- logInternal(tag, methodName, timedOut, maxWaitTime, 0);
- return false;
- }
-
- /**
- * Await the result by the {@link Completable.Int}, and log it if there is no result after
- * given timeout.
- *
- * @return the result once {@link ValueBase#onComplete()}
- */
- @AnyThread
- public static int getResultOrZero(@NonNull Completable.Int value, String tag,
- @NonNull String methodName, @Nullable CancellationGroup cancellationGroup,
- int maxWaitTime) {
- final boolean timedOut = value.await(maxWaitTime, TimeUnit.MILLISECONDS, cancellationGroup);
- if (value.hasValue()) {
- return value.getValue();
- }
- logInternal(tag, methodName, timedOut, maxWaitTime, 0);
- return 0;
- }
-
- /**
- * Await the result by the {@link Completable.Values}, and log it if there is no result after
- * given timeout.
- *
- * @return the result once {@link ValueBase#onComplete()}
- */
- @AnyThread
- @Nullable
- public static <T> T getResultOrNull(@NonNull Completable.Values<T> value, String tag,
- @NonNull String methodName, @Nullable CancellationGroup cancellationGroup,
- int maxWaitTime) {
- final boolean timedOut = value.await(maxWaitTime, TimeUnit.MILLISECONDS, cancellationGroup);
- if (value.hasValue()) {
- return value.getValue();
- }
- logInternal(tag, methodName, timedOut, maxWaitTime, null);
- return null;
- }
-
- @AnyThread
- private static void logInternal(String tag, @Nullable String methodName, boolean timedOut,
- int maxWaitTime, @Nullable Object defaultValue) {
- if (timedOut) {
- Log.w(tag, methodName + " didn't respond in " + maxWaitTime + " msec."
- + " Returning default: " + defaultValue);
- } else {
- Log.w(tag, methodName + " was canceled before complete. Returning default: "
- + defaultValue);
- }
- }
-}
diff --git a/core/java/com/android/internal/inputmethod/CompletableFutureUtil.java b/core/java/com/android/internal/inputmethod/CompletableFutureUtil.java
new file mode 100644
index 0000000..ec10e01
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/CompletableFutureUtil.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.DurationMillisLong;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Log;
+
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * A set of helper methods to retrieve result values from {@link CompletableFuture}.
+ */
+public final class CompletableFutureUtil {
+ /**
+ * Not intended to be instantiated.
+ */
+ private CompletableFutureUtil() {
+ }
+
+ @AnyThread
+ @Nullable
+ private static <T> T getValueOrRethrowErrorInternal(@NonNull CompletableFuture<T> future) {
+ boolean interrupted = false;
+ try {
+ while (true) {
+ try {
+ return future.get();
+ } catch (ExecutionException e) {
+ final Throwable cause = e.getCause();
+ throw new RuntimeException(cause.getMessage(), cause.getCause());
+ } catch (InterruptedException e) {
+ interrupted = true;
+ }
+ }
+ } finally {
+ if (interrupted) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ @AnyThread
+ @Nullable
+ private static <T> T getValueOrNullInternal(@NonNull CompletableFuture<T> future,
+ @Nullable String tag, @Nullable String methodName,
+ @DurationMillisLong long timeoutMillis, @Nullable CancellationGroup cancellationGroup) {
+ // We intentionally do not use CompletableFuture.anyOf() to avoid additional object
+ // allocations.
+ final boolean needsToUnregister = cancellationGroup != null
+ && cancellationGroup.tryRegisterFutureOrCancelImmediately(future);
+ boolean interrupted = false;
+ try {
+ while (true) {
+ try {
+ return future.get(timeoutMillis, TimeUnit.MILLISECONDS);
+ } catch (CompletionException e) {
+ if (e.getCause() instanceof CancellationException) {
+ logCancellationInternal(tag, methodName);
+ return null;
+ }
+ logErrorInternal(tag, methodName, e.getMessage());
+ return null;
+ } catch (CancellationException e) {
+ logCancellationInternal(tag, methodName);
+ return null;
+ } catch (InterruptedException e) {
+ interrupted = true;
+ } catch (TimeoutException e) {
+ logTimeoutInternal(tag, methodName, timeoutMillis);
+ return null;
+ } catch (Throwable e) {
+ logErrorInternal(tag, methodName, e.getMessage());
+ return null;
+ }
+ }
+ } finally {
+ if (needsToUnregister) {
+ cancellationGroup.unregisterFuture(future);
+ }
+ if (interrupted) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ @AnyThread
+ private static void logTimeoutInternal(@Nullable String tag, @Nullable String methodName,
+ @DurationMillisLong long timeout) {
+ if (tag == null || methodName == null) {
+ return;
+ }
+ Log.w(tag, methodName + " didn't respond in " + timeout + " msec.");
+ }
+
+ @AnyThread
+ private static void logErrorInternal(@Nullable String tag, @Nullable String methodName,
+ @Nullable String errorString) {
+ if (tag == null || methodName == null) {
+ return;
+ }
+ Log.w(tag, methodName + " was failed with an exception=" + errorString);
+ }
+
+ @AnyThread
+ private static void logCancellationInternal(@Nullable String tag, @Nullable String methodName) {
+ if (tag == null || methodName == null) {
+ return;
+ }
+ Log.w(tag, methodName + " was cancelled.");
+ }
+
+ /**
+ * Return the result of the given {@link CompletableFuture<T>}.
+ *
+ * <p>This method may throw exception is the task is completed with an error.</p>
+ *
+ * @param future the object to extract the result from.
+ * @param <T> type of the result.
+ * @return the result.
+ */
+ @AnyThread
+ @Nullable
+ public static <T> T getResult(@NonNull CompletableFuture<T> future) {
+ return getValueOrRethrowErrorInternal(future);
+ }
+
+ /**
+ * Return the result of the given {@link CompletableFuture<Boolean>}.
+ *
+ * <p>This method may throw exception is the task is completed with an error.</p>
+ *
+ * @param future the object to extract the result from.
+ * @return the result.
+ */
+ @AnyThread
+ public static boolean getBooleanResult(@NonNull CompletableFuture<Boolean> future) {
+ return getValueOrRethrowErrorInternal(future);
+ }
+
+ /**
+ * Return the result of the given {@link CompletableFuture<Integer>}.
+ *
+ * <p>This method may throw exception is the task is completed with an error.</p>
+ *
+ * @param future the object to extract the result from.
+ * @return the result.
+ */
+ @AnyThread
+ public static int getIntegerResult(@NonNull CompletableFuture<Integer> future) {
+ return getValueOrRethrowErrorInternal(future);
+ }
+
+ /**
+ * Return the result of the given {@link CompletableFuture<Boolean>}.
+ *
+ * <p>This method is agnostic to {@link Thread#interrupt()}.</p>
+ *
+ * <p>CAVEAT: when {@code cancellationGroup} is specified and it is signalled, {@code future}
+ * will be cancelled permanently. You have to duplicate the {@link CompletableFuture} if you
+ * want to avoid this side-effect.</p>
+ *
+ * @param future the object to extract the result from.
+ * @param tag tag name for logging. Pass {@code null} to disable logging.
+ * @param methodName method name for logging. Pass {@code null} to disable logging.
+ * @param cancellationGroup an optional {@link CancellationGroup} to cancel {@code future}
+ * object. Can be {@code null}.
+ * @param timeoutMillis length of the timeout in millisecond.
+ * @return the result if it is completed within the given timeout. {@code false} otherwise.
+ */
+ @AnyThread
+ public static boolean getResultOrFalse(@NonNull CompletableFuture<Boolean> future,
+ @Nullable String tag, @Nullable String methodName,
+ @Nullable CancellationGroup cancellationGroup,
+ @DurationMillisLong long timeoutMillis) {
+ final Boolean obj = getValueOrNullInternal(future, tag, methodName, timeoutMillis,
+ cancellationGroup);
+ return obj != null ? obj : false;
+ }
+
+ /**
+ * Return the result of the given {@link CompletableFuture<Integer>}.
+ *
+ * <p>This method is agnostic to {@link Thread#interrupt()}.</p>
+ *
+ * <p>CAVEAT: when {@code cancellationGroup} is specified and it is signalled, {@code future}
+ * will be cancelled permanently. You have to duplicate the {@link CompletableFuture} if you
+ * want to avoid this side-effect.</p>
+ *
+ * @param future the object to extract the result from.
+ * @param tag tag name for logging. Pass {@code null} to disable logging.
+ * @param methodName method name for logging. Pass {@code null} to disable logging.
+ * @param cancellationGroup an optional {@link CancellationGroup} to cancel {@code future}
+ * object. Can be {@code null}.
+ * @param timeoutMillis length of the timeout in millisecond.
+ * @return the result if it is completed within the given timeout. {@code 0} otherwise.
+ */
+ @AnyThread
+ public static int getResultOrZero(@NonNull CompletableFuture<Integer> future,
+ @Nullable String tag, @Nullable String methodName,
+ @Nullable CancellationGroup cancellationGroup, @DurationMillisLong long timeoutMillis) {
+ final Integer obj = getValueOrNullInternal(future, tag, methodName, timeoutMillis,
+ cancellationGroup);
+ return obj != null ? obj : 0;
+ }
+
+ /**
+ * Return the result of the given {@link CompletableFuture<T>}.
+ *
+ * <p>This method is agnostic to {@link Thread#interrupt()}.</p>
+ *
+ * <p>CAVEAT: when {@code cancellationGroup} is specified and it is signalled, {@code future}
+ * will be cancelled permanently. You have to duplicate the {@link CompletableFuture} if you
+ * want to avoid this side-effect.</p>
+ *
+ * @param future the object to extract the result from.
+ * @param tag tag name for logging. Pass {@code null} to disable logging.
+ * @param methodName method name for logging. Pass {@code null} to disable logging.
+ * @param cancellationGroup an optional {@link CancellationGroup} to cancel {@code future}
+ * object. Can be {@code null}.
+ * @param timeoutMillis length of the timeout in millisecond.
+ * @param <T> Type of the result.
+ * @return the result if it is completed within the given timeout. {@code null} otherwise.
+ */
+ @AnyThread
+ @Nullable
+ public static <T> T getResultOrNull(@NonNull CompletableFuture<T> future, @Nullable String tag,
+ @Nullable String methodName, @Nullable CancellationGroup cancellationGroup,
+ @DurationMillisLong long timeoutMillis) {
+ return getValueOrNullInternal(future, tag, methodName, timeoutMillis, cancellationGroup);
+ }
+}
diff --git a/core/java/com/android/internal/inputmethod/IBooleanResultCallback.aidl b/core/java/com/android/internal/inputmethod/IBooleanResultCallback.aidl
deleted file mode 100644
index 6daeb3f..0000000
--- a/core/java/com/android/internal/inputmethod/IBooleanResultCallback.aidl
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.inputmethod;
-
-import com.android.internal.inputmethod.ThrowableHolder;
-
-oneway interface IBooleanResultCallback {
- void onResult(boolean result);
- void onError(in ThrowableHolder exception);
-}
\ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/ICharSequenceResultCallback.aidl b/core/java/com/android/internal/inputmethod/ICharSequenceResultCallback.aidl
deleted file mode 100644
index da56fd0..0000000
--- a/core/java/com/android/internal/inputmethod/ICharSequenceResultCallback.aidl
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.inputmethod;
-
-oneway interface ICharSequenceResultCallback {
- void onResult(in CharSequence result);
-}
diff --git a/core/java/com/android/internal/inputmethod/IExtractedTextResultCallback.aidl b/core/java/com/android/internal/inputmethod/IExtractedTextResultCallback.aidl
deleted file mode 100644
index b603f6a..0000000
--- a/core/java/com/android/internal/inputmethod/IExtractedTextResultCallback.aidl
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.inputmethod;
-
-import android.view.inputmethod.ExtractedText;
-
-oneway interface IExtractedTextResultCallback {
- void onResult(in ExtractedText result);
-}
diff --git a/core/java/com/android/internal/inputmethod/IInputContentUriTokenResultCallback.aidl b/core/java/com/android/internal/inputmethod/IInputContentUriTokenResultCallback.aidl
deleted file mode 100644
index 779acb5..0000000
--- a/core/java/com/android/internal/inputmethod/IInputContentUriTokenResultCallback.aidl
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.inputmethod;
-
-import com.android.internal.inputmethod.IInputContentUriToken;
-import com.android.internal.inputmethod.ThrowableHolder;
-
-oneway interface IInputContentUriTokenResultCallback {
- void onResult(in IInputContentUriToken result);
- void onError(in ThrowableHolder exception);
-}
\ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/IInputContextInvoker.java b/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
index 977f9a5..0cbdc13 100644
--- a/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
+++ b/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
@@ -23,16 +23,19 @@
import android.view.KeyEvent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputContentInfo;
+import android.view.inputmethod.SurroundingText;
+import com.android.internal.infra.AndroidFuture;
import com.android.internal.view.IInputContext;
import java.util.Objects;
/**
* A stateless wrapper of {@link com.android.internal.view.IInputContext} to encapsulate boilerplate
- * code around {@link Completable} and {@link RemoteException}.
+ * code around {@link AndroidFuture} and {@link RemoteException}.
*/
public final class IInputContextInvoker {
@@ -60,19 +63,19 @@
*
* @param length {@code length} parameter to be passed.
* @param flags {@code flags} parameter to be passed.
- * @return {@link Completable.CharSequence} that can be used to retrieve the invocation result.
- * {@link RemoteException} will be treated as an error.
+ * @return {@link AndroidFuture<CharSequence>} that can be used to retrieve the invocation
+ * result. {@link RemoteException} will be treated as an error.
*/
@AnyThread
@NonNull
- public Completable.CharSequence getTextAfterCursor(int length, int flags) {
- final Completable.CharSequence value = Completable.createCharSequence();
+ public AndroidFuture<CharSequence> getTextAfterCursor(int length, int flags) {
+ final AndroidFuture<CharSequence> future = new AndroidFuture<>();
try {
- mIInputContext.getTextAfterCursor(length, flags, ResultCallbacks.of(value));
+ mIInputContext.getTextAfterCursor(length, flags, future);
} catch (RemoteException e) {
- value.onError(ThrowableHolder.of(e));
+ future.completeExceptionally(e);
}
- return value;
+ return future;
}
/**
@@ -80,38 +83,38 @@
*
* @param length {@code length} parameter to be passed.
* @param flags {@code flags} parameter to be passed.
- * @return {@link Completable.CharSequence} that can be used to retrieve the invocation result.
- * {@link RemoteException} will be treated as an error.
+ * @return {@link AndroidFuture<CharSequence>} that can be used to retrieve the invocation
+ * result. {@link RemoteException} will be treated as an error.
*/
@AnyThread
@NonNull
- public Completable.CharSequence getTextBeforeCursor(int length, int flags) {
- final Completable.CharSequence value = Completable.createCharSequence();
+ public AndroidFuture<CharSequence> getTextBeforeCursor(int length, int flags) {
+ final AndroidFuture<CharSequence> future = new AndroidFuture<>();
try {
- mIInputContext.getTextBeforeCursor(length, flags, ResultCallbacks.of(value));
+ mIInputContext.getTextBeforeCursor(length, flags, future);
} catch (RemoteException e) {
- value.onError(ThrowableHolder.of(e));
+ future.completeExceptionally(e);
}
- return value;
+ return future;
}
/**
* Invokes {@link IInputContext#getSelectedText(int, ICharSequenceResultCallback)}.
*
* @param flags {@code flags} parameter to be passed.
- * @return {@link Completable.CharSequence} that can be used to retrieve the invocation result.
- * {@link RemoteException} will be treated as an error.
+ * @return {@link AndroidFuture<CharSequence>} that can be used to retrieve the invocation
+ * result. {@link RemoteException} will be treated as an error.
*/
@AnyThread
@NonNull
- public Completable.CharSequence getSelectedText(int flags) {
- final Completable.CharSequence value = Completable.createCharSequence();
+ public AndroidFuture<CharSequence> getSelectedText(int flags) {
+ final AndroidFuture<CharSequence> future = new AndroidFuture<>();
try {
- mIInputContext.getSelectedText(flags, ResultCallbacks.of(value));
+ mIInputContext.getSelectedText(flags, future);
} catch (RemoteException e) {
- value.onError(ThrowableHolder.of(e));
+ future.completeExceptionally(e);
}
- return value;
+ return future;
}
/**
@@ -121,40 +124,39 @@
* @param beforeLength {@code beforeLength} parameter to be passed.
* @param afterLength {@code afterLength} parameter to be passed.
* @param flags {@code flags} parameter to be passed.
- * @return {@link Completable.SurroundingText} that can be used to retrieve the invocation
- * result. {@link RemoteException} will be treated as an error.
+ * @return {@link AndroidFuture<SurroundingText>} that can be used to retrieve the
+ * invocation result. {@link RemoteException} will be treated as an error.
*/
@AnyThread
@NonNull
- public Completable.SurroundingText getSurroundingText(int beforeLength, int afterLength,
+ public AndroidFuture<SurroundingText> getSurroundingText(int beforeLength, int afterLength,
int flags) {
- final Completable.SurroundingText value = Completable.createSurroundingText();
+ final AndroidFuture<SurroundingText> future = new AndroidFuture<>();
try {
- mIInputContext.getSurroundingText(beforeLength, afterLength, flags,
- ResultCallbacks.of(value));
+ mIInputContext.getSurroundingText(beforeLength, afterLength, flags, future);
} catch (RemoteException e) {
- value.onError(ThrowableHolder.of(e));
+ future.completeExceptionally(e);
}
- return value;
+ return future;
}
/**
* Invokes {@link IInputContext#getCursorCapsMode(int, IIntResultCallback)}.
*
* @param reqModes {@code reqModes} parameter to be passed.
- * @return {@link Completable.Int} that can be used to retrieve the invocation result.
- * {@link RemoteException} will be treated as an error.
+ * @return {@link AndroidFuture<Integer>} that can be used to retrieve the invocation
+ * result. {@link RemoteException} will be treated as an error.
*/
@AnyThread
@NonNull
- public Completable.Int getCursorCapsMode(int reqModes) {
- final Completable.Int value = Completable.createInt();
+ public AndroidFuture<Integer> getCursorCapsMode(int reqModes) {
+ final AndroidFuture<Integer> future = new AndroidFuture<>();
try {
- mIInputContext.getCursorCapsMode(reqModes, ResultCallbacks.of(value));
+ mIInputContext.getCursorCapsMode(reqModes, future);
} catch (RemoteException e) {
- value.onError(ThrowableHolder.of(e));
+ future.completeExceptionally(e);
}
- return value;
+ return future;
}
/**
@@ -163,19 +165,20 @@
*
* @param request {@code request} parameter to be passed.
* @param flags {@code flags} parameter to be passed.
- * @return {@link Completable.ExtractedText} that can be used to retrieve the invocation result.
- * {@link RemoteException} will be treated as an error.
+ * @return {@link AndroidFuture<ExtractedText>} that can be used to retrieve the invocation
+ * result. {@link RemoteException} will be treated as an error.
*/
@AnyThread
@NonNull
- public Completable.ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
- final Completable.ExtractedText value = Completable.createExtractedText();
+ public AndroidFuture<ExtractedText> getExtractedText(ExtractedTextRequest request,
+ int flags) {
+ final AndroidFuture<ExtractedText> future = new AndroidFuture<>();
try {
- mIInputContext.getExtractedText(request, flags, ResultCallbacks.of(value));
+ mIInputContext.getExtractedText(request, flags, future);
} catch (RemoteException e) {
- value.onError(ThrowableHolder.of(e));
+ future.completeExceptionally(e);
}
- return value;
+ return future;
}
/**
@@ -474,19 +477,19 @@
* Invokes {@link IInputContext#requestCursorUpdates(int, IIntResultCallback)}.
*
* @param cursorUpdateMode {@code cursorUpdateMode} parameter to be passed.
- * @return {@link Completable.Boolean} that can be used to retrieve the invocation result.
- * {@link RemoteException} will be treated as an error.
+ * @return {@link AndroidFuture<Boolean>} that can be used to retrieve the invocation
+ * result. {@link RemoteException} will be treated as an error.
*/
@AnyThread
@NonNull
- public Completable.Boolean requestCursorUpdates(int cursorUpdateMode) {
- final Completable.Boolean value = Completable.createBoolean();
+ public AndroidFuture<Boolean> requestCursorUpdates(int cursorUpdateMode) {
+ final AndroidFuture<Boolean> future = new AndroidFuture<>();
try {
- mIInputContext.requestCursorUpdates(cursorUpdateMode, ResultCallbacks.of(value));
+ mIInputContext.requestCursorUpdates(cursorUpdateMode, future);
} catch (RemoteException e) {
- value.onError(ThrowableHolder.of(e));
+ future.completeExceptionally(e);
}
- return value;
+ return future;
}
/**
@@ -496,20 +499,20 @@
* @param inputContentInfo {@code inputContentInfo} parameter to be passed.
* @param flags {@code flags} parameter to be passed.
* @param opts {@code opts} parameter to be passed.
- * @return {@link Completable.Boolean} that can be used to retrieve the invocation result.
- * {@link RemoteException} will be treated as an error.
+ * @return {@link AndroidFuture<Boolean>} that can be used to retrieve the invocation
+ * result. {@link RemoteException} will be treated as an error.
*/
@AnyThread
@NonNull
- public Completable.Boolean commitContent(InputContentInfo inputContentInfo, int flags,
+ public AndroidFuture<Boolean> commitContent(InputContentInfo inputContentInfo, int flags,
Bundle opts) {
- final Completable.Boolean value = Completable.createBoolean();
+ final AndroidFuture<Boolean> future = new AndroidFuture<>();
try {
- mIInputContext.commitContent(inputContentInfo, flags, opts, ResultCallbacks.of(value));
+ mIInputContext.commitContent(inputContentInfo, flags, opts, future);
} catch (RemoteException e) {
- value.onError(ThrowableHolder.of(e));
+ future.completeExceptionally(e);
}
- return value;
+ return future;
}
/**
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
index 36943e3..9d0f209 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -19,10 +19,7 @@
import android.net.Uri;
import android.view.inputmethod.InputMethodSubtype;
-import com.android.internal.inputmethod.IBooleanResultCallback;
-import com.android.internal.inputmethod.IInputContentUriToken;
-import com.android.internal.inputmethod.IInputContentUriTokenResultCallback;
-import com.android.internal.inputmethod.IVoidResultCallback;
+import com.android.internal.infra.AndroidFuture;
/**
* Defines priviledged operations that only the current IME is allowed to call.
@@ -32,17 +29,17 @@
void setImeWindowStatusAsync(int vis, int backDisposition);
void reportStartInputAsync(in IBinder startInputToken);
void createInputContentUriToken(in Uri contentUri, in String packageName,
- in IInputContentUriTokenResultCallback resultCallback);
+ in AndroidFuture future /* T=IBinder */);
void reportFullscreenModeAsync(boolean fullscreen);
- void setInputMethod(String id, in IVoidResultCallback resultCallback);
+ void setInputMethod(String id, in AndroidFuture future /* T=Void */);
void setInputMethodAndSubtype(String id, in InputMethodSubtype subtype,
- in IVoidResultCallback resultCallback);
- void hideMySoftInput(int flags, in IVoidResultCallback resultCallback);
- void showMySoftInput(int flags, in IVoidResultCallback resultCallback);
+ in AndroidFuture future /* T=Void */);
+ void hideMySoftInput(int flags, in AndroidFuture future /* T=Void */);
+ void showMySoftInput(int flags, in AndroidFuture future /* T=Void */);
void updateStatusIconAsync(String packageName, int iconId);
- void switchToPreviousInputMethod(in IBooleanResultCallback resultCallback);
- void switchToNextInputMethod(boolean onlyCurrentIme, in IBooleanResultCallback resultCallback);
- void shouldOfferSwitchingToNextInputMethod(in IBooleanResultCallback resultCallback);
+ void switchToPreviousInputMethod(in AndroidFuture future /* T=Boolean */);
+ void switchToNextInputMethod(boolean onlyCurrentIme, in AndroidFuture future /* T=Boolean */);
+ void shouldOfferSwitchingToNextInputMethod(in AndroidFuture future /* T=Boolean */);
void notifyUserActionAsync();
void applyImeVisibilityAsync(IBinder showOrHideInputToken, boolean setVisible);
}
diff --git a/core/java/com/android/internal/inputmethod/IIntResultCallback.aidl b/core/java/com/android/internal/inputmethod/IIntResultCallback.aidl
deleted file mode 100644
index ceda66c..0000000
--- a/core/java/com/android/internal/inputmethod/IIntResultCallback.aidl
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.inputmethod;
-
-import com.android.internal.inputmethod.ThrowableHolder;
-
-oneway interface IIntResultCallback {
- void onResult(int result);
- void onError(in ThrowableHolder exception);
-}
diff --git a/core/java/com/android/internal/inputmethod/ISurroundingTextResultCallback.aidl b/core/java/com/android/internal/inputmethod/ISurroundingTextResultCallback.aidl
deleted file mode 100644
index 6c4f3d5..0000000
--- a/core/java/com/android/internal/inputmethod/ISurroundingTextResultCallback.aidl
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.inputmethod;
-
-import android.view.inputmethod.SurroundingText;
-
-oneway interface ISurroundingTextResultCallback {
- void onResult(in SurroundingText result);
-}
\ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/IVoidResultCallback.aidl b/core/java/com/android/internal/inputmethod/IVoidResultCallback.aidl
deleted file mode 100644
index 0b25a2b..0000000
--- a/core/java/com/android/internal/inputmethod/IVoidResultCallback.aidl
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.inputmethod;
-
-import com.android.internal.inputmethod.ThrowableHolder;
-
-oneway interface IVoidResultCallback {
- void onResult();
- void onError(in ThrowableHolder exception);
-}
\ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 9fb0bb5..d4cc376 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -28,6 +28,7 @@
import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.AndroidFuture;
import java.util.Objects;
@@ -142,7 +143,7 @@
/**
* Calls {@link IInputMethodPrivilegedOperations#createInputContentUriToken(Uri, String,
- * IInputContentUriTokenResultCallback)}.
+ * AndroidFuture)}.
*
* @param contentUri Content URI to which a temporary read permission should be granted
* @param packageName Indicates what package needs to have a temporary read permission
@@ -156,10 +157,9 @@
return null;
}
try {
- final Completable.IInputContentUriToken value =
- Completable.createIInputContentUriToken();
- ops.createInputContentUriToken(contentUri, packageName, ResultCallbacks.of(value));
- return Completable.getResult(value);
+ final AndroidFuture<IBinder> future = new AndroidFuture<>();
+ ops.createInputContentUriToken(contentUri, packageName, future);
+ return IInputContentUriToken.Stub.asInterface(CompletableFutureUtil.getResult(future));
} catch (RemoteException e) {
// For historical reasons, this error was silently ignored.
// Note that the caller already logs error so we do not need additional Log.e() here.
@@ -206,7 +206,7 @@
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#setInputMethod(String, IVoidResultCallback)}.
+ * Calls {@link IInputMethodPrivilegedOperations#setInputMethod(String, AndroidFuture)}.
*
* @param id IME ID of the IME to switch to
* @see android.view.inputmethod.InputMethodInfo#getId()
@@ -218,9 +218,9 @@
return;
}
try {
- final Completable.Void value = Completable.createVoid();
- ops.setInputMethod(id, ResultCallbacks.of(value));
- Completable.getResult(value);
+ final AndroidFuture<Void> future = new AndroidFuture<>();
+ ops.setInputMethod(id, future);
+ CompletableFutureUtil.getResult(future);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -228,7 +228,7 @@
/**
* Calls {@link IInputMethodPrivilegedOperations#setInputMethodAndSubtype(String,
- * InputMethodSubtype, IVoidResultCallback)}
+ * InputMethodSubtype, AndroidFuture)}
*
* @param id IME ID of the IME to switch to
* @param subtype {@link InputMethodSubtype} to switch to
@@ -241,9 +241,9 @@
return;
}
try {
- final Completable.Void value = Completable.createVoid();
- ops.setInputMethodAndSubtype(id, subtype, ResultCallbacks.of(value));
- Completable.getResult(value);
+ final AndroidFuture<Void> future = new AndroidFuture<>();
+ ops.setInputMethodAndSubtype(id, subtype, future);
+ CompletableFutureUtil.getResult(future);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -263,16 +263,16 @@
return;
}
try {
- final Completable.Void value = Completable.createVoid();
- ops.hideMySoftInput(flags, ResultCallbacks.of(value));
- Completable.getResult(value);
+ final AndroidFuture<Void> future = new AndroidFuture<>();
+ ops.hideMySoftInput(flags, future);
+ CompletableFutureUtil.getResult(future);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#showMySoftInput(int, IVoidResultCallback)}
+ * Calls {@link IInputMethodPrivilegedOperations#showMySoftInput(int, AndroidFuture)}
*
* @param flags additional operating flags
* @see android.view.inputmethod.InputMethodManager#SHOW_IMPLICIT
@@ -285,17 +285,16 @@
return;
}
try {
- final Completable.Void value = Completable.createVoid();
- ops.showMySoftInput(flags, ResultCallbacks.of(value));
- Completable.getResult(value);
+ final AndroidFuture<Void> future = new AndroidFuture<>();
+ ops.showMySoftInput(flags, future);
+ CompletableFutureUtil.getResult(future);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#switchToPreviousInputMethod(
- * IBooleanResultCallback)}
+ * Calls {@link IInputMethodPrivilegedOperations#switchToPreviousInputMethod(AndroidFuture)}
*
* @return {@code true} if handled
*/
@@ -306,9 +305,9 @@
return false;
}
try {
- final Completable.Boolean value = Completable.createBoolean();
- ops.switchToPreviousInputMethod(ResultCallbacks.of(value));
- return Completable.getResult(value);
+ final AndroidFuture<Boolean> value = new AndroidFuture<>();
+ ops.switchToPreviousInputMethod(value);
+ return CompletableFutureUtil.getResult(value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -329,9 +328,9 @@
return false;
}
try {
- final Completable.Boolean value = Completable.createBoolean();
- ops.switchToNextInputMethod(onlyCurrentIme, ResultCallbacks.of(value));
- return Completable.getResult(value);
+ final AndroidFuture<Boolean> future = new AndroidFuture<>();
+ ops.switchToNextInputMethod(onlyCurrentIme, future);
+ return CompletableFutureUtil.getResult(future);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -339,7 +338,7 @@
/**
* Calls {@link IInputMethodPrivilegedOperations#shouldOfferSwitchingToNextInputMethod(
- * IBooleanResultCallback)}
+ * AndroidFuture)}
*
* @return {@code true} if the IEM should offer a way to globally switch IME
*/
@@ -350,9 +349,9 @@
return false;
}
try {
- final Completable.Boolean value = Completable.createBoolean();
- ops.shouldOfferSwitchingToNextInputMethod(ResultCallbacks.of(value));
- return Completable.getResult(value);
+ final AndroidFuture<Boolean> future = new AndroidFuture<>();
+ ops.shouldOfferSwitchingToNextInputMethod(future);
+ return CompletableFutureUtil.getResult(future);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
index 0c27012..fc299fe 100644
--- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
@@ -21,7 +21,6 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
-import android.os.RemoteException;
import android.os.Trace;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
@@ -40,9 +39,11 @@
import android.view.inputmethod.SurroundingText;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.AndroidFuture;
import com.android.internal.view.IInputContext;
import java.lang.ref.WeakReference;
+import java.util.function.Supplier;
/**
* Takes care of remote method invocations of {@link InputConnection} in the IME client side.
@@ -220,8 +221,9 @@
}
@Override
- public void getTextAfterCursor(int length, int flags, ICharSequenceResultCallback callback) {
- dispatch(() -> {
+ public void getTextAfterCursor(int length, int flags,
+ AndroidFuture future /* T=CharSequence */) {
+ dispatch(future, () -> {
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextAfterCursor");
try {
final InputConnection ic = getInputConnection();
@@ -238,12 +240,7 @@
ImeTracing.getInstance().triggerClientDump(
TAG + "#getTextAfterCursor", mParentInputMethodManager, icProto);
}
- try {
- callback.onResult(result);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to return the result to getTextAfterCursor()."
- + " result=" + result, e);
- }
+ return result;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
}
@@ -251,8 +248,9 @@
}
@Override
- public void getTextBeforeCursor(int length, int flags, ICharSequenceResultCallback callback) {
- dispatch(() -> {
+ public void getTextBeforeCursor(int length, int flags,
+ AndroidFuture future /* T=CharSequence */) {
+ dispatch(future, () -> {
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextBeforeCursor");
try {
final InputConnection ic = getInputConnection();
@@ -269,12 +267,7 @@
ImeTracing.getInstance().triggerClientDump(
TAG + "#getTextBeforeCursor", mParentInputMethodManager, icProto);
}
- try {
- callback.onResult(result);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to return the result to getTextBeforeCursor()."
- + " result=" + result, e);
- }
+ return result;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
}
@@ -282,8 +275,8 @@
}
@Override
- public void getSelectedText(int flags, ICharSequenceResultCallback callback) {
- dispatch(() -> {
+ public void getSelectedText(int flags, AndroidFuture future /* T=CharSequence */) {
+ dispatch(future, () -> {
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSelectedText");
try {
final InputConnection ic = getInputConnection();
@@ -300,12 +293,7 @@
ImeTracing.getInstance().triggerClientDump(
TAG + "#getSelectedText", mParentInputMethodManager, icProto);
}
- try {
- callback.onResult(result);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to return the result to getSelectedText()."
- + " result=" + result, e);
- }
+ return result;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
}
@@ -314,8 +302,8 @@
@Override
public void getSurroundingText(int beforeLength, int afterLength, int flags,
- ISurroundingTextResultCallback callback) {
- dispatch(() -> {
+ AndroidFuture future /* T=SurroundingText */) {
+ dispatch(future, () -> {
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSurroundingText");
try {
final InputConnection ic = getInputConnection();
@@ -332,12 +320,7 @@
ImeTracing.getInstance().triggerClientDump(
TAG + "#getSurroundingText", mParentInputMethodManager, icProto);
}
- try {
- callback.onResult(result);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to return the result to getSurroundingText()."
- + " result=" + result, e);
- }
+ return result;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
}
@@ -345,8 +328,8 @@
}
@Override
- public void getCursorCapsMode(int reqModes, IIntResultCallback callback) {
- dispatch(() -> {
+ public void getCursorCapsMode(int reqModes, AndroidFuture future /* T=Integer */) {
+ dispatch(future, () -> {
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getCursorCapsMode");
try {
final InputConnection ic = getInputConnection();
@@ -363,12 +346,7 @@
ImeTracing.getInstance().triggerClientDump(
TAG + "#getCursorCapsMode", mParentInputMethodManager, icProto);
}
- try {
- callback.onResult(result);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to return the result to getCursorCapsMode()."
- + " result=" + result, e);
- }
+ return result;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
}
@@ -377,8 +355,8 @@
@Override
public void getExtractedText(ExtractedTextRequest request, int flags,
- IExtractedTextResultCallback callback) {
- dispatch(() -> {
+ AndroidFuture future /* T=ExtractedText */) {
+ dispatch(future, () -> {
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getExtractedText");
try {
final InputConnection ic = getInputConnection();
@@ -395,12 +373,7 @@
ImeTracing.getInstance().triggerClientDump(
TAG + "#getExtractedText", mParentInputMethodManager, icProto);
}
- try {
- callback.onResult(result);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to return the result to getExtractedText()."
- + " result=" + result, e);
- }
+ return result;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
}
@@ -710,24 +683,16 @@
}
@Override
- public void requestCursorUpdates(int cursorUpdateMode, IBooleanResultCallback callback) {
- dispatch(() -> {
+ public void requestCursorUpdates(int cursorUpdateMode, AndroidFuture future /* T=Boolean */) {
+ dispatch(future, () -> {
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#requestCursorUpdates");
try {
final InputConnection ic = getInputConnection();
- final boolean result;
if (ic == null || !isActive()) {
Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection");
- result = false;
- } else {
- result = ic.requestCursorUpdates(cursorUpdateMode);
+ return false;
}
- try {
- callback.onResult(result);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to return the result to requestCursorUpdates()."
- + " result=" + result, e);
- }
+ return ic.requestCursorUpdates(cursorUpdateMode);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
}
@@ -736,30 +701,20 @@
@Override
public void commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts,
- IBooleanResultCallback callback) {
- dispatch(() -> {
+ AndroidFuture future /* T=Boolean */) {
+ dispatch(future, () -> {
Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitContent");
try {
final InputConnection ic = getInputConnection();
- final boolean result;
if (ic == null || !isActive()) {
Log.w(TAG, "commitContent on inactive InputConnection");
- result = false;
- } else {
- if (inputContentInfo == null || !inputContentInfo.validate()) {
- Log.w(TAG, "commitContent with invalid inputContentInfo="
- + inputContentInfo);
- result = false;
- } else {
- result = ic.commitContent(inputContentInfo, flags, opts);
- }
+ return false;
}
- try {
- callback.onResult(result);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to return the result to commitContent()."
- + " result=" + result, e);
+ if (inputContentInfo == null || !inputContentInfo.validate()) {
+ Log.w(TAG, "commitContent with invalid inputContentInfo=" + inputContentInfo);
+ return false;
}
+ return ic.commitContent(inputContentInfo, flags, opts);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
}
@@ -793,4 +748,19 @@
mH.post(runnable);
}
+
+ private <T> void dispatch(@NonNull AndroidFuture untypedFuture, @NonNull Supplier<T> supplier) {
+ @SuppressWarnings("unchecked")
+ final AndroidFuture<T> future = untypedFuture;
+ dispatch(() -> {
+ final T result;
+ try {
+ result = supplier.get();
+ } catch (Throwable throwable) {
+ future.completeExceptionally(throwable);
+ throw throwable;
+ }
+ future.complete(result);
+ });
+ }
}
diff --git a/core/java/com/android/internal/inputmethod/ResultCallbacks.java b/core/java/com/android/internal/inputmethod/ResultCallbacks.java
deleted file mode 100644
index 343a6e6..0000000
--- a/core/java/com/android/internal/inputmethod/ResultCallbacks.java
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.inputmethod;
-
-import android.annotation.AnyThread;
-import android.annotation.BinderThread;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * Defines a set of factory methods to create {@link android.os.IBinder}-based callbacks that are
- * associated with completable objects defined in {@link Completable}.
- */
-public final class ResultCallbacks {
-
- /**
- * Not intended to be instantiated.
- */
- private ResultCallbacks() {
- }
-
- @AnyThread
- @Nullable
- private static <T> T unwrap(@NonNull AtomicReference<T> atomicRef) {
- // Only the first caller will receive the non-null original object.
- return atomicRef.getAndSet(null);
- }
-
- /**
- * Creates {@link IIntResultCallback.Stub} that is to set {@link Completable.Int} when receiving
- * the result.
- *
- * @param value {@link Completable.Int} to be set when receiving the result.
- * @return {@link IIntResultCallback.Stub} that can be passed as a binder IPC parameter.
- */
- @AnyThread
- public static IIntResultCallback.Stub of(@NonNull Completable.Int value) {
- final AtomicReference<Completable.Int> atomicRef = new AtomicReference<>(value);
-
- return new IIntResultCallback.Stub() {
- @BinderThread
- @Override
- public void onResult(int result) {
- final Completable.Int value = unwrap(atomicRef);
- if (value == null) {
- return;
- }
- value.onComplete(result);
- }
-
- @BinderThread
- @Override
- public void onError(ThrowableHolder throwableHolder) {
- final Completable.Int value = unwrap(atomicRef);
- if (value == null) {
- return;
- }
- value.onError(throwableHolder);
- }
- };
- }
-
- /**
- * Creates {@link ICharSequenceResultCallback.Stub} that is to set
- * {@link Completable.CharSequence} when receiving the result.
- *
- * @param value {@link Completable.CharSequence} to be set when receiving the result.
- * @return {@link ICharSequenceResultCallback.Stub} that can be passed as a binder IPC
- * parameter.
- */
- @AnyThread
- public static ICharSequenceResultCallback.Stub of(
- @NonNull Completable.CharSequence value) {
- final AtomicReference<Completable.CharSequence> atomicRef = new AtomicReference<>(value);
-
- return new ICharSequenceResultCallback.Stub() {
- @BinderThread
- @Override
- public void onResult(CharSequence result) {
- final Completable.CharSequence value = unwrap(atomicRef);
- if (value == null) {
- return;
- }
- value.onComplete(result);
- }
- };
- }
-
- /**
- * Creates {@link IExtractedTextResultCallback.Stub} that is to set
- * {@link Completable.ExtractedText} when receiving the result.
- *
- * @param value {@link Completable.ExtractedText} to be set when receiving the result.
- * @return {@link IExtractedTextResultCallback.Stub} that can be passed as a binder IPC
- * parameter.
- */
- @AnyThread
- public static IExtractedTextResultCallback.Stub of(
- @NonNull Completable.ExtractedText value) {
- final AtomicReference<Completable.ExtractedText> atomicRef = new AtomicReference<>(value);
-
- return new IExtractedTextResultCallback.Stub() {
- @BinderThread
- @Override
- public void onResult(android.view.inputmethod.ExtractedText result) {
- final Completable.ExtractedText value = unwrap(atomicRef);
- if (value == null) {
- return;
- }
- value.onComplete(result);
- }
- };
- }
-
- /**
- * Creates {@link ISurroundingTextResultCallback.Stub} that is to set
- * {@link Completable.SurroundingText} when receiving the result.
- *
- * @param value {@link Completable.SurroundingText} to be set when receiving the result.
- * @return {@link ISurroundingTextResultCallback.Stub} that can be passed as a binder IPC
- * parameter.
- */
- @AnyThread
- public static ISurroundingTextResultCallback.Stub of(
- @NonNull Completable.SurroundingText value) {
- final AtomicReference<Completable.SurroundingText> atomicRef = new AtomicReference<>(value);
-
- return new ISurroundingTextResultCallback.Stub() {
- @BinderThread
- @Override
- public void onResult(android.view.inputmethod.SurroundingText result) {
- final Completable.SurroundingText value = unwrap(atomicRef);
- if (value == null) {
- return;
- }
- value.onComplete(result);
- }
- };
- }
-
- /**
- * Creates {@link IBooleanResultCallback.Stub} that is to set {@link Completable.Boolean} when
- * receiving the result.
- *
- * @param value {@link Completable.Boolean} to be set when receiving the result.
- * @return {@link IBooleanResultCallback.Stub} that can be passed as a binder IPC parameter.
- */
- @AnyThread
- public static IBooleanResultCallback.Stub of(@NonNull Completable.Boolean value) {
- final AtomicReference<Completable.Boolean> atomicRef = new AtomicReference<>(value);
-
- return new IBooleanResultCallback.Stub() {
- @BinderThread
- @Override
- public void onResult(boolean result) {
- final Completable.Boolean value = unwrap(atomicRef);
- if (value == null) {
- return;
- }
- value.onComplete(result);
- }
-
- @BinderThread
- @Override
- public void onError(ThrowableHolder throwableHolder) {
- final Completable.Boolean value = unwrap(atomicRef);
- if (value == null) {
- return;
- }
- value.onError(throwableHolder);
- }
- };
- }
-
- /**
- * Creates {@link IVoidResultCallback.Stub} that is to set {@link Completable.Void} when
- * receiving the result.
- *
- * @param value {@link Completable.Void} to be set when receiving the result.
- * @return {@link IVoidResultCallback.Stub} that can be passed as a binder IPC parameter.
- */
- @AnyThread
- public static IVoidResultCallback.Stub of(@NonNull Completable.Void value) {
- final AtomicReference<Completable.Void> atomicRef = new AtomicReference<>(value);
-
- return new IVoidResultCallback.Stub() {
- @BinderThread
- @Override
- public void onResult() {
- final Completable.Void value = unwrap(atomicRef);
- if (value == null) {
- return;
- }
- value.onComplete();
- }
-
- @BinderThread
- @Override
- public void onError(ThrowableHolder throwableHolder) {
- final Completable.Void value = unwrap(atomicRef);
- if (value == null) {
- return;
- }
- value.onError(throwableHolder);
- }
- };
- }
-
- /**
- * Creates {@link IInputContentUriTokenResultCallback.Stub} that is to set
- * {@link Completable.IInputContentUriToken} when receiving the result.
- *
- * @param value {@link Completable.IInputContentUriToken} to be set when receiving the result.
- * @return {@link IInputContentUriTokenResultCallback.Stub} that can be passed as a binder IPC
- * parameter.
- */
- @AnyThread
- public static IInputContentUriTokenResultCallback.Stub of(
- @NonNull Completable.IInputContentUriToken value) {
- final AtomicReference<Completable.IInputContentUriToken>
- atomicRef = new AtomicReference<>(value);
-
- return new IInputContentUriTokenResultCallback.Stub() {
- @BinderThread
- @Override
- public void onResult(IInputContentUriToken result) {
- final Completable.IInputContentUriToken value = unwrap(atomicRef);
- if (value == null) {
- return;
- }
- value.onComplete(result);
- }
-
- @BinderThread
- @Override
- public void onError(ThrowableHolder throwableHolder) {
- final Completable.IInputContentUriToken value = unwrap(atomicRef);
- if (value == null) {
- return;
- }
- value.onError(throwableHolder);
- }
- };
- }
-}
diff --git a/core/java/com/android/internal/inputmethod/ThrowableHolder.aidl b/core/java/com/android/internal/inputmethod/ThrowableHolder.aidl
deleted file mode 100644
index ed11293..0000000
--- a/core/java/com/android/internal/inputmethod/ThrowableHolder.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.inputmethod;
-
-parcelable ThrowableHolder;
\ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/ThrowableHolder.java b/core/java/com/android/internal/inputmethod/ThrowableHolder.java
deleted file mode 100644
index b6f4498..0000000
--- a/core/java/com/android/internal/inputmethod/ThrowableHolder.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.inputmethod;
-
-import android.annotation.AnyThread;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * A {@link Parcelable} helper class to encapsulate the exception information.
- */
-public final class ThrowableHolder implements Parcelable {
-
- @Nullable
- private final String mMessage;
-
- ThrowableHolder(@NonNull Throwable throwable) {
- mMessage = throwable.getMessage();
- }
-
- ThrowableHolder(Parcel source) {
- mMessage = source.readString();
- }
-
- /**
- * Returns a {@link ThrowableHolder} with given {@link Throwable}.
- */
- @NonNull
- @AnyThread
- public static ThrowableHolder of(@NonNull Throwable throwable) {
- return new ThrowableHolder(throwable);
- }
-
- /**
- * Gets the message in this {@link ThrowableHolder}.
- */
- @Nullable
- @AnyThread
- String getMessage() {
- return mMessage;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- /**
- * Used to package this object into a {@link Parcel}.
- *
- * @param dest The {@link Parcel} to be written.
- * @param flags The flags used for parceling.
- */
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mMessage);
- }
-
- /**
- * Used to make this class parcelable.
- */
- public static final Parcelable.Creator<ThrowableHolder> CREATOR =
- new Parcelable.Creator<ThrowableHolder>() {
-
- @Override
- public ThrowableHolder createFromParcel(Parcel source) {
- return new ThrowableHolder(source);
- }
-
- @Override
- public ThrowableHolder[] newArray(int size) {
- return new ThrowableHolder[size];
- }
- };
-}
diff --git a/core/java/com/android/internal/logging/UiEventLogger.java b/core/java/com/android/internal/logging/UiEventLogger.java
index 5212265..2f4a14f 100644
--- a/core/java/com/android/internal/logging/UiEventLogger.java
+++ b/core/java/com/android/internal/logging/UiEventLogger.java
@@ -32,6 +32,22 @@
* OEMs should use event IDs above 100000 and below 1000000 (1 million).
*/
interface UiEventEnum {
+
+ /**
+ * Tag used to request new UI Event IDs via presubmit analysis.
+ *
+ * <p>Use RESERVE_NEW_UI_EVENT_ID as the constructor parameter for a new {@link EventEnum}
+ * to signal the presubmit analyzer to reserve a new ID for the event. The new ID will be
+ * returned as a Gerrit presubmit finding. Do not submit {@code RESERVE_NEW_UI_EVENT_ID} as
+ * the constructor parameter for any event.
+ *
+ * <pre>
+ * @UiEvent(doc = "Briefly describe the interaction when this event will be logged")
+ * UNIQUE_EVENT_NAME(RESERVE_NEW_UI_EVENT_ID);
+ * </pre>
+ */
+ int RESERVE_NEW_UI_EVENT_ID = Integer.MIN_VALUE; // Negative IDs are ignored by the logger.
+
int getId();
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 4043060..719dc53 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -661,6 +661,10 @@
* Mapping isolated uids to the actual owning app uid.
*/
final SparseIntArray mIsolatedUids = new SparseIntArray();
+ /**
+ * Internal reference count of isolated uids.
+ */
+ final SparseIntArray mIsolatedUidRefCounts = new SparseIntArray();
/**
* The statistics we have collected organized by uids.
@@ -3855,6 +3859,7 @@
public void addIsolatedUidLocked(int isolatedUid, int appUid,
long elapsedRealtimeMs, long uptimeMs) {
mIsolatedUids.put(isolatedUid, appUid);
+ mIsolatedUidRefCounts.put(isolatedUid, 1);
final Uid u = getUidStatsLocked(appUid, elapsedRealtimeMs, uptimeMs);
u.addIsolatedUid(isolatedUid);
}
@@ -3873,19 +3878,51 @@
}
/**
- * This should only be called after the cpu times have been read.
+ * Isolated uid should only be removed after all wakelocks associated with the uid are stopped
+ * and the cpu time-in-state has been read one last time for the uid.
+ *
* @see #scheduleRemoveIsolatedUidLocked(int, int)
+ *
+ * @return true if the isolated uid is actually removed.
*/
@GuardedBy("this")
- public void removeIsolatedUidLocked(int isolatedUid, long elapsedRealtimeMs, long uptimeMs) {
+ public boolean maybeRemoveIsolatedUidLocked(int isolatedUid, long elapsedRealtimeMs,
+ long uptimeMs) {
+ final int refCount = mIsolatedUidRefCounts.get(isolatedUid, 0) - 1;
+ if (refCount > 0) {
+ // Isolated uid is still being tracked
+ mIsolatedUidRefCounts.put(isolatedUid, refCount);
+ return false;
+ }
+
final int idx = mIsolatedUids.indexOfKey(isolatedUid);
if (idx >= 0) {
final int ownerUid = mIsolatedUids.valueAt(idx);
final Uid u = getUidStatsLocked(ownerUid, elapsedRealtimeMs, uptimeMs);
u.removeIsolatedUid(isolatedUid);
mIsolatedUids.removeAt(idx);
+ mIsolatedUidRefCounts.delete(isolatedUid);
+ } else {
+ Slog.w(TAG, "Attempted to remove untracked isolated uid (" + isolatedUid + ")");
}
mPendingRemovedUids.add(new UidToRemove(isolatedUid, elapsedRealtimeMs));
+
+ return true;
+ }
+
+ /**
+ * Increment the ref count for an isolated uid.
+ * call #maybeRemoveIsolatedUidLocked to decrement.
+ */
+ public void incrementIsolatedUidRefCount(int uid) {
+ final int refCount = mIsolatedUidRefCounts.get(uid, 0);
+ if (refCount <= 0) {
+ // Uid is not mapped or referenced
+ Slog.w(TAG,
+ "Attempted to increment ref counted of untracked isolated uid (" + uid + ")");
+ return;
+ }
+ mIsolatedUidRefCounts.put(uid, refCount + 1);
}
public int mapUid(int uid) {
@@ -4245,7 +4282,7 @@
public void noteStartWakeLocked(int uid, int pid, WorkChain wc, String name, String historyName,
int type, boolean unimportantForLogging, long elapsedRealtimeMs, long uptimeMs) {
- uid = mapUid(uid);
+ final int mappedUid = mapUid(uid);
if (type == WAKE_TYPE_PARTIAL) {
// Only care about partial wake locks, since full wake locks
// will be canceled when the user puts the screen to sleep.
@@ -4255,9 +4292,9 @@
}
if (mRecordAllHistory) {
if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_START, historyName,
- uid, 0)) {
+ mappedUid, 0)) {
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs,
- HistoryItem.EVENT_WAKE_LOCK_START, historyName, uid);
+ HistoryItem.EVENT_WAKE_LOCK_START, historyName, mappedUid);
}
}
if (mWakeLockNesting == 0) {
@@ -4266,7 +4303,7 @@
+ Integer.toHexString(mHistoryCur.states));
mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
mHistoryCur.wakelockTag.string = mInitialAcquireWakeName = historyName;
- mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid;
+ mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = mappedUid;
mWakeLockImportant = !unimportantForLogging;
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
} else if (!mWakeLockImportant && !unimportantForLogging
@@ -4276,14 +4313,19 @@
mHistoryLastWritten.wakelockTag = null;
mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
mHistoryCur.wakelockTag.string = mInitialAcquireWakeName = historyName;
- mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid;
+ mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = mappedUid;
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
mWakeLockImportant = true;
}
mWakeLockNesting++;
}
- if (uid >= 0) {
+ if (mappedUid >= 0) {
+ if (mappedUid != uid) {
+ // Prevent the isolated uid mapping from being removed while the wakelock is
+ // being held.
+ incrementIsolatedUidRefCount(uid);
+ }
if (mOnBatteryScreenOffTimeBase.isRunning()) {
// We only update the cpu time when a wake lock is acquired if the screen is off.
// If the screen is on, we don't distribute the power amongst partial wakelocks.
@@ -4293,7 +4335,7 @@
requestWakelockCpuUpdate();
}
- getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+ getUidStatsLocked(mappedUid, elapsedRealtimeMs, uptimeMs)
.noteStartWakeLocked(pid, name, type, elapsedRealtimeMs);
if (wc != null) {
@@ -4301,8 +4343,8 @@
wc.getTags(), getPowerManagerWakeLockLevel(type), name,
FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
} else {
- FrameworkStatsLog.write_non_chained(FrameworkStatsLog.WAKELOCK_STATE_CHANGED, uid,
- null, getPowerManagerWakeLockLevel(type), name,
+ FrameworkStatsLog.write_non_chained(FrameworkStatsLog.WAKELOCK_STATE_CHANGED,
+ mappedUid, null, getPowerManagerWakeLockLevel(type), name,
FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
}
}
@@ -4316,7 +4358,7 @@
public void noteStopWakeLocked(int uid, int pid, WorkChain wc, String name, String historyName,
int type, long elapsedRealtimeMs, long uptimeMs) {
- uid = mapUid(uid);
+ final int mappedUid = mapUid(uid);
if (type == WAKE_TYPE_PARTIAL) {
mWakeLockNesting--;
if (mRecordAllHistory) {
@@ -4324,9 +4366,9 @@
historyName = name;
}
if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_FINISH, historyName,
- uid, 0)) {
+ mappedUid, 0)) {
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs,
- HistoryItem.EVENT_WAKE_LOCK_FINISH, historyName, uid);
+ HistoryItem.EVENT_WAKE_LOCK_FINISH, historyName, mappedUid);
}
}
if (mWakeLockNesting == 0) {
@@ -4338,7 +4380,7 @@
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
}
- if (uid >= 0) {
+ if (mappedUid >= 0) {
if (mOnBatteryScreenOffTimeBase.isRunning()) {
if (DEBUG_ENERGY_CPU) {
Slog.d(TAG, "Updating cpu time because of -wake_lock");
@@ -4346,17 +4388,22 @@
requestWakelockCpuUpdate();
}
- getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+ getUidStatsLocked(mappedUid, elapsedRealtimeMs, uptimeMs)
.noteStopWakeLocked(pid, name, type, elapsedRealtimeMs);
if (wc != null) {
FrameworkStatsLog.write(FrameworkStatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(),
wc.getTags(), getPowerManagerWakeLockLevel(type), name,
FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
} else {
- FrameworkStatsLog.write_non_chained(FrameworkStatsLog.WAKELOCK_STATE_CHANGED, uid,
- null, getPowerManagerWakeLockLevel(type), name,
+ FrameworkStatsLog.write_non_chained(FrameworkStatsLog.WAKELOCK_STATE_CHANGED,
+ mappedUid, null, getPowerManagerWakeLockLevel(type), name,
FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
}
+
+ if (mappedUid != uid) {
+ // Decrement the ref count for the isolated uid and delete the mapping if uneeded.
+ maybeRemoveIsolatedUidLocked(uid, elapsedRealtimeMs, uptimeMs);
+ }
}
}
@@ -16769,6 +16816,15 @@
pw.print("UIDs removed since the later of device start or stats reset: ");
pw.println(mNumUidsRemoved);
+ pw.println("Currently mapped isolated uids:");
+ final int numIsolatedUids = mIsolatedUids.size();
+ for (int i = 0; i < numIsolatedUids; i++) {
+ final int isolatedUid = mIsolatedUids.keyAt(i);
+ final int ownerUid = mIsolatedUids.valueAt(i);
+ final int refCount = mIsolatedUidRefCounts.get(isolatedUid);
+ pw.println(" " + isolatedUid + "->" + ownerUid + " (ref count = " + refCount + ")");
+ }
+
pw.println();
dumpConstantsLocked(pw);
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 0f26f57e..29e5a5a 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -33,13 +33,11 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.ZygoteProcess;
-import android.os.storage.StorageManager;
import android.provider.DeviceConfig;
import android.security.keystore2.AndroidKeyStoreProvider;
import android.system.ErrnoException;
@@ -58,7 +56,6 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.Preconditions;
-import dalvik.system.DexFile;
import dalvik.system.VMRuntime;
import dalvik.system.ZygoteHooks;
@@ -514,7 +511,6 @@
final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
if (systemServerClasspath != null) {
- performSystemServerDexOpt(systemServerClasspath);
// Capturing profiles is only supported for debug or eng builds since selinux normally
// prevents it.
if (shouldProfileSystemServer() && (Build.IS_USERDEBUG || Build.IS_ENG)) {
@@ -656,95 +652,6 @@
}
/**
- * Performs dex-opt on the elements of {@code classPath}, if needed. We choose the instruction
- * set of the current runtime.
- */
- private static void performSystemServerDexOpt(String classPath) {
- final String[] classPathElements = classPath.split(":");
- final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
-
- String classPathForElement = "";
- for (String classPathElement : classPathElements) {
- // We default to the verify filter because the compilation will happen on /data and
- // system server cannot load executable code outside /system.
- String systemServerFilter = SystemProperties.get(
- "dalvik.vm.systemservercompilerfilter", "verify");
-
- String classLoaderContext =
- getSystemServerClassLoaderContext(classPathForElement);
- int dexoptNeeded;
- try {
- dexoptNeeded = DexFile.getDexOptNeeded(
- classPathElement, instructionSet, systemServerFilter,
- classLoaderContext, false /* newProfile */, false /* downgrade */);
- } catch (FileNotFoundException ignored) {
- // Do not add to the classpath.
- Log.w(TAG, "Missing classpath element for system server: " + classPathElement);
- continue;
- } catch (IOException e) {
- // Not fully clear what to do here as we don't know the cause of the
- // IO exception. Add to the classpath to be conservative, but don't
- // attempt to compile it.
- Log.w(TAG, "Error checking classpath element for system server: "
- + classPathElement, e);
- dexoptNeeded = DexFile.NO_DEXOPT_NEEDED;
- }
-
- if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
- final String packageName = "*";
- final String outputPath = null;
- final int dexFlags = 0;
- final String uuid = StorageManager.UUID_PRIVATE_INTERNAL;
- final String seInfo = null;
- final int targetSdkVersion = 0; // SystemServer targets the system's SDK version
- // Wait for installd to be made available
- IInstalld installd = IInstalld.Stub.asInterface(
- ServiceManager.waitForService("installd"));
-
- try {
- installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName,
- instructionSet, dexoptNeeded, outputPath, dexFlags, systemServerFilter,
- uuid, classLoaderContext, seInfo, false /* downgrade */,
- targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null,
- "server-dexopt");
- } catch (RemoteException | ServiceSpecificException e) {
- // Ignore (but log), we need this on the classpath for fallback mode.
- Log.w(TAG, "Failed compiling classpath element for system server: "
- + classPathElement, e);
- }
- }
-
- classPathForElement = encodeSystemServerClassPath(
- classPathForElement, classPathElement);
- }
- }
-
- /**
- * Encodes the system server class loader context in a format that is accepted by dexopt. This
- * assumes the system server is always loaded with a {@link dalvik.system.PathClassLoader}.
- *
- * Note that ideally we would use the {@code DexoptUtils} to compute this. However we have no
- * dependency here on the server so we hard code the logic again.
- */
- private static String getSystemServerClassLoaderContext(String classPath) {
- return classPath == null ? "PCL[]" : "PCL[" + classPath + "]";
- }
-
- /**
- * Encodes the class path in a format accepted by dexopt.
- *
- * @param classPath The old class path (may be empty).
- * @param newElement The new class path elements
- * @return The class path encoding resulted from appending {@code newElement} to {@code
- * classPath}.
- */
- private static String encodeSystemServerClassPath(String classPath, String newElement) {
- return (classPath == null || classPath.isEmpty())
- ? newElement
- : classPath + ":" + newElement;
- }
-
- /**
* Prepare the arguments and forks for the system server process.
*
* @return A {@code Runnable} that provides an entrypoint into system_server code in the child
diff --git a/core/java/com/android/internal/protolog/ProtoLogGroup.java b/core/java/com/android/internal/protolog/ProtoLogGroup.java
index ce3efd3..0f153bc 100644
--- a/core/java/com/android/internal/protolog/ProtoLogGroup.java
+++ b/core/java/com/android/internal/protolog/ProtoLogGroup.java
@@ -80,6 +80,8 @@
Consts.TAG_WM),
WM_DEBUG_WINDOW_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
Consts.TAG_WM),
+ WM_DEBUG_WINDOW_INSETS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM),
TEST_GROUP(true, true, false, "WindowManagerProtoLogTest");
private final boolean mEnabled;
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 84a7f2f..b427e8b 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -149,7 +149,7 @@
*/
void showAuthenticationDialog(in PromptInfo promptInfo, IBiometricSysuiReceiver sysuiReceiver,
in int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation, int userId,
- String opPackageName, long operationId, int multiSensorConfig);
+ long operationId, String opPackageName, long requestId, int multiSensorConfig);
/**
* Used to notify the authentication dialog that a biometric has been authenticated.
*/
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index e7d6d6c..b3499db 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -110,7 +110,8 @@
// Used to show the authentication dialog (Biometrics, Device Credential)
void showAuthenticationDialog(in PromptInfo promptInfo, IBiometricSysuiReceiver sysuiReceiver,
in int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation,
- int userId, String opPackageName, long operationId, int multiSensorConfig);
+ int userId, long operationId, String opPackageName, long requestId,
+ int multiSensorConfig);
// Used to notify the authentication dialog that a biometric has been authenticated
void onBiometricAuthenticated();
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index f3d0858..4b1753a 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -63,6 +63,7 @@
void onActiveDataSubIdChanged(in int subId);
void onRadioPowerStateChanged(in int state);
void onCallAttributesChanged(in CallAttributes callAttributes);
+ @SuppressWarnings(value={"untyped-collection"})
void onEmergencyNumberListChanged(in Map emergencyNumberList);
void onOutgoingEmergencyCall(in EmergencyNumber placedEmergencyNumber, int subscriptionId);
void onOutgoingEmergencySms(in EmergencyNumber sentEmergencyNumber, int subscriptionId);
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index dd42c40e..12a98c1 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -23,11 +23,7 @@
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputContentInfo;
-import com.android.internal.inputmethod.IBooleanResultCallback;
-import com.android.internal.inputmethod.ICharSequenceResultCallback;
-import com.android.internal.inputmethod.IExtractedTextResultCallback;
-import com.android.internal.inputmethod.IIntResultCallback;
-import com.android.internal.inputmethod.ISurroundingTextResultCallback;
+import com.android.internal.infra.AndroidFuture;
/**
* Interface from an input method to the application, allowing it to perform
@@ -35,14 +31,14 @@
* {@hide}
*/
oneway interface IInputContext {
- void getTextBeforeCursor(int length, int flags, ICharSequenceResultCallback callback);
+ void getTextBeforeCursor(int length, int flags, in AndroidFuture future /* T=CharSequence */);
- void getTextAfterCursor(int length, int flags, ICharSequenceResultCallback callback);
+ void getTextAfterCursor(int length, int flags, in AndroidFuture future /* T=CharSequence */);
- void getCursorCapsMode(int reqModes, IIntResultCallback callback);
+ void getCursorCapsMode(int reqModes, in AndroidFuture future /* T=Integer */);
void getExtractedText(in ExtractedTextRequest request, int flags,
- IExtractedTextResultCallback callback);
+ in AndroidFuture future /* T=ExtractedText */);
void deleteSurroundingText(int beforeLength, int afterLength);
void deleteSurroundingTextInCodePoints(int beforeLength, int afterLength);
@@ -50,7 +46,7 @@
void setComposingText(CharSequence text, int newCursorPosition);
void finishComposingText();
-
+
void commitText(CharSequence text, int newCursorPosition);
void commitCompletion(in CompletionInfo completion);
@@ -58,34 +54,34 @@
void commitCorrection(in CorrectionInfo correction);
void setSelection(int start, int end);
-
+
void performEditorAction(int actionCode);
-
+
void performContextMenuAction(int id);
-
+
void beginBatchEdit();
-
+
void endBatchEdit();
void sendKeyEvent(in KeyEvent event);
-
+
void clearMetaKeyStates(int states);
-
+
void performSpellCheck();
void performPrivateCommand(String action, in Bundle data);
void setComposingRegion(int start, int end);
- void getSelectedText(int flags, ICharSequenceResultCallback callback);
+ void getSelectedText(int flags, in AndroidFuture future /* T=CharSequence */);
- void requestCursorUpdates(int cursorUpdateMode, IBooleanResultCallback callback);
+ void requestCursorUpdates(int cursorUpdateMode, in AndroidFuture future /* T=Boolean */);
void commitContent(in InputContentInfo inputContentInfo, int flags, in Bundle opts,
- IBooleanResultCallback callback);
+ in AndroidFuture future /* T=Boolean */);
void getSurroundingText(int beforeLength, int afterLength, int flags,
- ISurroundingTextResultCallback callback);
+ in AndroidFuture future /* T=SurroundingText */);
void setImeConsumesInput(boolean imeConsumesInput);
}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 6cbace4..b0cf5dc 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -573,13 +573,14 @@
if (!isSystemProcess()) {
return;
}
- // Read configuration of libs from apex module.
+ // Read configuration of features and libs from apex module.
+ int apexPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES;
// TODO: Use a solid way to filter apex module folders?
for (File f: FileUtils.listFilesOrEmpty(Environment.getApexDirectory())) {
if (f.isFile() || f.getPath().contains("@")) {
continue;
}
- readPermissions(Environment.buildPath(f, "etc", "permissions"), ALLOW_LIBS);
+ readPermissions(Environment.buildPath(f, "etc", "permissions"), apexPermissionFlag);
}
}
diff --git a/core/jni/android_media_audio_common_AidlConversion.cpp b/core/jni/android_media_audio_common_AidlConversion.cpp
index 6dcaf76..2ef817c 100644
--- a/core/jni/android_media_audio_common_AidlConversion.cpp
+++ b/core/jni/android_media_audio_common_AidlConversion.cpp
@@ -16,6 +16,9 @@
#define LOG_TAG "AidlConversion"
+#include <sstream>
+#include <type_traits>
+
#include <android_os_Parcel.h>
#include <binder/Parcel.h>
#include <jni.h>
@@ -28,13 +31,64 @@
namespace {
using namespace android;
+using media::audio::common::AudioChannelLayout;
+using media::audio::common::AudioEncapsulationMode;
+using media::audio::common::AudioFormatDescription;
+using media::audio::common::AudioStreamType;
+using media::audio::common::AudioUsage;
#define PACKAGE "android/media/audio/common"
#define CLASSNAME PACKAGE "/AidlConversion"
+// Used for creating messages.
+template <typename T>
+struct type_info {
+ static constexpr const char* name = "";
+};
+#define TYPE_NAME_QUOTE(x) #x
+#define TYPE_NAME_STRINGIFY(x) TYPE_NAME_QUOTE(x)
+#define TYPE_NAME(n) \
+ template <> \
+ struct type_info<n> { \
+ static constexpr const char* name = TYPE_NAME_STRINGIFY(n); \
+ }
+
+TYPE_NAME(AudioChannelLayout);
+TYPE_NAME(AudioEncapsulationMode);
+TYPE_NAME(AudioFormatDescription);
+TYPE_NAME(AudioStreamType);
+TYPE_NAME(AudioUsage);
+TYPE_NAME(audio_encapsulation_mode_t);
+TYPE_NAME(audio_stream_type_t);
+TYPE_NAME(audio_usage_t);
+
+template <typename AidlType, typename LegacyType, typename ConvFunc>
+int aidl2legacy(JNIEnv* env, AidlType aidl, const ConvFunc& conv, LegacyType fallbackValue) {
+ const auto result = conv(aidl);
+ if (result.ok()) {
+ return result.value();
+ }
+ std::ostringstream msg;
+ msg << "Failed to convert " << type_info<AidlType>::name << " value "
+ << static_cast<std::underlying_type_t<AidlType>>(aidl);
+ jniThrowException(env, "java/lang/IllegalArgumentException", msg.str().c_str());
+ return fallbackValue;
+}
+
+template <typename LegacyType, typename AidlType, typename ConvFunc>
+int legacy2aidl(JNIEnv* env, LegacyType legacy, const ConvFunc& conv, AidlType fallbackValue) {
+ const auto result = conv(legacy);
+ if (result.ok()) {
+ return static_cast<std::underlying_type_t<AidlType>>(result.value());
+ }
+ std::ostringstream msg;
+ msg << "Failed to convert legacy " << type_info<LegacyType>::name << " value " << legacy;
+ jniThrowException(env, "java/lang/IllegalArgumentException", msg.str().c_str());
+ return static_cast<std::underlying_type_t<AidlType>>(fallbackValue);
+}
+
template <typename AidlType, typename ConvFunc>
-int aidl2legacy(JNIEnv* env, jobject clazz, jobject jParcel, const ConvFunc& conv,
- int fallbackValue) {
+int aidlParcel2legacy(JNIEnv* env, jobject jParcel, const ConvFunc& conv, int fallbackValue) {
if (Parcel* parcel = parcelForJavaObject(env, jParcel); parcel != nullptr) {
AidlType aidl{};
if (status_t status = aidl.readFromParcel(parcel); status == OK) {
@@ -43,8 +97,11 @@
return legacy.value();
}
} else {
- ALOGE("aidl2legacy: Failed to read from parcel: %d", status);
+ ALOGE("aidl2legacy: Failed to read from parcel: %s", statusToString(status).c_str());
}
+ std::ostringstream msg;
+ msg << "Failed to convert " << type_info<AidlType>::name << " value " << aidl.toString();
+ jniThrowException(env, "java/lang/IllegalArgumentException", msg.str().c_str());
} else {
ALOGE("aidl2legacy: Failed to retrieve the native parcel from Java parcel");
}
@@ -52,9 +109,12 @@
}
template <typename LegacyType, typename ConvFunc>
-jobject legacy2aidl(JNIEnv* env, jobject clazz, LegacyType legacy, const ConvFunc& conv) {
+jobject legacy2aidlParcel(JNIEnv* env, LegacyType legacy, const ConvFunc& conv) {
auto aidl = conv(legacy);
if (!aidl.ok()) {
+ std::ostringstream msg;
+ msg << "Failed to convert legacy " << type_info<LegacyType>::name << " value " << legacy;
+ jniThrowException(env, "java/lang/IllegalArgumentException", msg.str().c_str());
return 0;
}
if (jobject jParcel = createJavaParcelObject(env); jParcel != 0) {
@@ -62,6 +122,9 @@
if (status_t status = aidl.value().writeToParcel(parcel); status == OK) {
parcel->setDataPosition(0);
return jParcel;
+ } else {
+ ALOGE("legacy2aidl: Failed to write to parcel: %s, aidl value: %s",
+ statusToString(status).c_str(), aidl.value().toString().c_str());
}
} else {
ALOGE("legacy2aidl: Failed to retrieve the native parcel from Java parcel");
@@ -73,38 +136,71 @@
return 0;
}
-int aidl2legacy_AudioChannelLayout_Parcel_audio_channel_mask_t(JNIEnv* env, jobject clazz,
+int aidl2legacy_AudioChannelLayout_Parcel_audio_channel_mask_t(JNIEnv* env, jobject,
jobject jParcel, jboolean isInput) {
- return aidl2legacy<media::audio::common::AudioChannelLayout>(
- env, clazz, jParcel,
- [isInput](const media::audio::common::AudioChannelLayout& l) {
+ return aidlParcel2legacy<AudioChannelLayout>(
+ env, jParcel,
+ [isInput](const AudioChannelLayout& l) {
return aidl2legacy_AudioChannelLayout_audio_channel_mask_t(l, isInput);
},
AUDIO_CHANNEL_INVALID);
}
jobject legacy2aidl_audio_channel_mask_t_AudioChannelLayout_Parcel(
- JNIEnv* env, jobject clazz, int /*audio_channel_mask_t*/ legacy, jboolean isInput) {
- return legacy2aidl<audio_channel_mask_t>(
- env, clazz, static_cast<audio_channel_mask_t>(legacy),
- [isInput](audio_channel_mask_t m) {
+ JNIEnv* env, jobject, int /*audio_channel_mask_t*/ legacy, jboolean isInput) {
+ return legacy2aidlParcel(
+ env, static_cast<audio_channel_mask_t>(legacy), [isInput](audio_channel_mask_t m) {
return legacy2aidl_audio_channel_mask_t_AudioChannelLayout(m, isInput);
});
}
-int aidl2legacy_AudioFormatDescription_Parcel_audio_format_t(JNIEnv* env, jobject clazz,
+int aidl2legacy_AudioFormatDescription_Parcel_audio_format_t(JNIEnv* env, jobject,
jobject jParcel) {
- return aidl2legacy<
- media::audio::common::
- AudioFormatDescription>(env, clazz, jParcel,
- aidl2legacy_AudioFormatDescription_audio_format_t,
- AUDIO_FORMAT_INVALID);
+ return aidlParcel2legacy<
+ AudioFormatDescription>(env, jParcel, aidl2legacy_AudioFormatDescription_audio_format_t,
+ AUDIO_FORMAT_INVALID);
}
-jobject legacy2aidl_audio_format_t_AudioFormatDescription_Parcel(JNIEnv* env, jobject clazz,
+jobject legacy2aidl_audio_format_t_AudioFormatDescription_Parcel(JNIEnv* env, jobject,
int /*audio_format_t*/ legacy) {
- return legacy2aidl<audio_format_t>(env, clazz, static_cast<audio_format_t>(legacy),
- legacy2aidl_audio_format_t_AudioFormatDescription);
+ return legacy2aidlParcel(env, static_cast<audio_format_t>(legacy),
+ legacy2aidl_audio_format_t_AudioFormatDescription);
+}
+
+jint aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t(JNIEnv* env, jobject,
+ jint aidl) {
+ return aidl2legacy(env, AudioEncapsulationMode(static_cast<int32_t>(aidl)),
+ android::aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t,
+ AUDIO_ENCAPSULATION_MODE_NONE);
+}
+
+jint legacy2aidl_audio_encapsulation_mode_t_AudioEncapsulationMode(JNIEnv* env, jobject,
+ jint legacy) {
+ return legacy2aidl(env, static_cast<audio_encapsulation_mode_t>(legacy),
+ android::legacy2aidl_audio_encapsulation_mode_t_AudioEncapsulationMode,
+ AudioEncapsulationMode::INVALID);
+}
+
+jint aidl2legacy_AudioStreamType_audio_stream_type_t(JNIEnv* env, jobject, jint aidl) {
+ return aidl2legacy(env, AudioStreamType(static_cast<int32_t>(aidl)),
+ android::aidl2legacy_AudioStreamType_audio_stream_type_t,
+ AUDIO_STREAM_DEFAULT);
+}
+
+jint legacy2aidl_audio_stream_type_t_AudioStreamType(JNIEnv* env, jobject, jint legacy) {
+ return legacy2aidl(env, static_cast<audio_stream_type_t>(legacy),
+ android::legacy2aidl_audio_stream_type_t_AudioStreamType,
+ AudioStreamType::INVALID);
+}
+
+jint aidl2legacy_AudioUsage_audio_usage_t(JNIEnv* env, jobject, jint aidl) {
+ return aidl2legacy(env, AudioUsage(static_cast<int32_t>(aidl)),
+ android::aidl2legacy_AudioUsage_audio_usage_t, AUDIO_USAGE_UNKNOWN);
+}
+
+jint legacy2aidl_audio_usage_t_AudioUsage(JNIEnv* env, jobject, jint legacy) {
+ return legacy2aidl(env, static_cast<audio_usage_t>(legacy),
+ android::legacy2aidl_audio_usage_t_AudioUsage, AudioUsage::INVALID);
}
const JNINativeMethod gMethods[] = {
@@ -116,6 +212,18 @@
reinterpret_cast<void*>(aidl2legacy_AudioFormatDescription_Parcel_audio_format_t)},
{"legacy2aidl_audio_format_t_AudioFormatDescription_Parcel", "(I)Landroid/os/Parcel;",
reinterpret_cast<void*>(legacy2aidl_audio_format_t_AudioFormatDescription_Parcel)},
+ {"aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t", "(I)I",
+ reinterpret_cast<void*>(aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t)},
+ {"legacy2aidl_audio_encapsulation_mode_t_AudioEncapsulationMode", "(I)I",
+ reinterpret_cast<void*>(legacy2aidl_audio_encapsulation_mode_t_AudioEncapsulationMode)},
+ {"aidl2legacy_AudioStreamType_audio_stream_type_t", "(I)I",
+ reinterpret_cast<void*>(aidl2legacy_AudioStreamType_audio_stream_type_t)},
+ {"legacy2aidl_audio_stream_type_t_AudioStreamType", "(I)I",
+ reinterpret_cast<void*>(legacy2aidl_audio_stream_type_t_AudioStreamType)},
+ {"aidl2legacy_AudioUsage_audio_usage_t", "(I)I",
+ reinterpret_cast<void*>(aidl2legacy_AudioUsage_audio_usage_t)},
+ {"legacy2aidl_audio_usage_t_AudioUsage", "(I)I",
+ reinterpret_cast<void*>(legacy2aidl_audio_usage_t_AudioUsage)},
};
} // namespace
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index dd62bb1..4d8d4db 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -261,8 +261,12 @@
sprintf(proc_path, "/proc/%d/cmdline", pid);
fd = open(proc_path, O_RDONLY | O_CLOEXEC);
if (fd >= 0) {
- int rc = read(fd, cmdline, sizeof(cmdline)-1);
- cmdline[rc] = 0;
+ ssize_t rc = read(fd, cmdline, sizeof(cmdline) - 1);
+ if (rc < 0) {
+ ALOGE("read /proc/%d/cmdline (%s)", pid, strerror(errno));
+ } else {
+ cmdline[rc] = 0;
+ }
close(fd);
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index b9233a0..eeb6ce5 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1815,6 +1815,12 @@
return toRotationInt(ui::Transform::toRotation((transformHintRotationFlags)));
}
+static jint nativeGetLayerId(JNIEnv* env, jclass clazz, jlong nativeSurfaceControl) {
+ sp<SurfaceControl> surface(reinterpret_cast<SurfaceControl*>(nativeSurfaceControl));
+
+ return surface->getLayerId();
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod sSurfaceControlMethods[] = {
@@ -2010,6 +2016,8 @@
(void*)nativeGetTransformHint },
{"nativeSetTrustedOverlay", "(JJZ)V",
(void*)nativeSetTrustedOverlay },
+ {"nativeGetLayerId", "(J)I",
+ (void*)nativeGetLayerId },
// clang-format on
};
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index bed0aae..ce44e07 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -62,7 +62,6 @@
#include <sys/time.h>
#include <sys/types.h>
#include <sys/un.h>
-#include <sys/utsname.h>
#include <sys/wait.h>
#include <unistd.h>
@@ -850,26 +849,6 @@
}
}
-static bool NeedsNoRandomizeWorkaround() {
-#if !defined(__arm__)
- return false;
-#else
- int major;
- int minor;
- struct utsname uts;
- if (uname(&uts) == -1) {
- return false;
- }
-
- if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
- return false;
- }
-
- // Kernels before 3.4.* need the workaround.
- return (major < 3) || ((major == 3) && (minor < 4));
-#endif
-}
-
// Utility to close down the Zygote socket file descriptors while
// the child is still running as root with Zygote's privileges. Each
// descriptor (if any) is closed via dup3(), replacing it with a valid
@@ -1740,15 +1719,6 @@
// runtime.
runtime_flags &= ~RuntimeFlags::GWP_ASAN_LEVEL_MASK;
- if (NeedsNoRandomizeWorkaround()) {
- // Work around ARM kernel ASLR lossage (http://b/5817320).
- int old_personality = personality(0xffffffff);
- int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
- if (new_personality == -1) {
- ALOGW("personality(%d) failed: %s", new_personality, strerror(errno));
- }
- }
-
SetCapabilities(permitted_capabilities, effective_capabilities, permitted_capabilities,
fail_fn);
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 6bc00e2..83e26f6 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -972,6 +972,7 @@
optional SettingProto usb_mass_storage_enabled = 127 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto use_google_mail = 128 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto use_open_wifi_package = 129 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto uwb_enabled = 155 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto vt_ims_enabled = 130 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto wait_for_debugger = 131 [ (android.privacy).dest = DEST_AUTOMATIC ];
@@ -1065,5 +1066,5 @@
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 155;
+ // Next tag = 156;
}
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 0121bff..4af9d75 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -480,6 +480,7 @@
optional SurfaceAnimatorProto surface_animator = 4;
repeated WindowContainerChildProto children = 5;
optional IdentifierProto identifier = 6;
+ optional .android.view.SurfaceControlProto surface_control = 7;
}
/* represents a generic child of a WindowContainer */
diff --git a/core/proto/android/view/surfacecontrol.proto b/core/proto/android/view/surfacecontrol.proto
index cbb243b..5a5f035 100644
--- a/core/proto/android/view/surfacecontrol.proto
+++ b/core/proto/android/view/surfacecontrol.proto
@@ -29,4 +29,5 @@
optional int32 hash_code = 1;
optional string name = 2 [ (android.privacy).dest = DEST_EXPLICIT ];
+ optional int32 layerId = 3;
}
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index a398c35..43e7fd6 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -100,7 +100,7 @@
<string name="peerTtyModeHco" msgid="5626377160840915617">"নেটৱৰ্ক পীয়েৰে TTY ম\'ড HCOলৈ সলনি কৰিবলৈ অনুৰোধ কৰিছে"</string>
<string name="peerTtyModeVco" msgid="572208600818270944">"নেটৱৰ্ক পীয়েৰে TTY ম\'ড VCO লৈ সলনি কৰিবলৈ অনুৰোধ কৰিছে"</string>
<string name="peerTtyModeOff" msgid="2420380956369226583">"নেটৱৰ্ক পীয়েৰে TTY ম\'ড OFFলৈ সলনি কৰিবলৈ অনুৰোধ কৰিছে"</string>
- <string name="serviceClassVoice" msgid="2065556932043454987">"Voice"</string>
+ <string name="serviceClassVoice" msgid="2065556932043454987">"কণ্ঠস্বৰ"</string>
<string name="serviceClassData" msgid="4148080018967300248">"ডেটা"</string>
<string name="serviceClassFAX" msgid="2561653371698904118">"ফেক্স"</string>
<string name="serviceClassSMS" msgid="1547664561704509004">"এছএমএছ"</string>
@@ -815,7 +815,7 @@
<string name="phoneTypeTtyTdd" msgid="532038552105328779">"TTY TDD"</string>
<string name="phoneTypeWorkMobile" msgid="7522314392003565121">"কৰ্মস্থানৰ ম’বাইল নম্বৰ"</string>
<string name="phoneTypeWorkPager" msgid="3748332310638505234">"কৰ্মস্থানৰ পেজাৰৰ নম্বৰ"</string>
- <string name="phoneTypeAssistant" msgid="757550783842231039">"Assistant"</string>
+ <string name="phoneTypeAssistant" msgid="757550783842231039">"সহায়ক"</string>
<string name="phoneTypeMms" msgid="1799747455131365989">"এমএমএছ"</string>
<string name="eventTypeCustom" msgid="3257367158986466481">"নিজৰ উপযোগিতা অনুযায়ী"</string>
<string name="eventTypeBirthday" msgid="7770026752793912283">"জন্মদিন"</string>
@@ -848,7 +848,7 @@
<string name="orgTypeOther" msgid="5450675258408005553">"অন্যান্য"</string>
<string name="orgTypeCustom" msgid="1126322047677329218">"নিজৰ উপযোগিতা অনুযায়ী"</string>
<string name="relationTypeCustom" msgid="282938315217441351">"নিজৰ উপযোগিতা অনুযায়ী"</string>
- <string name="relationTypeAssistant" msgid="4057605157116589315">"Assistant"</string>
+ <string name="relationTypeAssistant" msgid="4057605157116589315">"সহায়ক"</string>
<string name="relationTypeBrother" msgid="7141662427379247820">"ভাতৃ"</string>
<string name="relationTypeChild" msgid="9076258911292693601">"শিশু"</string>
<string name="relationTypeDomesticPartner" msgid="7825306887697559238">"সংগী"</string>
@@ -1038,9 +1038,9 @@
<string name="menu_space_shortcut_label" msgid="5949311515646872071">"স্পেচ"</string>
<string name="menu_enter_shortcut_label" msgid="6709499510082897320">"লিখক"</string>
<string name="menu_delete_shortcut_label" msgid="4365787714477739080">"মচক"</string>
- <string name="search_go" msgid="2141477624421347086">"Search"</string>
- <string name="search_hint" msgid="455364685740251925">"অনুসন্ধান কৰক…"</string>
- <string name="searchview_description_search" msgid="1045552007537359343">"Search"</string>
+ <string name="search_go" msgid="2141477624421347086">"সন্ধান কৰক"</string>
+ <string name="search_hint" msgid="455364685740251925">"সন্ধান কৰক…"</string>
+ <string name="searchview_description_search" msgid="1045552007537359343">"সন্ধান কৰক"</string>
<string name="searchview_description_query" msgid="7430242366971716338">"প্ৰশ্নৰ সন্ধান কৰক"</string>
<string name="searchview_description_clear" msgid="1989371719192982900">"প্ৰশ্ন মচক"</string>
<string name="searchview_description_submit" msgid="6771060386117334686">"প্ৰশ্ন দাখিল কৰক"</string>
@@ -1468,7 +1468,7 @@
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"জুম নিয়ন্ত্ৰণ কৰিবলৈ দুবাৰ টিপক"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ৱিজেট যোগ কৰিব পৰা নগ\'ল।"</string>
<string name="ime_action_go" msgid="5536744546326495436">"যাওক"</string>
- <string name="ime_action_search" msgid="4501435960587287668">"Search"</string>
+ <string name="ime_action_search" msgid="4501435960587287668">"সন্ধান কৰক"</string>
<string name="ime_action_send" msgid="8456843745664334138">"পঠিয়াওক"</string>
<string name="ime_action_next" msgid="4169702997635728543">"পৰৱৰ্তী"</string>
<string name="ime_action_done" msgid="6299921014822891569">"সম্পন্ন হ’ল"</string>
@@ -1973,7 +1973,7 @@
<string name="language_picker_section_suggested" msgid="6556199184638990447">"প্ৰস্তাৱিত"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"সকলো ভাষা"</string>
<string name="region_picker_section_all" msgid="756441309928774155">"সকলো অঞ্চল"</string>
- <string name="locale_search_menu" msgid="6258090710176422934">"Search"</string>
+ <string name="locale_search_menu" msgid="6258090710176422934">"সন্ধান কৰক"</string>
<string name="app_suspended_title" msgid="888873445010322650">"এপটো নাই"</string>
<string name="app_suspended_default_message" msgid="6451215678552004172">"এই মুহূৰ্তত <xliff:g id="APP_NAME_0">%1$s</xliff:g> উপলব্ধ নহয়। ইয়াক <xliff:g id="APP_NAME_1">%2$s</xliff:g>এ পৰিচালনা কৰে।"</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"অধিক জানক"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index a4726cf..a1a1731 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -972,7 +972,7 @@
<string name="keyguard_accessibility_pattern_area" msgid="1419570880512350689">"Вобласць узора."</string>
<string name="keyguard_accessibility_slide_area" msgid="4331399051142520176">"Вобласць слайда."</string>
<string name="password_keyboard_label_symbol_key" msgid="2716255580853511949">"123"</string>
- <string name="password_keyboard_label_alpha_key" msgid="5294837425652726684">"ABC"</string>
+ <string name="password_keyboard_label_alpha_key" msgid="5294837425652726684">"АБВ"</string>
<string name="password_keyboard_label_alt_key" msgid="8528261816395508841">"Alt"</string>
<string name="granularity_label_character" msgid="8903387663153706317">"Знак"</string>
<string name="granularity_label_word" msgid="3686589158760620518">"слова"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 710e943..540e7bd 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -100,7 +100,7 @@
<string name="peerTtyModeHco" msgid="5626377160840915617">"પીઅરે TTY મોડ HCO ની વિનંતી કરી"</string>
<string name="peerTtyModeVco" msgid="572208600818270944">"પીઅરે TTY મોડ VCO ની વિનંતી કરી"</string>
<string name="peerTtyModeOff" msgid="2420380956369226583">"પીઅરે TTY મોડ બંધ કરવાની વિનંતી કરી"</string>
- <string name="serviceClassVoice" msgid="2065556932043454987">"Voice"</string>
+ <string name="serviceClassVoice" msgid="2065556932043454987">"વૉઇસ"</string>
<string name="serviceClassData" msgid="4148080018967300248">"ડેટા"</string>
<string name="serviceClassFAX" msgid="2561653371698904118">"ફેક્સ"</string>
<string name="serviceClassSMS" msgid="1547664561704509004">"SMS"</string>
@@ -306,7 +306,7 @@
<string name="permgroupdesc_contacts" msgid="9163927941244182567">"તમારા સંપર્કોને ઍક્સેસ કરવાની"</string>
<string name="permgrouplab_location" msgid="1858277002233964394">"સ્થાન"</string>
<string name="permgroupdesc_location" msgid="1995955142118450685">"આ ઉપકરણના સ્થાનને ઍક્સેસ કરવાની"</string>
- <string name="permgrouplab_calendar" msgid="6426860926123033230">"Calendar"</string>
+ <string name="permgrouplab_calendar" msgid="6426860926123033230">"કૅલેન્ડર"</string>
<string name="permgroupdesc_calendar" msgid="6762751063361489379">"તમારા કેલેન્ડરને ઍક્સેસ કરવાની"</string>
<string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
<string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS સંદેશા મોકલવાની અને જોવાની"</string>
@@ -848,7 +848,7 @@
<string name="orgTypeOther" msgid="5450675258408005553">"અન્ય"</string>
<string name="orgTypeCustom" msgid="1126322047677329218">"કસ્ટમ"</string>
<string name="relationTypeCustom" msgid="282938315217441351">"કસ્ટમ"</string>
- <string name="relationTypeAssistant" msgid="4057605157116589315">"Assistant"</string>
+ <string name="relationTypeAssistant" msgid="4057605157116589315">"આસિસ્ટંટ"</string>
<string name="relationTypeBrother" msgid="7141662427379247820">"ભાઈ"</string>
<string name="relationTypeChild" msgid="9076258911292693601">"બાળક"</string>
<string name="relationTypeDomesticPartner" msgid="7825306887697559238">"ઘરેલું ભાગીદાર"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 4d328d3..c6ae32a 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -306,7 +306,7 @@
<string name="permgroupdesc_contacts" msgid="9163927941244182567">"ನಿಮ್ಮ ಸಂಪರ್ಕಗಳನ್ನು ಪ್ರವೇಶಿಸಲು"</string>
<string name="permgrouplab_location" msgid="1858277002233964394">"ಸ್ಥಳ"</string>
<string name="permgroupdesc_location" msgid="1995955142118450685">"ಈ ಸಾಧನದ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
- <string name="permgrouplab_calendar" msgid="6426860926123033230">"Calendar"</string>
+ <string name="permgrouplab_calendar" msgid="6426860926123033230">"ಕ್ಯಾಲೆಂಡರ್"</string>
<string name="permgroupdesc_calendar" msgid="6762751063361489379">"ನಿಮ್ಮ ಕ್ಯಾಲೆಂಡರ್ ಪ್ರವೇಶಿಸಲು"</string>
<string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
<string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS ಸಂದೇಶಗಳನ್ನು ಕಳುಹಿಸಲು ಮತ್ತು ನಿರ್ವಹಿಸಲು"</string>
@@ -815,7 +815,7 @@
<string name="phoneTypeTtyTdd" msgid="532038552105328779">"TTY TDD"</string>
<string name="phoneTypeWorkMobile" msgid="7522314392003565121">"ಕಚೇರಿ ಮೊಬೈಲ್"</string>
<string name="phoneTypeWorkPager" msgid="3748332310638505234">"ಕಚೇರಿ ಪೇಜರ್"</string>
- <string name="phoneTypeAssistant" msgid="757550783842231039">"Assistant"</string>
+ <string name="phoneTypeAssistant" msgid="757550783842231039">"ಅಸಿಸ್ಟೆಂಟ್"</string>
<string name="phoneTypeMms" msgid="1799747455131365989">"MMS"</string>
<string name="eventTypeCustom" msgid="3257367158986466481">"ಕಸ್ಟಮ್"</string>
<string name="eventTypeBirthday" msgid="7770026752793912283">"ಜನ್ಮದಿನ"</string>
@@ -848,7 +848,7 @@
<string name="orgTypeOther" msgid="5450675258408005553">"ಇತರೆ"</string>
<string name="orgTypeCustom" msgid="1126322047677329218">"ಕಸ್ಟಮ್"</string>
<string name="relationTypeCustom" msgid="282938315217441351">"ಕಸ್ಟಮ್"</string>
- <string name="relationTypeAssistant" msgid="4057605157116589315">"Assistant"</string>
+ <string name="relationTypeAssistant" msgid="4057605157116589315">"ಅಸಿಸ್ಟೆಂಟ್"</string>
<string name="relationTypeBrother" msgid="7141662427379247820">"ಸಹೋದರ"</string>
<string name="relationTypeChild" msgid="9076258911292693601">"ಮಗು"</string>
<string name="relationTypeDomesticPartner" msgid="7825306887697559238">"ಸ್ಥಳೀಯ ಪಾಲುದಾರ"</string>
@@ -1038,9 +1038,9 @@
<string name="menu_space_shortcut_label" msgid="5949311515646872071">"space"</string>
<string name="menu_enter_shortcut_label" msgid="6709499510082897320">"enter"</string>
<string name="menu_delete_shortcut_label" msgid="4365787714477739080">"ಅಳಿಸಿ"</string>
- <string name="search_go" msgid="2141477624421347086">"Search"</string>
+ <string name="search_go" msgid="2141477624421347086">"ಹುಡುಕಿ"</string>
<string name="search_hint" msgid="455364685740251925">"ಹುಡುಕಿ…"</string>
- <string name="searchview_description_search" msgid="1045552007537359343">"Search"</string>
+ <string name="searchview_description_search" msgid="1045552007537359343">"ಹುಡುಕಿ"</string>
<string name="searchview_description_query" msgid="7430242366971716338">"ಪ್ರಶ್ನೆಯನ್ನು ಹುಡುಕಿ"</string>
<string name="searchview_description_clear" msgid="1989371719192982900">"ಪ್ರಶ್ನೆಯನ್ನು ತೆರವುಗೊಳಿಸು"</string>
<string name="searchview_description_submit" msgid="6771060386117334686">"ಪ್ರಶ್ನೆಯನ್ನು ಸಲ್ಲಿಸು"</string>
@@ -1468,7 +1468,7 @@
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ಝೂಮ್ ನಿಯಂತ್ರಿಸಲು ಎರಡು ಬಾರಿ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ವಿಜೆಟ್ ಸೇರಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ."</string>
<string name="ime_action_go" msgid="5536744546326495436">"ಹೋಗು"</string>
- <string name="ime_action_search" msgid="4501435960587287668">"Search"</string>
+ <string name="ime_action_search" msgid="4501435960587287668">"ಹುಡುಕಿ"</string>
<string name="ime_action_send" msgid="8456843745664334138">"ಕಳುಹಿಸು"</string>
<string name="ime_action_next" msgid="4169702997635728543">"ಮುಂದೆ"</string>
<string name="ime_action_done" msgid="6299921014822891569">"ಮುಗಿದಿದೆ"</string>
@@ -1973,7 +1973,7 @@
<string name="language_picker_section_suggested" msgid="6556199184638990447">"ಸೂಚಿತ ಭಾಷೆ"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"ಎಲ್ಲಾ ಭಾಷೆಗಳು"</string>
<string name="region_picker_section_all" msgid="756441309928774155">"ಎಲ್ಲಾ ಪ್ರದೇಶಗಳು"</string>
- <string name="locale_search_menu" msgid="6258090710176422934">"Search"</string>
+ <string name="locale_search_menu" msgid="6258090710176422934">"ಹುಡುಕಿ"</string>
<string name="app_suspended_title" msgid="888873445010322650">"ಅಪ್ಲಿಕೇಶನ್ ಲಭ್ಯವಿಲ್ಲ"</string>
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ಅಪ್ಲಿಕೇಶನ್ ಸದ್ಯಕ್ಕೆ ಲಭ್ಯವಿಲ್ಲ. ಇದನ್ನು <xliff:g id="APP_NAME_1">%2$s</xliff:g> ನಲ್ಲಿ ನಿರ್ವಹಿಸಲಾಗುತ್ತಿದೆ."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index d6491b7..5e8b1f1 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -100,7 +100,7 @@
<string name="peerTtyModeHco" msgid="5626377160840915617">"പിയർ അഭ്യർത്ഥിച്ച TTY മോഡ് HCO"</string>
<string name="peerTtyModeVco" msgid="572208600818270944">"പിയർ അഭ്യർത്ഥിച്ച TTY മോഡ് VCO"</string>
<string name="peerTtyModeOff" msgid="2420380956369226583">"പിയർ അഭ്യർത്ഥിച്ച TTY മോഡ് \'ഓഫ്\'"</string>
- <string name="serviceClassVoice" msgid="2065556932043454987">"Voice"</string>
+ <string name="serviceClassVoice" msgid="2065556932043454987">"ശബ്ദം"</string>
<string name="serviceClassData" msgid="4148080018967300248">"ഡാറ്റ"</string>
<string name="serviceClassFAX" msgid="2561653371698904118">"ഫാക്സ്"</string>
<string name="serviceClassSMS" msgid="1547664561704509004">"SMS"</string>
@@ -815,7 +815,7 @@
<string name="phoneTypeTtyTdd" msgid="532038552105328779">"TTY TDD"</string>
<string name="phoneTypeWorkMobile" msgid="7522314392003565121">"ഓഫീസ് മൊബൈല്"</string>
<string name="phoneTypeWorkPager" msgid="3748332310638505234">"ഔദ്യോഗിക പേജര്"</string>
- <string name="phoneTypeAssistant" msgid="757550783842231039">"അസിസ്റ്റന്റ്"</string>
+ <string name="phoneTypeAssistant" msgid="757550783842231039">"അസിസ്റ്റന്റ്"</string>
<string name="phoneTypeMms" msgid="1799747455131365989">"MMS"</string>
<string name="eventTypeCustom" msgid="3257367158986466481">"ഇഷ്ടാനുസൃതം"</string>
<string name="eventTypeBirthday" msgid="7770026752793912283">"ജന്മദിനം"</string>
@@ -1038,9 +1038,9 @@
<string name="menu_space_shortcut_label" msgid="5949311515646872071">"space"</string>
<string name="menu_enter_shortcut_label" msgid="6709499510082897320">"enter"</string>
<string name="menu_delete_shortcut_label" msgid="4365787714477739080">"delete"</string>
- <string name="search_go" msgid="2141477624421347086">"Search"</string>
+ <string name="search_go" msgid="2141477624421347086">"തിരയുക"</string>
<string name="search_hint" msgid="455364685740251925">"തിരയുക…"</string>
- <string name="searchview_description_search" msgid="1045552007537359343">"Search"</string>
+ <string name="searchview_description_search" msgid="1045552007537359343">"തിരയുക"</string>
<string name="searchview_description_query" msgid="7430242366971716338">"തിരയൽ അന്വേഷണം"</string>
<string name="searchview_description_clear" msgid="1989371719192982900">"അന്വേഷണം മായ്ക്കുക"</string>
<string name="searchview_description_submit" msgid="6771060386117334686">"ചോദ്യം സമർപ്പിക്കുക"</string>
@@ -1468,7 +1468,7 @@
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"സൂം നിയന്ത്രണം ലഭിക്കാൻ രണ്ടുതവണ ടാപ്പുചെയ്യുക"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"വിജറ്റ് ചേർക്കാനായില്ല."</string>
<string name="ime_action_go" msgid="5536744546326495436">"പോവുക"</string>
- <string name="ime_action_search" msgid="4501435960587287668">"Search"</string>
+ <string name="ime_action_search" msgid="4501435960587287668">"തിരയുക"</string>
<string name="ime_action_send" msgid="8456843745664334138">"അയയ്ക്കുക"</string>
<string name="ime_action_next" msgid="4169702997635728543">"അടുത്തത്"</string>
<string name="ime_action_done" msgid="6299921014822891569">"പൂർത്തിയായി"</string>
@@ -1973,7 +1973,7 @@
<string name="language_picker_section_suggested" msgid="6556199184638990447">"നിര്ദ്ദേശിച്ചത്"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"എല്ലാ ഭാഷകളും"</string>
<string name="region_picker_section_all" msgid="756441309928774155">"എല്ലാ പ്രദേശങ്ങളും"</string>
- <string name="locale_search_menu" msgid="6258090710176422934">"Search"</string>
+ <string name="locale_search_menu" msgid="6258090710176422934">"തിരയുക"</string>
<string name="app_suspended_title" msgid="888873445010322650">"ആപ്പ് ലഭ്യമല്ല"</string>
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ഇപ്പോൾ ലഭ്യമല്ല. <xliff:g id="APP_NAME_1">%2$s</xliff:g> ആണ് ഇത് മാനേജ് ചെയ്യുന്നത്."</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"കൂടുതലറിയുക"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 7c120f4..424a521 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -815,7 +815,7 @@
<string name="phoneTypeTtyTdd" msgid="532038552105328779">"TTY TDD"</string>
<string name="phoneTypeWorkMobile" msgid="7522314392003565121">"कार्य मोबाइल"</string>
<string name="phoneTypeWorkPager" msgid="3748332310638505234">"कार्य पेजर"</string>
- <string name="phoneTypeAssistant" msgid="757550783842231039">"असिस्टंट"</string>
+ <string name="phoneTypeAssistant" msgid="757550783842231039">"Assistant"</string>
<string name="phoneTypeMms" msgid="1799747455131365989">"MMS"</string>
<string name="eventTypeCustom" msgid="3257367158986466481">"कस्टम"</string>
<string name="eventTypeBirthday" msgid="7770026752793912283">"वाढदिवस"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 7d27d4e..9d42402 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1038,9 +1038,9 @@
<string name="menu_space_shortcut_label" msgid="5949311515646872071">"ସ୍ପେସ୍"</string>
<string name="menu_enter_shortcut_label" msgid="6709499510082897320">"ଏଣ୍ଟର୍"</string>
<string name="menu_delete_shortcut_label" msgid="4365787714477739080">"ଡିଲିଟ୍ କରନ୍ତୁ"</string>
- <string name="search_go" msgid="2141477624421347086">"Search"</string>
- <string name="search_hint" msgid="455364685740251925">"ସର୍ଚ୍ଚ…"</string>
- <string name="searchview_description_search" msgid="1045552007537359343">"Search"</string>
+ <string name="search_go" msgid="2141477624421347086">"ସନ୍ଧାନ କରନ୍ତୁ"</string>
+ <string name="search_hint" msgid="455364685740251925">"ସନ୍ଧାନ…"</string>
+ <string name="searchview_description_search" msgid="1045552007537359343">"ସନ୍ଧାନ କରନ୍ତୁ"</string>
<string name="searchview_description_query" msgid="7430242366971716338">"କ୍ୱେରୀ ସର୍ଚ୍ଚ କରନ୍ତୁ"</string>
<string name="searchview_description_clear" msgid="1989371719192982900">"କ୍ୱେରୀ ଖାଲି କରନ୍ତୁ"</string>
<string name="searchview_description_submit" msgid="6771060386117334686">"କ୍ୱେରୀ ଦାଖଲ କରନ୍ତୁ"</string>
@@ -1468,7 +1468,7 @@
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ଜୁମ୍ ନିୟନ୍ତ୍ରଣ ପାଇଁ ଦୁଇଥର ଟାପ୍ କରନ୍ତୁ"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ୱିଜେଟ୍ ଯୋଡ଼ିପାରିବ ନାହିଁ।"</string>
<string name="ime_action_go" msgid="5536744546326495436">"ଯାଆନ୍ତୁ"</string>
- <string name="ime_action_search" msgid="4501435960587287668">"Search"</string>
+ <string name="ime_action_search" msgid="4501435960587287668">"ସନ୍ଧାନ କରନ୍ତୁ"</string>
<string name="ime_action_send" msgid="8456843745664334138">"ପଠାନ୍ତୁ"</string>
<string name="ime_action_next" msgid="4169702997635728543">"ପରବର୍ତ୍ତୀ"</string>
<string name="ime_action_done" msgid="6299921014822891569">"ହୋଇଗଲା"</string>
@@ -1973,7 +1973,7 @@
<string name="language_picker_section_suggested" msgid="6556199184638990447">"ପ୍ରସ୍ତାବିତ"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"ସମସ୍ତ ଭାଷା"</string>
<string name="region_picker_section_all" msgid="756441309928774155">"ସମସ୍ତ ଅଞ୍ଚଳ"</string>
- <string name="locale_search_menu" msgid="6258090710176422934">"Search"</string>
+ <string name="locale_search_menu" msgid="6258090710176422934">"ସନ୍ଧାନ କରନ୍ତୁ"</string>
<string name="app_suspended_title" msgid="888873445010322650">"ଆପ୍ ଉପଲବ୍ଧ ନାହିଁ"</string>
<string name="app_suspended_default_message" msgid="6451215678552004172">"ବର୍ତ୍ତମାନ <xliff:g id="APP_NAME_0">%1$s</xliff:g> ଉପଲବ୍ଧ ନାହିଁ। ଏହା <xliff:g id="APP_NAME_1">%2$s</xliff:g> ଦ୍ଵାରା ପରିଚାଳିତ ହେଉଛି।"</string>
<string name="app_suspended_more_details" msgid="211260942831587014">"ଅଧିକ ଜାଣନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 12bf1bb..7577457 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -306,7 +306,7 @@
<string name="permgroupdesc_contacts" msgid="9163927941244182567">"ਆਪਣੇ ਸੰਪਰਕਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ"</string>
<string name="permgrouplab_location" msgid="1858277002233964394">"ਟਿਕਾਣਾ"</string>
<string name="permgroupdesc_location" msgid="1995955142118450685">"ਇਸ ਡੀਵਾਈਸ ਦੇ ਨਿਰਧਾਰਤ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚੋ"</string>
- <string name="permgrouplab_calendar" msgid="6426860926123033230">"Calendar"</string>
+ <string name="permgrouplab_calendar" msgid="6426860926123033230">"ਕੈਲੰਡਰ"</string>
<string name="permgroupdesc_calendar" msgid="6762751063361489379">"ਤੁਹਾਡੇ ਕੈਲੰਡਰ ਤੱਕ ਪਹੁੰਚ ਕਰਨ"</string>
<string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
<string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS ਸੁਨੇਹੇ ਭੇਜੋ ਅਤੇ ਦੇਖੋ"</string>
@@ -815,7 +815,7 @@
<string name="phoneTypeTtyTdd" msgid="532038552105328779">"TTY TDD"</string>
<string name="phoneTypeWorkMobile" msgid="7522314392003565121">"ਕੰਮ ਦਾ ਮੋਬਾਈਲ"</string>
<string name="phoneTypeWorkPager" msgid="3748332310638505234">"ਦਫ਼ਤਰ ਦਾ ਪੇਜਰ"</string>
- <string name="phoneTypeAssistant" msgid="757550783842231039">"ਸਹਾਇਕ"</string>
+ <string name="phoneTypeAssistant" msgid="757550783842231039">"Assistant"</string>
<string name="phoneTypeMms" msgid="1799747455131365989">"MMS"</string>
<string name="eventTypeCustom" msgid="3257367158986466481">"ਵਿਉਂਂਤੀ"</string>
<string name="eventTypeBirthday" msgid="7770026752793912283">"ਜਨਮਦਿਨ"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index cb18e2c..80f55c6 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -322,7 +322,7 @@
<string name="permgroupdesc_microphone" msgid="1047786732792487722">"nahrávanie zvuku"</string>
<string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Fyzická aktivita"</string>
<string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"prístup k vašej fyzickej aktivite"</string>
- <string name="permgrouplab_camera" msgid="9090413408963547706">"Fotoaparát"</string>
+ <string name="permgrouplab_camera" msgid="9090413408963547706">"Kamera"</string>
<string name="permgroupdesc_camera" msgid="7585150538459320326">"fotenie a natáčanie videí"</string>
<string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"Zariadenia v okolí"</string>
<string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"objavovať a pripájať zariadenia v okolí"</string>
diff --git a/core/res/res/values-sw600dp/config.xml b/core/res/res/values-sw600dp/config.xml
index 34b6a54..861e329 100644
--- a/core/res/res/values-sw600dp/config.xml
+++ b/core/res/res/values-sw600dp/config.xml
@@ -51,5 +51,10 @@
<!-- If true, show multiuser switcher by default unless the user specifically disables it. -->
<bool name="config_showUserSwitcherByDefault">true</bool>
+
+ <!-- Enable dynamic keyguard positioning for large-width screens. This will cause the keyguard
+ to be aligned to one side of the screen when in landscape mode. -->
+ <bool name="config_enableDynamicKeyguardPositioning">true</bool>
+
</resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index ffd5078..c6b6b77 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -100,7 +100,7 @@
<string name="peerTtyModeHco" msgid="5626377160840915617">"TTY Mode HCOஐ இணைச் செயல்பாடு கோரியது"</string>
<string name="peerTtyModeVco" msgid="572208600818270944">"TTY Mode VCOஐ இணைச் செயல்பாடு கோரியது"</string>
<string name="peerTtyModeOff" msgid="2420380956369226583">"TTY Mode OFFஐ இணைச் செயல்பாடு கோரியது"</string>
- <string name="serviceClassVoice" msgid="2065556932043454987">"Voice"</string>
+ <string name="serviceClassVoice" msgid="2065556932043454987">"குரல்"</string>
<string name="serviceClassData" msgid="4148080018967300248">"தரவு"</string>
<string name="serviceClassFAX" msgid="2561653371698904118">"தொலைநகல்"</string>
<string name="serviceClassSMS" msgid="1547664561704509004">"SMS"</string>
@@ -815,7 +815,7 @@
<string name="phoneTypeTtyTdd" msgid="532038552105328779">"TTY TDD"</string>
<string name="phoneTypeWorkMobile" msgid="7522314392003565121">"பணியிட மொபைல்"</string>
<string name="phoneTypeWorkPager" msgid="3748332310638505234">"பணியிட பேஜர்"</string>
- <string name="phoneTypeAssistant" msgid="757550783842231039">"Assistant"</string>
+ <string name="phoneTypeAssistant" msgid="757550783842231039">"உதவியாளர்"</string>
<string name="phoneTypeMms" msgid="1799747455131365989">"MMS"</string>
<string name="eventTypeCustom" msgid="3257367158986466481">"பிரத்தியேகம்"</string>
<string name="eventTypeBirthday" msgid="7770026752793912283">"பிறந்தநாள்"</string>
@@ -848,7 +848,7 @@
<string name="orgTypeOther" msgid="5450675258408005553">"மற்றவை"</string>
<string name="orgTypeCustom" msgid="1126322047677329218">"பிரத்தியேகம்"</string>
<string name="relationTypeCustom" msgid="282938315217441351">"பிரத்தியேகம்"</string>
- <string name="relationTypeAssistant" msgid="4057605157116589315">"Assistant"</string>
+ <string name="relationTypeAssistant" msgid="4057605157116589315">"உதவியாளர்"</string>
<string name="relationTypeBrother" msgid="7141662427379247820">"சகோதரர்"</string>
<string name="relationTypeChild" msgid="9076258911292693601">"குழந்தை"</string>
<string name="relationTypeDomesticPartner" msgid="7825306887697559238">"வாழ்வுத் துணை"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index f3d2faa..478886a 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -71,10 +71,10 @@
<string name="RuacMmi" msgid="1876047385848991110">"అవాంఛిత అంతరాయ కాల్స్ల తిరస్కరణ"</string>
<string name="CndMmi" msgid="185136449405618437">"కాలింగ్ నంబర్ బట్వాడా"</string>
<string name="DndMmi" msgid="8797375819689129800">"అంతరాయం కలిగించవద్దు"</string>
- <string name="CLIRDefaultOnNextCallOn" msgid="4511621022859867988">"కాలర్ ID డిఫాల్ట్గా పరిమితానికి ఉంటుంది. తర్వాత కాల్: పరిమితం చేయబడింది"</string>
- <string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"కాలర్ ID డిఫాల్ట్గా పరిమితానికి ఉంటుంది. తర్వాత కాల్: అపరిమితం"</string>
- <string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"కాలర్ ID డిఫాల్ట్గా అపరిమితానికి ఉంటుంది. తర్వాత కాల్: పరిమితం చేయబడింది"</string>
- <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"కాలర్ ID డిఫాల్ట్గా అపరిమితానికి ఉంటుంది. తర్వాత కాల్: అపరిమితం"</string>
+ <string name="CLIRDefaultOnNextCallOn" msgid="4511621022859867988">"కాలర్ ID ఆటోమేటిక్లపై పరిమితి ఉంటుంది. తర్వాత కాల్: పరిమితి ఉంటుంది"</string>
+ <string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"కాలర్ ID ఆటోమేటిక్లపై పరిమితి ఉంటుంది. తర్వాత కాల్: పరిమితి లేదు"</string>
+ <string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"కాలర్ ID ఆటోమేటిక్లపై పరిమితి లేదు. తర్వాత కాల్: పరిమితి ఉంటుంది"</string>
+ <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"కాలర్ ID ఆటోమేటిక్లపై పరిమితి లేదు. తర్వాత కాల్: పరిమితి లేదు"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"సేవ కేటాయించబడలేదు."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"మీరు కాలర్ ID సెట్టింగ్ను మార్చలేరు."</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"మొబైల్ డేటా సేవ లేదు"</string>
@@ -100,7 +100,7 @@
<string name="peerTtyModeHco" msgid="5626377160840915617">"అవతలి వారు HCO TTY మోడ్ని అభ్యర్థించారు"</string>
<string name="peerTtyModeVco" msgid="572208600818270944">"అవతలి వారు VCO TTY మోడ్ని అభ్యర్థించారు"</string>
<string name="peerTtyModeOff" msgid="2420380956369226583">"అవతలి వారు OFF TTY మోడ్ని అభ్యర్థించారు"</string>
- <string name="serviceClassVoice" msgid="2065556932043454987">"Voice"</string>
+ <string name="serviceClassVoice" msgid="2065556932043454987">"వాయిస్"</string>
<string name="serviceClassData" msgid="4148080018967300248">"డేటా"</string>
<string name="serviceClassFAX" msgid="2561653371698904118">"ఫ్యాక్స్"</string>
<string name="serviceClassSMS" msgid="1547664561704509004">"SMS"</string>
@@ -306,7 +306,7 @@
<string name="permgroupdesc_contacts" msgid="9163927941244182567">"మీ కాంటాక్ట్లను యాక్సెస్ చేయడానికి"</string>
<string name="permgrouplab_location" msgid="1858277002233964394">"లొకేషన్"</string>
<string name="permgroupdesc_location" msgid="1995955142118450685">"ఈ పరికర స్థానాన్ని యాక్సెస్ చేయడానికి"</string>
- <string name="permgrouplab_calendar" msgid="6426860926123033230">"Calendar"</string>
+ <string name="permgrouplab_calendar" msgid="6426860926123033230">"క్యాలెండర్"</string>
<string name="permgroupdesc_calendar" msgid="6762751063361489379">"మీ క్యాలెండర్ను యాక్సెస్ చేయడానికి"</string>
<string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
<string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS మెసేజ్లను పంపడం మరియు వీక్షించడం"</string>
@@ -435,8 +435,8 @@
<string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"ఈ యాప్ మీ Android TV పరికరంలో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ యజమానుల నుండి వచ్చినట్లుగా మెసేజ్లను పంపగలదు లేదా ఈవెంట్లను వాటి యజమానులకు తెలియకుండానే మార్చగలదు."</string>
<string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"ఈ యాప్ మీ ఫోన్లో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ యజమానుల నుండి వచ్చినట్లుగా మెసేజ్లను పంపగలదు లేదా ఈవెంట్లను వాటి యజమానులకు తెలియకుండానే మార్చగలదు."</string>
<string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"అదనపు స్థాన ప్రదాత ఆదేశాలను యాక్సెస్ చేయడం"</string>
- <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"అదనపు స్థాన ప్రదాత ఆదేశాలను యాక్సెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. ఇది GPS లేదా ఇతర స్థాన మూలాల నిర్వహణలో యాప్ ప్రమేయం ఉండేలా అనుమతించవచ్చు."</string>
- <string name="permlab_accessFineLocation" msgid="6426318438195622966">"స్క్రీన్పై ఉన్నప్పుడు మాత్రమే ఖచ్చితమైన స్థానాన్ని యాక్సెస్ చేయండి"</string>
+ <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"అదనపు లొకేషన్ ప్రొవైడర్ కమాండ్లను యాక్సెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. ఇది GPS లేదా ఇతర లొకేషన్ సోర్స్ల నిర్వహణలో యాప్ ప్రమేయం ఉండేలా అనుమతించవచ్చు."</string>
+ <string name="permlab_accessFineLocation" msgid="6426318438195622966">"స్క్రీన్పై ఉన్నప్పుడు మాత్రమే ఖచ్చితమైన లొకేషన్ను యాక్సెస్ చేయండి"</string>
<string name="permdesc_accessFineLocation" msgid="6732174080240016335">"యాప్ ఉపయోగంలో ఉన్నప్పుడు మాత్రమే ఈ యాప్ మీ ఖచ్చితమైన లొకేషన్ను లొకేషన్ సర్వీస్ల ద్వారా తెలుసుకోగలదు. లొకేషన్ను యాప్ పొందాలంటే, దాని కోసం మీ పరికరం యొక్క లొకేషన్ సర్వీస్లను తప్పనిసరిగా ఆన్ చేయాలి. ఇది బ్యాటరీ వినియోగాన్ని పెంచవచ్చు."</string>
<string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"స్క్రీన్పై ఉన్నప్పుడు మాత్రమే సుమారు లొకేషన్ను యాక్సెస్ చేయండి"</string>
<string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"యాప్ ఉపయోగంలో ఉన్నప్పుడు మాత్రమే ఈ యాప్ మీ ఇంచుమించు లొకేషన్ను లొకేషన్ సర్వీస్ల నుండి తెలుసుకోగలదు. లొకేషన్ను యాప్ పొందాలంటే, దాని కోసం మీ పరికరం యొక్క లొకేషన్ సర్వీస్లను తప్పనిసరిగా ఆన్ చేయాలి."</string>
@@ -848,7 +848,7 @@
<string name="orgTypeOther" msgid="5450675258408005553">"ఇతరం"</string>
<string name="orgTypeCustom" msgid="1126322047677329218">"అనుకూలం"</string>
<string name="relationTypeCustom" msgid="282938315217441351">"అనుకూలం"</string>
- <string name="relationTypeAssistant" msgid="4057605157116589315">"Assistant"</string>
+ <string name="relationTypeAssistant" msgid="4057605157116589315">"అసిస్టెంట్"</string>
<string name="relationTypeBrother" msgid="7141662427379247820">"సోదరుడు"</string>
<string name="relationTypeChild" msgid="9076258911292693601">"బిడ్డ"</string>
<string name="relationTypeDomesticPartner" msgid="7825306887697559238">"జీవిత భాగస్వామి"</string>
@@ -916,12 +916,12 @@
<string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"మీరు మీ అన్లాక్ నమూనాని <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విజయవంతం కాని ప్రయత్నాల తర్వాత, మీరు మీ Google సైన్ఇన్ను ఉపయోగించి మీ టాబ్లెట్ను అన్లాక్ చేయడానికి అడగబడతారు.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
<string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"మీరు మీ అన్లాక్ నమూనాని <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, మీరు మీ Google సైన్ఇన్ను ఉపయోగించి మీ Android TV పరికరాన్ని అన్లాక్ చేయాల్సిందిగా మీకు తెలపబడుతుంది.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
<string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"మీరు మీ అన్లాక్ నమూనాని <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విజయవంతం కాని ప్రయత్నాల తర్వాత, మీరు మీ Google సైన్ఇన్ను ఉపయోగించి మీ ఫోన్ను అన్లాక్ చేయడానికి అడగబడతారు.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="7914445759242151426">"మీరు టాబ్లెట్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా ప్రయత్నించారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> వైఫల్య ప్రయత్నాల తర్వాత, టాబ్లెట్ ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"మీరు మీ Android TV పరికరాన్ని అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు విఫల ప్రయత్నాలు చేశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, మీ Android TV పరికరం ఫ్యాక్టరీ డిఫాల్ట్కి రీసెట్ చేయబడుతుంది, అలాగే వినియోగదారు డేటా మొత్తాన్ని కోల్పోతారు."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="1166532464798446579">"మీరు ఫోన్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా ప్రయత్నించారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> వైఫల్య ప్రయత్నాల తర్వాత, ఫోన్ ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string>
- <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="8682445539263683414">"మీరు టాబ్లెట్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు తప్పుగా ప్రయత్నించారు. టాబ్లెట్ ఇప్పుడు ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="7914445759242151426">"మీరు టాబ్లెట్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా ప్రయత్నించారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, టాబ్లెట్ ఫ్యాక్టరీ ఆటోమేటిక్కు రీసెట్ చేయబడుతుంది, అలాగే మొత్తం యూజర్ డేటాను కోల్పోతారు."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"మీరు మీ Android TV పరికరాన్ని అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు విఫల ప్రయత్నాలు చేశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, మీ Android TV పరికరం ఫ్యాక్టరీ ఆటోమేటిక్కు రీసెట్ చేయబడుతుంది, అలాగే యూజర్ డేటా మొత్తాన్ని కోల్పోతారు."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="1166532464798446579">"మీరు ఫోన్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా ప్రయత్నించారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఫోన్, ఫ్యాక్టరీ ఆటోమేటిక్కు రీసెట్ చేయబడుతుంది, అలాగే మొత్తం యూజర్ డేటాను కోల్పోతారు."</string>
+ <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="8682445539263683414">"మీరు టాబ్లెట్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు తప్పుగా ప్రయత్నించారు. టాబ్లెట్ ఇప్పుడు ఫ్యాక్టరీ ఆటోమేటిక్కు రీసెట్ చేయబడుతుంది."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="2205435033340091883">"మీరు మీ Android TV పరికరాన్ని అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు విఫల ప్రయత్నాలు చేశారు. మీ Android TV పరికరం ఇప్పుడు ఫ్యాక్టరీ రీసెట్ చేయబడుతుంది."</string>
- <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="2203704707679895487">"మీరు ఫోన్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు తప్పుగా ప్రయత్నించారు. ఫోన్ ఇప్పుడు ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది."</string>
+ <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="2203704707679895487">"మీరు ఫోన్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు తప్పుగా ప్రయత్నించారు. ఫోన్ ఇప్పుడు ఫ్యాక్టరీ ఆటోమేటిక్కు రీసెట్ చేయబడుతుంది."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6807200118164539589">"<xliff:g id="NUMBER">%d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"నమూనాను మర్చిపోయారా?"</string>
<string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"ఖాతా అన్లాక్"</string>
@@ -1014,7 +1014,7 @@
<string name="permlab_addVoicemail" msgid="4770245808840814471">"వాయిస్ మెయిల్ను జోడించడం"</string>
<string name="permdesc_addVoicemail" msgid="5470312139820074324">"మీ వాయిస్ మెయిల్ ఇన్బాక్స్కి మెసేజ్లను జోడించడానికి యాప్ను అనుమతిస్తుంది."</string>
<string name="permlab_writeGeolocationPermissions" msgid="8605631647492879449">"బ్రౌజర్ భౌగోళిక స్థానం అనుమతులను సవరించడం"</string>
- <string name="permdesc_writeGeolocationPermissions" msgid="5817346421222227772">"బ్రౌజర్ యొక్క భౌగోళిక స్థానం అనుమతులను సవరించడానికి యాప్ను అనుమతిస్తుంది. హానికరమైన యాప్లు ఏకపక్ష వెబ్ సైట్లకు స్థాన సమాచారాన్ని అనుమతించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
+ <string name="permdesc_writeGeolocationPermissions" msgid="5817346421222227772">"బ్రౌజర్ యొక్క భౌగోళిక లొకేషన్ అనుమతులను సవరించడానికి యాప్ను అనుమతిస్తుంది. హానికరమైన యాప్లు ఏకపక్ష వెబ్ సైట్లకు లొకేషన్ సమాచారాన్ని అనుమతించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
<string name="save_password_message" msgid="2146409467245462965">"మీరు బ్రౌజర్ ఈ పాస్వర్డ్ను గుర్తుపెట్టుకోవాలని కోరుకుంటున్నారా?"</string>
<string name="save_password_notnow" msgid="2878327088951240061">"ఇప్పుడు కాదు"</string>
<string name="save_password_remember" msgid="6490888932657708341">"గుర్తుంచుకో"</string>
@@ -1039,7 +1039,7 @@
<string name="menu_enter_shortcut_label" msgid="6709499510082897320">"enter"</string>
<string name="menu_delete_shortcut_label" msgid="4365787714477739080">"delete"</string>
<string name="search_go" msgid="2141477624421347086">"సెర్చ్"</string>
- <string name="search_hint" msgid="455364685740251925">"వెతుకు..."</string>
+ <string name="search_hint" msgid="455364685740251925">"సెర్చ్ చేయండి..."</string>
<string name="searchview_description_search" msgid="1045552007537359343">"సెర్చ్"</string>
<string name="searchview_description_query" msgid="7430242366971716338">"ప్రశ్నను వెతకండి"</string>
<string name="searchview_description_clear" msgid="1989371719192982900">"ప్రశ్నను క్లియర్ చేయి"</string>
@@ -1198,7 +1198,7 @@
<string name="whichEditApplicationNamed" msgid="8096494987978521514">"%1$sతో సవరించు"</string>
<string name="whichEditApplicationLabel" msgid="1463288652070140285">"సవరించు"</string>
<string name="whichSendApplication" msgid="4143847974460792029">"షేర్ చేయండి"</string>
- <string name="whichSendApplicationNamed" msgid="4470386782693183461">"%1$sతో భాగస్వామ్యం చేయి"</string>
+ <string name="whichSendApplicationNamed" msgid="4470386782693183461">"%1$sతో షేర్ చేయండి"</string>
<string name="whichSendApplicationLabel" msgid="7467813004769188515">"షేర్ చేయి"</string>
<string name="whichSendToApplication" msgid="77101541959464018">"దీన్ని ఉపయోగించి పంపండి"</string>
<string name="whichSendToApplicationNamed" msgid="3385686512014670003">"%1$sని ఉపయోగించి పంపండి"</string>
@@ -1209,9 +1209,9 @@
<string name="whichImageCaptureApplication" msgid="2737413019463215284">"దీనితో చిత్రాన్ని క్యాప్చర్ చేయి"</string>
<string name="whichImageCaptureApplicationNamed" msgid="8820702441847612202">"%1$sతో చిత్రాన్ని క్యాప్చర్ చేయండి"</string>
<string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"చిత్రాన్ని క్యాప్చర్ చేయి"</string>
- <string name="alwaysUse" msgid="3153558199076112903">"ఈ చర్యకు డిఫాల్ట్గా ఉపయోగించండి."</string>
+ <string name="alwaysUse" msgid="3153558199076112903">"ఈ చర్యకు ఆటోమేటిక్గా ఉపయోగించండి."</string>
<string name="use_a_different_app" msgid="4987790276170972776">"వేరొక యాప్ను ఉపయోగించండి"</string>
- <string name="clearDefaultHintMsg" msgid="1325866337702524936">"సిస్టమ్ సెట్టింగ్లు > యాప్లు > డౌన్లోడ్ చేయబడినవిలో డిఫాల్ట్ను క్లియర్ చేయి."</string>
+ <string name="clearDefaultHintMsg" msgid="1325866337702524936">"సిస్టమ్ సెట్టింగ్లు > యాప్లు > డౌన్లోడ్ చేయబడినవిలో ఆటోమేటిక్ను క్లియర్ చేయి."</string>
<string name="chooseActivity" msgid="8563390197659779956">"చర్యను ఎంచుకోండి"</string>
<string name="chooseUsbActivity" msgid="2096269989990986612">"USB పరికరం కోసం యాప్ను ఎంచుకోండి"</string>
<string name="noApplications" msgid="1186909265235544019">"ఈ చర్యను అమలు చేయగల యాప్లు ఏవీ లేవు."</string>
@@ -1275,7 +1275,7 @@
<string name="dump_heap_notification" msgid="5316644945404825032">"<xliff:g id="PROC">%1$s</xliff:g> మెమరీ పరిమితిని మించిపోయింది"</string>
<string name="dump_heap_ready_notification" msgid="2302452262927390268">"<xliff:g id="PROC">%1$s</xliff:g> హీప్ డంప్ సిద్ధంగా ఉంది"</string>
<string name="dump_heap_notification_detail" msgid="8431586843001054050">"కుప్పలు తెప్పలుగా సేకరించబడింది. షేర్ చేయడానికి నొక్కండి"</string>
- <string name="dump_heap_title" msgid="4367128917229233901">"హీప్ డంప్ను భాగస్వామ్యం చేయాలా?"</string>
+ <string name="dump_heap_title" msgid="4367128917229233901">"హీప్ డంప్ను షేర్ చేయాలా?"</string>
<string name="dump_heap_text" msgid="1692649033835719336">"ఈ <xliff:g id="PROC">%1$s</xliff:g> ప్రాసెస్ దీని మెమరీ పరిమితి అయిన <xliff:g id="SIZE">%2$s</xliff:g>ని మించిపోయింది. మీరు దీని డెవలపర్తో షేర్ చేయడానికి హీప్ డంప్ అందుబాటులో ఉంది. జాగ్రత్త: ఈ హీప్ డంప్లో అప్లికేషన్ యాక్సెస్ కలిగి ఉన్న మీ వ్యక్తిగత సమాచారం ఏదైనా ఉండవచ్చు."</string>
<string name="dump_heap_system_text" msgid="6805155514925350849">"ఈ <xliff:g id="PROC">%1$s</xliff:g> ప్రాసెస్ దాని మెమరీ పరిమితి <xliff:g id="SIZE">%2$s</xliff:g>ని మించిపోయింది. మీరు షేర్ చేయడానికి హీప్ డంప్ అందుబాటులో ఉంది. జాగ్రత్త: ఈ హీప్ డంప్ ప్రాసెస్ విధానంలో గోప్యమైన వ్యక్తిగత సమాచారం యాక్సెస్ చేసే అవకాశం ఉంది, వీటిలో మీరు టైప్ చేసే అంశాలు కూడా ఉండవచ్చు."</string>
<string name="dump_heap_ready_text" msgid="5849618132123045516">"మీరు షేర్ చేయదలుచుకున్న <xliff:g id="PROC">%1$s</xliff:g> యొక్క హీప్ డంప్ ప్రాసెస్ విధానం అందుబాటులో ఉంది. జాగ్రత్త: ఈ హీప్ డంప్ ప్రాసెస్ విధానంలో గోప్యమైన వ్యక్తిగత సమాచారం యాక్సెస్ చేసే అవకాశం ఉంది, వీటిలో మీరు టైప్ చేసే అంశాలు కూడా ఉండవచ్చు."</string>
@@ -1294,7 +1294,7 @@
<string name="volume_icon_description_incall" msgid="4491255105381227919">"కాల్ వాల్యూమ్"</string>
<string name="volume_icon_description_media" msgid="4997633254078171233">"మీడియా వాల్యూమ్"</string>
<string name="volume_icon_description_notification" msgid="579091344110747279">"నోటిఫికేషన్ వాల్యూమ్"</string>
- <string name="ringtone_default" msgid="9118299121288174597">"డిఫాల్ట్ రింగ్టోన్"</string>
+ <string name="ringtone_default" msgid="9118299121288174597">"ఆటోమేటిక్ రింగ్టోన్"</string>
<string name="ringtone_default_with_actual" msgid="2709686194556159773">"ఆటోమేటిక్ (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
<string name="ringtone_silent" msgid="397111123930141876">"ఏదీ వద్దు"</string>
<string name="ringtone_picker_title" msgid="667342618626068253">"రింగ్టోన్లు"</string>
@@ -1386,7 +1386,7 @@
<string name="usb_contaminant_not_detected_title" msgid="2651167729563264053">"USB పోర్ట్ను ఉపయోగించడం సురక్షితం"</string>
<string name="usb_contaminant_not_detected_message" msgid="892863190942660462">"ఫోన్ ఇకపై ద్రవ లేదా వ్యర్థ పదార్థాలను గుర్తించదు."</string>
<string name="taking_remote_bugreport_notification_title" msgid="1582531382166919850">"బగ్ రిపోర్ట్ను తీస్తోంది…"</string>
- <string name="share_remote_bugreport_notification_title" msgid="6708897723753334999">"బగ్ రిపోర్ట్ను భాగస్వామ్యం చేయాలా?"</string>
+ <string name="share_remote_bugreport_notification_title" msgid="6708897723753334999">"బగ్ రిపోర్ట్ను షేర్ చేయాలా?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="3077385149217638550">"బగ్ రిపోర్ట్ను భాగస్వామ్యం చేస్తోంది..."</string>
<string name="share_remote_bugreport_notification_message_finished" msgid="7325635795739260135">"మీ నిర్వాహకులు ఈ పరికరం సమస్యకు పరిష్కారాన్ని కనుగొనడంలో సహాయం కోసం బగ్ రిపోర్ట్ను అభ్యర్థించారు. యాప్లు మరియు డేటా భాగస్వామ్యం చేయబడవచ్చు."</string>
<string name="share_remote_bugreport_action" msgid="7630880678785123682">"షేర్ చేయి"</string>
@@ -1526,8 +1526,8 @@
<string name="websearch" msgid="5624340204512793290">"వెబ్ శోధన"</string>
<string name="find_next" msgid="5341217051549648153">"తదుపరిదాన్ని కనుగొను"</string>
<string name="find_previous" msgid="4405898398141275532">"మునుపటిదాన్ని కనుగొను"</string>
- <string name="gpsNotifTicker" msgid="3207361857637620780">"<xliff:g id="NAME">%s</xliff:g> నుండి స్థాన రిక్వెస్ట్"</string>
- <string name="gpsNotifTitle" msgid="1590033371665669570">"స్థాన రిక్వెస్ట్"</string>
+ <string name="gpsNotifTicker" msgid="3207361857637620780">"<xliff:g id="NAME">%s</xliff:g> నుండి లొకేషన్ రిక్వెస్ట్"</string>
+ <string name="gpsNotifTitle" msgid="1590033371665669570">"లొకేషన్ రిక్వెస్ట్"</string>
<string name="gpsNotifMessage" msgid="7346649122793758032">"<xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>) ద్వారా అభ్యర్థించబడింది"</string>
<string name="gpsVerifYes" msgid="3719843080744112940">"అవును"</string>
<string name="gpsVerifNo" msgid="1671201856091564741">"కాదు"</string>
@@ -1676,12 +1676,12 @@
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"మీరు మీ పిన్ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేశారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"మీరు మీ పాస్వర్డ్ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేశారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="7357404233979139075">"మీరు మీ అన్లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"మీరు టాబ్లెట్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> చెల్లని ప్రయత్నాలు చేశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, టాబ్లెట్ ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="9064457748587850217">"మీరు మీ Android TV పరికరాన్ని అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు విఫల ప్రయత్నాలు చేశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, మీ Android TV పరికరం ఫ్యాక్టరీ డిఫాల్ట్కి రీసెట్ చేయబడుతుంది, అలాగే వినియోగదారు డేటా మొత్తాన్ని కోల్పోతారు."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="5955398963754432548">"మీరు ఫోన్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> చెల్లని ప్రయత్నాలు చేశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఫోన్ ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string>
- <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2299099385175083308">"మీరు టాబ్లెట్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> చెల్లని ప్రయత్నాలు చేశారు. టాబ్లెట్ ఇప్పుడు ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"మీరు టాబ్లెట్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> చెల్లని ప్రయత్నాలు చేశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, టాబ్లెట్ ఫ్యాక్టరీ ఆటోమేటిక్కు రీసెట్ చేయబడుతుంది, అలాగే మొత్తం యూజర్ డేటాను కోల్పోతారు."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="9064457748587850217">"మీరు మీ Android TV పరికరాన్ని అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు విఫల ప్రయత్నాలు చేశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, మీ Android TV పరికరం ఫ్యాక్టరీ ఆటోమేటిక్కు రీసెట్ చేయబడుతుంది, అలాగే యూజర్, డేటా మొత్తాన్ని కోల్పోతారు."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="5955398963754432548">"మీరు ఫోన్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> చెల్లని ప్రయత్నాలు చేశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఫోన్ ఫ్యాక్టరీ ఆటోమేటిక్కు రీసెట్ చేయబడుతుంది, అలాగే మొత్తం యూజర్ డేటాను కోల్పోతారు."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2299099385175083308">"మీరు టాబ్లెట్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> చెల్లని ప్రయత్నాలు చేశారు. టాబ్లెట్ ఇప్పుడు ఫ్యాక్టరీ ఆటోమేటిక్కు రీసెట్ చేయబడుతుంది."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"మీరు మీ Android TV పరికరాన్ని అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు విఫల ప్రయత్నాలు చేశారు. మీ Android TV పరికరం ఇప్పుడు ఫ్యాక్టరీ రీసెట్ చేయబడుతుంది."</string>
- <string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"మీరు ఫోన్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> చెల్లని ప్రయత్నాలు చేశారు. ఫోన్ ఇప్పుడు ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"మీరు ఫోన్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> చెల్లని ప్రయత్నాలు చేశారు. ఫోన్ ఇప్పుడు ఫ్యాక్టరీ ఆటోమేటిక్కు రీసెట్ చేయబడుతుంది."</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="7086799295109717623">"మీరు మీ అన్లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఇమెయిల్ ఖాతాను ఉపయోగించి మీ టాబ్లెట్ను అన్లాక్ చేయాల్సిందిగా మిమ్మల్ని అడుగుతారు.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
<string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"మీరు మీ అన్లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత మీ Android TV పరికరాన్ని ఇమెయిల్ ఖాతా ద్వారా అన్లాక్ చేయాల్సిందిగా మిమ్మల్ని కోరడం జరుగుతుంది.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"మీరు మీ అన్లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఇమెయిల్ ఖాతాను ఉపయోగించి మీ ఫోన్ను అన్లాక్ చేయాల్సిందిగా మిమ్మల్ని అడుగుతారు.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index cbf35f4..c9df3ff 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1224,6 +1224,22 @@
resource] to be present in order to function. Default value is false. -->
<attr name="isSplitRequired" format="boolean" />
+ <!-- List of split types required by this APK to be present in order to function properly,
+ separated by commas. The platform will reject installation of an app that is missing
+ any required split types. Each split type is an arbitrary string that has no specific
+ meaning to the platform, and is only used for matching <code>splitTypes</code> and
+ <code>requiredSplitTypes</code>. As an example, if a split requires strings, drawables,
+ and native code this value could be "language,density,abi". Default value is null to
+ indicate no split types are required. -->
+ <attr name="requiredSplitTypes" format="string" />
+
+ <!-- List of split types offered by this APK, separated by commas. Each split type is an
+ arbitrary string that has no specific meaning to the platform, and is only used for
+ matching <code>splitTypes</code> and <code>requiredSplitTypes</code>. As an example,
+ if a split offers strings and drawables the value could be "language,density". Default
+ value is null to indicate no split types are offered. -->
+ <attr name="splitTypes" format="string" />
+
<!-- Flag to specify if this app wants to run the dex within its APK but not extracted or
locally compiled variants. This keeps the dex code protected by the APK signature. Such
apps will always run in JIT mode (same when they are first installed), and the system will
@@ -1664,6 +1680,8 @@
<attr name="compileSdkVersion" />
<attr name="compileSdkVersionCodename" />
<attr name="isSplitRequired" />
+ <attr name="requiredSplitTypes" />
+ <attr name="splitTypes" />
</declare-styleable>
<!-- The <code>application</code> tag describes application-level components
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 17bb2d2..8813c43 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -660,6 +660,9 @@
<!-- Indicates whether to enable an animation when unfolding a device or not -->
<bool name="config_unfoldTransitionEnabled">false</bool>
+ <!-- Indicates whether to enable hinge angle sensor when using unfold animation -->
+ <bool name="config_unfoldTransitionHingeAngle">false</bool>
+
<!-- Indicates that the device supports having more than one internal display on at the same
time. Only applicable to devices with more than one internal display. If this option is
set to false, DisplayManager will make additional effort to ensure no more than 1 internal
@@ -4887,9 +4890,8 @@
- Option 3 is selected for R.integer.config_letterboxBackgroundType and blur requested
but isn't supported on the device or both dark scrim alpha and blur radius aren't
provided.
- Defaults to black if not specified.
-->
- <color name="config_letterboxBackgroundColor">#000</color>
+ <color name="config_letterboxBackgroundColor">@android:color/system_neutral2_500</color>
<!-- Horizonal position of a center of the letterboxed app window.
0 corresponds to the left side of the screen and 1 to the right side. If given value < 0
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index a20477c..e28288d 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3299,6 +3299,8 @@
<staging-public-group type="attr" first-id="0x01df0000">
<public name="sharedUserMaxSdkVersion" />
+ <public name="requiredSplitTypes" />
+ <public name="splitTypes" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01de0000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d2c6f78..4c1cc87 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1650,6 +1650,8 @@
<!-- Array containing custom error messages from vendor. Vendor is expected to add and translate these strings -->
<string-array name="fingerprint_error_vendor">
</string-array>
+ <!-- Default error message to use when fingerprint_error_vendor does not contain a message. [CHAR LIMIT=NONE] -->
+ <string name="fingerprint_error_vendor_unknown">Something went wrong. Try again.</string>
<!-- Content description which should be used for the fingerprint icon. -->
<string name="fingerprint_icon_content_description">Fingerprint icon</string>
@@ -1760,6 +1762,8 @@
<!-- Array containing custom error messages from vendor. Vendor is expected to add and translate these strings -->
<string-array name="face_error_vendor">
</string-array>
+ <!-- Default error message to use when face_error_vendor does not contain a message. [CHAR LIMIT=NONE] -->
+ <string name="face_error_vendor_unknown">Something went wrong. Try again.</string>
<!-- Content description which should be used for the face icon. [CHAR LIMIT=10] -->
<string name="face_icon_content_description">Face icon</string>
@@ -4557,7 +4561,7 @@
<string name="accessibility_shortcut_multiple_service_warning">Holding down both volume keys for a few seconds turns on accessibility features. This may change how your device works.\n\nCurrent features:\n<xliff:g id="service" example="TalkBack">%1$s</xliff:g>\nYou can change selected features in Settings > Accessibility.</string>
<!-- Used in multiple service warning to list current features. [CHAR LIMIT=none] -->
- <string name="accessibility_shortcut_multiple_service_list">\t• <xliff:g id="service" example="TalkBack">%1$s</xliff:g>\n</string>
+ <string name="accessibility_shortcut_multiple_service_list">\u0020• <xliff:g id="service" example="TalkBack">%1$s</xliff:g>\n</string>
<!-- Dialog title for dialog shown when this accessibility shortcut is activated, and we want to confirm that the user understands what's going to happen. [CHAR LIMIT=none] -->
<string name="accessibility_shortcut_single_service_warning_title">Turn on <xliff:g id="service" example="TalkBack">%1$s</xliff:g> shortcut?</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 527b928..6a1152b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2528,6 +2528,7 @@
<java-symbol type="string" name="fingerprint_error_no_space" />
<java-symbol type="string" name="fingerprint_error_timeout" />
<java-symbol type="array" name="fingerprint_error_vendor" />
+ <java-symbol type="string" name="fingerprint_error_vendor_unknown" />
<java-symbol type="string" name="fingerprint_acquired_partial" />
<java-symbol type="string" name="fingerprint_acquired_insufficient" />
<java-symbol type="string" name="fingerprint_acquired_imager_dirty" />
@@ -2567,6 +2568,7 @@
<java-symbol type="string" name="face_error_no_space" />
<java-symbol type="string" name="face_error_timeout" />
<java-symbol type="array" name="face_error_vendor" />
+ <java-symbol type="string" name="face_error_vendor_unknown" />
<java-symbol type="string" name="face_error_canceled" />
<java-symbol type="string" name="face_error_user_canceled" />
<java-symbol type="string" name="face_error_lockout" />
@@ -3844,6 +3846,7 @@
<java-symbol type="string" name="config_foldedArea" />
<java-symbol type="bool" name="config_supportsConcurrentInternalDisplays" />
<java-symbol type="bool" name="config_unfoldTransitionEnabled" />
+ <java-symbol type="bool" name="config_unfoldTransitionHingeAngle" />
<java-symbol type="array" name="config_perDeviceStateRotationLockDefaults" />
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 37d059a..69d2c74 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -274,6 +274,6 @@
<shortcode country="yt" pattern="\\d{1,5}" free="38600,36300,36303,959" />
<!-- South Africa -->
- <shortcode country="za" pattern="\d{1,5}" free="44136|30791|36056" />
+ <shortcode country="za" pattern="\\d{1,5}" free="44136|30791|36056" />
</shortcodes>
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
index 3d820ac..6884f13d 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
@@ -62,18 +62,4 @@
assertThat(outDoc.getPropertyDocument("propDocument").getPropertyBytesArray("propBytes"))
.isEqualTo(new byte[][] {{3, 4}});
}
-
- @Test
- public void testPutLargeDocument_exceedLimit() throws Exception {
- // Create a String property that has a very large property.
- char[] chars = new char[10_000_000];
- String property = new StringBuilder().append(chars).append("the end.").toString();
-
- GenericDocument doc =
- new GenericDocument.Builder<>("namespace", "id1", "schema1")
- .setPropertyString("propString", property)
- .build();
-
- assertThat(doc.getPropertyString("propString")).isEqualTo(property);
- }
}
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
index dfc9013..149f58f 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
+++ b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
@@ -32,6 +32,7 @@
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@@ -121,6 +122,46 @@
Mockito.verifyZeroInteractions(mListener);
}
+ @Test
+ public void testDisplayManagerGlobalRegistersWithDisplayManager_WhenThereAreNoOtherListeners()
+ throws RemoteException {
+ mDisplayManagerGlobal.registerNativeChoreographerForRefreshRateCallbacks();
+ Mockito.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), eq(ALL_DISPLAY_EVENTS));
+
+ mDisplayManagerGlobal.unregisterNativeChoreographerForRefreshRateCallbacks();
+ Mockito.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), eq(0L));
+
+ }
+
+ @Test
+ public void testDisplayManagerGlobalRegistersWithDisplayManager_WhenThereAreListeners()
+ throws RemoteException {
+ mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler,
+ DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS);
+ InOrder inOrder = Mockito.inOrder(mDisplayManager);
+
+ inOrder.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(),
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
+
+ mDisplayManagerGlobal.registerNativeChoreographerForRefreshRateCallbacks();
+ inOrder.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(),
+ eq(ALL_DISPLAY_EVENTS | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
+
+ mDisplayManagerGlobal.unregisterNativeChoreographerForRefreshRateCallbacks();
+ inOrder.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(),
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
+
+ mDisplayManagerGlobal.unregisterDisplayListener(mListener);
+ inOrder.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), eq(0L));
+
+ }
+
private void waitForHandler() {
mHandler.runWithScissors(() -> { }, 0);
}
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index fd7753b..5b3be58 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -22,6 +22,7 @@
import static org.testng.Assert.assertThrows;
+import android.app.ActivityThread;
import android.content.ContentResolver;
import android.os.Bundle;
import android.platform.test.annotations.Presubmit;
@@ -35,6 +36,12 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
/** Tests that ensure appropriate settings are backed up. */
@Presubmit
@RunWith(AndroidJUnit4.class)
@@ -701,66 +708,67 @@
assertThat(result.getString(KEY4, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE);
}
- // TODO(mpape): resolve b/142727848 and re-enable listener tests
-// @Test
-// public void onPropertiesChangedListener_setPropertyCallback() throws InterruptedException {
-// final CountDownLatch countDownLatch = new CountDownLatch(1);
-//
-// DeviceConfig.OnPropertiesChangedListener changeListener = (properties) -> {
-// assertThat(properties.getNamespace()).isEqualTo(NAMESPACE);
-// assertThat(properties.getKeyset()).contains(KEY);
-// assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE);
-// countDownLatch.countDown();
-// };
-//
-// try {
-// DeviceConfig.addOnPropertiesChangedListener(NAMESPACE,
-// ActivityThread.currentApplication().getMainExecutor(), changeListener);
-// DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
-// assertThat(countDownLatch.await(
-// WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
-// } catch (InterruptedException e) {
-// Assert.fail(e.getMessage());
-// } finally {
-// DeviceConfig.removeOnPropertiesChangedListener(changeListener);
-// }
-// }
-//
-// @Test
-// public void onPropertiesChangedListener_setPropertiesCallback() throws InterruptedException {
-// final CountDownLatch countDownLatch = new CountDownLatch(1);
-// DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
-// DeviceConfig.setProperty(NAMESPACE, KEY2, VALUE2, false);
-//
-// Map<String, String> keyValues = new HashMap<>(2);
-// keyValues.put(KEY, VALUE2);
-// keyValues.put(KEY3, VALUE3);
-// Properties setProperties = new Properties(NAMESPACE, keyValues);
-//
-// DeviceConfig.OnPropertiesChangedListener changeListener = (properties) -> {
-// assertThat(properties.getNamespace()).isEqualTo(NAMESPACE);
-// assertThat(properties.getKeyset()).containsExactly(KEY, KEY2, KEY3);
-// // KEY updated from VALUE to VALUE2
-// assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE2);
-// // KEY2 deleted (returns default_value)
-// assertThat(properties.getString(KEY2, "default_value")).isEqualTo("default_value");
-// //KEY3 added with VALUE3
-// assertThat(properties.getString(KEY3, "default_value")).isEqualTo(VALUE3);
-// countDownLatch.countDown();
-// };
-//
-// try {
-// DeviceConfig.addOnPropertiesChangedListener(NAMESPACE,
-// ActivityThread.currentApplication().getMainExecutor(), changeListener);
-// DeviceConfig.setProperties(setProperties);
-// assertThat(countDownLatch.await(
-// WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
-// } catch (InterruptedException e) {
-// Assert.fail(e.getMessage());
-// } finally {
-// DeviceConfig.removeOnPropertiesChangedListener(changeListener);
-// }
-// }
+ @Test
+ public void onPropertiesChangedListener_setPropertyCallback() throws InterruptedException {
+ final CountDownLatch countDownLatch = new CountDownLatch(1);
+
+ DeviceConfig.OnPropertiesChangedListener changeListener = (properties) -> {
+ assertThat(properties.getNamespace()).isEqualTo(NAMESPACE);
+ assertThat(properties.getKeyset()).contains(KEY);
+ assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE);
+ countDownLatch.countDown();
+ };
+
+ try {
+ DeviceConfig.addOnPropertiesChangedListener(NAMESPACE,
+ Objects.requireNonNull(ActivityThread.currentApplication()).getMainExecutor(),
+ changeListener);
+ DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
+ assertThat(countDownLatch.await(
+ WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
+ } catch (InterruptedException e) {
+ Assert.fail(e.getMessage());
+ } finally {
+ DeviceConfig.removeOnPropertiesChangedListener(changeListener);
+ }
+ }
+
+ @Test
+ public void onPropertiesChangedListener_setPropertiesCallback() {
+ final CountDownLatch countDownLatch = new CountDownLatch(1);
+ DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
+ DeviceConfig.setProperty(NAMESPACE, KEY2, VALUE2, false);
+
+ Map<String, String> keyValues = new HashMap<>(2);
+ keyValues.put(KEY, VALUE2);
+ keyValues.put(KEY3, VALUE3);
+ Properties setProperties = new Properties(NAMESPACE, keyValues);
+
+ DeviceConfig.OnPropertiesChangedListener changeListener = (properties) -> {
+ assertThat(properties.getNamespace()).isEqualTo(NAMESPACE);
+ assertThat(properties.getKeyset()).containsExactly(KEY, KEY2, KEY3);
+ // KEY updated from VALUE to VALUE2
+ assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE2);
+ // KEY2 deleted (returns default_value)
+ assertThat(properties.getString(KEY2, "default_value")).isEqualTo("default_value");
+ //KEY3 added with VALUE3
+ assertThat(properties.getString(KEY3, "default_value")).isEqualTo(VALUE3);
+ countDownLatch.countDown();
+ };
+
+ try {
+ DeviceConfig.addOnPropertiesChangedListener(NAMESPACE,
+ Objects.requireNonNull(ActivityThread.currentApplication()).getMainExecutor(),
+ changeListener);
+ DeviceConfig.setProperties(setProperties);
+ assertThat(countDownLatch.await(
+ WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
+ } catch (InterruptedException | DeviceConfig.BadConfigException e) {
+ Assert.fail(e.getMessage());
+ } finally {
+ DeviceConfig.removeOnPropertiesChangedListener(changeListener);
+ }
+ }
@Test
public void syncDisabling() throws Exception {
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index 8fd1af8..4f1da1b2 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -111,7 +111,7 @@
new Rect(0, 0, 500, 500), mInsetsState, mMockListener, systemBars(),
mMockController, 10 /* durationMs */, new LinearInterpolator(),
0 /* animationType */, 0 /* layoutInsetsDuringAnimation */, null /* translator */);
- mController.mReadyDispatched = true;
+ mController.setReadyDispatched(true);
}
@Test
@@ -197,7 +197,7 @@
@Test
public void testCancelled_beforeReadyDispatched() {
- mController.mReadyDispatched = false;
+ mController.setReadyDispatched(false);
mController.cancel();
assertFalse(mController.isReady());
assertFalse(mController.isFinished());
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 507638e..227a8657 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -19,13 +19,17 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.InsetsController.ANIMATION_TYPE_HIDE;
import static android.view.InsetsController.ANIMATION_TYPE_NONE;
+import static android.view.InsetsController.ANIMATION_TYPE_RESIZE;
import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
+import static android.view.InsetsController.AnimationType;
import static android.view.InsetsSourceConsumer.ShowResult.IME_SHOW_DELAYED;
import static android.view.InsetsSourceConsumer.ShowResult.SHOW_IMMEDIATELY;
+import static android.view.InsetsState.FIRST_TYPE;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.InsetsState.LAST_TYPE;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
@@ -662,6 +666,97 @@
}
@Test
+ public void testResizeAnimation_insetsTypes() {
+ for (@InternalInsetsType int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
+ final @AnimationType int expectedAnimationType =
+ (InsetsState.toPublicType(type) & Type.systemBars()) != 0
+ ? ANIMATION_TYPE_RESIZE
+ : ANIMATION_TYPE_NONE;
+ doTestResizeAnimation_insetsTypes(type, expectedAnimationType);
+ }
+ }
+
+ private void doTestResizeAnimation_insetsTypes(@InternalInsetsType int type,
+ @AnimationType int expectedAnimationType) {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ final InsetsState state1 = new InsetsState();
+ state1.getSource(type).setVisible(true);
+ state1.getSource(type).setFrame(0, 0, 500, 50);
+ final InsetsState state2 = new InsetsState(state1, true /* copySources */);
+ state2.getSource(type).setFrame(0, 0, 500, 60);
+ final String message = "Animation type of " + InsetsState.typeToString(type) + ":";
+
+ // New insets source won't cause the resize animation.
+ mController.onStateChanged(state1);
+ assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));
+
+ // Changing frame might cause the resize animation. This depends on the insets type.
+ mController.onStateChanged(state2);
+ assertEquals(message, expectedAnimationType, mController.getAnimationType(type));
+
+ // Cancel the existing animations for the next iteration.
+ mController.cancelExistingAnimations();
+ assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));
+ });
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ @Test
+ public void testResizeAnimation_displayFrame() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ final @InternalInsetsType int type = ITYPE_STATUS_BAR;
+ final InsetsState state1 = new InsetsState();
+ state1.setDisplayFrame(new Rect(0, 0, 500, 1000));
+ state1.getSource(type).setFrame(0, 0, 500, 50);
+ final InsetsState state2 = new InsetsState(state1, true /* copySources */);
+ state2.setDisplayFrame(new Rect(0, 0, 500, 1010));
+ state2.getSource(type).setFrame(0, 0, 500, 60);
+ final String message = "There must not be resize animation.";
+
+ // New insets source won't cause the resize animation.
+ mController.onStateChanged(state1);
+ assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));
+
+ // Changing frame won't cause the resize animation if the display frame is also changed.
+ mController.onStateChanged(state2);
+ assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));
+ });
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ @Test
+ public void testResizeAnimation_visibility() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ final @InternalInsetsType int type = ITYPE_STATUS_BAR;
+ final InsetsState state1 = new InsetsState();
+ state1.getSource(type).setVisible(true);
+ state1.getSource(type).setFrame(0, 0, 500, 50);
+ final InsetsState state2 = new InsetsState(state1, true /* copySources */);
+ state2.getSource(type).setVisible(false);
+ state2.getSource(type).setFrame(0, 0, 500, 60);
+ final InsetsState state3 = new InsetsState(state2, true /* copySources */);
+ state3.getSource(type).setVisible(true);
+ state3.getSource(type).setFrame(0, 0, 500, 70);
+ final String message = "There must not be resize animation.";
+
+ // New insets source won't cause the resize animation.
+ mController.onStateChanged(state1);
+ assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));
+
+ // Changing source visibility (visible --> invisible) won't cause the resize animation.
+ // The previous source and the current one must be both visible.
+ mController.onStateChanged(state2);
+ assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));
+
+ // Changing source visibility (invisible --> visible) won't cause the resize animation.
+ // The previous source and the current one must be both visible.
+ mController.onStateChanged(state3);
+ assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));
+ });
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ @Test
public void testCaptionInsetsStateAssemble() {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.onFrameChanged(new Rect(0, 0, 100, 300));
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 3bd2939..bf8bb76 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -216,9 +216,9 @@
mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(0, 0, 100, 300));
mState.getSource(ITYPE_CAPTION_BAR).setVisible(true);
- Rect visibleInsets = mState.calculateVisibleInsets(
+ Insets visibleInsets = mState.calculateVisibleInsets(
new Rect(0, 0, 100, 400), SOFT_INPUT_ADJUST_NOTHING);
- assertEquals(new Rect(0, 300, 0, 0), visibleInsets);
+ assertEquals(Insets.of(0, 300, 0, 0), visibleInsets);
}
@Test
@@ -226,9 +226,9 @@
mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(0, 0, 100, 300));
mState.getSource(ITYPE_CAPTION_BAR).setVisible(true);
- Rect visibleInsets = mState.calculateVisibleInsets(
+ Insets visibleInsets = mState.calculateVisibleInsets(
new Rect(0, 0, 150, 400), SOFT_INPUT_ADJUST_NOTHING);
- assertEquals(new Rect(0, 300, 0, 0), visibleInsets);
+ assertEquals(Insets.of(0, 300, 0, 0), visibleInsets);
}
@Test
@@ -413,9 +413,9 @@
// Make sure bottom gestures are ignored
mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300));
mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true);
- Rect visibleInsets = mState.calculateVisibleInsets(
+ Insets visibleInsets = mState.calculateVisibleInsets(
new Rect(0, 0, 100, 300), SOFT_INPUT_ADJUST_PAN);
- assertEquals(new Rect(0, 100, 0, 100), visibleInsets);
+ assertEquals(Insets.of(0, 100, 0, 100), visibleInsets);
}
@Test
@@ -428,9 +428,9 @@
// Make sure bottom gestures are ignored
mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300));
mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true);
- Rect visibleInsets = mState.calculateVisibleInsets(
+ Insets visibleInsets = mState.calculateVisibleInsets(
new Rect(0, 0, 100, 300), SOFT_INPUT_ADJUST_NOTHING);
- assertEquals(new Rect(0, 100, 0, 0), visibleInsets);
+ assertEquals(Insets.of(0, 100, 0, 0), visibleInsets);
}
@Test
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
index d2b52ba..0e78f87 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
@@ -16,6 +16,12 @@
package android.view.accessibility;
+import static android.view.accessibility.AccessibilityNodeInfo.FOCUS_ACCESSIBILITY;
+import static android.view.accessibility.AccessibilityNodeInfo.FOCUS_INPUT;
+import static android.view.accessibility.AccessibilityNodeInfo.ROOT_ITEM_ID;
+import static android.view.accessibility.AccessibilityNodeInfo.ROOT_NODE_ID;
+import static android.view.accessibility.AccessibilityWindowInfo.ANY_WINDOW_ID;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
@@ -597,6 +603,97 @@
}
@Test
+ public void getFocus_focusedNodeAsInitialNode_returnsNodeWithA11yFocus() {
+ AccessibilityNodeInfo nodeInfo = addFocusedNode(FOCUS_ACCESSIBILITY);
+ assertFocus(nodeInfo, FOCUS_ACCESSIBILITY, nodeInfo.getSourceNodeId(),
+ nodeInfo.getWindowId());
+ }
+
+ @Test
+ public void getFocus_focusedNodeAsInitialNode_returnsNodeWithInputFocus() {
+ AccessibilityNodeInfo nodeInfo = addFocusedNode(FOCUS_INPUT);
+ assertFocus(nodeInfo, FOCUS_INPUT, nodeInfo.getSourceNodeId(), nodeInfo.getWindowId());
+ }
+
+
+ @Test
+ public void getFocus_fromAnyWindow_returnsNodeWithA11yFocus() {
+ AccessibilityNodeInfo parentNodeInfo =
+ getNodeWithA11yAndWindowId(ROOT_ITEM_ID, WINDOW_ID_1);
+ AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
+ nodeInfo.setParent(getMockViewWithA11yAndWindowIds(ROOT_ITEM_ID, WINDOW_ID_1));
+ setFocus(nodeInfo, FOCUS_ACCESSIBILITY);
+ mAccessibilityCache.add(parentNodeInfo);
+ mAccessibilityCache.add(nodeInfo);
+
+ AccessibilityNodeInfo focusedNodeInfo =
+ mAccessibilityCache.getFocus(FOCUS_ACCESSIBILITY, ROOT_NODE_ID, ANY_WINDOW_ID);
+ try {
+ assertEquals(focusedNodeInfo, nodeInfo);
+ } finally {
+ nodeInfo.recycle();
+ parentNodeInfo.recycle();
+ }
+ }
+
+ @Test
+ public void getFocus_fromAnyWindow_returnsNodeWithInputFocus() {
+ AccessibilityNodeInfo parentNodeInfo =
+ getNodeWithA11yAndWindowId(ROOT_ITEM_ID, WINDOW_ID_1);
+ AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
+ nodeInfo.setParent(getMockViewWithA11yAndWindowIds(ROOT_ITEM_ID, WINDOW_ID_1));
+ setFocus(nodeInfo, FOCUS_INPUT);
+ mAccessibilityCache.add(parentNodeInfo);
+ mAccessibilityCache.add(nodeInfo);
+
+ AccessibilityNodeInfo focusedNodeInfo =
+ mAccessibilityCache.getFocus(FOCUS_INPUT, ROOT_NODE_ID, ANY_WINDOW_ID);
+ try {
+ assertEquals(focusedNodeInfo, nodeInfo);
+ } finally {
+ nodeInfo.recycle();
+ parentNodeInfo.recycle();
+ }
+ }
+
+
+ @Test
+ public void getFocus_parentNodeAsInitialNode_returnsNodeWithA11yFocus() {
+ findFocusReturnsFocus_initialNodeIsParent(FOCUS_ACCESSIBILITY);
+ }
+
+ @Test
+ public void getFocus_parentNodeAsInitialNode_returnsNodeWithInputFocus() {
+ findFocusReturnsFocus_initialNodeIsParent(FOCUS_INPUT);
+
+ }
+
+ @Test
+ public void getFocus_nonFocusedChildNodeAsInitialNode_returnsNullA11yFocus() {
+ findFocusReturnNull_initialNodeIsNotFocusOrFocusAncestor(FOCUS_ACCESSIBILITY);
+ }
+
+ @Test
+ public void getFocus_nonFocusedChildNodeAsInitialNode_returnsNullInputFocus() {
+ findFocusReturnNull_initialNodeIsNotFocusOrFocusAncestor(FOCUS_INPUT);
+ }
+
+ @Test
+ public void getFocus_cacheCleared_returnsNullA11yFocus() {
+ AccessibilityNodeInfo nodeInfo = addFocusedNode(FOCUS_ACCESSIBILITY);
+ mAccessibilityCache.clear();
+ assertFocus(null, FOCUS_ACCESSIBILITY, nodeInfo.getSourceNodeId(),
+ nodeInfo.getWindowId());
+ }
+
+ @Test
+ public void getFocus_cacheCleared_returnsNullInputFocus() {
+ AccessibilityNodeInfo nodeInfo = addFocusedNode(FOCUS_INPUT);
+ mAccessibilityCache.clear();
+ assertFocus(null, FOCUS_INPUT, nodeInfo.getSourceNodeId(), nodeInfo.getWindowId());
+ }
+
+ @Test
public void nodeSourceOfInputFocusEvent_getsRefreshed() {
AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
nodeInfo.setFocused(false);
@@ -786,6 +883,86 @@
}
}
+ private AccessibilityNodeInfo addFocusedNode(int focusType) {
+ AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
+ setFocus(nodeInfo, focusType);
+ mAccessibilityCache.add(nodeInfo);
+ return nodeInfo;
+ }
+
+ private void assertFocus(AccessibilityNodeInfo focus, int focusType, long nodeId,
+ int windowId) {
+ AccessibilityNodeInfo focusedNodeInfo = mAccessibilityCache.getFocus(
+ focusType, nodeId, windowId);
+ try {
+ assertEquals(focusedNodeInfo, focus);
+ } finally {
+ if (focus != null) {
+ focus.recycle();
+ }
+ if (focusedNodeInfo != null) {
+ focusedNodeInfo.recycle();
+ }
+ }
+ }
+
+
+ private void findFocusReturnNull_initialNodeIsNotFocusOrFocusAncestor(int focusType) {
+ AccessibilityNodeInfo parentNodeInfo =
+ getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1);
+ AccessibilityNodeInfo childNodeInfo =
+ getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1);
+ AccessibilityNodeInfo otherChildNodeInfo =
+ getNodeWithA11yAndWindowId(OTHER_CHILD_VIEW_ID, WINDOW_ID_1);
+ childNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
+ otherChildNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
+ setFocus(childNodeInfo, focusType);
+ mAccessibilityCache.add(parentNodeInfo);
+ mAccessibilityCache.add(childNodeInfo);
+ mAccessibilityCache.add(otherChildNodeInfo);
+
+ AccessibilityNodeInfo focusedNodeInfo = mAccessibilityCache.getFocus(
+ focusType, otherChildNodeInfo.getSourceNodeId(), WINDOW_ID_1);
+
+ try {
+ assertNull(focusedNodeInfo);
+
+ } finally {
+ parentNodeInfo.recycle();
+ childNodeInfo.recycle();
+ }
+ }
+
+ private void findFocusReturnsFocus_initialNodeIsParent(int focusType) {
+ AccessibilityNodeInfo parentNodeInfo =
+ getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1);
+ AccessibilityNodeInfo childNodeInfo =
+ getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1);
+ childNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
+ setFocus(childNodeInfo, focusType);
+ mAccessibilityCache.add(parentNodeInfo);
+ mAccessibilityCache.add(childNodeInfo);
+
+ AccessibilityNodeInfo focusedNodeInfo = mAccessibilityCache.getFocus(
+ focusType, parentNodeInfo.getSourceNodeId(), WINDOW_ID_1);
+
+ try {
+ assertEquals(focusedNodeInfo, childNodeInfo);
+
+ } finally {
+ parentNodeInfo.recycle();
+ childNodeInfo.recycle();
+ }
+ }
+
+ private void setFocus(AccessibilityNodeInfo info, int focusType) {
+ if (focusType == FOCUS_ACCESSIBILITY) {
+ info.setAccessibilityFocused(true);
+ } else {
+ info.setFocused(true);
+ }
+ }
+
private AccessibilityWindowInfo obtainAccessibilityWindowInfo(int windowId, int layer) {
AccessibilityWindowInfo windowInfo = AccessibilityWindowInfo.obtain();
windowInfo.setId(windowId);
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index b5ab282..cfcbc7d 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -2243,12 +2243,6 @@
(chooserListAdapter.getUserHandle().getIdentifier() == 10);
return null;
};
- boolean[] isQueryTargetServicesCalledOnWorkProfile = new boolean[] { false };
- sOverrides.onQueryTargetServices = chooserListAdapter -> {
- isQueryTargetServicesCalledOnWorkProfile[0] =
- (chooserListAdapter.getUserHandle().getIdentifier() == 10);
- return null;
- };
Intent sendIntent = createSendTextIntent();
sendIntent.setType("TestType");
@@ -2261,8 +2255,6 @@
assertFalse("Direct share targets were queried on a paused work profile",
isQueryDirectShareCalledOnWorkProfile[0]);
- assertFalse("Target services were queried on a paused work profile",
- isQueryTargetServicesCalledOnWorkProfile[0]);
}
@Test
@@ -2282,12 +2274,6 @@
(chooserListAdapter.getUserHandle().getIdentifier() == 10);
return null;
};
- boolean[] isQueryTargetServicesCalledOnWorkProfile = new boolean[] { false };
- sOverrides.onQueryTargetServices = chooserListAdapter -> {
- isQueryTargetServicesCalledOnWorkProfile[0] =
- (chooserListAdapter.getUserHandle().getIdentifier() == 10);
- return null;
- };
Intent sendIntent = createSendTextIntent();
sendIntent.setType("TestType");
@@ -2300,8 +2286,6 @@
assertFalse("Direct share targets were queried on a locked work profile user",
isQueryDirectShareCalledOnWorkProfile[0]);
- assertFalse("Target services were queried on a locked work profile user",
- isQueryTargetServicesCalledOnWorkProfile[0]);
}
@Test
@@ -2346,12 +2330,6 @@
(chooserListAdapter.getUserHandle().getIdentifier() == 10);
return null;
};
- boolean[] isQueryTargetServicesCalledOnWorkProfile = new boolean[] { false };
- sOverrides.onQueryTargetServices = chooserListAdapter -> {
- isQueryTargetServicesCalledOnWorkProfile[0] =
- (chooserListAdapter.getUserHandle().getIdentifier() == 10);
- return null;
- };
Intent sendIntent = createSendTextIntent();
sendIntent.setType("TestType");
@@ -2364,8 +2342,6 @@
assertFalse("Direct share targets were queried on a locked work profile user",
isQueryDirectShareCalledOnWorkProfile[0]);
- assertFalse("Target services were queried on a locked work profile user",
- isQueryTargetServicesCalledOnWorkProfile[0]);
}
@Test
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index 9fcab09..6b3d657 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -229,14 +229,6 @@
}
@Override
- protected void queryTargetServices(ChooserListAdapter adapter) {
- if (sOverrides.onQueryTargetServices != null) {
- sOverrides.onQueryTargetServices.apply(adapter);
- }
- super.queryTargetServices(adapter);
- }
-
- @Override
protected boolean isQuietModeEnabled(UserHandle userHandle) {
return sOverrides.isQuietModeEnabled;
}
@@ -267,7 +259,6 @@
public Function<PackageManager, PackageManager> createPackageManager;
public Function<TargetInfo, Boolean> onSafelyStartCallback;
public Function<ChooserListAdapter, Void> onQueryDirectShareTargets;
- public Function<ChooserListAdapter, Void> onQueryTargetServices;
public ResolverListController resolverListController;
public ResolverListController workResolverListController;
public Boolean isVoiceInteraction;
@@ -290,7 +281,6 @@
public void reset() {
onSafelyStartCallback = null;
onQueryDirectShareTargets = null;
- onQueryTargetServices = null;
isVoiceInteraction = null;
createPackageManager = null;
previewThumbnail = null;
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/CompletableFutureUtilTest.kt b/core/tests/coretests/src/com/android/internal/inputmethod/CompletableFutureUtilTest.kt
new file mode 100644
index 0000000..8355daa
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/CompletableFutureUtilTest.kt
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod
+
+import android.annotation.DurationMillisLong
+import android.os.Handler
+import android.os.SystemClock
+import androidx.test.filters.LargeTest
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.runner.AndroidJUnit4
+import com.google.common.collect.Range
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.fail
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.atomic.AtomicBoolean
+import java.util.concurrent.atomic.AtomicLong
+import java.util.concurrent.atomic.AtomicReference
+
+@DurationMillisLong
+const val SHORT_PERIOD_MILLI = 50L
+const val SHORT_PERIOD_NANO = SHORT_PERIOD_MILLI * 1_000_000L
+
+@DurationMillisLong
+const val TIMEOUT_MILLI = 10_000L
+const val TIMEOUT_NANO = TIMEOUT_MILLI * 1_000_000L
+
+const val ERROR_MESSAGE = "Test Error Message!"
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class CompletableFutureUtilTest {
+
+ private inline fun assertRuntimeException(expectedMessage: String, block: () -> Unit) {
+ try {
+ block()
+ fail()
+ } catch (exception: RuntimeException) {
+ assertThat(exception.message).isEqualTo(expectedMessage)
+ // Expected
+ } catch (exception: Throwable) {
+ fail("RuntimeException is expected but got $exception")
+ }
+ }
+
+ private inline fun runOnMainDelayed(delay: Long, crossinline block: () -> Unit) {
+ val handler = Handler.createAsync(
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getMainLooper())
+ handler.postDelayed({
+ block()
+ }, delay)
+ }
+
+ @Test
+ fun testCharSequenceTimedOut() {
+ val completable = CompletableFuture<CharSequence>()
+
+ assertThat(completable.isDone).isFalse()
+
+ val beginNanos = SystemClock.elapsedRealtimeNanos()
+ val result = CompletableFutureUtil.getResultOrNull(
+ completable, null, null, null, SHORT_PERIOD_MILLI)
+ val elapsed = SystemClock.elapsedRealtimeNanos() - beginNanos
+
+ assertThat(completable.isDone).isFalse()
+ assertThat(result).isNull()
+ assertThat(elapsed).isGreaterThan(SHORT_PERIOD_NANO)
+ }
+
+ @Test
+ fun testCharSequenceTimedOutWithInterruption() {
+ val completable = CompletableFuture<CharSequence>()
+
+ val beginNanosRef = AtomicLong()
+ val endNanosRef = AtomicLong()
+ val isInterruptedRef = AtomicBoolean()
+ val resultRef = AtomicReference<CharSequence>()
+
+ // Verifies that calling getResultOrNull() on an interrupted thread still times out with
+ // preserving the interrupted state.
+ val thread = Thread {
+ val currentThread = Thread.currentThread()
+ currentThread.interrupt()
+ beginNanosRef.set(SystemClock.elapsedRealtimeNanos())
+ resultRef.set(CompletableFutureUtil.getResultOrNull(
+ completable, null, null, null, SHORT_PERIOD_MILLI))
+ endNanosRef.set(SystemClock.elapsedRealtimeNanos())
+ isInterruptedRef.set(currentThread.isInterrupted())
+ }
+
+ thread.run()
+ thread.join(TIMEOUT_MILLI)
+ assertThat(thread.isAlive).isFalse()
+
+ val elapsedTime = endNanosRef.get() - beginNanosRef.get()
+ assertThat(elapsedTime).isGreaterThan(SHORT_PERIOD_NANO)
+ assertThat(resultRef.get()).isNull()
+ assertThat(isInterruptedRef.get()).isTrue()
+ }
+
+ @Test
+ fun testCharSequenceAfterCompletion() {
+ val expectedValue = "Expected Value"
+ val completable = CompletableFuture<CharSequence>()
+
+ assertThat(completable.isDone).isFalse()
+ completable.complete(expectedValue)
+ assertThat(completable.isDone).isTrue()
+
+ val beginNanos = SystemClock.elapsedRealtimeNanos()
+ val result = CompletableFutureUtil.getResultOrNull(
+ completable, null, null, null,
+ TIMEOUT_MILLI)
+ val elapsed = SystemClock.elapsedRealtimeNanos() - beginNanos
+
+ assertThat(result).isEqualTo(expectedValue)
+ assertThat(elapsed).isLessThan(SHORT_PERIOD_NANO)
+ }
+
+ @Test
+ fun testCharSequenceAfterError() {
+ val completable = CompletableFuture<CharSequence>()
+
+ assertThat(completable.isDone).isFalse()
+ completable.completeExceptionally(UnsupportedOperationException(ERROR_MESSAGE))
+ assertThat(completable.isDone).isTrue()
+
+ val beginNanos = SystemClock.elapsedRealtimeNanos()
+ val result = CompletableFutureUtil.getResultOrNull(
+ completable, null, null, null, TIMEOUT_MILLI)
+ val elapsed = SystemClock.elapsedRealtimeNanos() - beginNanos
+
+ assertThat(result).isNull()
+ assertThat(elapsed).isLessThan(SHORT_PERIOD_NANO)
+
+ assertRuntimeException(ERROR_MESSAGE) {
+ CompletableFutureUtil.getResult(completable)
+ }
+ }
+
+ @Test
+ fun testCharSequenceAfterCancellation() {
+ val completable = CompletableFuture<CharSequence>()
+ val cancellationGroup = CancellationGroup()
+ cancellationGroup.cancelAll()
+
+ val beginNanos = SystemClock.elapsedRealtimeNanos()
+ val result = CompletableFutureUtil.getResultOrNull(
+ completable, null, null, cancellationGroup, TIMEOUT_MILLI)
+ val elapsed = SystemClock.elapsedRealtimeNanos() - beginNanos
+
+ // due to the side-effect of cancellationGroup, the object is already completed here.
+ assertThat(completable.isDone).isTrue()
+ assertThat(result).isNull()
+ assertThat(elapsed).isLessThan(SHORT_PERIOD_NANO)
+
+ // as the object is already cancelled due to the side-effect of cancellationGroup, it cannot
+ // accept a result any more.
+ completable.complete("Hello!")
+ assertThat(completable.isCancelled).isTrue()
+ }
+
+ @Test
+ fun testCharSequenceAfterCompleteAndCancellation() {
+ val expectedValue = "Expected Value"
+ val completable = CompletableFuture<CharSequence>()
+ completable.complete(expectedValue)
+
+ val cancellationGroup = CancellationGroup()
+ cancellationGroup.cancelAll()
+
+ val beginNanos = SystemClock.elapsedRealtimeNanos()
+ val result = CompletableFutureUtil.getResultOrNull(
+ completable, null, null, cancellationGroup, TIMEOUT_MILLI)
+ val elapsed = SystemClock.elapsedRealtimeNanos() - beginNanos
+
+ assertThat(result).isEqualTo(expectedValue)
+ assertThat(CompletableFutureUtil.getResult(completable)).isEqualTo(expectedValue)
+ assertThat(elapsed).isLessThan(SHORT_PERIOD_NANO)
+ }
+
+ @Test
+ fun testCharSequenceMultipleAssignment() {
+ val expectedValue = "Expected Value"
+ val notExpectedValue = "Not Expected Value"
+ val completable = CompletableFuture<CharSequence>()
+ completable.complete(expectedValue)
+ completable.complete(notExpectedValue)
+ assertThat(completable.isDone).isTrue()
+
+ assertThat(CompletableFutureUtil.getResult(completable)).isEqualTo(expectedValue)
+ }
+
+ @Test
+ fun testCharSequenceUnblockByCompletion() {
+ val expectedValue = "Expected Value"
+ val completable = CompletableFuture<CharSequence>()
+
+ val beginNanos = SystemClock.elapsedRealtimeNanos()
+ runOnMainDelayed(SHORT_PERIOD_MILLI) {
+ completable.complete(expectedValue)
+ }
+ val result = CompletableFutureUtil.getResultOrNull(
+ completable, null, null, null, TIMEOUT_MILLI)
+ val elapsed = SystemClock.elapsedRealtimeNanos() - beginNanos
+
+ assertThat(completable.isDone).isTrue()
+ assertThat(result).isEqualTo(expectedValue)
+ assertThat(elapsed).isIn(Range.closedOpen(SHORT_PERIOD_NANO, TIMEOUT_NANO))
+ }
+
+ @Test
+ fun testCharSequenceUnblockByCompletionWithCancellationGroup() {
+ val expectedValue = "Expected Value"
+ val completable = CompletableFuture<CharSequence>()
+ var cancellationGroup = CancellationGroup()
+
+ assertThat(cancellationGroup.isCanceled).isFalse()
+
+ val beginNanos = SystemClock.elapsedRealtimeNanos()
+ runOnMainDelayed(SHORT_PERIOD_MILLI) {
+ completable.complete(expectedValue)
+ }
+ val result = CompletableFutureUtil.getResultOrNull(
+ completable, null, null, cancellationGroup, TIMEOUT_MILLI)
+ val elapsed = SystemClock.elapsedRealtimeNanos() - beginNanos
+
+ assertThat(cancellationGroup.isCanceled).isFalse()
+ assertThat(completable.isDone).isTrue()
+ assertThat(result).isEqualTo(expectedValue)
+ assertThat(elapsed).isIn(Range.closedOpen(SHORT_PERIOD_NANO, TIMEOUT_NANO))
+ }
+
+ @Test
+ fun testCharSequenceUnblockByError() {
+ val completable = CompletableFuture<CharSequence>()
+
+ val beginNanos = SystemClock.elapsedRealtimeNanos()
+ runOnMainDelayed(SHORT_PERIOD_MILLI) {
+ completable.completeExceptionally(UnsupportedOperationException(ERROR_MESSAGE))
+ }
+ val result = CompletableFutureUtil.getResultOrNull(
+ completable, null, null, null, TIMEOUT_MILLI)
+ val elapsed = SystemClock.elapsedRealtimeNanos() - beginNanos
+
+ assertThat(completable.isDone).isTrue()
+ assertThat(result).isNull()
+ assertThat(elapsed).isIn(Range.closedOpen(SHORT_PERIOD_NANO, TIMEOUT_NANO))
+ }
+
+ @Test
+ fun testCharSequenceUnblockByCancellation() {
+ val completable = CompletableFuture<CharSequence>()
+ val cancellationGroup = CancellationGroup()
+
+ val beginNanos = SystemClock.elapsedRealtimeNanos()
+ runOnMainDelayed(SHORT_PERIOD_MILLI) {
+ cancellationGroup.cancelAll()
+ }
+ val result = CompletableFutureUtil.getResultOrNull(
+ completable, null, null, cancellationGroup, TIMEOUT_MILLI)
+ val elapsed = SystemClock.elapsedRealtimeNanos() - beginNanos
+
+ // due to the side-effect of cancellationGroup.
+ assertThat(completable.isDone).isTrue()
+ assertThat(result).isNull()
+ assertThat(elapsed).isIn(Range.closedOpen(SHORT_PERIOD_NANO, TIMEOUT_NANO))
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index a43d32d..e8e4330 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -23,6 +23,8 @@
import android.os.BatteryStats;
import android.os.BatteryStats.HistoryItem;
import android.os.BatteryStats.Uid.Sensor;
+import android.os.Process;
+import android.os.UserHandle;
import android.os.WorkSource;
import android.util.SparseLongArray;
import android.view.Display;
@@ -53,6 +55,8 @@
public class BatteryStatsNoteTest extends TestCase {
private static final int UID = 10500;
+ private static final int ISOLATED_APP_ID = Process.FIRST_ISOLATED_UID + 23;
+ private static final int ISOLATED_UID = UserHandle.getUid(0, ISOLATED_APP_ID);
private static final WorkSource WS = new WorkSource(UID);
/**
@@ -114,6 +118,88 @@
assertEquals(120_000, bgTime);
}
+ /**
+ * Test BatteryStatsImpl.Uid.noteStartWakeLocked for an isolated uid.
+ */
+ @SmallTest
+ public void testNoteStartWakeLocked_isolatedUid() throws Exception {
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+
+ int pid = 10;
+ String name = "name";
+ String historyName = "historyName";
+
+ WorkSource.WorkChain isolatedWorkChain = new WorkSource.WorkChain();
+ isolatedWorkChain.addNode(ISOLATED_UID, name);
+
+ // Map ISOLATED_UID to UID.
+ bi.addIsolatedUidLocked(ISOLATED_UID, UID);
+
+ bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+ bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP);
+ bi.noteStartWakeLocked(ISOLATED_UID, pid, isolatedWorkChain, name, historyName,
+ WAKE_TYPE_PARTIAL, false);
+
+ clocks.realtime = clocks.uptime = 100;
+ bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+
+ clocks.realtime = clocks.uptime = 220;
+ bi.noteStopWakeLocked(ISOLATED_UID, pid, isolatedWorkChain, name, historyName,
+ WAKE_TYPE_PARTIAL);
+
+ // ISOLATED_UID wakelock time should be attributed to UID.
+ BatteryStats.Timer aggregTimer = bi.getUidStats().get(UID)
+ .getAggregatedPartialWakelockTimer();
+ long actualTime = aggregTimer.getTotalTimeLocked(300_000, STATS_SINCE_CHARGED);
+ long bgTime = aggregTimer.getSubTimer().getTotalTimeLocked(300_000, STATS_SINCE_CHARGED);
+ assertEquals(220_000, actualTime);
+ assertEquals(120_000, bgTime);
+ }
+
+ /**
+ * Test BatteryStatsImpl.Uid.noteStartWakeLocked for an isolated uid, with a race where the
+ * isolated uid is removed from batterystats before the wakelock has been stopped.
+ */
+ @SmallTest
+ public void testNoteStartWakeLocked_isolatedUidRace() throws Exception {
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+
+ int pid = 10;
+ String name = "name";
+ String historyName = "historyName";
+
+ WorkSource.WorkChain isolatedWorkChain = new WorkSource.WorkChain();
+ isolatedWorkChain.addNode(ISOLATED_UID, name);
+
+ // Map ISOLATED_UID to UID.
+ bi.addIsolatedUidLocked(ISOLATED_UID, UID);
+
+ bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+ bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP);
+ bi.noteStartWakeLocked(ISOLATED_UID, pid, isolatedWorkChain, name, historyName,
+ WAKE_TYPE_PARTIAL, false);
+
+ clocks.realtime = clocks.uptime = 100;
+ bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+
+ clocks.realtime = clocks.uptime = 150;
+ bi.maybeRemoveIsolatedUidLocked(ISOLATED_UID, clocks.realtime, clocks.uptime);
+
+ clocks.realtime = clocks.uptime = 220;
+ bi.noteStopWakeLocked(ISOLATED_UID, pid, isolatedWorkChain, name, historyName,
+ WAKE_TYPE_PARTIAL);
+
+ // ISOLATED_UID wakelock time should be attributed to UID.
+ BatteryStats.Timer aggregTimer = bi.getUidStats().get(UID)
+ .getAggregatedPartialWakelockTimer();
+ long actualTime = aggregTimer.getTotalTimeLocked(300_000, STATS_SINCE_CHARGED);
+ long bgTime = aggregTimer.getSubTimer().getTotalTimeLocked(300_000, STATS_SINCE_CHARGED);
+ assertEquals(220_000, actualTime);
+ assertEquals(120_000, bgTime);
+ }
+
/**
* Test BatteryStatsImpl.noteUidProcessStateLocked.
diff --git a/data/etc/car/com.android.car.carlauncher.xml b/data/etc/car/com.android.car.carlauncher.xml
index abde232..33f885a 100644
--- a/data/etc/car/com.android.car.carlauncher.xml
+++ b/data/etc/car/com.android.car.carlauncher.xml
@@ -17,6 +17,7 @@
<permissions>
<privapp-permissions package="com.android.car.carlauncher">
<permission name="android.permission.ACTIVITY_EMBEDDING"/>
+ <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
<permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MANAGE_USERS"/>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index b49b289..6a93761 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -571,6 +571,12 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityStarter.java"
},
+ "-1483435730": {
+ "message": "InsetsSource setWin %s for type %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_WINDOW_INSETS",
+ "at": "com\/android\/server\/wm\/InsetsSourceProvider.java"
+ },
"-1480772131": {
"message": "No app or window is requesting an orientation, return %d for display id=%d",
"level": "VERBOSE",
@@ -673,6 +679,12 @@
"group": "WM_DEBUG_IME",
"at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java"
},
+ "-1394745488": {
+ "message": "ControlAdapter onAnimationCancelled mSource: %s mControlTarget: %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_WINDOW_INSETS",
+ "at": "com\/android\/server\/wm\/InsetsSourceProvider.java"
+ },
"-1391944764": {
"message": "SURFACE DESTROY: %s. %s",
"level": "INFO",
@@ -721,12 +733,6 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "-1312861660": {
- "message": "notifyInsetsControlChanged for %s ",
- "level": "DEBUG",
- "group": "WM_DEBUG_IME",
- "at": "com\/android\/server\/wm\/WindowState.java"
- },
"-1311436264": {
"message": "Unregister task fragment organizer=%s uid=%d pid=%d",
"level": "VERBOSE",
@@ -835,6 +841,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/TaskFragment.java"
},
+ "-1185473319": {
+ "message": "ControlAdapter startAnimation mSource: %s controlTarget: %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_WINDOW_INSETS",
+ "at": "com\/android\/server\/wm\/InsetsSourceProvider.java"
+ },
"-1176488860": {
"message": "SURFACE isSecure=%b: %s",
"level": "INFO",
@@ -1249,6 +1261,12 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
+ "-702650156": {
+ "message": "Override with TaskFragment remote animation for transit=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransitionController.java"
+ },
"-701167286": {
"message": "applyAnimation: transit=%s, enter=%b, wc=%s",
"level": "VERBOSE",
@@ -1531,12 +1549,6 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
- "-395922585": {
- "message": "InsetsSource setWin %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_IME",
- "at": "com\/android\/server\/wm\/InsetsSourceProvider.java"
- },
"-393505149": {
"message": "unable to update pointer icon",
"level": "WARN",
@@ -1741,12 +1753,6 @@
"group": "WM_DEBUG_SCREEN_ON",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-112805366": {
- "message": "InsetsSource updateVisibility serverVisible: %s clientVisible: %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_IME",
- "at": "com\/android\/server\/wm\/InsetsSourceProvider.java"
- },
"-108977760": {
"message": "Sandbox max bounds for uid %s to bounds %s. config to never sandbox = %s, config to always sandbox = %s, letterboxing from mismatch with parent bounds = %s, has mCompatDisplayInsets = %s, should create compatDisplayInsets = %s",
"level": "DEBUG",
@@ -1789,6 +1795,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/TaskFragment.java"
},
+ "-70719599": {
+ "message": "Unregister remote animations for organizer=%s uid=%d pid=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
+ },
"-55185509": {
"message": "setFocusedTask: taskId=%d touchedActivity=%s",
"level": "DEBUG",
@@ -1849,12 +1861,6 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimationController.java"
},
- "29780972": {
- "message": "InsetsSource Control %s for target %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_IME",
- "at": "com\/android\/server\/wm\/InsetsSourceProvider.java"
- },
"35398067": {
"message": "goodToGo(): onAnimationStart, transit=%s, apps=%d, wallpapers=%d, nonApps=%d",
"level": "DEBUG",
@@ -1897,24 +1903,12 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
- "73987756": {
- "message": "ControlAdapter onAnimationCancelled mSource: %s mControlTarget: %s",
- "level": "INFO",
- "group": "WM_DEBUG_IME",
- "at": "com\/android\/server\/wm\/InsetsSourceProvider.java"
- },
"74885950": {
"message": "Waiting for top state to be released by %s",
"level": "VERBOSE",
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java"
},
- "75707221": {
- "message": "ControlAdapter startAnimation mSource: %s controlTarget: %s",
- "level": "INFO",
- "group": "WM_DEBUG_IME",
- "at": "com\/android\/server\/wm\/InsetsSourceProvider.java"
- },
"83950285": {
"message": "removeAnimation(%d)",
"level": "DEBUG",
@@ -2287,6 +2281,12 @@
"group": "WM_SHOW_SURFACE_ALLOC",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
+ "416924848": {
+ "message": "InsetsSource Control %s for target %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_WINDOW_INSETS",
+ "at": "com\/android\/server\/wm\/InsetsSourceProvider.java"
+ },
"417311568": {
"message": "onResize: Resizing %s",
"level": "DEBUG",
@@ -2803,6 +2803,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "1030898920": {
+ "message": "notifyInsetsControlChanged for %s ",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_WINDOW_INSETS",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"1033274509": {
"message": "moveWindowTokenToDisplay: Attempted to move non-existing token: %s",
"level": "WARN",
@@ -2821,6 +2827,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "1047505501": {
+ "message": "notifyInsetsChanged for %s ",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_WINDOW_INSETS",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"1047769218": {
"message": "Finishing activity r=%s, result=%d, data=%s, reason=%s",
"level": "VERBOSE",
@@ -2941,6 +2953,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "1210037962": {
+ "message": "Register remote animations for organizer=%s uid=%d pid=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
+ },
"1219600119": {
"message": "addWindow: win=%s Callers=%s",
"level": "DEBUG",
@@ -3211,12 +3229,6 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "1533154777": {
- "message": "notifyInsetsChanged for %s ",
- "level": "DEBUG",
- "group": "WM_DEBUG_IME",
- "at": "com\/android\/server\/wm\/WindowState.java"
- },
"1557732761": {
"message": "For Intent %s bringing to top: %s",
"level": "DEBUG",
@@ -3643,6 +3655,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "2070726247": {
+ "message": "InsetsSource updateVisibility for %s, serverVisible: %s clientVisible: %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_WINDOW_INSETS",
+ "at": "com\/android\/server\/wm\/InsetsSourceProvider.java"
+ },
"2083556954": {
"message": "Set mOrientationChanging of %s",
"level": "VERBOSE",
@@ -3768,6 +3786,9 @@
"WM_DEBUG_TASKS": {
"tag": "WindowManager"
},
+ "WM_DEBUG_WINDOW_INSETS": {
+ "tag": "WindowManager"
+ },
"WM_DEBUG_WINDOW_MOVEMENT": {
"tag": "WindowManager"
},
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
index 4206d03..9212a0f 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
@@ -62,6 +62,7 @@
final Map<IBinder, Configuration> mFragmentParentConfigs = new ArrayMap<>();
private final TaskFragmentCallback mCallback;
+ private TaskFragmentAnimationController mAnimationController;
/**
* Callback that notifies the controller about changes to task fragments.
@@ -83,6 +84,25 @@
mCallback = callback;
}
+ @Override
+ public void registerOrganizer() {
+ if (mAnimationController != null) {
+ throw new IllegalStateException("Must unregister the organizer before re-register.");
+ }
+ super.registerOrganizer();
+ mAnimationController = new TaskFragmentAnimationController(this);
+ mAnimationController.registerRemoteAnimations();
+ }
+
+ @Override
+ public void unregisterOrganizer() {
+ if (mAnimationController != null) {
+ mAnimationController.unregisterRemoteAnimations();
+ mAnimationController = null;
+ }
+ super.unregisterOrganizer();
+ }
+
/**
* Starts a new Activity and puts it into split with an existing Activity side-by-side.
* @param launchingFragmentToken token for the launching TaskFragment. If it exists, it will
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationController.java
new file mode 100644
index 0000000..b85287d
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationController.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.organizer;
+
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
+
+import android.util.Log;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationDefinition;
+import android.window.TaskFragmentOrganizer;
+
+/** Controls the TaskFragment remote animations. */
+class TaskFragmentAnimationController {
+
+ private static final String TAG = "TaskFragAnimationCtrl";
+ // TODO(b/196173550) turn off when finalize
+ static final boolean DEBUG = false;
+
+ private final TaskFragmentOrganizer mOrganizer;
+ private final TaskFragmentAnimationRunner mRemoteRunner = new TaskFragmentAnimationRunner();
+
+ TaskFragmentAnimationController(TaskFragmentOrganizer organizer) {
+ mOrganizer = organizer;
+ }
+
+ void registerRemoteAnimations() {
+ if (DEBUG) {
+ Log.v(TAG, "registerRemoteAnimations");
+ }
+ final RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
+ final RemoteAnimationAdapter animationAdapter =
+ new RemoteAnimationAdapter(mRemoteRunner, 0, 0);
+ definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, animationAdapter);
+ definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, animationAdapter);
+ definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, animationAdapter);
+ mOrganizer.registerRemoteAnimations(definition);
+ }
+
+ void unregisterRemoteAnimations() {
+ if (DEBUG) {
+ Log.v(TAG, "unregisterRemoteAnimations");
+ }
+ mOrganizer.unregisterRemoteAnimations();
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationRunner.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationRunner.java
new file mode 100644
index 0000000..9ee60d8
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationRunner.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.organizer;
+
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+
+import androidx.annotation.Nullable;
+
+/** To run the TaskFragment animations. */
+class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
+
+ private static final String TAG = "TaskFragAnimationRunner";
+ private final Handler mHandler = new Handler(Looper.myLooper());
+
+ @Nullable
+ private IRemoteAnimationFinishedCallback mFinishedCallback;
+
+ @Override
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ if (wallpapers.length != 0 || nonApps.length != 0) {
+ throw new IllegalArgumentException("TaskFragment shouldn't handle animation with"
+ + "wallpaper or non-app windows.");
+ }
+ if (TaskFragmentAnimationController.DEBUG) {
+ Log.v(TAG, "onAnimationStart transit=" + transit);
+ }
+ mHandler.post(() -> startAnimation(apps, finishedCallback));
+ }
+
+ @Override
+ public void onAnimationCancelled() {
+ if (TaskFragmentAnimationController.DEBUG) {
+ Log.v(TAG, "onAnimationCancelled");
+ }
+ mHandler.post(this::onAnimationFinished);
+ }
+
+ private void startAnimation(RemoteAnimationTarget[] targets,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ // TODO(b/196173550) replace with actual animations
+ mFinishedCallback = finishedCallback;
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ for (RemoteAnimationTarget target : targets) {
+ if (target.mode == MODE_OPENING) {
+ t.show(target.leash);
+ t.setAlpha(target.leash, 1);
+ }
+ t.setPosition(target.leash, target.localBounds.left, target.localBounds.top);
+ }
+ t.apply();
+ onAnimationFinished();
+ }
+
+ private void onAnimationFinished() {
+ if (mFinishedCallback == null) {
+ return;
+ }
+ try {
+ mFinishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ mFinishedCallback = null;
+ }
+}
diff --git a/libs/WindowManager/Shell/res/color/split_divider_background.xml b/libs/WindowManager/Shell/res/color/split_divider_background.xml
new file mode 100644
index 0000000..84f4fdf
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/split_divider_background.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral2_500" android:lStar="35" />
+</selector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/split_rounded_bottom.xml b/libs/WindowManager/Shell/res/drawable/split_rounded_bottom.xml
new file mode 100644
index 0000000..18dc909
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/split_rounded_bottom.xml
@@ -0,0 +1,30 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/split_divider_corner_size"
+ android:height="@dimen/split_divider_corner_size"
+ android:viewportWidth="42"
+ android:viewportHeight="42">
+
+ <group android:pivotX="21"
+ android:pivotY="21"
+ android:rotation="180">
+ <path
+ android:fillColor="@color/split_divider_background"
+ android:pathData="m 0 0 c 8 0 16 8 16 16 h 10 c 0 -8 8 -16 16 -16 z" />
+ </group>
+
+</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/split_rounded_left.xml b/libs/WindowManager/Shell/res/drawable/split_rounded_left.xml
new file mode 100644
index 0000000..931cacf
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/split_rounded_left.xml
@@ -0,0 +1,30 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/split_divider_corner_size"
+ android:height="@dimen/split_divider_corner_size"
+ android:viewportWidth="42"
+ android:viewportHeight="42">
+
+ <group android:pivotX="21"
+ android:pivotY="21"
+ android:rotation="-90">
+ <path
+ android:fillColor="@color/split_divider_background"
+ android:pathData="m 0 0 c 8 0 16 8 16 16 h 10 c 0 -8 8 -16 16 -16 z" />
+ </group>
+
+</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/split_rounded_right.xml b/libs/WindowManager/Shell/res/drawable/split_rounded_right.xml
new file mode 100644
index 0000000..54e4761
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/split_rounded_right.xml
@@ -0,0 +1,30 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/split_divider_corner_size"
+ android:height="@dimen/split_divider_corner_size"
+ android:viewportWidth="42"
+ android:viewportHeight="42">
+
+ <group android:pivotX="21"
+ android:pivotY="21"
+ android:rotation="90">
+ <path
+ android:fillColor="@color/split_divider_background"
+ android:pathData="m 0 0 c 8 0 16 8 16 16 h 10 c 0 -8 8 -16 16 -16 z" />
+ </group>
+
+</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/split_rounded_top.xml b/libs/WindowManager/Shell/res/drawable/split_rounded_top.xml
new file mode 100644
index 0000000..9115b5a
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/split_rounded_top.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/split_divider_corner_size"
+ android:height="@dimen/split_divider_corner_size"
+ android:viewportWidth="42"
+ android:viewportHeight="42">
+
+ <path
+ android:fillColor="@color/split_divider_background"
+ android:pathData="m 0 0 c 8 0 16 8 16 16 h 10 c 0 -8 8 -16 16 -16 z" />
+
+</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/docked_stack_divider.xml b/libs/WindowManager/Shell/res/layout/docked_stack_divider.xml
index ed5d2e1..d732b01 100644
--- a/libs/WindowManager/Shell/res/layout/docked_stack_divider.xml
+++ b/libs/WindowManager/Shell/res/layout/docked_stack_divider.xml
@@ -22,7 +22,7 @@
<View
style="@style/DockedDividerBackground"
android:id="@+id/docked_divider_background"
- android:background="@color/docked_divider_background"/>
+ android:background="@color/split_divider_background"/>
<com.android.wm.shell.legacysplitscreen.MinimizedDockShadow
style="@style/DockedDividerMinimizedShadow"
diff --git a/libs/WindowManager/Shell/res/layout/split_divider.xml b/libs/WindowManager/Shell/res/layout/split_divider.xml
index 7f583f3..94182cd 100644
--- a/libs/WindowManager/Shell/res/layout/split_divider.xml
+++ b/libs/WindowManager/Shell/res/layout/split_divider.xml
@@ -19,15 +19,25 @@
android:layout_height="match_parent"
android:layout_width="match_parent">
- <View
- style="@style/DockedDividerBackground"
- android:id="@+id/docked_divider_background"
- android:background="@color/docked_divider_background"/>
+ <FrameLayout
+ android:id="@+id/divider_bar"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
- <com.android.wm.shell.common.split.DividerHandleView
- style="@style/DockedDividerHandle"
- android:id="@+id/docked_divider_handle"
- android:contentDescription="@string/accessibility_divider"
- android:background="@null"/>
+ <View style="@style/DockedDividerTopLeftRoundCorner"/>
+
+ <View
+ style="@style/DockedDividerBackground"
+ android:id="@+id/docked_divider_background"/>
+
+ <View style="@style/DockedDividerBottomRightRoundCorner"/>
+
+ <com.android.wm.shell.common.split.DividerHandleView
+ style="@style/DockedDividerHandle"
+ android:id="@+id/docked_divider_handle"
+ android:contentDescription="@string/accessibility_divider"
+ android:background="@null"/>
+
+ </FrameLayout>
</com.android.wm.shell.common.split.DividerView>
diff --git a/libs/WindowManager/Shell/res/values-land/dimens.xml b/libs/WindowManager/Shell/res/values-land/dimens.xml
index aafba58..a95323f 100644
--- a/libs/WindowManager/Shell/res/values-land/dimens.xml
+++ b/libs/WindowManager/Shell/res/values-land/dimens.xml
@@ -16,8 +16,12 @@
*/
-->
<resources>
+ <!-- Divider handle size for legacy split screen -->
<dimen name="docked_divider_handle_width">2dp</dimen>
<dimen name="docked_divider_handle_height">16dp</dimen>
+ <!-- Divider handle size for split screen -->
+ <dimen name="split_divider_handle_width">3dp</dimen>
+ <dimen name="split_divider_handle_height">72dp</dimen>
<!-- Padding between status bar and bubbles when displayed in expanded state, smaller
value in landscape since we have limited vertical space-->
diff --git a/libs/WindowManager/Shell/res/values-land/styles.xml b/libs/WindowManager/Shell/res/values-land/styles.xml
index 863bb69..e5707f3 100644
--- a/libs/WindowManager/Shell/res/values-land/styles.xml
+++ b/libs/WindowManager/Shell/res/values-land/styles.xml
@@ -19,6 +19,7 @@
<item name="android:layout_width">10dp</item>
<item name="android:layout_height">match_parent</item>
<item name="android:layout_gravity">center_horizontal</item>
+ <item name="android:background">@color/split_divider_background</item>
</style>
<style name="DockedDividerHandle">
@@ -27,6 +28,20 @@
<item name="android:layout_height">96dp</item>
</style>
+ <style name="DockedDividerTopLeftRoundCorner">
+ <item name="android:layout_gravity">center_horizontal|top</item>
+ <item name="android:background">@drawable/split_rounded_top</item>
+ <item name="android:layout_width">@dimen/split_divider_corner_size</item>
+ <item name="android:layout_height">@dimen/split_divider_corner_size</item>
+ </style>
+
+ <style name="DockedDividerBottomRightRoundCorner">
+ <item name="android:layout_gravity">center_horizontal|bottom</item>
+ <item name="android:background">@drawable/split_rounded_bottom</item>
+ <item name="android:layout_width">@dimen/split_divider_corner_size</item>
+ <item name="android:layout_height">@dimen/split_divider_corner_size</item>
+ </style>
+
<style name="DockedDividerMinimizedShadow">
<item name="android:layout_width">8dp</item>
<item name="android:layout_height">match_parent</item>
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index 350beaf..93c0352 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -17,7 +17,6 @@
*/
-->
<resources>
- <color name="docked_divider_background">#ff000000</color>
<color name="docked_divider_handle">#ffffff</color>
<drawable name="forced_resizable_background">#59000000</drawable>
<color name="minimize_dock_shadow_start">#60000000</color>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 130f741..f8576643 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -76,8 +76,15 @@
<!-- How high we lift the divider when touching -->
<dimen name="docked_stack_divider_lift_elevation">4dp</dimen>
+ <!-- Divider handle size for legacy split screen -->
<dimen name="docked_divider_handle_width">16dp</dimen>
<dimen name="docked_divider_handle_height">2dp</dimen>
+ <!-- Divider handle size for split screen -->
+ <dimen name="split_divider_handle_width">72dp</dimen>
+ <dimen name="split_divider_handle_height">3dp</dimen>
+
+ <dimen name="split_divider_bar_width">10dp</dimen>
+ <dimen name="split_divider_corner_size">42dp</dimen>
<!-- One-Handed Mode -->
<!-- Threshold for dragging distance to enable one-handed mode -->
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index fffcd33..28ff25a 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -32,8 +32,23 @@
<style name="DockedDividerBackground">
<item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">10dp</item>
+ <item name="android:layout_height">@dimen/split_divider_bar_width</item>
<item name="android:layout_gravity">center_vertical</item>
+ <item name="android:background">@color/split_divider_background</item>
+ </style>
+
+ <style name="DockedDividerTopLeftRoundCorner">
+ <item name="android:layout_gravity">center_vertical|left</item>
+ <item name="android:background">@drawable/split_rounded_left</item>
+ <item name="android:layout_width">@dimen/split_divider_corner_size</item>
+ <item name="android:layout_height">@dimen/split_divider_corner_size</item>
+ </style>
+
+ <style name="DockedDividerBottomRightRoundCorner">
+ <item name="android:layout_gravity">center_vertical|right</item>
+ <item name="android:background">@drawable/split_rounded_right</item>
+ <item name="android:layout_width">@dimen/split_divider_corner_size</item>
+ <item name="android:layout_height">@dimen/split_divider_corner_size</item>
</style>
<style name="DockedDividerMinimizedShadow">
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index 9a3bdab..a1fb658 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -67,7 +67,10 @@
*/
public void initialize() {
try {
- mWmService.registerDisplayWindowListener(mDisplayContainerListener);
+ int[] displayIds = mWmService.registerDisplayWindowListener(mDisplayContainerListener);
+ for (int i = 0; i < displayIds.length; i++) {
+ onDisplayAdded(displayIds[i]);
+ }
} catch (RemoteException e) {
throw new RuntimeException("Unable to register display controller");
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java
index 5f3de7e..565f148 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java
@@ -233,18 +233,18 @@
* control. Won't be called if config_remoteInsetsControllerControlsSystemBars is false.
* @param packageName: Passes the top package name
*/
- void topFocusedWindowChanged(String packageName);
+ default void topFocusedWindowChanged(String packageName) {}
/**
* Called when the window insets configuration has changed.
*/
- void insetsChanged(InsetsState insetsState);
+ default void insetsChanged(InsetsState insetsState) {}
/**
* Called when this window retrieved control over a specified set of insets sources.
*/
- void insetsControlChanged(InsetsState insetsState,
- InsetsSourceControl[] activeControls);
+ default void insetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] activeControls) {}
/**
* Called when a set of insets source window should be shown by policy.
@@ -252,7 +252,7 @@
* @param types internal insets types (WindowInsets.Type.InsetsType) to show
* @param fromIme true if this request originated from IME (InputMethodService).
*/
- void showInsets(int types, boolean fromIme);
+ default void showInsets(int types, boolean fromIme) {}
/**
* Called when a set of insets source window should be hidden by policy.
@@ -260,6 +260,6 @@
* @param types internal insets types (WindowInsets.Type.InsetsType) to hide
* @param fromIme true if this request originated from IME (InputMethodService).
*/
- void hideInsets(int types, boolean fromIme);
+ default void hideInsets(int types, boolean fromIme) {}
}
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
index f3a8620..4c0281d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
@@ -68,6 +68,10 @@
* Queues a sync transaction to be sent serially to WM.
*/
public void queue(WindowContainerTransaction wct) {
+ if (wct.isEmpty()) {
+ if (DEBUG) Slog.d(TAG, "Skip queue due to transaction change is empty");
+ return;
+ }
SyncCallback cb = new SyncCallback(wct);
synchronized (mQueue) {
if (DEBUG) Slog.d(TAG, "Queueing up " + wct);
@@ -83,6 +87,10 @@
*/
public void queue(LegacyTransitions.ILegacyTransition transition,
@WindowManager.TransitionType int type, WindowContainerTransaction wct) {
+ if (wct.isEmpty()) {
+ if (DEBUG) Slog.d(TAG, "Skip queue due to transaction change is empty");
+ return;
+ }
SyncCallback cb = new SyncCallback(transition, type, wct);
synchronized (mQueue) {
if (DEBUG) Slog.d(TAG, "Queueing up legacy transition " + wct);
@@ -99,6 +107,10 @@
* @return {@code true} if queued, {@code false} if not.
*/
public boolean queueIfWaiting(WindowContainerTransaction wct) {
+ if (wct.isEmpty()) {
+ if (DEBUG) Slog.d(TAG, "Skip queueIfWaiting due to transaction change is empty");
+ return false;
+ }
synchronized (mQueue) {
if (mQueue.isEmpty()) {
if (DEBUG) Slog.d(TAG, "Nothing in queue, so skip queueing up " + wct);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java
index 218bf47..c76937d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java
@@ -70,7 +70,8 @@
private final Paint mPaint = new Paint();
private final int mWidth;
private final int mHeight;
- private final int mCircleDiameter;
+ private final int mTouchingWidth;
+ private final int mTouchingHeight;
private int mCurrentWidth;
private int mCurrentHeight;
private AnimatorSet mAnimator;
@@ -80,11 +81,12 @@
super(context, attrs);
mPaint.setColor(getResources().getColor(R.color.docked_divider_handle, null));
mPaint.setAntiAlias(true);
- mWidth = getResources().getDimensionPixelSize(R.dimen.docked_divider_handle_width);
- mHeight = getResources().getDimensionPixelSize(R.dimen.docked_divider_handle_height);
+ mWidth = getResources().getDimensionPixelSize(R.dimen.split_divider_handle_width);
+ mHeight = getResources().getDimensionPixelSize(R.dimen.split_divider_handle_height);
mCurrentWidth = mWidth;
mCurrentHeight = mHeight;
- mCircleDiameter = (mWidth + mHeight) / 3;
+ mTouchingWidth = mWidth > mHeight ? mWidth / 2 : mWidth;
+ mTouchingHeight = mHeight > mWidth ? mHeight / 2 : mHeight;
}
/** Sets touching state for this handle view. */
@@ -98,16 +100,16 @@
}
if (!animate) {
if (touching) {
- mCurrentWidth = mCircleDiameter;
- mCurrentHeight = mCircleDiameter;
+ mCurrentWidth = mTouchingWidth;
+ mCurrentHeight = mTouchingHeight;
} else {
mCurrentWidth = mWidth;
mCurrentHeight = mHeight;
}
invalidate();
} else {
- animateToTarget(touching ? mCircleDiameter : mWidth,
- touching ? mCircleDiameter : mHeight, touching);
+ animateToTarget(touching ? mTouchingWidth : mWidth,
+ touching ? mTouchingHeight : mHeight, touching);
}
mTouching = touching;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index cba019a..826e2f5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -19,15 +19,23 @@
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
+import android.animation.ObjectAnimator;
import android.content.Context;
+import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.Property;
+import android.util.TypedValue;
import android.view.GestureDetector;
+import android.view.InsetsSource;
+import android.view.InsetsState;
import android.view.MotionEvent;
import android.view.SurfaceControlViewHost;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.ViewGroup;
import android.view.WindowManager;
+import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
@@ -44,6 +52,23 @@
public static final long TOUCH_ANIMATION_DURATION = 150;
public static final long TOUCH_RELEASE_ANIMATION_DURATION = 200;
+ // TODO(b/191269755): use the value defined in InsetsController.
+ private static final Interpolator RESIZE_INTERPOLATOR = Interpolators.LINEAR;
+
+ // TODO(b/191269755): use the value defined in InsetsController.
+ private static final int ANIMATION_DURATION_RESIZE = 300;
+
+ /**
+ * The task bar height defined in launcher. Used to determine whether to insets divider bounds
+ * or not.
+ */
+ private static final int EXPANDED_TASK_BAR_HEIGHT_IN_DP = 60;
+
+ /** The task bar expanded height. Used to determine whether to insets divider bounds or not. */
+ private final float mExpandedTaskBarHeight = TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, EXPANDED_TASK_BAR_HEIGHT_IN_DP,
+ getResources().getDisplayMetrics());
+
private final int mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
private SplitLayout mSplitLayout;
@@ -58,6 +83,31 @@
private GestureDetector mDoubleTapDetector;
private boolean mInteractive;
+ /**
+ * Tracks divider bar visible bounds in screen-based coordination. Used to calculate with
+ * insets.
+ */
+ private final Rect mDividerBounds = new Rect();
+ private final Rect mTempRect = new Rect();
+ private FrameLayout mDividerBar;
+
+
+ static final Property<DividerView, Integer> DIVIDER_HEIGHT_PROPERTY =
+ new Property<DividerView, Integer>(Integer.class, "height") {
+ @Override
+ public Integer get(DividerView object) {
+ return object.mDividerBar.getLayoutParams().height;
+ }
+
+ @Override
+ public void set(DividerView object, Integer value) {
+ ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams)
+ object.mDividerBar.getLayoutParams();
+ lp.height = value;
+ object.mDividerBar.setLayoutParams(lp);
+ }
+ };
+
public DividerView(@NonNull Context context) {
super(context);
}
@@ -79,14 +129,42 @@
/** Sets up essential dependencies of the divider bar. */
public void setup(
SplitLayout layout,
- SurfaceControlViewHost viewHost) {
+ SurfaceControlViewHost viewHost,
+ InsetsState insetsState) {
mSplitLayout = layout;
mViewHost = viewHost;
+ mDividerBounds.set(layout.getDividerBounds());
+ onInsetsChanged(insetsState, false /* animate */);
+ }
+
+ void onInsetsChanged(InsetsState insetsState, boolean animate) {
+ mTempRect.set(mSplitLayout.getDividerBounds());
+ final InsetsSource taskBarInsetsSource =
+ insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
+ // Only insets the divider bar with task bar when it's expanded so that the rounded corners
+ // will be drawn against task bar.
+ if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) {
+ mTempRect.inset(taskBarInsetsSource.calculateVisibleInsets(mTempRect));
+ }
+
+ if (!mTempRect.equals(mDividerBounds)) {
+ if (animate) {
+ ObjectAnimator animator = ObjectAnimator.ofInt(this,
+ DIVIDER_HEIGHT_PROPERTY, mDividerBounds.height(), mTempRect.height());
+ animator.setInterpolator(RESIZE_INTERPOLATOR);
+ animator.setDuration(ANIMATION_DURATION_RESIZE);
+ animator.start();
+ } else {
+ DIVIDER_HEIGHT_PROPERTY.set(this, mTempRect.height());
+ }
+ mDividerBounds.set(mTempRect);
+ }
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+ mDividerBar = findViewById(R.id.divider_bar);
mHandle = findViewById(R.id.docked_divider_handle);
mBackground = findViewById(R.id.docked_divider_background);
mTouchElevation = getResources().getDimensionPixelSize(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 3b9bcd3..81cad5a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -40,6 +40,8 @@
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.WindowInsets;
import android.view.WindowManager;
@@ -50,15 +52,17 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.DividerSnapAlgorithm;
+import com.android.internal.policy.DockedDividerUtils;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
/**
* Records and handles layout of splits. Helps to calculate proper bounds when configuration or
* divide position changes.
*/
-public final class SplitLayout {
+public final class SplitLayout implements DisplayInsetsController.OnInsetsChangedListener {
/**
* Split position isn't specified normally meaning to use what ever it is currently set to.
*/
@@ -93,16 +97,20 @@
private final Rect mDividerBounds = new Rect();
private final Rect mBounds1 = new Rect();
private final Rect mBounds2 = new Rect();
- private final Rect mTmpBounds = new Rect();
+ private final Rect mWinBounds1 = new Rect();
+ private final Rect mWinBounds2 = new Rect();
private final SplitLayoutHandler mSplitLayoutHandler;
private final SplitWindowManager mSplitWindowManager;
private final DisplayImeController mDisplayImeController;
private final ImePositionProcessor mImePositionProcessor;
private final DismissingParallaxPolicy mDismissingParallaxPolicy;
private final ShellTaskOrganizer mTaskOrganizer;
+ private final InsetsState mInsetsState = new InsetsState();
private Context mContext;
private DividerSnapAlgorithm mDividerSnapAlgorithm;
+ private WindowContainerToken mWinToken1;
+ private WindowContainerToken mWinToken2;
private int mDividePosition;
private boolean mInitialized = false;
private int mOrientation;
@@ -189,10 +197,10 @@
final int rotation = configuration.windowConfiguration.getRotation();
final Rect rootBounds = configuration.windowConfiguration.getBounds();
if (rotation != mRotation || !mRootBounds.equals(rootBounds)) {
- mTmpBounds.set(mRootBounds);
+ mTempRect.set(mRootBounds);
mRootBounds.set(rootBounds);
mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
- initDividerPosition(mTmpBounds);
+ initDividerPosition(mTempRect);
affectsLayout = true;
}
@@ -236,6 +244,8 @@
mBounds1.bottom = position;
mBounds2.top = mBounds1.bottom + mDividerSize;
}
+ DockedDividerUtils.sanitizeStackBounds(mBounds1, true /** topLeft */);
+ DockedDividerUtils.sanitizeStackBounds(mBounds2, false /** topLeft */);
mDismissingParallaxPolicy.applyDividerPosition(position, isLandscape);
}
@@ -243,7 +253,7 @@
public void init() {
if (mInitialized) return;
mInitialized = true;
- mSplitWindowManager.init(this);
+ mSplitWindowManager.init(this, mInsetsState);
mDisplayImeController.addPositionProcessor(mImePositionProcessor);
}
@@ -256,6 +266,23 @@
mImePositionProcessor.reset();
}
+ @Override
+ public void insetsChanged(InsetsState insetsState) {
+ mInsetsState.set(insetsState);
+ if (!mInitialized) {
+ return;
+ }
+ mSplitWindowManager.onInsetsChanged(insetsState);
+ }
+
+ @Override
+ public void insetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] activeControls) {
+ if (!mInsetsState.equals(insetsState)) {
+ insetsChanged(insetsState);
+ }
+ }
+
/**
* Updates bounds with the passing position. Usually used to update recording bounds while
* performing animation or dragging divider bar to resize the splits.
@@ -403,8 +430,16 @@
return;
}
- wct.setBounds(task1.token, mBounds1)
- .setBounds(task2.token, mBounds2);
+ if (!mBounds1.equals(mWinBounds1) || !task1.token.equals(mWinToken1)) {
+ wct.setBounds(task1.token, mBounds1);
+ mWinBounds1.set(mBounds1);
+ mWinToken1 = task1.token;
+ }
+ if (!mBounds2.equals(mWinBounds2) || !task2.token.equals(mWinToken2)) {
+ wct.setBounds(task2.token, mBounds2);
+ mWinBounds2.set(mBounds2);
+ mWinToken2 = task2.token;
+ }
}
/**
@@ -492,7 +527,7 @@
/**
* Applies a parallax to the task to hint dismissing progress.
*
- * @param position the split position to apply dismissing parallax effect
+ * @param position the split position to apply dismissing parallax effect
* @param isLandscape indicates whether it's splitting horizontally or vertically
*/
void applyDividerPosition(int position, boolean isLandscape) {
@@ -504,11 +539,11 @@
if (position <= mDividerSnapAlgorithm.getFirstSplitTarget().position) {
mDismissingSide = isLandscape ? DOCKED_LEFT : DOCKED_TOP;
totalDismissingDistance = mDividerSnapAlgorithm.getDismissStartTarget().position
- - mDividerSnapAlgorithm.getFirstSplitTarget().position;
+ - mDividerSnapAlgorithm.getFirstSplitTarget().position;
} else if (position >= mDividerSnapAlgorithm.getLastSplitTarget().position) {
mDismissingSide = isLandscape ? DOCKED_RIGHT : DOCKED_BOTTOM;
totalDismissingDistance = mDividerSnapAlgorithm.getLastSplitTarget().position
- - mDividerSnapAlgorithm.getDismissEndTarget().position;
+ - mDividerSnapAlgorithm.getDismissEndTarget().position;
}
if (mDismissingSide != DOCKED_INVALID) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
index b7bbe80..fc7edfc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
@@ -36,6 +36,7 @@
import android.os.RemoteException;
import android.util.Slog;
import android.view.IWindow;
+import android.view.InsetsState;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
@@ -103,7 +104,7 @@
}
/** Inflates {@link DividerView} on to the root surface. */
- void init(SplitLayout splitLayout) {
+ void init(SplitLayout splitLayout, InsetsState insetsState) {
if (mDividerView != null || mViewHost != null) {
throw new UnsupportedOperationException(
"Try to inflate divider view again without release first");
@@ -123,7 +124,7 @@
lp.setTitle(mWindowName);
lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
mViewHost.setView(mDividerView, lp);
- mDividerView.setup(splitLayout, mViewHost);
+ mDividerView.setup(splitLayout, mViewHost, insetsState);
}
/**
@@ -169,4 +170,10 @@
SurfaceControl getSurfaceControl() {
return mLeash;
}
+
+ void onInsetsChanged(InsetsState insetsState) {
+ if (mDividerView != null) {
+ mDividerView.onInsetsChanged(insetsState, true /* animate */);
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitDisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitDisplayLayout.java
index 40244fb..f201634 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitDisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitDisplayLayout.java
@@ -62,6 +62,7 @@
Rect mSecondary = null;
Rect mAdjustedPrimary = null;
Rect mAdjustedSecondary = null;
+ final Rect mTmpBounds = new Rect();
public LegacySplitDisplayLayout(Context ctx, DisplayLayout dl,
LegacySplitScreenTaskListener taskTiles) {
@@ -136,31 +137,41 @@
return mMinimizedSnapAlgorithm;
}
- void resizeSplits(int position) {
+ /**
+ * Resize primary bounds and secondary bounds by divider position.
+ *
+ * @param position divider position.
+ * @return true if calculated bounds changed.
+ */
+ boolean resizeSplits(int position) {
mPrimary = mPrimary == null ? new Rect() : mPrimary;
mSecondary = mSecondary == null ? new Rect() : mSecondary;
- calcSplitBounds(position, mPrimary, mSecondary);
+ int dockSide = getPrimarySplitSide();
+ boolean boundsChanged;
+
+ mTmpBounds.set(mPrimary);
+ DockedDividerUtils.calculateBoundsForPosition(position, dockSide, mPrimary,
+ mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize);
+ boundsChanged = !mPrimary.equals(mTmpBounds);
+
+ mTmpBounds.set(mSecondary);
+ DockedDividerUtils.calculateBoundsForPosition(position,
+ DockedDividerUtils.invertDockSide(dockSide), mSecondary, mDisplayLayout.width(),
+ mDisplayLayout.height(), mDividerSize);
+ boundsChanged |= !mSecondary.equals(mTmpBounds);
+ return boundsChanged;
}
void resizeSplits(int position, WindowContainerTransaction t) {
- resizeSplits(position);
- t.setBounds(mTiles.mPrimary.token, mPrimary);
- t.setBounds(mTiles.mSecondary.token, mSecondary);
+ if (resizeSplits(position)) {
+ t.setBounds(mTiles.mPrimary.token, mPrimary);
+ t.setBounds(mTiles.mSecondary.token, mSecondary);
- t.setSmallestScreenWidthDp(mTiles.mPrimary.token,
- getSmallestWidthDpForBounds(mContext, mDisplayLayout, mPrimary));
- t.setSmallestScreenWidthDp(mTiles.mSecondary.token,
- getSmallestWidthDpForBounds(mContext, mDisplayLayout, mSecondary));
- }
-
- void calcSplitBounds(int position, @NonNull Rect outPrimary, @NonNull Rect outSecondary) {
- int dockSide = getPrimarySplitSide();
- DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outPrimary,
- mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize);
-
- DockedDividerUtils.calculateBoundsForPosition(position,
- DockedDividerUtils.invertDockSide(dockSide), outSecondary, mDisplayLayout.width(),
- mDisplayLayout.height(), mDividerSize);
+ t.setSmallestScreenWidthDp(mTiles.mPrimary.token,
+ getSmallestWidthDpForBounds(mContext, mDisplayLayout, mPrimary));
+ t.setSmallestScreenWidthDp(mTiles.mSecondary.token,
+ getSmallestWidthDpForBounds(mContext, mDisplayLayout, mSecondary));
+ }
}
Rect calcResizableMinimizedHomeStackBounds() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index a646b07..ae8c1b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -60,8 +60,7 @@
private static final boolean DEBUG = false;
public static final int MENU_STATE_NONE = 0;
- public static final int MENU_STATE_CLOSE = 1;
- public static final int MENU_STATE_FULL = 2;
+ public static final int MENU_STATE_FULL = 1;
/**
* A listener interface to receive notification on changes in PIP.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 67b1e6d..8ef2b6b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -23,7 +23,6 @@
import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
-import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_CLOSE;
import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_FULL;
import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_NONE;
@@ -203,7 +202,7 @@
@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
- if (action == ACTION_CLICK && mMenuState == MENU_STATE_CLOSE) {
+ if (action == ACTION_CLICK && mMenuState != MENU_STATE_FULL) {
mController.showMenu();
}
return super.performAccessibilityAction(host, action, args);
@@ -271,13 +270,12 @@
mDismissButton.getAlpha(), 1f);
ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
mResizeHandle.getAlpha(),
- ENABLE_RESIZE_HANDLE && menuState == MENU_STATE_CLOSE && showResizeHandle
- ? 1f : 0f);
+ ENABLE_RESIZE_HANDLE && showResizeHandle ? 1f : 0f);
if (menuState == MENU_STATE_FULL) {
mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
resizeAnim);
} else {
- mMenuContainerAnimator.playTogether(dismissAnim, resizeAnim);
+ mMenuContainerAnimator.playTogether(resizeAnim);
}
mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
mMenuContainerAnimator.setDuration(ANIMATION_HIDE_DURATION_MS);
@@ -429,7 +427,7 @@
FrameLayout.LayoutParams expandedLp =
(FrameLayout.LayoutParams) expandContainer.getLayoutParams();
- if (mActions.isEmpty() || menuState == MENU_STATE_CLOSE || menuState == MENU_STATE_NONE) {
+ if (mActions.isEmpty() || menuState == MENU_STATE_NONE) {
actionsContainer.setVisibility(View.INVISIBLE);
// Update the expand container margin to adjust the center of the expand button to
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 7867f93..9f2f6a5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -22,7 +22,6 @@
import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_LEFT;
import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE;
import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT;
-import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_CLOSE;
import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_FULL;
import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_NONE;
import static com.android.wm.shell.pip.phone.PipMenuView.ANIM_TYPE_NONE;
@@ -81,7 +80,6 @@
private final PhonePipMenuController mMenuController;
private final AccessibilityManager mAccessibilityManager;
- private boolean mShowPipMenuOnAnimationEnd = false;
/**
* Whether PIP stash is enabled or not. When enabled, if the user flings toward the edge of the
@@ -280,7 +278,6 @@
public void onActivityPinned() {
mPipDismissTargetHandler.createOrUpdateDismissTarget();
- mShowPipMenuOnAnimationEnd = true;
mPipResizeGestureHandler.onActivityPinned();
mFloatingContentCoordinator.onContentAdded(mMotionHelper);
}
@@ -304,13 +301,6 @@
// Set the initial bounds as the user resize bounds.
mPipResizeGestureHandler.setUserResizeBounds(mPipBoundsState.getBounds());
}
-
- if (mShowPipMenuOnAnimationEnd) {
- mMenuController.showMenu(MENU_STATE_CLOSE, mPipBoundsState.getBounds(),
- true /* allowMenuTimeout */, false /* willResizeMenu */,
- shouldShowResizeHandle());
- mShowPipMenuOnAnimationEnd = false;
- }
}
public void onConfigurationChanged() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 67223c3..437b52a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -42,8 +42,8 @@
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
-import android.view.WindowManager;
import android.view.SurfaceSession;
+import android.view.WindowManager;
import android.window.IRemoteTransition;
import android.window.WindowContainerTransaction;
@@ -56,6 +56,7 @@
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -67,6 +68,7 @@
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
+import java.util.Arrays;
import java.util.concurrent.Executor;
/**
@@ -85,6 +87,7 @@
private final ShellExecutor mMainExecutor;
private final SplitScreenImpl mImpl = new SplitScreenImpl();
private final DisplayImeController mDisplayImeController;
+ private final DisplayInsetsController mDisplayInsetsController;
private final Transitions mTransitions;
private final TransactionPool mTransactionPool;
private final SplitscreenEventLogger mLogger;
@@ -95,6 +98,7 @@
SyncTransactionQueue syncQueue, Context context,
RootTaskDisplayAreaOrganizer rootTDAOrganizer,
ShellExecutor mainExecutor, DisplayImeController displayImeController,
+ DisplayInsetsController displayInsetsController,
Transitions transitions, TransactionPool transactionPool) {
mTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
@@ -102,6 +106,7 @@
mRootTDAOrganizer = rootTDAOrganizer;
mMainExecutor = mainExecutor;
mDisplayImeController = displayImeController;
+ mDisplayInsetsController = displayInsetsController;
mTransitions = transitions;
mTransactionPool = transactionPool;
mLogger = new SplitscreenEventLogger();
@@ -125,8 +130,8 @@
if (mStageCoordinator == null) {
// TODO: Multi-display
mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
- mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController, mTransitions,
- mTransactionPool, mLogger);
+ mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController,
+ mDisplayInsetsController, mTransitions, mTransactionPool, mLogger);
}
}
@@ -295,11 +300,15 @@
mRootTDAOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, builder);
SurfaceControl sc = builder.build();
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+
+ // Ensure that we order these in the parent in the right z-order as their previous order
+ Arrays.sort(apps, (a1, a2) -> a1.prefixOrderIndex - a2.prefixOrderIndex);
+ int layer = 1;
for (RemoteAnimationTarget appTarget : apps) {
- // TODO(b/195958376) set the correct layer/z-order in transaction for the new surface
transaction.reparent(appTarget.leash, sc);
transaction.setPosition(appTarget.leash, appTarget.screenSpaceBounds.left,
appTarget.screenSpaceBounds.top);
+ transaction.setLayer(appTarget.leash, layer++);
}
transaction.apply();
transaction.close();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 736fae4..9e6edd2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -85,6 +85,7 @@
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
@@ -136,6 +137,7 @@
private final Context mContext;
private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>();
private final DisplayImeController mDisplayImeController;
+ private final DisplayInsetsController mDisplayInsetsController;
private final SplitScreenTransitions mSplitTransitions;
private final SplitscreenEventLogger mLogger;
private boolean mExitSplitScreenOnHide;
@@ -163,7 +165,8 @@
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
- DisplayImeController displayImeController, Transitions transitions,
+ DisplayImeController displayImeController,
+ DisplayInsetsController displayInsetsController, Transitions transitions,
TransactionPool transactionPool, SplitscreenEventLogger logger) {
mContext = context;
mDisplayId = displayId;
@@ -185,6 +188,7 @@
mSyncQueue,
mSurfaceSession);
mDisplayImeController = displayImeController;
+ mDisplayInsetsController = displayInsetsController;
mRootTDAOrganizer.registerListener(displayId, this);
final DeviceStateManager deviceStateManager =
mContext.getSystemService(DeviceStateManager.class);
@@ -199,7 +203,8 @@
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
MainStage mainStage, SideStage sideStage, DisplayImeController displayImeController,
- SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool,
+ DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
+ Transitions transitions, TransactionPool transactionPool,
SplitscreenEventLogger logger) {
mContext = context;
mDisplayId = displayId;
@@ -209,6 +214,7 @@
mMainStage = mainStage;
mSideStage = sideStage;
mDisplayImeController = displayImeController;
+ mDisplayInsetsController = displayInsetsController;
mRootTDAOrganizer.registerListener(displayId, this);
mSplitLayout = splitLayout;
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
@@ -832,6 +838,7 @@
mDisplayAreaInfo.configuration, this,
b -> mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b),
mDisplayImeController, mTaskOrganizer);
+ mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java
index 01c9b66..76105a3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java
@@ -36,4 +36,12 @@
default int getBackgroundColor(TaskInfo taskInfo) {
return Color.BLACK;
}
+
+ /** Set the proxy to communicate with SysUi side components. */
+ void setSysuiProxy(SysuiProxy proxy);
+
+ /** Callback to tell SysUi components execute some methods. */
+ interface SysuiProxy {
+ void requestTopUi(boolean requestTopUi, String componentTag);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 52a3ac5..6c60bad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -117,6 +117,7 @@
final SplashscreenContentDrawer mSplashscreenContentDrawer;
private Choreographer mChoreographer;
private final WindowManagerGlobal mWindowManagerGlobal;
+ private StartingSurface.SysuiProxy mSysuiProxy;
/**
* @param splashScreenExecutor The thread used to control add and remove starting window.
@@ -151,6 +152,11 @@
: activityInfo.getThemeResource() != 0 ? activityInfo.getThemeResource()
: com.android.internal.R.style.Theme_DeviceDefault_DayNight;
}
+
+ void setSysuiProxy(StartingSurface.SysuiProxy sysuiProxy) {
+ mSysuiProxy = sysuiProxy;
+ }
+
/**
* Called when a task need a splash screen starting window.
*
@@ -317,6 +323,9 @@
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
};
+ if (mSysuiProxy != null) {
+ mSysuiProxy.requestTopUi(true, TAG);
+ }
mSplashscreenContentDrawer.createContentView(context, suggestType, activityInfo, taskId,
viewSupplier::setView);
try {
@@ -573,6 +582,9 @@
}
private void removeWindowInner(View decorView, boolean hideView) {
+ if (mSysuiProxy != null) {
+ mSysuiProxy.requestTopUi(false, TAG);
+ }
if (hideView) {
decorView.setVisibility(View.GONE);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
index e84d498..a5c47c4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
@@ -217,6 +217,11 @@
return color != Color.TRANSPARENT
? color : SplashscreenContentDrawer.getSystemBGColor();
}
+
+ @Override
+ public void setSysuiProxy(SysuiProxy proxy) {
+ mSplashScreenExecutor.execute(() -> mStartingSurfaceDrawer.setSysuiProxy(proxy));
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index 7d011e6..16fddb0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -343,7 +343,7 @@
static Rect getSystemBarInsets(Rect frame, InsetsState state) {
return state.calculateInsets(frame, WindowInsets.Type.systemBars(),
- false /* ignoreVisibility */);
+ false /* ignoreVisibility */).toRect();
}
private void drawSnapshot() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index 1529f5b..7a17bb3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -129,7 +129,7 @@
}
/**
- * Expands the pip window and dismisses it by clicking on the X button.
+ * Taps the pip window and dismisses it by clicking on the X button.
*/
fun closePipWindow(wmHelper: WindowManagerStateHelper) {
if (isTelevision) {
@@ -137,9 +137,12 @@
} else {
val windowRect = getWindowRect(wmHelper)
uiDevice.click(windowRect.centerX(), windowRect.centerY())
- val exitPipObject = uiDevice.findObject(By.res(SYSTEMUI_PACKAGE, "dismiss"))
+ // search and interact with the dismiss button
+ val dismissSelector = By.res(SYSTEMUI_PACKAGE, "dismiss")
+ uiDevice.wait(Until.hasObject(dismissSelector), FIND_TIMEOUT)
+ val dismissPipObject = uiDevice.findObject(dismissSelector)
?: error("PIP window dismiss button not found")
- val dismissButtonBounds = exitPipObject.visibleBounds
+ val dismissButtonBounds = dismissPipObject.visibleBounds
uiDevice.click(dismissButtonBounds.centerX(), dismissButtonBounds.centerY())
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
index 508e939..18e0cc9e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
@@ -25,7 +25,7 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.navBarWindowIsVisible
@@ -50,7 +50,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
+@Group4
class EnterSplitScreenDockActivity(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
index 12f3909..6080912 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
@@ -23,6 +23,7 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
@@ -43,6 +44,7 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@Group4
class EnterSplitScreenFromDetachedRecentTask(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
index ac85c48..1dfc59e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
@@ -23,7 +23,7 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
@@ -49,7 +49,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
+@Group4
class EnterSplitScreenLaunchToSide(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
index 964af23..a278704 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
@@ -23,7 +23,7 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.canSplitScreen
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
@@ -51,7 +51,7 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@Group1
+@Group4
class EnterSplitScreenNotSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
index 7b4b71b..5fb6f1f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
@@ -104,7 +104,7 @@
@Presubmit
@Test
- fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.endRotation)
+ fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
index ec0c73a..a7ac6a7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
@@ -28,7 +28,6 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.appPairsDividerBecomesVisible
@@ -77,7 +76,7 @@
@Presubmit
@Test
- fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
+ fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index d7f71a8..cd15051 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -129,7 +129,7 @@
fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
@Test
- fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.endRotation)
+ fun entireScreenCovered() = testSpec.entireScreenCovered()
@Test
fun navBarLayerRotatesAndScales() =
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index f84908e..046972d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.pip
-import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -105,7 +104,7 @@
* Checks that the pip app layer remains inside the display bounds throughout the whole
* animation
*/
- @Postsubmit
+ @Presubmit
@Test
fun pipLayerRemainInsideVisibleBounds() {
testSpec.assertLayers {
@@ -116,7 +115,7 @@
/**
* Checks that the visible region of [pipApp] always reduces during the animation
*/
- @Postsubmit
+ @Presubmit
@Test
fun pipLayerReduces() {
val layerName = pipApp.component.toLayerName()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index 38f403b..097ccb8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.pip
-import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -119,18 +118,19 @@
* Checks that the [WindowManagerStateHelper.STATUS_BAR_COMPONENT] has the correct position at
* the start and end of the transition
*/
- @Postsubmit
+ @Presubmit
@Test
override fun statusBarLayerRotatesScales() =
testSpec.statusBarLayerRotatesScales(Surface.ROTATION_90, Surface.ROTATION_0)
/**
* Checks that all parts of the screen are covered at the start and end of the transition
+ *
+ * TODO b/197726599 Prevents all states from being checked
*/
@Presubmit
@Test
- override fun entireScreenCovered() =
- testSpec.entireScreenCovered(Surface.ROTATION_90, Surface.ROTATION_0, allStates = false)
+ override fun entireScreenCovered() = testSpec.entireScreenCovered(allStates = false)
/**
* Checks [pipApp] window remains visible and on top throughout the transition
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
index 596c92a..fa100b5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.pip
-import android.platform.test.annotations.Postsubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -26,7 +25,6 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.traces.parser.toWindowName
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -78,18 +76,6 @@
}
}
- @Postsubmit
- @Test
- override fun pipAppCoversFullScreenAtEnd() = super.pipAppCoversFullScreenAtEnd()
-
- @Postsubmit
- @Test
- override fun showBothAppLayersThenHidePip() = super.showBothAppLayersThenHidePip()
-
- @Postsubmit
- @Test
- override fun showBothAppWindowsThenHidePip() = super.showBothAppWindowsThenHidePip()
-
companion object {
/**
* Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index c525d46..89b2c40 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -80,7 +80,7 @@
* Checks that the pip app layer remains inside the display bounds throughout the whole
* animation
*/
- @Postsubmit
+ @Presubmit
@Test
fun pipLayerRemainInsideVisibleBounds() {
testSpec.assertLayers {
@@ -91,6 +91,7 @@
/**
* Checks [pipApp] window remains visible throughout the animation
*/
+ @Postsubmit
@Test
fun pipWindowIsAlwaysVisible() {
testSpec.assertWm {
@@ -101,7 +102,7 @@
/**
* Checks [pipApp] layer remains visible throughout the animation
*/
- @Postsubmit
+ @Presubmit
@Test
fun pipLayerIsAlwaysVisible() {
testSpec.assertLayers {
@@ -112,7 +113,7 @@
/**
* Checks that the visible region of [pipApp] always expands during the animation
*/
- @Postsubmit
+ @Presubmit
@Test
fun pipLayerExpands() {
val layerName = pipApp.component.toLayerName()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
index 934255f..ed04fc9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.pip
-import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.server.wm.flicker.FlickerTestParameter
@@ -43,7 +42,7 @@
/**
* Checks [pipApp] window remains visible throughout the animation
*/
- @Postsubmit
+ @Presubmit
@Test
open fun pipWindowIsAlwaysVisible() {
testSpec.assertWm {
@@ -54,7 +53,7 @@
/**
* Checks [pipApp] layer remains visible throughout the animation
*/
- @Postsubmit
+ @Presubmit
@Test
open fun pipLayerIsAlwaysVisible() {
testSpec.assertLayers {
@@ -66,7 +65,7 @@
* Checks that the pip app window remains inside the display bounds throughout the whole
* animation
*/
- @Postsubmit
+ @Presubmit
@Test
open fun pipWindowRemainInsideVisibleBounds() {
testSpec.assertWm {
@@ -78,7 +77,7 @@
* Checks that the pip app layer remains inside the display bounds throughout the whole
* animation
*/
- @Postsubmit
+ @Presubmit
@Test
open fun pipLayerRemainInsideVisibleBounds() {
testSpec.assertLayers {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index 5719413..9dad336 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -22,7 +22,7 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group3
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
@@ -43,7 +43,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group3
+@Group4
class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
private val imeApp = ImeAppHelper(instrumentation)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index 050beb3..d6030401 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -23,17 +23,19 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group3
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.wm.shell.flicker.helpers.ImeAppHelper
-import com.android.wm.shell.flicker.helpers.FixedAppHelper
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled
+import com.android.wm.shell.flicker.helpers.FixedAppHelper
+import com.android.wm.shell.flicker.helpers.ImeAppHelper
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Test
@@ -49,13 +51,15 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group3
+@Group4
class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
private val imeApp = ImeAppHelper(instrumentation)
private val testApp = FixedAppHelper(instrumentation)
@Before
open fun setup() {
+ // Only run legacy split tests when the system is using legacy split screen.
+ assumeTrue(SplitScreenHelper.isUsingLegacySplit())
// Legacy split is having some issue with Shell transition, and will be deprecated soon.
assumeFalse(isShellTransitionsEnabled())
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index 7ea7d5f..08d5209 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -23,7 +23,7 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group3
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.entireScreenCovered
@@ -62,7 +62,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group3
+@Group4
class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
private val fixedApp = FixedAppHelper(instrumentation)
private val screenBoundsStart = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
@@ -88,8 +88,7 @@
*/
@Presubmit
@Test
- override fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
- testSpec.config.endRotation, allStates = false)
+ override fun entireScreenCovered() = testSpec.entireScreenCovered()
/**
* Checks the position of the navigation bar at the start and end of the transition
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index ca80d18..ce89fb6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -187,6 +187,5 @@
@Presubmit
@Test
- open fun entireScreenCovered() =
- testSpec.entireScreenCovered(testSpec.config.startRotation, Surface.ROTATION_0)
+ open fun entireScreenCovered() = testSpec.entireScreenCovered()
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index e7b6197..d6dbc36 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -22,7 +22,7 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group3
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
@@ -43,7 +43,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group3
+@Group4
class SetRequestedOrientationWhilePinnedTest(
testSpec: FlickerTestParameter
) : PipTransition(testSpec) {
@@ -144,9 +144,7 @@
@FlakyTest
@Test
- override fun entireScreenCovered() {
- super.entireScreenCovered()
- }
+ override fun entireScreenCovered() = super.entireScreenCovered()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java
index 698315a..c456c7d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java
@@ -22,6 +22,7 @@
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.view.InsetsState;
import android.view.SurfaceControl;
import androidx.test.annotation.UiThreadTest;
@@ -59,7 +60,7 @@
@Test
@UiThreadTest
public void testInitRelease() {
- mSplitWindowManager.init(mSplitLayout);
+ mSplitWindowManager.init(mSplitLayout, new InsetsState());
assertThat(mSplitWindowManager.getSurfaceControl()).isNotNull();
mSplitWindowManager.release();
assertThat(mSplitWindowManager.getSurfaceControl()).isNull();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index b0a39d6..736566e5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -33,6 +33,7 @@
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
@@ -65,10 +66,12 @@
TestStageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
MainStage mainStage, SideStage sideStage, DisplayImeController imeController,
- SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool,
+ DisplayInsetsController insetsController, SplitLayout splitLayout,
+ Transitions transitions, TransactionPool transactionPool,
SplitscreenEventLogger logger) {
super(context, displayId, syncQueue, rootTDAOrganizer, taskOrganizer, mainStage,
- sideStage, imeController, splitLayout, transitions, transactionPool, logger);
+ sideStage, imeController, insetsController, splitLayout, transitions,
+ transactionPool, logger);
// Prepare default TaskDisplayArea for testing.
mDisplayAreaInfo = new DisplayAreaInfo(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index cb759dc..a53d2e8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -58,6 +58,7 @@
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
@@ -79,6 +80,7 @@
@Mock private SyncTransactionQueue mSyncQueue;
@Mock private RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
@Mock private DisplayImeController mDisplayImeController;
+ @Mock private DisplayInsetsController mDisplayInsetsController;
@Mock private TransactionPool mTransactionPool;
@Mock private Transitions mTransitions;
@Mock private SurfaceSession mSurfaceSession;
@@ -107,9 +109,10 @@
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession);
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
- mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
- mDisplayImeController, mSplitLayout, mTransitions, mTransactionPool,
- mLogger);
+ mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
+ mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions,
+ mTransactionPool,
+ mLogger);
mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class))
.when(mTransitions).startTransition(anyInt(), any(), any());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index a4b76fb..6cce0ab 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -37,6 +37,7 @@
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.transition.Transitions;
@@ -57,6 +58,7 @@
@Mock private MainStage mMainStage;
@Mock private SideStage mSideStage;
@Mock private DisplayImeController mDisplayImeController;
+ @Mock private DisplayInsetsController mDisplayInsetsController;
@Mock private Transitions mTransitions;
@Mock private TransactionPool mTransactionPool;
@Mock private SplitscreenEventLogger mLogger;
@@ -67,8 +69,8 @@
MockitoAnnotations.initMocks(this);
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
- mDisplayImeController, null /* splitLayout */, mTransitions, mTransactionPool,
- mLogger);
+ mDisplayImeController, mDisplayInsetsController, null /* splitLayout */,
+ mTransitions, mTransactionPool, mLogger);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 54eacee..b4ef5bf 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -731,24 +731,12 @@
IWindowManager mockWM = mock(IWindowManager.class);
final IDisplayWindowListener[] displayListener = new IDisplayWindowListener[1];
try {
- doAnswer(new Answer() {
- @Override
- public Object answer(InvocationOnMock invocation) {
- displayListener[0] = invocation.getArgument(0);
- return null;
- }
- }).when(mockWM).registerDisplayWindowListener(any());
+ doReturn(new int[] {DEFAULT_DISPLAY}).when(mockWM).registerDisplayWindowListener(any());
} catch (RemoteException e) {
// No remote stuff happening, so this can't be hit
}
DisplayController out = new DisplayController(mContext, mockWM, mMainExecutor);
out.initialize();
- try {
- displayListener[0].onDisplayAdded(DEFAULT_DISPLAY);
- mMainExecutor.flushAll();
- } catch (RemoteException e) {
- // Again, no remote stuff
- }
return out;
}
diff --git a/libs/androidfw/LocaleDataTables.cpp b/libs/androidfw/LocaleDataTables.cpp
index 8a10599..2c3567a 100644
--- a/libs/androidfw/LocaleDataTables.cpp
+++ b/libs/androidfw/LocaleDataTables.cpp
@@ -1,37 +1,37 @@
// Auto-generated by ./tools/localedata/extract_icu_data.py
const char SCRIPT_CODES[][4] = {
- /* 0 */ {'A', 'h', 'o', 'm'},
- /* 1 */ {'A', 'r', 'a', 'b'},
- /* 2 */ {'A', 'r', 'm', 'i'},
- /* 3 */ {'A', 'r', 'm', 'n'},
- /* 4 */ {'A', 'v', 's', 't'},
- /* 5 */ {'B', 'a', 'm', 'u'},
- /* 6 */ {'B', 'a', 's', 's'},
- /* 7 */ {'B', 'e', 'n', 'g'},
- /* 8 */ {'B', 'r', 'a', 'h'},
- /* 9 */ {'C', 'a', 'k', 'm'},
- /* 10 */ {'C', 'a', 'n', 's'},
- /* 11 */ {'C', 'a', 'r', 'i'},
- /* 12 */ {'C', 'h', 'a', 'm'},
- /* 13 */ {'C', 'h', 'e', 'r'},
- /* 14 */ {'C', 'h', 'r', 's'},
- /* 15 */ {'C', 'o', 'p', 't'},
- /* 16 */ {'C', 'p', 'r', 't'},
- /* 17 */ {'C', 'y', 'r', 'l'},
- /* 18 */ {'D', 'e', 'v', 'a'},
- /* 19 */ {'E', 'g', 'y', 'p'},
- /* 20 */ {'E', 't', 'h', 'i'},
- /* 21 */ {'G', 'e', 'o', 'r'},
- /* 22 */ {'G', 'o', 'n', 'g'},
- /* 23 */ {'G', 'o', 'n', 'm'},
- /* 24 */ {'G', 'o', 't', 'h'},
- /* 25 */ {'G', 'r', 'e', 'k'},
- /* 26 */ {'G', 'u', 'j', 'r'},
- /* 27 */ {'G', 'u', 'r', 'u'},
- /* 28 */ {'H', 'a', 'n', 's'},
- /* 29 */ {'H', 'a', 'n', 't'},
- /* 30 */ {'H', 'a', 't', 'r'},
+ /* 0 */ {'A', 'g', 'h', 'b'},
+ /* 1 */ {'A', 'h', 'o', 'm'},
+ /* 2 */ {'A', 'r', 'a', 'b'},
+ /* 3 */ {'A', 'r', 'm', 'i'},
+ /* 4 */ {'A', 'r', 'm', 'n'},
+ /* 5 */ {'A', 'v', 's', 't'},
+ /* 6 */ {'B', 'a', 'm', 'u'},
+ /* 7 */ {'B', 'a', 's', 's'},
+ /* 8 */ {'B', 'e', 'n', 'g'},
+ /* 9 */ {'B', 'r', 'a', 'h'},
+ /* 10 */ {'C', 'a', 'k', 'm'},
+ /* 11 */ {'C', 'a', 'n', 's'},
+ /* 12 */ {'C', 'a', 'r', 'i'},
+ /* 13 */ {'C', 'h', 'a', 'm'},
+ /* 14 */ {'C', 'h', 'e', 'r'},
+ /* 15 */ {'C', 'h', 'r', 's'},
+ /* 16 */ {'C', 'o', 'p', 't'},
+ /* 17 */ {'C', 'p', 'r', 't'},
+ /* 18 */ {'C', 'y', 'r', 'l'},
+ /* 19 */ {'D', 'e', 'v', 'a'},
+ /* 20 */ {'E', 'g', 'y', 'p'},
+ /* 21 */ {'E', 't', 'h', 'i'},
+ /* 22 */ {'G', 'e', 'o', 'r'},
+ /* 23 */ {'G', 'o', 'n', 'g'},
+ /* 24 */ {'G', 'o', 'n', 'm'},
+ /* 25 */ {'G', 'o', 't', 'h'},
+ /* 26 */ {'G', 'r', 'e', 'k'},
+ /* 27 */ {'G', 'u', 'j', 'r'},
+ /* 28 */ {'G', 'u', 'r', 'u'},
+ /* 29 */ {'H', 'a', 'n', 's'},
+ /* 30 */ {'H', 'a', 'n', 't'},
/* 31 */ {'H', 'e', 'b', 'r'},
/* 32 */ {'H', 'l', 'u', 'w'},
/* 33 */ {'H', 'm', 'n', 'g'},
@@ -55,52 +55,53 @@
/* 51 */ {'L', 'y', 'd', 'i'},
/* 52 */ {'M', 'a', 'n', 'd'},
/* 53 */ {'M', 'a', 'n', 'i'},
- /* 54 */ {'M', 'e', 'r', 'c'},
- /* 55 */ {'M', 'l', 'y', 'm'},
- /* 56 */ {'M', 'o', 'n', 'g'},
- /* 57 */ {'M', 'r', 'o', 'o'},
- /* 58 */ {'M', 'y', 'm', 'r'},
- /* 59 */ {'N', 'a', 'r', 'b'},
- /* 60 */ {'N', 'k', 'o', 'o'},
- /* 61 */ {'N', 's', 'h', 'u'},
- /* 62 */ {'O', 'g', 'a', 'm'},
- /* 63 */ {'O', 'l', 'c', 'k'},
- /* 64 */ {'O', 'r', 'k', 'h'},
- /* 65 */ {'O', 'r', 'y', 'a'},
- /* 66 */ {'O', 's', 'g', 'e'},
- /* 67 */ {'P', 'a', 'u', 'c'},
- /* 68 */ {'P', 'h', 'l', 'i'},
- /* 69 */ {'P', 'h', 'n', 'x'},
- /* 70 */ {'P', 'l', 'r', 'd'},
- /* 71 */ {'P', 'r', 't', 'i'},
- /* 72 */ {'R', 'u', 'n', 'r'},
- /* 73 */ {'S', 'a', 'm', 'r'},
- /* 74 */ {'S', 'a', 'r', 'b'},
- /* 75 */ {'S', 'a', 'u', 'r'},
- /* 76 */ {'S', 'g', 'n', 'w'},
- /* 77 */ {'S', 'i', 'n', 'h'},
- /* 78 */ {'S', 'o', 'g', 'd'},
- /* 79 */ {'S', 'o', 'r', 'a'},
- /* 80 */ {'S', 'o', 'y', 'o'},
- /* 81 */ {'S', 'y', 'r', 'c'},
- /* 82 */ {'T', 'a', 'l', 'e'},
- /* 83 */ {'T', 'a', 'l', 'u'},
- /* 84 */ {'T', 'a', 'm', 'l'},
- /* 85 */ {'T', 'a', 'n', 'g'},
- /* 86 */ {'T', 'a', 'v', 't'},
- /* 87 */ {'T', 'e', 'l', 'u'},
- /* 88 */ {'T', 'f', 'n', 'g'},
- /* 89 */ {'T', 'h', 'a', 'a'},
- /* 90 */ {'T', 'h', 'a', 'i'},
- /* 91 */ {'T', 'i', 'b', 't'},
- /* 92 */ {'U', 'g', 'a', 'r'},
- /* 93 */ {'V', 'a', 'i', 'i'},
- /* 94 */ {'W', 'c', 'h', 'o'},
- /* 95 */ {'X', 'p', 'e', 'o'},
- /* 96 */ {'X', 's', 'u', 'x'},
- /* 97 */ {'Y', 'i', 'i', 'i'},
- /* 98 */ {'~', '~', '~', 'A'},
- /* 99 */ {'~', '~', '~', 'B'},
+ /* 54 */ {'M', 'e', 'd', 'f'},
+ /* 55 */ {'M', 'e', 'r', 'c'},
+ /* 56 */ {'M', 'l', 'y', 'm'},
+ /* 57 */ {'M', 'o', 'n', 'g'},
+ /* 58 */ {'M', 'r', 'o', 'o'},
+ /* 59 */ {'M', 'y', 'm', 'r'},
+ /* 60 */ {'N', 'a', 'r', 'b'},
+ /* 61 */ {'N', 'k', 'o', 'o'},
+ /* 62 */ {'N', 's', 'h', 'u'},
+ /* 63 */ {'O', 'g', 'a', 'm'},
+ /* 64 */ {'O', 'l', 'c', 'k'},
+ /* 65 */ {'O', 'r', 'k', 'h'},
+ /* 66 */ {'O', 'r', 'y', 'a'},
+ /* 67 */ {'O', 's', 'g', 'e'},
+ /* 68 */ {'P', 'a', 'u', 'c'},
+ /* 69 */ {'P', 'h', 'l', 'i'},
+ /* 70 */ {'P', 'h', 'n', 'x'},
+ /* 71 */ {'P', 'l', 'r', 'd'},
+ /* 72 */ {'P', 'r', 't', 'i'},
+ /* 73 */ {'R', 'u', 'n', 'r'},
+ /* 74 */ {'S', 'a', 'm', 'r'},
+ /* 75 */ {'S', 'a', 'r', 'b'},
+ /* 76 */ {'S', 'a', 'u', 'r'},
+ /* 77 */ {'S', 'g', 'n', 'w'},
+ /* 78 */ {'S', 'i', 'n', 'h'},
+ /* 79 */ {'S', 'o', 'g', 'd'},
+ /* 80 */ {'S', 'o', 'r', 'a'},
+ /* 81 */ {'S', 'o', 'y', 'o'},
+ /* 82 */ {'S', 'y', 'r', 'c'},
+ /* 83 */ {'T', 'a', 'l', 'e'},
+ /* 84 */ {'T', 'a', 'l', 'u'},
+ /* 85 */ {'T', 'a', 'm', 'l'},
+ /* 86 */ {'T', 'a', 'n', 'g'},
+ /* 87 */ {'T', 'a', 'v', 't'},
+ /* 88 */ {'T', 'e', 'l', 'u'},
+ /* 89 */ {'T', 'f', 'n', 'g'},
+ /* 90 */ {'T', 'h', 'a', 'a'},
+ /* 91 */ {'T', 'h', 'a', 'i'},
+ /* 92 */ {'T', 'i', 'b', 't'},
+ /* 93 */ {'U', 'g', 'a', 'r'},
+ /* 94 */ {'V', 'a', 'i', 'i'},
+ /* 95 */ {'W', 'c', 'h', 'o'},
+ /* 96 */ {'X', 'p', 'e', 'o'},
+ /* 97 */ {'X', 's', 'u', 'x'},
+ /* 98 */ {'Y', 'i', 'i', 'i'},
+ /* 99 */ {'~', '~', '~', 'A'},
+ /* 100 */ {'~', '~', '~', 'B'},
};
@@ -109,9 +110,9 @@
{0xA0000000u, 46u}, // aai -> Latn
{0xA8000000u, 46u}, // aak -> Latn
{0xD0000000u, 46u}, // aau -> Latn
- {0x61620000u, 17u}, // ab -> Cyrl
+ {0x61620000u, 18u}, // ab -> Cyrl
{0xA0200000u, 46u}, // abi -> Latn
- {0xC0200000u, 17u}, // abq -> Cyrl
+ {0xC0200000u, 18u}, // abq -> Cyrl
{0xC4200000u, 46u}, // abr -> Latn
{0xCC200000u, 46u}, // abt -> Latn
{0xE0200000u, 46u}, // aby -> Latn
@@ -121,11 +122,11 @@
{0x80600000u, 46u}, // ada -> Latn
{0x90600000u, 46u}, // ade -> Latn
{0xA4600000u, 46u}, // adj -> Latn
- {0xBC600000u, 91u}, // adp -> Tibt
- {0xE0600000u, 17u}, // ady -> Cyrl
+ {0xBC600000u, 92u}, // adp -> Tibt
+ {0xE0600000u, 18u}, // ady -> Cyrl
{0xE4600000u, 46u}, // adz -> Latn
- {0x61650000u, 4u}, // ae -> Avst
- {0x84800000u, 1u}, // aeb -> Arab
+ {0x61650000u, 5u}, // ae -> Avst
+ {0x84800000u, 2u}, // aeb -> Arab
{0xE0800000u, 46u}, // aey -> Latn
{0x61660000u, 46u}, // af -> Latn
{0x88C00000u, 46u}, // agc -> Latn
@@ -136,15 +137,15 @@
{0xC0C00000u, 46u}, // agq -> Latn
{0x80E00000u, 46u}, // aha -> Latn
{0xACE00000u, 46u}, // ahl -> Latn
- {0xB8E00000u, 0u}, // aho -> Ahom
+ {0xB8E00000u, 1u}, // aho -> Ahom
{0x99200000u, 46u}, // ajg -> Latn
{0x616B0000u, 46u}, // ak -> Latn
- {0xA9400000u, 96u}, // akk -> Xsux
+ {0xA9400000u, 97u}, // akk -> Xsux
{0x81600000u, 46u}, // ala -> Latn
{0xA1600000u, 46u}, // ali -> Latn
{0xB5600000u, 46u}, // aln -> Latn
- {0xCD600000u, 17u}, // alt -> Cyrl
- {0x616D0000u, 20u}, // am -> Ethi
+ {0xCD600000u, 18u}, // alt -> Cyrl
+ {0x616D0000u, 21u}, // am -> Ethi
{0xB1800000u, 46u}, // amm -> Latn
{0xB5800000u, 46u}, // amn -> Latn
{0xB9800000u, 46u}, // amo -> Latn
@@ -157,25 +158,25 @@
{0xA5C00000u, 46u}, // aoj -> Latn
{0xB1C00000u, 46u}, // aom -> Latn
{0xE5C00000u, 46u}, // aoz -> Latn
- {0x89E00000u, 1u}, // apc -> Arab
- {0x8DE00000u, 1u}, // apd -> Arab
+ {0x89E00000u, 2u}, // apc -> Arab
+ {0x8DE00000u, 2u}, // apd -> Arab
{0x91E00000u, 46u}, // ape -> Latn
{0xC5E00000u, 46u}, // apr -> Latn
{0xC9E00000u, 46u}, // aps -> Latn
{0xE5E00000u, 46u}, // apz -> Latn
- {0x61720000u, 1u}, // ar -> Arab
- {0x61725842u, 99u}, // ar-XB -> ~~~B
- {0x8A200000u, 2u}, // arc -> Armi
+ {0x61720000u, 2u}, // ar -> Arab
+ {0x61725842u, 100u}, // ar-XB -> ~~~B
+ {0x8A200000u, 3u}, // arc -> Armi
{0x9E200000u, 46u}, // arh -> Latn
{0xB6200000u, 46u}, // arn -> Latn
{0xBA200000u, 46u}, // aro -> Latn
- {0xC2200000u, 1u}, // arq -> Arab
- {0xCA200000u, 1u}, // ars -> Arab
- {0xE2200000u, 1u}, // ary -> Arab
- {0xE6200000u, 1u}, // arz -> Arab
- {0x61730000u, 7u}, // as -> Beng
+ {0xC2200000u, 2u}, // arq -> Arab
+ {0xCA200000u, 2u}, // ars -> Arab
+ {0xE2200000u, 2u}, // ary -> Arab
+ {0xE6200000u, 2u}, // arz -> Arab
+ {0x61730000u, 8u}, // as -> Beng
{0x82400000u, 46u}, // asa -> Latn
- {0x92400000u, 76u}, // ase -> Sgnw
+ {0x92400000u, 77u}, // ase -> Sgnw
{0x9A400000u, 46u}, // asg -> Latn
{0xBA400000u, 46u}, // aso -> Latn
{0xCE400000u, 46u}, // ast -> Latn
@@ -183,29 +184,29 @@
{0x9A600000u, 46u}, // atg -> Latn
{0xA6600000u, 46u}, // atj -> Latn
{0xE2800000u, 46u}, // auy -> Latn
- {0x61760000u, 17u}, // av -> Cyrl
- {0xAEA00000u, 1u}, // avl -> Arab
+ {0x61760000u, 18u}, // av -> Cyrl
+ {0xAEA00000u, 2u}, // avl -> Arab
{0xB6A00000u, 46u}, // avn -> Latn
{0xCEA00000u, 46u}, // avt -> Latn
{0xD2A00000u, 46u}, // avu -> Latn
- {0x82C00000u, 18u}, // awa -> Deva
+ {0x82C00000u, 19u}, // awa -> Deva
{0x86C00000u, 46u}, // awb -> Latn
{0xBAC00000u, 46u}, // awo -> Latn
{0xDEC00000u, 46u}, // awx -> Latn
{0x61790000u, 46u}, // ay -> Latn
{0x87000000u, 46u}, // ayb -> Latn
{0x617A0000u, 46u}, // az -> Latn
- {0x617A4951u, 1u}, // az-IQ -> Arab
- {0x617A4952u, 1u}, // az-IR -> Arab
- {0x617A5255u, 17u}, // az-RU -> Cyrl
- {0x62610000u, 17u}, // ba -> Cyrl
- {0xAC010000u, 1u}, // bal -> Arab
+ {0x617A4951u, 2u}, // az-IQ -> Arab
+ {0x617A4952u, 2u}, // az-IR -> Arab
+ {0x617A5255u, 18u}, // az-RU -> Cyrl
+ {0x62610000u, 18u}, // ba -> Cyrl
+ {0xAC010000u, 2u}, // bal -> Arab
{0xB4010000u, 46u}, // ban -> Latn
- {0xBC010000u, 18u}, // bap -> Deva
+ {0xBC010000u, 19u}, // bap -> Deva
{0xC4010000u, 46u}, // bar -> Latn
{0xC8010000u, 46u}, // bas -> Latn
{0xD4010000u, 46u}, // bav -> Latn
- {0xDC010000u, 5u}, // bax -> Bamu
+ {0xDC010000u, 6u}, // bax -> Bamu
{0x80210000u, 46u}, // bba -> Latn
{0x84210000u, 46u}, // bbb -> Latn
{0x88210000u, 46u}, // bbc -> Latn
@@ -219,31 +220,31 @@
{0xB0410000u, 46u}, // bcm -> Latn
{0xB4410000u, 46u}, // bcn -> Latn
{0xB8410000u, 46u}, // bco -> Latn
- {0xC0410000u, 20u}, // bcq -> Ethi
+ {0xC0410000u, 21u}, // bcq -> Ethi
{0xD0410000u, 46u}, // bcu -> Latn
{0x8C610000u, 46u}, // bdd -> Latn
- {0x62650000u, 17u}, // be -> Cyrl
+ {0x62650000u, 18u}, // be -> Cyrl
{0x94810000u, 46u}, // bef -> Latn
{0x9C810000u, 46u}, // beh -> Latn
- {0xA4810000u, 1u}, // bej -> Arab
+ {0xA4810000u, 2u}, // bej -> Arab
{0xB0810000u, 46u}, // bem -> Latn
{0xCC810000u, 46u}, // bet -> Latn
{0xD8810000u, 46u}, // bew -> Latn
{0xDC810000u, 46u}, // bex -> Latn
{0xE4810000u, 46u}, // bez -> Latn
{0x8CA10000u, 46u}, // bfd -> Latn
- {0xC0A10000u, 84u}, // bfq -> Taml
- {0xCCA10000u, 1u}, // bft -> Arab
- {0xE0A10000u, 18u}, // bfy -> Deva
- {0x62670000u, 17u}, // bg -> Cyrl
- {0x88C10000u, 18u}, // bgc -> Deva
- {0xB4C10000u, 1u}, // bgn -> Arab
- {0xDCC10000u, 25u}, // bgx -> Grek
- {0x84E10000u, 18u}, // bhb -> Deva
+ {0xC0A10000u, 85u}, // bfq -> Taml
+ {0xCCA10000u, 2u}, // bft -> Arab
+ {0xE0A10000u, 19u}, // bfy -> Deva
+ {0x62670000u, 18u}, // bg -> Cyrl
+ {0x88C10000u, 19u}, // bgc -> Deva
+ {0xB4C10000u, 2u}, // bgn -> Arab
+ {0xDCC10000u, 26u}, // bgx -> Grek
+ {0x84E10000u, 19u}, // bhb -> Deva
{0x98E10000u, 46u}, // bhg -> Latn
- {0xA0E10000u, 18u}, // bhi -> Deva
+ {0xA0E10000u, 19u}, // bhi -> Deva
{0xACE10000u, 46u}, // bhl -> Latn
- {0xB8E10000u, 18u}, // bho -> Deva
+ {0xB8E10000u, 19u}, // bho -> Deva
{0xE0E10000u, 46u}, // bhy -> Latn
{0x62690000u, 46u}, // bi -> Latn
{0x85010000u, 46u}, // bib -> Latn
@@ -254,8 +255,8 @@
{0xB9010000u, 46u}, // bio -> Latn
{0xC1010000u, 46u}, // biq -> Latn
{0x9D210000u, 46u}, // bjh -> Latn
- {0xA1210000u, 20u}, // bji -> Ethi
- {0xA5210000u, 18u}, // bjj -> Deva
+ {0xA1210000u, 21u}, // bji -> Ethi
+ {0xA5210000u, 19u}, // bjj -> Deva
{0xB5210000u, 46u}, // bjn -> Latn
{0xB9210000u, 46u}, // bjo -> Latn
{0xC5210000u, 46u}, // bjr -> Latn
@@ -266,39 +267,39 @@
{0xC1410000u, 46u}, // bkq -> Latn
{0xD1410000u, 46u}, // bku -> Latn
{0xD5410000u, 46u}, // bkv -> Latn
- {0xCD610000u, 86u}, // blt -> Tavt
+ {0xCD610000u, 87u}, // blt -> Tavt
{0x626D0000u, 46u}, // bm -> Latn
{0x9D810000u, 46u}, // bmh -> Latn
{0xA9810000u, 46u}, // bmk -> Latn
{0xC1810000u, 46u}, // bmq -> Latn
{0xD1810000u, 46u}, // bmu -> Latn
- {0x626E0000u, 7u}, // bn -> Beng
+ {0x626E0000u, 8u}, // bn -> Beng
{0x99A10000u, 46u}, // bng -> Latn
{0xB1A10000u, 46u}, // bnm -> Latn
{0xBDA10000u, 46u}, // bnp -> Latn
- {0x626F0000u, 91u}, // bo -> Tibt
+ {0x626F0000u, 92u}, // bo -> Tibt
{0xA5C10000u, 46u}, // boj -> Latn
{0xB1C10000u, 46u}, // bom -> Latn
{0xB5C10000u, 46u}, // bon -> Latn
- {0xE1E10000u, 7u}, // bpy -> Beng
+ {0xE1E10000u, 8u}, // bpy -> Beng
{0x8A010000u, 46u}, // bqc -> Latn
- {0xA2010000u, 1u}, // bqi -> Arab
+ {0xA2010000u, 2u}, // bqi -> Arab
{0xBE010000u, 46u}, // bqp -> Latn
{0xD6010000u, 46u}, // bqv -> Latn
{0x62720000u, 46u}, // br -> Latn
- {0x82210000u, 18u}, // bra -> Deva
- {0x9E210000u, 1u}, // brh -> Arab
- {0xDE210000u, 18u}, // brx -> Deva
+ {0x82210000u, 19u}, // bra -> Deva
+ {0x9E210000u, 2u}, // brh -> Arab
+ {0xDE210000u, 19u}, // brx -> Deva
{0xE6210000u, 46u}, // brz -> Latn
{0x62730000u, 46u}, // bs -> Latn
{0xA6410000u, 46u}, // bsj -> Latn
- {0xC2410000u, 6u}, // bsq -> Bass
+ {0xC2410000u, 7u}, // bsq -> Bass
{0xCA410000u, 46u}, // bss -> Latn
- {0xCE410000u, 20u}, // bst -> Ethi
+ {0xCE410000u, 21u}, // bst -> Ethi
{0xBA610000u, 46u}, // bto -> Latn
{0xCE610000u, 46u}, // btt -> Latn
- {0xD6610000u, 18u}, // btv -> Deva
- {0x82810000u, 17u}, // bua -> Cyrl
+ {0xD6610000u, 19u}, // btv -> Deva
+ {0x82810000u, 18u}, // bua -> Cyrl
{0x8A810000u, 46u}, // buc -> Latn
{0x8E810000u, 46u}, // bud -> Latn
{0x9A810000u, 46u}, // bug -> Latn
@@ -312,7 +313,7 @@
{0xC6C10000u, 46u}, // bwr -> Latn
{0x9EE10000u, 46u}, // bxh -> Latn
{0x93010000u, 46u}, // bye -> Latn
- {0xB7010000u, 20u}, // byn -> Ethi
+ {0xB7010000u, 21u}, // byn -> Ethi
{0xC7010000u, 46u}, // byr -> Latn
{0xCB010000u, 46u}, // bys -> Latn
{0xD7010000u, 46u}, // byv -> Latn
@@ -327,44 +328,44 @@
{0xB4020000u, 46u}, // can -> Latn
{0xA4220000u, 46u}, // cbj -> Latn
{0x9C420000u, 46u}, // cch -> Latn
- {0xBC420000u, 9u}, // ccp -> Cakm
- {0x63650000u, 17u}, // ce -> Cyrl
+ {0xBC420000u, 10u}, // ccp -> Cakm
+ {0x63650000u, 18u}, // ce -> Cyrl
{0x84820000u, 46u}, // ceb -> Latn
{0x80A20000u, 46u}, // cfa -> Latn
{0x98C20000u, 46u}, // cgg -> Latn
{0x63680000u, 46u}, // ch -> Latn
{0xA8E20000u, 46u}, // chk -> Latn
- {0xB0E20000u, 17u}, // chm -> Cyrl
+ {0xB0E20000u, 18u}, // chm -> Cyrl
{0xB8E20000u, 46u}, // cho -> Latn
{0xBCE20000u, 46u}, // chp -> Latn
- {0xC4E20000u, 13u}, // chr -> Cher
+ {0xC4E20000u, 14u}, // chr -> Cher
{0x89020000u, 46u}, // cic -> Latn
- {0x81220000u, 1u}, // cja -> Arab
- {0xB1220000u, 12u}, // cjm -> Cham
+ {0x81220000u, 2u}, // cja -> Arab
+ {0xB1220000u, 13u}, // cjm -> Cham
{0xD5220000u, 46u}, // cjv -> Latn
- {0x85420000u, 1u}, // ckb -> Arab
+ {0x85420000u, 2u}, // ckb -> Arab
{0xAD420000u, 46u}, // ckl -> Latn
{0xB9420000u, 46u}, // cko -> Latn
{0xE1420000u, 46u}, // cky -> Latn
{0x81620000u, 46u}, // cla -> Latn
{0x91820000u, 46u}, // cme -> Latn
- {0x99820000u, 80u}, // cmg -> Soyo
+ {0x99820000u, 81u}, // cmg -> Soyo
{0x636F0000u, 46u}, // co -> Latn
- {0xBDC20000u, 15u}, // cop -> Copt
+ {0xBDC20000u, 16u}, // cop -> Copt
{0xC9E20000u, 46u}, // cps -> Latn
- {0x63720000u, 10u}, // cr -> Cans
- {0x9E220000u, 17u}, // crh -> Cyrl
- {0xA6220000u, 10u}, // crj -> Cans
- {0xAA220000u, 10u}, // crk -> Cans
- {0xAE220000u, 10u}, // crl -> Cans
- {0xB2220000u, 10u}, // crm -> Cans
+ {0x63720000u, 11u}, // cr -> Cans
+ {0x9E220000u, 18u}, // crh -> Cyrl
+ {0xA6220000u, 11u}, // crj -> Cans
+ {0xAA220000u, 11u}, // crk -> Cans
+ {0xAE220000u, 11u}, // crl -> Cans
+ {0xB2220000u, 11u}, // crm -> Cans
{0xCA220000u, 46u}, // crs -> Latn
{0x63730000u, 46u}, // cs -> Latn
{0x86420000u, 46u}, // csb -> Latn
- {0xDA420000u, 10u}, // csw -> Cans
- {0x8E620000u, 67u}, // ctd -> Pauc
- {0x63750000u, 17u}, // cu -> Cyrl
- {0x63760000u, 17u}, // cv -> Cyrl
+ {0xDA420000u, 11u}, // csw -> Cans
+ {0x8E620000u, 68u}, // ctd -> Pauc
+ {0x63750000u, 18u}, // cu -> Cyrl
+ {0x63760000u, 18u}, // cv -> Cyrl
{0x63790000u, 46u}, // cy -> Latn
{0x64610000u, 46u}, // da -> Latn
{0x8C030000u, 46u}, // dad -> Latn
@@ -372,11 +373,11 @@
{0x98030000u, 46u}, // dag -> Latn
{0x9C030000u, 46u}, // dah -> Latn
{0xA8030000u, 46u}, // dak -> Latn
- {0xC4030000u, 17u}, // dar -> Cyrl
+ {0xC4030000u, 18u}, // dar -> Cyrl
{0xD4030000u, 46u}, // dav -> Latn
{0x8C230000u, 46u}, // dbd -> Latn
{0xC0230000u, 46u}, // dbq -> Latn
- {0x88430000u, 1u}, // dcc -> Arab
+ {0x88430000u, 2u}, // dcc -> Arab
{0xB4630000u, 46u}, // ddn -> Latn
{0x64650000u, 46u}, // de -> Latn
{0x8C830000u, 46u}, // ded -> Latn
@@ -384,53 +385,54 @@
{0x80C30000u, 46u}, // dga -> Latn
{0x9CC30000u, 46u}, // dgh -> Latn
{0xA0C30000u, 46u}, // dgi -> Latn
- {0xACC30000u, 1u}, // dgl -> Arab
+ {0xACC30000u, 2u}, // dgl -> Arab
{0xC4C30000u, 46u}, // dgr -> Latn
{0xE4C30000u, 46u}, // dgz -> Latn
{0x81030000u, 46u}, // dia -> Latn
{0x91230000u, 46u}, // dje -> Latn
+ {0x95830000u, 54u}, // dmf -> Medf
{0xA5A30000u, 46u}, // dnj -> Latn
{0x85C30000u, 46u}, // dob -> Latn
- {0xA1C30000u, 18u}, // doi -> Deva
+ {0xA1C30000u, 19u}, // doi -> Deva
{0xBDC30000u, 46u}, // dop -> Latn
{0xD9C30000u, 46u}, // dow -> Latn
- {0x9E230000u, 56u}, // drh -> Mong
+ {0x9E230000u, 57u}, // drh -> Mong
{0xA2230000u, 46u}, // dri -> Latn
- {0xCA230000u, 20u}, // drs -> Ethi
+ {0xCA230000u, 21u}, // drs -> Ethi
{0x86430000u, 46u}, // dsb -> Latn
{0xB2630000u, 46u}, // dtm -> Latn
{0xBE630000u, 46u}, // dtp -> Latn
{0xCA630000u, 46u}, // dts -> Latn
- {0xE2630000u, 18u}, // dty -> Deva
+ {0xE2630000u, 19u}, // dty -> Deva
{0x82830000u, 46u}, // dua -> Latn
{0x8A830000u, 46u}, // duc -> Latn
{0x8E830000u, 46u}, // dud -> Latn
{0x9A830000u, 46u}, // dug -> Latn
- {0x64760000u, 89u}, // dv -> Thaa
+ {0x64760000u, 90u}, // dv -> Thaa
{0x82A30000u, 46u}, // dva -> Latn
{0xDAC30000u, 46u}, // dww -> Latn
{0xBB030000u, 46u}, // dyo -> Latn
{0xD3030000u, 46u}, // dyu -> Latn
- {0x647A0000u, 91u}, // dz -> Tibt
+ {0x647A0000u, 92u}, // dz -> Tibt
{0x9B230000u, 46u}, // dzg -> Latn
{0xD0240000u, 46u}, // ebu -> Latn
{0x65650000u, 46u}, // ee -> Latn
{0xA0A40000u, 46u}, // efi -> Latn
{0xACC40000u, 46u}, // egl -> Latn
- {0xE0C40000u, 19u}, // egy -> Egyp
+ {0xE0C40000u, 20u}, // egy -> Egyp
{0x81440000u, 46u}, // eka -> Latn
{0xE1440000u, 37u}, // eky -> Kali
- {0x656C0000u, 25u}, // el -> Grek
+ {0x656C0000u, 26u}, // el -> Grek
{0x81840000u, 46u}, // ema -> Latn
{0xA1840000u, 46u}, // emi -> Latn
{0x656E0000u, 46u}, // en -> Latn
- {0x656E5841u, 98u}, // en-XA -> ~~~A
+ {0x656E5841u, 99u}, // en-XA -> ~~~A
{0xB5A40000u, 46u}, // enn -> Latn
{0xC1A40000u, 46u}, // enq -> Latn
{0x656F0000u, 46u}, // eo -> Latn
{0xA2240000u, 46u}, // eri -> Latn
{0x65730000u, 46u}, // es -> Latn
- {0x9A440000u, 23u}, // esg -> Gonm
+ {0x9A440000u, 24u}, // esg -> Gonm
{0xD2440000u, 46u}, // esu -> Latn
{0x65740000u, 46u}, // et -> Latn
{0xC6640000u, 46u}, // etr -> Latn
@@ -441,7 +443,7 @@
{0xBAC40000u, 46u}, // ewo -> Latn
{0xCEE40000u, 46u}, // ext -> Latn
{0x83240000u, 46u}, // eza -> Latn
- {0x66610000u, 1u}, // fa -> Arab
+ {0x66610000u, 2u}, // fa -> Arab
{0x80050000u, 46u}, // faa -> Latn
{0x84050000u, 46u}, // fab -> Latn
{0x98050000u, 46u}, // fag -> Latn
@@ -451,7 +453,7 @@
{0xA0A50000u, 46u}, // ffi -> Latn
{0xB0A50000u, 46u}, // ffm -> Latn
{0x66690000u, 46u}, // fi -> Latn
- {0x81050000u, 1u}, // fia -> Arab
+ {0x81050000u, 2u}, // fia -> Arab
{0xAD050000u, 46u}, // fil -> Latn
{0xCD050000u, 46u}, // fit -> Latn
{0x666A0000u, 46u}, // fj -> Latn
@@ -468,7 +470,7 @@
{0xBE250000u, 46u}, // frp -> Latn
{0xC6250000u, 46u}, // frr -> Latn
{0xCA250000u, 46u}, // frs -> Latn
- {0x86850000u, 1u}, // fub -> Arab
+ {0x86850000u, 2u}, // fub -> Arab
{0x8E850000u, 46u}, // fud -> Latn
{0x92850000u, 46u}, // fue -> Latn
{0x96850000u, 46u}, // fuf -> Latn
@@ -486,14 +488,14 @@
{0x9C060000u, 46u}, // gah -> Latn
{0xA4060000u, 46u}, // gaj -> Latn
{0xB0060000u, 46u}, // gam -> Latn
- {0xB4060000u, 28u}, // gan -> Hans
+ {0xB4060000u, 29u}, // gan -> Hans
{0xD8060000u, 46u}, // gaw -> Latn
{0xE0060000u, 46u}, // gay -> Latn
{0x80260000u, 46u}, // gba -> Latn
{0x94260000u, 46u}, // gbf -> Latn
- {0xB0260000u, 18u}, // gbm -> Deva
+ {0xB0260000u, 19u}, // gbm -> Deva
{0xE0260000u, 46u}, // gby -> Latn
- {0xE4260000u, 1u}, // gbz -> Arab
+ {0xE4260000u, 2u}, // gbz -> Arab
{0xC4460000u, 46u}, // gcr -> Latn
{0x67640000u, 46u}, // gd -> Latn
{0x90660000u, 46u}, // gde -> Latn
@@ -502,38 +504,38 @@
{0x84860000u, 46u}, // geb -> Latn
{0xA4860000u, 46u}, // gej -> Latn
{0xAC860000u, 46u}, // gel -> Latn
- {0xE4860000u, 20u}, // gez -> Ethi
+ {0xE4860000u, 21u}, // gez -> Ethi
{0xA8A60000u, 46u}, // gfk -> Latn
- {0xB4C60000u, 18u}, // ggn -> Deva
+ {0xB4C60000u, 19u}, // ggn -> Deva
{0xC8E60000u, 46u}, // ghs -> Latn
{0xAD060000u, 46u}, // gil -> Latn
{0xB1060000u, 46u}, // gim -> Latn
- {0xA9260000u, 1u}, // gjk -> Arab
+ {0xA9260000u, 2u}, // gjk -> Arab
{0xB5260000u, 46u}, // gjn -> Latn
- {0xD1260000u, 1u}, // gju -> Arab
+ {0xD1260000u, 2u}, // gju -> Arab
{0xB5460000u, 46u}, // gkn -> Latn
{0xBD460000u, 46u}, // gkp -> Latn
{0x676C0000u, 46u}, // gl -> Latn
- {0xA9660000u, 1u}, // glk -> Arab
+ {0xA9660000u, 2u}, // glk -> Arab
{0xB1860000u, 46u}, // gmm -> Latn
- {0xD5860000u, 20u}, // gmv -> Ethi
+ {0xD5860000u, 21u}, // gmv -> Ethi
{0x676E0000u, 46u}, // gn -> Latn
{0x8DA60000u, 46u}, // gnd -> Latn
{0x99A60000u, 46u}, // gng -> Latn
{0x8DC60000u, 46u}, // god -> Latn
- {0x95C60000u, 20u}, // gof -> Ethi
+ {0x95C60000u, 21u}, // gof -> Ethi
{0xA1C60000u, 46u}, // goi -> Latn
- {0xB1C60000u, 18u}, // gom -> Deva
- {0xB5C60000u, 87u}, // gon -> Telu
+ {0xB1C60000u, 19u}, // gom -> Deva
+ {0xB5C60000u, 88u}, // gon -> Telu
{0xC5C60000u, 46u}, // gor -> Latn
{0xC9C60000u, 46u}, // gos -> Latn
- {0xCDC60000u, 24u}, // got -> Goth
+ {0xCDC60000u, 25u}, // got -> Goth
{0x86260000u, 46u}, // grb -> Latn
- {0x8A260000u, 16u}, // grc -> Cprt
- {0xCE260000u, 7u}, // grt -> Beng
+ {0x8A260000u, 17u}, // grc -> Cprt
+ {0xCE260000u, 8u}, // grt -> Beng
{0xDA260000u, 46u}, // grw -> Latn
{0xDA460000u, 46u}, // gsw -> Latn
- {0x67750000u, 26u}, // gu -> Gujr
+ {0x67750000u, 27u}, // gu -> Gujr
{0x86860000u, 46u}, // gub -> Latn
{0x8A860000u, 46u}, // guc -> Latn
{0x8E860000u, 46u}, // gud -> Latn
@@ -543,25 +545,25 @@
{0xE6860000u, 46u}, // guz -> Latn
{0x67760000u, 46u}, // gv -> Latn
{0x96A60000u, 46u}, // gvf -> Latn
- {0xC6A60000u, 18u}, // gvr -> Deva
+ {0xC6A60000u, 19u}, // gvr -> Deva
{0xCAA60000u, 46u}, // gvs -> Latn
- {0x8AC60000u, 1u}, // gwc -> Arab
+ {0x8AC60000u, 2u}, // gwc -> Arab
{0xA2C60000u, 46u}, // gwi -> Latn
- {0xCEC60000u, 1u}, // gwt -> Arab
+ {0xCEC60000u, 2u}, // gwt -> Arab
{0xA3060000u, 46u}, // gyi -> Latn
{0x68610000u, 46u}, // ha -> Latn
- {0x6861434Du, 1u}, // ha-CM -> Arab
- {0x68615344u, 1u}, // ha-SD -> Arab
+ {0x6861434Du, 2u}, // ha-CM -> Arab
+ {0x68615344u, 2u}, // ha-SD -> Arab
{0x98070000u, 46u}, // hag -> Latn
- {0xA8070000u, 28u}, // hak -> Hans
+ {0xA8070000u, 29u}, // hak -> Hans
{0xB0070000u, 46u}, // ham -> Latn
{0xD8070000u, 46u}, // haw -> Latn
- {0xE4070000u, 1u}, // haz -> Arab
+ {0xE4070000u, 2u}, // haz -> Arab
{0x84270000u, 46u}, // hbb -> Latn
- {0xE0670000u, 20u}, // hdy -> Ethi
+ {0xE0670000u, 21u}, // hdy -> Ethi
{0x68650000u, 31u}, // he -> Hebr
{0xE0E70000u, 46u}, // hhy -> Latn
- {0x68690000u, 18u}, // hi -> Deva
+ {0x68690000u, 19u}, // hi -> Deva
{0x81070000u, 46u}, // hia -> Latn
{0x95070000u, 46u}, // hif -> Latn
{0x99070000u, 46u}, // hig -> Latn
@@ -569,24 +571,24 @@
{0xAD070000u, 46u}, // hil -> Latn
{0x81670000u, 46u}, // hla -> Latn
{0xD1670000u, 32u}, // hlu -> Hluw
- {0x8D870000u, 70u}, // hmd -> Plrd
+ {0x8D870000u, 71u}, // hmd -> Plrd
{0xCD870000u, 46u}, // hmt -> Latn
- {0x8DA70000u, 1u}, // hnd -> Arab
- {0x91A70000u, 18u}, // hne -> Deva
+ {0x8DA70000u, 2u}, // hnd -> Arab
+ {0x91A70000u, 19u}, // hne -> Deva
{0xA5A70000u, 33u}, // hnj -> Hmng
{0xB5A70000u, 46u}, // hnn -> Latn
- {0xB9A70000u, 1u}, // hno -> Arab
+ {0xB9A70000u, 2u}, // hno -> Arab
{0x686F0000u, 46u}, // ho -> Latn
- {0x89C70000u, 18u}, // hoc -> Deva
- {0xA5C70000u, 18u}, // hoj -> Deva
+ {0x89C70000u, 19u}, // hoc -> Deva
+ {0xA5C70000u, 19u}, // hoj -> Deva
{0xCDC70000u, 46u}, // hot -> Latn
{0x68720000u, 46u}, // hr -> Latn
{0x86470000u, 46u}, // hsb -> Latn
- {0xB6470000u, 28u}, // hsn -> Hans
+ {0xB6470000u, 29u}, // hsn -> Hans
{0x68740000u, 46u}, // ht -> Latn
{0x68750000u, 46u}, // hu -> Latn
{0xA2870000u, 46u}, // hui -> Latn
- {0x68790000u, 3u}, // hy -> Armn
+ {0x68790000u, 4u}, // hy -> Armn
{0x687A0000u, 46u}, // hz -> Latn
{0x69610000u, 46u}, // ia -> Latn
{0xB4080000u, 46u}, // ian -> Latn
@@ -604,7 +606,7 @@
{0x69670000u, 46u}, // ig -> Latn
{0x84C80000u, 46u}, // igb -> Latn
{0x90C80000u, 46u}, // ige -> Latn
- {0x69690000u, 97u}, // ii -> Yiii
+ {0x69690000u, 98u}, // ii -> Yiii
{0xA5280000u, 46u}, // ijj -> Latn
{0x696B0000u, 46u}, // ik -> Latn
{0xA9480000u, 46u}, // ikk -> Latn
@@ -614,13 +616,13 @@
{0xB9680000u, 46u}, // ilo -> Latn
{0xB9880000u, 46u}, // imo -> Latn
{0x696E0000u, 46u}, // in -> Latn
- {0x9DA80000u, 17u}, // inh -> Cyrl
+ {0x9DA80000u, 18u}, // inh -> Cyrl
{0x696F0000u, 46u}, // io -> Latn
{0xD1C80000u, 46u}, // iou -> Latn
{0xA2280000u, 46u}, // iri -> Latn
{0x69730000u, 46u}, // is -> Latn
{0x69740000u, 46u}, // it -> Latn
- {0x69750000u, 10u}, // iu -> Cans
+ {0x69750000u, 11u}, // iu -> Cans
{0x69770000u, 31u}, // iw -> Hebr
{0xB2C80000u, 46u}, // iwm -> Latn
{0xCAC80000u, 46u}, // iws -> Latn
@@ -638,13 +640,13 @@
{0x6A690000u, 31u}, // ji -> Hebr
{0x85090000u, 46u}, // jib -> Latn
{0x89890000u, 46u}, // jmc -> Latn
- {0xAD890000u, 18u}, // jml -> Deva
+ {0xAD890000u, 19u}, // jml -> Deva
{0x82290000u, 46u}, // jra -> Latn
{0xCE890000u, 46u}, // jut -> Latn
{0x6A760000u, 46u}, // jv -> Latn
{0x6A770000u, 46u}, // jw -> Latn
- {0x6B610000u, 21u}, // ka -> Geor
- {0x800A0000u, 17u}, // kaa -> Cyrl
+ {0x6B610000u, 22u}, // ka -> Geor
+ {0x800A0000u, 18u}, // kaa -> Cyrl
{0x840A0000u, 46u}, // kab -> Latn
{0x880A0000u, 46u}, // kac -> Latn
{0x8C0A0000u, 46u}, // kad -> Latn
@@ -652,37 +654,37 @@
{0xA40A0000u, 46u}, // kaj -> Latn
{0xB00A0000u, 46u}, // kam -> Latn
{0xB80A0000u, 46u}, // kao -> Latn
- {0x8C2A0000u, 17u}, // kbd -> Cyrl
+ {0x8C2A0000u, 18u}, // kbd -> Cyrl
{0xB02A0000u, 46u}, // kbm -> Latn
{0xBC2A0000u, 46u}, // kbp -> Latn
{0xC02A0000u, 46u}, // kbq -> Latn
{0xDC2A0000u, 46u}, // kbx -> Latn
- {0xE02A0000u, 1u}, // kby -> Arab
+ {0xE02A0000u, 2u}, // kby -> Arab
{0x984A0000u, 46u}, // kcg -> Latn
{0xA84A0000u, 46u}, // kck -> Latn
{0xAC4A0000u, 46u}, // kcl -> Latn
{0xCC4A0000u, 46u}, // kct -> Latn
{0x906A0000u, 46u}, // kde -> Latn
- {0x9C6A0000u, 1u}, // kdh -> Arab
+ {0x9C6A0000u, 2u}, // kdh -> Arab
{0xAC6A0000u, 46u}, // kdl -> Latn
- {0xCC6A0000u, 90u}, // kdt -> Thai
+ {0xCC6A0000u, 91u}, // kdt -> Thai
{0x808A0000u, 46u}, // kea -> Latn
{0xB48A0000u, 46u}, // ken -> Latn
{0xE48A0000u, 46u}, // kez -> Latn
{0xB8AA0000u, 46u}, // kfo -> Latn
- {0xC4AA0000u, 18u}, // kfr -> Deva
- {0xE0AA0000u, 18u}, // kfy -> Deva
+ {0xC4AA0000u, 19u}, // kfr -> Deva
+ {0xE0AA0000u, 19u}, // kfy -> Deva
{0x6B670000u, 46u}, // kg -> Latn
{0x90CA0000u, 46u}, // kge -> Latn
{0x94CA0000u, 46u}, // kgf -> Latn
{0xBCCA0000u, 46u}, // kgp -> Latn
{0x80EA0000u, 46u}, // kha -> Latn
- {0x84EA0000u, 83u}, // khb -> Talu
- {0xB4EA0000u, 18u}, // khn -> Deva
+ {0x84EA0000u, 84u}, // khb -> Talu
+ {0xB4EA0000u, 19u}, // khn -> Deva
{0xC0EA0000u, 46u}, // khq -> Latn
{0xC8EA0000u, 46u}, // khs -> Latn
- {0xCCEA0000u, 58u}, // kht -> Mymr
- {0xD8EA0000u, 1u}, // khw -> Arab
+ {0xCCEA0000u, 59u}, // kht -> Mymr
+ {0xD8EA0000u, 2u}, // khw -> Arab
{0xE4EA0000u, 46u}, // khz -> Latn
{0x6B690000u, 46u}, // ki -> Latn
{0xA50A0000u, 46u}, // kij -> Latn
@@ -693,11 +695,11 @@
{0x992A0000u, 45u}, // kjg -> Laoo
{0xC92A0000u, 46u}, // kjs -> Latn
{0xE12A0000u, 46u}, // kjy -> Latn
- {0x6B6B0000u, 17u}, // kk -> Cyrl
- {0x6B6B4146u, 1u}, // kk-AF -> Arab
- {0x6B6B434Eu, 1u}, // kk-CN -> Arab
- {0x6B6B4952u, 1u}, // kk-IR -> Arab
- {0x6B6B4D4Eu, 1u}, // kk-MN -> Arab
+ {0x6B6B0000u, 18u}, // kk -> Cyrl
+ {0x6B6B4146u, 2u}, // kk-AF -> Arab
+ {0x6B6B434Eu, 2u}, // kk-CN -> Arab
+ {0x6B6B4952u, 2u}, // kk-IR -> Arab
+ {0x6B6B4D4Eu, 2u}, // kk-MN -> Arab
{0x894A0000u, 46u}, // kkc -> Latn
{0xA54A0000u, 46u}, // kkj -> Latn
{0x6B6C0000u, 46u}, // kl -> Latn
@@ -716,8 +718,8 @@
{0x95AA0000u, 46u}, // knf -> Latn
{0xBDAA0000u, 46u}, // knp -> Latn
{0x6B6F0000u, 43u}, // ko -> Kore
- {0xA1CA0000u, 17u}, // koi -> Cyrl
- {0xA9CA0000u, 18u}, // kok -> Deva
+ {0xA1CA0000u, 18u}, // koi -> Cyrl
+ {0xA9CA0000u, 19u}, // kok -> Deva
{0xADCA0000u, 46u}, // kol -> Latn
{0xC9CA0000u, 46u}, // kos -> Latn
{0xE5CA0000u, 46u}, // koz -> Latn
@@ -729,58 +731,58 @@
{0x860A0000u, 46u}, // kqb -> Latn
{0x960A0000u, 46u}, // kqf -> Latn
{0xCA0A0000u, 46u}, // kqs -> Latn
- {0xE20A0000u, 20u}, // kqy -> Ethi
+ {0xE20A0000u, 21u}, // kqy -> Ethi
{0x6B720000u, 46u}, // kr -> Latn
- {0x8A2A0000u, 17u}, // krc -> Cyrl
+ {0x8A2A0000u, 18u}, // krc -> Cyrl
{0xA22A0000u, 46u}, // kri -> Latn
{0xA62A0000u, 46u}, // krj -> Latn
{0xAE2A0000u, 46u}, // krl -> Latn
{0xCA2A0000u, 46u}, // krs -> Latn
- {0xD22A0000u, 18u}, // kru -> Deva
- {0x6B730000u, 1u}, // ks -> Arab
+ {0xD22A0000u, 19u}, // kru -> Deva
+ {0x6B730000u, 2u}, // ks -> Arab
{0x864A0000u, 46u}, // ksb -> Latn
{0x8E4A0000u, 46u}, // ksd -> Latn
{0x964A0000u, 46u}, // ksf -> Latn
{0x9E4A0000u, 46u}, // ksh -> Latn
{0xA64A0000u, 46u}, // ksj -> Latn
{0xC64A0000u, 46u}, // ksr -> Latn
- {0x866A0000u, 20u}, // ktb -> Ethi
+ {0x866A0000u, 21u}, // ktb -> Ethi
{0xB26A0000u, 46u}, // ktm -> Latn
{0xBA6A0000u, 46u}, // kto -> Latn
{0xC66A0000u, 46u}, // ktr -> Latn
{0x6B750000u, 46u}, // ku -> Latn
- {0x6B754952u, 1u}, // ku-IR -> Arab
- {0x6B754C42u, 1u}, // ku-LB -> Arab
+ {0x6B754952u, 2u}, // ku-IR -> Arab
+ {0x6B754C42u, 2u}, // ku-LB -> Arab
{0x868A0000u, 46u}, // kub -> Latn
{0x8E8A0000u, 46u}, // kud -> Latn
{0x928A0000u, 46u}, // kue -> Latn
{0xA68A0000u, 46u}, // kuj -> Latn
- {0xB28A0000u, 17u}, // kum -> Cyrl
+ {0xB28A0000u, 18u}, // kum -> Cyrl
{0xB68A0000u, 46u}, // kun -> Latn
{0xBE8A0000u, 46u}, // kup -> Latn
{0xCA8A0000u, 46u}, // kus -> Latn
- {0x6B760000u, 17u}, // kv -> Cyrl
+ {0x6B760000u, 18u}, // kv -> Cyrl
{0x9AAA0000u, 46u}, // kvg -> Latn
{0xC6AA0000u, 46u}, // kvr -> Latn
- {0xDEAA0000u, 1u}, // kvx -> Arab
+ {0xDEAA0000u, 2u}, // kvx -> Arab
{0x6B770000u, 46u}, // kw -> Latn
{0xA6CA0000u, 46u}, // kwj -> Latn
{0xBACA0000u, 46u}, // kwo -> Latn
{0xC2CA0000u, 46u}, // kwq -> Latn
{0x82EA0000u, 46u}, // kxa -> Latn
- {0x8AEA0000u, 20u}, // kxc -> Ethi
+ {0x8AEA0000u, 21u}, // kxc -> Ethi
{0x92EA0000u, 46u}, // kxe -> Latn
- {0xAEEA0000u, 18u}, // kxl -> Deva
- {0xB2EA0000u, 90u}, // kxm -> Thai
- {0xBEEA0000u, 1u}, // kxp -> Arab
+ {0xAEEA0000u, 19u}, // kxl -> Deva
+ {0xB2EA0000u, 91u}, // kxm -> Thai
+ {0xBEEA0000u, 2u}, // kxp -> Arab
{0xDAEA0000u, 46u}, // kxw -> Latn
{0xE6EA0000u, 46u}, // kxz -> Latn
- {0x6B790000u, 17u}, // ky -> Cyrl
- {0x6B79434Eu, 1u}, // ky-CN -> Arab
+ {0x6B790000u, 18u}, // ky -> Cyrl
+ {0x6B79434Eu, 2u}, // ky-CN -> Arab
{0x6B795452u, 46u}, // ky-TR -> Latn
{0x930A0000u, 46u}, // kye -> Latn
{0xDF0A0000u, 46u}, // kyx -> Latn
- {0x9F2A0000u, 1u}, // kzh -> Arab
+ {0x9F2A0000u, 2u}, // kzh -> Arab
{0xA72A0000u, 46u}, // kzj -> Latn
{0xC72A0000u, 46u}, // kzr -> Latn
{0xCF2A0000u, 46u}, // kzt -> Latn
@@ -788,15 +790,15 @@
{0x840B0000u, 48u}, // lab -> Lina
{0x8C0B0000u, 31u}, // lad -> Hebr
{0x980B0000u, 46u}, // lag -> Latn
- {0x9C0B0000u, 1u}, // lah -> Arab
+ {0x9C0B0000u, 2u}, // lah -> Arab
{0xA40B0000u, 46u}, // laj -> Latn
{0xC80B0000u, 46u}, // las -> Latn
{0x6C620000u, 46u}, // lb -> Latn
- {0x902B0000u, 17u}, // lbe -> Cyrl
+ {0x902B0000u, 18u}, // lbe -> Cyrl
{0xD02B0000u, 46u}, // lbu -> Latn
{0xD82B0000u, 46u}, // lbw -> Latn
{0xB04B0000u, 46u}, // lcm -> Latn
- {0xBC4B0000u, 90u}, // lcp -> Thai
+ {0xBC4B0000u, 91u}, // lcp -> Thai
{0x846B0000u, 46u}, // ldb -> Latn
{0x8C8B0000u, 46u}, // led -> Latn
{0x908B0000u, 46u}, // lee -> Latn
@@ -804,23 +806,23 @@
{0xBC8B0000u, 47u}, // lep -> Lepc
{0xC08B0000u, 46u}, // leq -> Latn
{0xD08B0000u, 46u}, // leu -> Latn
- {0xE48B0000u, 17u}, // lez -> Cyrl
+ {0xE48B0000u, 18u}, // lez -> Cyrl
{0x6C670000u, 46u}, // lg -> Latn
{0x98CB0000u, 46u}, // lgg -> Latn
{0x6C690000u, 46u}, // li -> Latn
{0x810B0000u, 46u}, // lia -> Latn
{0x8D0B0000u, 46u}, // lid -> Latn
- {0x950B0000u, 18u}, // lif -> Deva
+ {0x950B0000u, 19u}, // lif -> Deva
{0x990B0000u, 46u}, // lig -> Latn
{0x9D0B0000u, 46u}, // lih -> Latn
{0xA50B0000u, 46u}, // lij -> Latn
{0xC90B0000u, 49u}, // lis -> Lisu
{0xBD2B0000u, 46u}, // ljp -> Latn
- {0xA14B0000u, 1u}, // lki -> Arab
+ {0xA14B0000u, 2u}, // lki -> Arab
{0xCD4B0000u, 46u}, // lkt -> Latn
{0x916B0000u, 46u}, // lle -> Latn
{0xB56B0000u, 46u}, // lln -> Latn
- {0xB58B0000u, 87u}, // lmn -> Telu
+ {0xB58B0000u, 88u}, // lmn -> Telu
{0xB98B0000u, 46u}, // lmo -> Latn
{0xBD8B0000u, 46u}, // lmp -> Latn
{0x6C6E0000u, 46u}, // ln -> Latn
@@ -833,25 +835,25 @@
{0xC5CB0000u, 46u}, // lor -> Latn
{0xC9CB0000u, 46u}, // los -> Latn
{0xE5CB0000u, 46u}, // loz -> Latn
- {0x8A2B0000u, 1u}, // lrc -> Arab
+ {0x8A2B0000u, 2u}, // lrc -> Arab
{0x6C740000u, 46u}, // lt -> Latn
{0x9A6B0000u, 46u}, // ltg -> Latn
{0x6C750000u, 46u}, // lu -> Latn
{0x828B0000u, 46u}, // lua -> Latn
{0xBA8B0000u, 46u}, // luo -> Latn
{0xE28B0000u, 46u}, // luy -> Latn
- {0xE68B0000u, 1u}, // luz -> Arab
+ {0xE68B0000u, 2u}, // luz -> Arab
{0x6C760000u, 46u}, // lv -> Latn
- {0xAECB0000u, 90u}, // lwl -> Thai
- {0x9F2B0000u, 28u}, // lzh -> Hans
+ {0xAECB0000u, 91u}, // lwl -> Thai
+ {0x9F2B0000u, 29u}, // lzh -> Hans
{0xE72B0000u, 46u}, // lzz -> Latn
{0x8C0C0000u, 46u}, // mad -> Latn
{0x940C0000u, 46u}, // maf -> Latn
- {0x980C0000u, 18u}, // mag -> Deva
- {0xA00C0000u, 18u}, // mai -> Deva
+ {0x980C0000u, 19u}, // mag -> Deva
+ {0xA00C0000u, 19u}, // mai -> Deva
{0xA80C0000u, 46u}, // mak -> Latn
{0xB40C0000u, 46u}, // man -> Latn
- {0xB40C474Eu, 60u}, // man-GN -> Nkoo
+ {0xB40C474Eu, 61u}, // man-GN -> Nkoo
{0xC80C0000u, 46u}, // mas -> Latn
{0xD80C0000u, 46u}, // maw -> Latn
{0xE40C0000u, 46u}, // maz -> Latn
@@ -866,12 +868,12 @@
{0xC44C0000u, 46u}, // mcr -> Latn
{0xD04C0000u, 46u}, // mcu -> Latn
{0x806C0000u, 46u}, // mda -> Latn
- {0x906C0000u, 1u}, // mde -> Arab
- {0x946C0000u, 17u}, // mdf -> Cyrl
+ {0x906C0000u, 2u}, // mde -> Arab
+ {0x946C0000u, 18u}, // mdf -> Cyrl
{0x9C6C0000u, 46u}, // mdh -> Latn
{0xA46C0000u, 46u}, // mdj -> Latn
{0xC46C0000u, 46u}, // mdr -> Latn
- {0xDC6C0000u, 20u}, // mdx -> Ethi
+ {0xDC6C0000u, 21u}, // mdx -> Ethi
{0x8C8C0000u, 46u}, // med -> Latn
{0x908C0000u, 46u}, // mee -> Latn
{0xA88C0000u, 46u}, // mek -> Latn
@@ -879,7 +881,7 @@
{0xC48C0000u, 46u}, // mer -> Latn
{0xCC8C0000u, 46u}, // met -> Latn
{0xD08C0000u, 46u}, // meu -> Latn
- {0x80AC0000u, 1u}, // mfa -> Arab
+ {0x80AC0000u, 2u}, // mfa -> Arab
{0x90AC0000u, 46u}, // mfe -> Latn
{0xB4AC0000u, 46u}, // mfn -> Latn
{0xB8AC0000u, 46u}, // mfo -> Latn
@@ -888,7 +890,7 @@
{0x9CCC0000u, 46u}, // mgh -> Latn
{0xACCC0000u, 46u}, // mgl -> Latn
{0xB8CC0000u, 46u}, // mgo -> Latn
- {0xBCCC0000u, 18u}, // mgp -> Deva
+ {0xBCCC0000u, 19u}, // mgp -> Deva
{0xE0CC0000u, 46u}, // mgy -> Latn
{0x6D680000u, 46u}, // mh -> Latn
{0xA0EC0000u, 46u}, // mhi -> Latn
@@ -896,26 +898,25 @@
{0x6D690000u, 46u}, // mi -> Latn
{0x950C0000u, 46u}, // mif -> Latn
{0xB50C0000u, 46u}, // min -> Latn
- {0xC90C0000u, 30u}, // mis -> Hatr
{0xD90C0000u, 46u}, // miw -> Latn
- {0x6D6B0000u, 17u}, // mk -> Cyrl
- {0xA14C0000u, 1u}, // mki -> Arab
+ {0x6D6B0000u, 18u}, // mk -> Cyrl
+ {0xA14C0000u, 2u}, // mki -> Arab
{0xAD4C0000u, 46u}, // mkl -> Latn
{0xBD4C0000u, 46u}, // mkp -> Latn
{0xD94C0000u, 46u}, // mkw -> Latn
- {0x6D6C0000u, 55u}, // ml -> Mlym
+ {0x6D6C0000u, 56u}, // ml -> Mlym
{0x916C0000u, 46u}, // mle -> Latn
{0xBD6C0000u, 46u}, // mlp -> Latn
{0xC96C0000u, 46u}, // mls -> Latn
{0xB98C0000u, 46u}, // mmo -> Latn
{0xD18C0000u, 46u}, // mmu -> Latn
{0xDD8C0000u, 46u}, // mmx -> Latn
- {0x6D6E0000u, 17u}, // mn -> Cyrl
- {0x6D6E434Eu, 56u}, // mn-CN -> Mong
+ {0x6D6E0000u, 18u}, // mn -> Cyrl
+ {0x6D6E434Eu, 57u}, // mn-CN -> Mong
{0x81AC0000u, 46u}, // mna -> Latn
{0x95AC0000u, 46u}, // mnf -> Latn
- {0xA1AC0000u, 7u}, // mni -> Beng
- {0xD9AC0000u, 58u}, // mnw -> Mymr
+ {0xA1AC0000u, 8u}, // mni -> Beng
+ {0xD9AC0000u, 59u}, // mnw -> Mymr
{0x6D6F0000u, 46u}, // mo -> Latn
{0x81CC0000u, 46u}, // moa -> Latn
{0x91CC0000u, 46u}, // moe -> Latn
@@ -927,39 +928,39 @@
{0xCDEC0000u, 46u}, // mpt -> Latn
{0xDDEC0000u, 46u}, // mpx -> Latn
{0xAE0C0000u, 46u}, // mql -> Latn
- {0x6D720000u, 18u}, // mr -> Deva
- {0x8E2C0000u, 18u}, // mrd -> Deva
- {0xA62C0000u, 17u}, // mrj -> Cyrl
- {0xBA2C0000u, 57u}, // mro -> Mroo
+ {0x6D720000u, 19u}, // mr -> Deva
+ {0x8E2C0000u, 19u}, // mrd -> Deva
+ {0xA62C0000u, 18u}, // mrj -> Cyrl
+ {0xBA2C0000u, 58u}, // mro -> Mroo
{0x6D730000u, 46u}, // ms -> Latn
- {0x6D734343u, 1u}, // ms-CC -> Arab
+ {0x6D734343u, 2u}, // ms-CC -> Arab
{0x6D740000u, 46u}, // mt -> Latn
{0x8A6C0000u, 46u}, // mtc -> Latn
{0x966C0000u, 46u}, // mtf -> Latn
{0xA26C0000u, 46u}, // mti -> Latn
- {0xC66C0000u, 18u}, // mtr -> Deva
+ {0xC66C0000u, 19u}, // mtr -> Deva
{0x828C0000u, 46u}, // mua -> Latn
{0xC68C0000u, 46u}, // mur -> Latn
{0xCA8C0000u, 46u}, // mus -> Latn
{0x82AC0000u, 46u}, // mva -> Latn
{0xB6AC0000u, 46u}, // mvn -> Latn
- {0xE2AC0000u, 1u}, // mvy -> Arab
+ {0xE2AC0000u, 2u}, // mvy -> Arab
{0xAACC0000u, 46u}, // mwk -> Latn
- {0xC6CC0000u, 18u}, // mwr -> Deva
+ {0xC6CC0000u, 19u}, // mwr -> Deva
{0xD6CC0000u, 46u}, // mwv -> Latn
{0xDACC0000u, 34u}, // mww -> Hmnp
{0x8AEC0000u, 46u}, // mxc -> Latn
{0xB2EC0000u, 46u}, // mxm -> Latn
- {0x6D790000u, 58u}, // my -> Mymr
+ {0x6D790000u, 59u}, // my -> Mymr
{0xAB0C0000u, 46u}, // myk -> Latn
- {0xB30C0000u, 20u}, // mym -> Ethi
- {0xD70C0000u, 17u}, // myv -> Cyrl
+ {0xB30C0000u, 21u}, // mym -> Ethi
+ {0xD70C0000u, 18u}, // myv -> Cyrl
{0xDB0C0000u, 46u}, // myw -> Latn
{0xDF0C0000u, 46u}, // myx -> Latn
{0xE70C0000u, 52u}, // myz -> Mand
{0xAB2C0000u, 46u}, // mzk -> Latn
{0xB32C0000u, 46u}, // mzm -> Latn
- {0xB72C0000u, 1u}, // mzn -> Arab
+ {0xB72C0000u, 2u}, // mzn -> Arab
{0xBF2C0000u, 46u}, // mzp -> Latn
{0xDB2C0000u, 46u}, // mzw -> Latn
{0xE72C0000u, 46u}, // mzz -> Latn
@@ -967,7 +968,7 @@
{0x880D0000u, 46u}, // nac -> Latn
{0x940D0000u, 46u}, // naf -> Latn
{0xA80D0000u, 46u}, // nak -> Latn
- {0xB40D0000u, 28u}, // nan -> Hans
+ {0xB40D0000u, 29u}, // nan -> Hans
{0xBC0D0000u, 46u}, // nap -> Latn
{0xC00D0000u, 46u}, // naq -> Latn
{0xC80D0000u, 46u}, // nas -> Latn
@@ -981,9 +982,9 @@
{0x6E640000u, 46u}, // nd -> Latn
{0x886D0000u, 46u}, // ndc -> Latn
{0xC86D0000u, 46u}, // nds -> Latn
- {0x6E650000u, 18u}, // ne -> Deva
+ {0x6E650000u, 19u}, // ne -> Deva
{0x848D0000u, 46u}, // neb -> Latn
- {0xD88D0000u, 18u}, // new -> Deva
+ {0xD88D0000u, 19u}, // new -> Deva
{0xDC8D0000u, 46u}, // nex -> Latn
{0xC4AD0000u, 46u}, // nfr -> Latn
{0x6E670000u, 46u}, // ng -> Latn
@@ -1011,17 +1012,17 @@
{0x9DAD0000u, 46u}, // nnh -> Latn
{0xA9AD0000u, 46u}, // nnk -> Latn
{0xB1AD0000u, 46u}, // nnm -> Latn
- {0xBDAD0000u, 94u}, // nnp -> Wcho
+ {0xBDAD0000u, 95u}, // nnp -> Wcho
{0x6E6F0000u, 46u}, // no -> Latn
{0x8DCD0000u, 44u}, // nod -> Lana
- {0x91CD0000u, 18u}, // noe -> Deva
- {0xB5CD0000u, 72u}, // non -> Runr
+ {0x91CD0000u, 19u}, // noe -> Deva
+ {0xB5CD0000u, 73u}, // non -> Runr
{0xBDCD0000u, 46u}, // nop -> Latn
{0xD1CD0000u, 46u}, // nou -> Latn
- {0xBA0D0000u, 60u}, // nqo -> Nkoo
+ {0xBA0D0000u, 61u}, // nqo -> Nkoo
{0x6E720000u, 46u}, // nr -> Latn
{0x862D0000u, 46u}, // nrb -> Latn
- {0xAA4D0000u, 10u}, // nsk -> Cans
+ {0xAA4D0000u, 11u}, // nsk -> Cans
{0xB64D0000u, 46u}, // nsn -> Latn
{0xBA4D0000u, 46u}, // nso -> Latn
{0xCA4D0000u, 46u}, // nss -> Latn
@@ -1049,18 +1050,18 @@
{0xB5AE0000u, 46u}, // onn -> Latn
{0xC9AE0000u, 46u}, // ons -> Latn
{0xB1EE0000u, 46u}, // opm -> Latn
- {0x6F720000u, 65u}, // or -> Orya
+ {0x6F720000u, 66u}, // or -> Orya
{0xBA2E0000u, 46u}, // oro -> Latn
- {0xD22E0000u, 1u}, // oru -> Arab
- {0x6F730000u, 17u}, // os -> Cyrl
- {0x824E0000u, 66u}, // osa -> Osge
- {0x826E0000u, 1u}, // ota -> Arab
- {0xAA6E0000u, 64u}, // otk -> Orkh
+ {0xD22E0000u, 2u}, // oru -> Arab
+ {0x6F730000u, 18u}, // os -> Cyrl
+ {0x824E0000u, 67u}, // osa -> Osge
+ {0x826E0000u, 2u}, // ota -> Arab
+ {0xAA6E0000u, 65u}, // otk -> Orkh
{0xB32E0000u, 46u}, // ozm -> Latn
- {0x70610000u, 27u}, // pa -> Guru
- {0x7061504Bu, 1u}, // pa-PK -> Arab
+ {0x70610000u, 28u}, // pa -> Guru
+ {0x7061504Bu, 2u}, // pa-PK -> Arab
{0x980F0000u, 46u}, // pag -> Latn
- {0xAC0F0000u, 68u}, // pal -> Phli
+ {0xAC0F0000u, 69u}, // pal -> Phli
{0xB00F0000u, 46u}, // pam -> Latn
{0xBC0F0000u, 46u}, // pap -> Latn
{0xD00F0000u, 46u}, // pau -> Latn
@@ -1070,28 +1071,28 @@
{0x886F0000u, 46u}, // pdc -> Latn
{0xCC6F0000u, 46u}, // pdt -> Latn
{0x8C8F0000u, 46u}, // ped -> Latn
- {0xB88F0000u, 95u}, // peo -> Xpeo
+ {0xB88F0000u, 96u}, // peo -> Xpeo
{0xDC8F0000u, 46u}, // pex -> Latn
{0xACAF0000u, 46u}, // pfl -> Latn
- {0xACEF0000u, 1u}, // phl -> Arab
- {0xB4EF0000u, 69u}, // phn -> Phnx
+ {0xACEF0000u, 2u}, // phl -> Arab
+ {0xB4EF0000u, 70u}, // phn -> Phnx
{0xAD0F0000u, 46u}, // pil -> Latn
{0xBD0F0000u, 46u}, // pip -> Latn
- {0x814F0000u, 8u}, // pka -> Brah
+ {0x814F0000u, 9u}, // pka -> Brah
{0xB94F0000u, 46u}, // pko -> Latn
{0x706C0000u, 46u}, // pl -> Latn
{0x816F0000u, 46u}, // pla -> Latn
{0xC98F0000u, 46u}, // pms -> Latn
{0x99AF0000u, 46u}, // png -> Latn
{0xB5AF0000u, 46u}, // pnn -> Latn
- {0xCDAF0000u, 25u}, // pnt -> Grek
+ {0xCDAF0000u, 26u}, // pnt -> Grek
{0xB5CF0000u, 46u}, // pon -> Latn
- {0x81EF0000u, 18u}, // ppa -> Deva
+ {0x81EF0000u, 19u}, // ppa -> Deva
{0xB9EF0000u, 46u}, // ppo -> Latn
{0x822F0000u, 39u}, // pra -> Khar
- {0x8E2F0000u, 1u}, // prd -> Arab
+ {0x8E2F0000u, 2u}, // prd -> Arab
{0x9A2F0000u, 46u}, // prg -> Latn
- {0x70730000u, 1u}, // ps -> Arab
+ {0x70730000u, 2u}, // ps -> Arab
{0xCA4F0000u, 46u}, // pss -> Latn
{0x70740000u, 46u}, // pt -> Latn
{0xBE6F0000u, 46u}, // ptp -> Latn
@@ -1101,23 +1102,23 @@
{0x8A900000u, 46u}, // quc -> Latn
{0x9A900000u, 46u}, // qug -> Latn
{0xA0110000u, 46u}, // rai -> Latn
- {0xA4110000u, 18u}, // raj -> Deva
+ {0xA4110000u, 19u}, // raj -> Deva
{0xB8110000u, 46u}, // rao -> Latn
{0x94510000u, 46u}, // rcf -> Latn
{0xA4910000u, 46u}, // rej -> Latn
{0xAC910000u, 46u}, // rel -> Latn
{0xC8910000u, 46u}, // res -> Latn
{0xB4D10000u, 46u}, // rgn -> Latn
- {0x98F10000u, 1u}, // rhg -> Arab
+ {0x98F10000u, 2u}, // rhg -> Arab
{0x81110000u, 46u}, // ria -> Latn
- {0x95110000u, 88u}, // rif -> Tfng
+ {0x95110000u, 89u}, // rif -> Tfng
{0x95114E4Cu, 46u}, // rif-NL -> Latn
- {0xC9310000u, 18u}, // rjs -> Deva
- {0xCD510000u, 7u}, // rkt -> Beng
+ {0xC9310000u, 19u}, // rjs -> Deva
+ {0xCD510000u, 8u}, // rkt -> Beng
{0x726D0000u, 46u}, // rm -> Latn
{0x95910000u, 46u}, // rmf -> Latn
{0xB9910000u, 46u}, // rmo -> Latn
- {0xCD910000u, 1u}, // rmt -> Arab
+ {0xCD910000u, 2u}, // rmt -> Arab
{0xD1910000u, 46u}, // rmu -> Latn
{0x726E0000u, 46u}, // rn -> Latn
{0x81B10000u, 46u}, // rna -> Latn
@@ -1128,49 +1129,49 @@
{0xB9D10000u, 46u}, // roo -> Latn
{0xBA310000u, 46u}, // rro -> Latn
{0xB2710000u, 46u}, // rtm -> Latn
- {0x72750000u, 17u}, // ru -> Cyrl
- {0x92910000u, 17u}, // rue -> Cyrl
+ {0x72750000u, 18u}, // ru -> Cyrl
+ {0x92910000u, 18u}, // rue -> Cyrl
{0x9A910000u, 46u}, // rug -> Latn
{0x72770000u, 46u}, // rw -> Latn
{0xAAD10000u, 46u}, // rwk -> Latn
{0xBAD10000u, 46u}, // rwo -> Latn
{0xD3110000u, 38u}, // ryu -> Kana
- {0x73610000u, 18u}, // sa -> Deva
+ {0x73610000u, 19u}, // sa -> Deva
{0x94120000u, 46u}, // saf -> Latn
- {0x9C120000u, 17u}, // sah -> Cyrl
+ {0x9C120000u, 18u}, // sah -> Cyrl
{0xC0120000u, 46u}, // saq -> Latn
{0xC8120000u, 46u}, // sas -> Latn
- {0xCC120000u, 63u}, // sat -> Olck
+ {0xCC120000u, 64u}, // sat -> Olck
{0xD4120000u, 46u}, // sav -> Latn
- {0xE4120000u, 75u}, // saz -> Saur
+ {0xE4120000u, 76u}, // saz -> Saur
{0x80320000u, 46u}, // sba -> Latn
{0x90320000u, 46u}, // sbe -> Latn
{0xBC320000u, 46u}, // sbp -> Latn
{0x73630000u, 46u}, // sc -> Latn
- {0xA8520000u, 18u}, // sck -> Deva
- {0xAC520000u, 1u}, // scl -> Arab
+ {0xA8520000u, 19u}, // sck -> Deva
+ {0xAC520000u, 2u}, // scl -> Arab
{0xB4520000u, 46u}, // scn -> Latn
{0xB8520000u, 46u}, // sco -> Latn
{0xC8520000u, 46u}, // scs -> Latn
- {0x73640000u, 1u}, // sd -> Arab
+ {0x73640000u, 2u}, // sd -> Arab
{0x88720000u, 46u}, // sdc -> Latn
- {0x9C720000u, 1u}, // sdh -> Arab
+ {0x9C720000u, 2u}, // sdh -> Arab
{0x73650000u, 46u}, // se -> Latn
{0x94920000u, 46u}, // sef -> Latn
{0x9C920000u, 46u}, // seh -> Latn
{0xA0920000u, 46u}, // sei -> Latn
{0xC8920000u, 46u}, // ses -> Latn
{0x73670000u, 46u}, // sg -> Latn
- {0x80D20000u, 62u}, // sga -> Ogam
+ {0x80D20000u, 63u}, // sga -> Ogam
{0xC8D20000u, 46u}, // sgs -> Latn
- {0xD8D20000u, 20u}, // sgw -> Ethi
+ {0xD8D20000u, 21u}, // sgw -> Ethi
{0xE4D20000u, 46u}, // sgz -> Latn
{0x73680000u, 46u}, // sh -> Latn
- {0xA0F20000u, 88u}, // shi -> Tfng
+ {0xA0F20000u, 89u}, // shi -> Tfng
{0xA8F20000u, 46u}, // shk -> Latn
- {0xB4F20000u, 58u}, // shn -> Mymr
- {0xD0F20000u, 1u}, // shu -> Arab
- {0x73690000u, 77u}, // si -> Sinh
+ {0xB4F20000u, 59u}, // shn -> Mymr
+ {0xD0F20000u, 2u}, // shu -> Arab
+ {0x73690000u, 78u}, // si -> Sinh
{0x8D120000u, 46u}, // sid -> Latn
{0x99120000u, 46u}, // sig -> Latn
{0xAD120000u, 46u}, // sil -> Latn
@@ -1178,7 +1179,7 @@
{0xC5320000u, 46u}, // sjr -> Latn
{0x736B0000u, 46u}, // sk -> Latn
{0x89520000u, 46u}, // skc -> Latn
- {0xC5520000u, 1u}, // skr -> Arab
+ {0xC5520000u, 2u}, // skr -> Arab
{0xC9520000u, 46u}, // sks -> Latn
{0x736C0000u, 46u}, // sl -> Latn
{0x8D720000u, 46u}, // sld -> Latn
@@ -1189,7 +1190,7 @@
{0x81920000u, 46u}, // sma -> Latn
{0xA5920000u, 46u}, // smj -> Latn
{0xB5920000u, 46u}, // smn -> Latn
- {0xBD920000u, 73u}, // smp -> Samr
+ {0xBD920000u, 74u}, // smp -> Samr
{0xC1920000u, 46u}, // smq -> Latn
{0xC9920000u, 46u}, // sms -> Latn
{0x736E0000u, 46u}, // sn -> Latn
@@ -1199,24 +1200,24 @@
{0xDDB20000u, 46u}, // snx -> Latn
{0xE1B20000u, 46u}, // sny -> Latn
{0x736F0000u, 46u}, // so -> Latn
- {0x99D20000u, 78u}, // sog -> Sogd
+ {0x99D20000u, 79u}, // sog -> Sogd
{0xA9D20000u, 46u}, // sok -> Latn
{0xC1D20000u, 46u}, // soq -> Latn
- {0xD1D20000u, 90u}, // sou -> Thai
+ {0xD1D20000u, 91u}, // sou -> Thai
{0xE1D20000u, 46u}, // soy -> Latn
{0x8DF20000u, 46u}, // spd -> Latn
{0xADF20000u, 46u}, // spl -> Latn
{0xC9F20000u, 46u}, // sps -> Latn
{0x73710000u, 46u}, // sq -> Latn
- {0x73720000u, 17u}, // sr -> Cyrl
+ {0x73720000u, 18u}, // sr -> Cyrl
{0x73724D45u, 46u}, // sr-ME -> Latn
{0x7372524Fu, 46u}, // sr-RO -> Latn
{0x73725255u, 46u}, // sr-RU -> Latn
{0x73725452u, 46u}, // sr-TR -> Latn
- {0x86320000u, 79u}, // srb -> Sora
+ {0x86320000u, 80u}, // srb -> Sora
{0xB6320000u, 46u}, // srn -> Latn
{0xC6320000u, 46u}, // srr -> Latn
- {0xDE320000u, 18u}, // srx -> Deva
+ {0xDE320000u, 19u}, // srx -> Deva
{0x73730000u, 46u}, // ss -> Latn
{0x8E520000u, 46u}, // ssd -> Latn
{0x9A520000u, 46u}, // ssg -> Latn
@@ -1232,18 +1233,18 @@
{0xCA920000u, 46u}, // sus -> Latn
{0x73760000u, 46u}, // sv -> Latn
{0x73770000u, 46u}, // sw -> Latn
- {0x86D20000u, 1u}, // swb -> Arab
+ {0x86D20000u, 2u}, // swb -> Arab
{0x8AD20000u, 46u}, // swc -> Latn
{0x9AD20000u, 46u}, // swg -> Latn
{0xBED20000u, 46u}, // swp -> Latn
- {0xD6D20000u, 18u}, // swv -> Deva
+ {0xD6D20000u, 19u}, // swv -> Deva
{0xB6F20000u, 46u}, // sxn -> Latn
{0xDAF20000u, 46u}, // sxw -> Latn
- {0xAF120000u, 7u}, // syl -> Beng
- {0xC7120000u, 81u}, // syr -> Syrc
+ {0xAF120000u, 8u}, // syl -> Beng
+ {0xC7120000u, 82u}, // syr -> Syrc
{0xAF320000u, 46u}, // szl -> Latn
- {0x74610000u, 84u}, // ta -> Taml
- {0xA4130000u, 18u}, // taj -> Deva
+ {0x74610000u, 85u}, // ta -> Taml
+ {0xA4130000u, 19u}, // taj -> Deva
{0xAC130000u, 46u}, // tal -> Latn
{0xB4130000u, 46u}, // tan -> Latn
{0xC0130000u, 46u}, // taq -> Latn
@@ -1256,28 +1257,28 @@
{0xE4330000u, 46u}, // tbz -> Latn
{0xA0530000u, 46u}, // tci -> Latn
{0xE0530000u, 42u}, // tcy -> Knda
- {0x8C730000u, 82u}, // tdd -> Tale
- {0x98730000u, 18u}, // tdg -> Deva
- {0x9C730000u, 18u}, // tdh -> Deva
+ {0x8C730000u, 83u}, // tdd -> Tale
+ {0x98730000u, 19u}, // tdg -> Deva
+ {0x9C730000u, 19u}, // tdh -> Deva
{0xD0730000u, 46u}, // tdu -> Latn
- {0x74650000u, 87u}, // te -> Telu
+ {0x74650000u, 88u}, // te -> Telu
{0x8C930000u, 46u}, // ted -> Latn
{0xB0930000u, 46u}, // tem -> Latn
{0xB8930000u, 46u}, // teo -> Latn
{0xCC930000u, 46u}, // tet -> Latn
{0xA0B30000u, 46u}, // tfi -> Latn
- {0x74670000u, 17u}, // tg -> Cyrl
- {0x7467504Bu, 1u}, // tg-PK -> Arab
+ {0x74670000u, 18u}, // tg -> Cyrl
+ {0x7467504Bu, 2u}, // tg-PK -> Arab
{0x88D30000u, 46u}, // tgc -> Latn
{0xB8D30000u, 46u}, // tgo -> Latn
{0xD0D30000u, 46u}, // tgu -> Latn
- {0x74680000u, 90u}, // th -> Thai
- {0xACF30000u, 18u}, // thl -> Deva
- {0xC0F30000u, 18u}, // thq -> Deva
- {0xC4F30000u, 18u}, // thr -> Deva
- {0x74690000u, 20u}, // ti -> Ethi
+ {0x74680000u, 91u}, // th -> Thai
+ {0xACF30000u, 19u}, // thl -> Deva
+ {0xC0F30000u, 19u}, // thq -> Deva
+ {0xC4F30000u, 19u}, // thr -> Deva
+ {0x74690000u, 21u}, // ti -> Ethi
{0x95130000u, 46u}, // tif -> Latn
- {0x99130000u, 20u}, // tig -> Ethi
+ {0x99130000u, 21u}, // tig -> Ethi
{0xA9130000u, 46u}, // tik -> Latn
{0xB1130000u, 46u}, // tim -> Latn
{0xB9130000u, 46u}, // tio -> Latn
@@ -1285,7 +1286,7 @@
{0x746B0000u, 46u}, // tk -> Latn
{0xAD530000u, 46u}, // tkl -> Latn
{0xC5530000u, 46u}, // tkr -> Latn
- {0xCD530000u, 18u}, // tkt -> Deva
+ {0xCD530000u, 19u}, // tkt -> Deva
{0x746C0000u, 46u}, // tl -> Latn
{0x95730000u, 46u}, // tlf -> Latn
{0xDD730000u, 46u}, // tlx -> Latn
@@ -1305,19 +1306,19 @@
{0x74720000u, 46u}, // tr -> Latn
{0xD2330000u, 46u}, // tru -> Latn
{0xD6330000u, 46u}, // trv -> Latn
- {0xDA330000u, 1u}, // trw -> Arab
+ {0xDA330000u, 2u}, // trw -> Arab
{0x74730000u, 46u}, // ts -> Latn
- {0x8E530000u, 25u}, // tsd -> Grek
- {0x96530000u, 18u}, // tsf -> Deva
+ {0x8E530000u, 26u}, // tsd -> Grek
+ {0x96530000u, 19u}, // tsf -> Deva
{0x9A530000u, 46u}, // tsg -> Latn
- {0xA6530000u, 91u}, // tsj -> Tibt
+ {0xA6530000u, 92u}, // tsj -> Tibt
{0xDA530000u, 46u}, // tsw -> Latn
- {0x74740000u, 17u}, // tt -> Cyrl
+ {0x74740000u, 18u}, // tt -> Cyrl
{0x8E730000u, 46u}, // ttd -> Latn
{0x92730000u, 46u}, // tte -> Latn
{0xA6730000u, 46u}, // ttj -> Latn
{0xC6730000u, 46u}, // ttr -> Latn
- {0xCA730000u, 90u}, // tts -> Thai
+ {0xCA730000u, 91u}, // tts -> Thai
{0xCE730000u, 46u}, // ttt -> Latn
{0x9E930000u, 46u}, // tuh -> Latn
{0xAE930000u, 46u}, // tul -> Latn
@@ -1328,25 +1329,26 @@
{0xD2B30000u, 46u}, // tvu -> Latn
{0x9ED30000u, 46u}, // twh -> Latn
{0xC2D30000u, 46u}, // twq -> Latn
- {0x9AF30000u, 85u}, // txg -> Tang
+ {0x9AF30000u, 86u}, // txg -> Tang
{0x74790000u, 46u}, // ty -> Latn
{0x83130000u, 46u}, // tya -> Latn
- {0xD7130000u, 17u}, // tyv -> Cyrl
+ {0xD7130000u, 18u}, // tyv -> Cyrl
{0xB3330000u, 46u}, // tzm -> Latn
{0xD0340000u, 46u}, // ubu -> Latn
- {0xB0740000u, 17u}, // udm -> Cyrl
- {0x75670000u, 1u}, // ug -> Arab
- {0x75674B5Au, 17u}, // ug-KZ -> Cyrl
- {0x75674D4Eu, 17u}, // ug-MN -> Cyrl
- {0x80D40000u, 92u}, // uga -> Ugar
- {0x756B0000u, 17u}, // uk -> Cyrl
+ {0xA0740000u, 0u}, // udi -> Aghb
+ {0xB0740000u, 18u}, // udm -> Cyrl
+ {0x75670000u, 2u}, // ug -> Arab
+ {0x75674B5Au, 18u}, // ug-KZ -> Cyrl
+ {0x75674D4Eu, 18u}, // ug-MN -> Cyrl
+ {0x80D40000u, 93u}, // uga -> Ugar
+ {0x756B0000u, 18u}, // uk -> Cyrl
{0xA1740000u, 46u}, // uli -> Latn
{0x85940000u, 46u}, // umb -> Latn
- {0xC5B40000u, 7u}, // unr -> Beng
- {0xC5B44E50u, 18u}, // unr-NP -> Deva
- {0xDDB40000u, 7u}, // unx -> Beng
+ {0xC5B40000u, 8u}, // unr -> Beng
+ {0xC5B44E50u, 19u}, // unr-NP -> Deva
+ {0xDDB40000u, 8u}, // unx -> Beng
{0xA9D40000u, 46u}, // uok -> Latn
- {0x75720000u, 1u}, // ur -> Arab
+ {0x75720000u, 2u}, // ur -> Arab
{0xA2340000u, 46u}, // uri -> Latn
{0xCE340000u, 46u}, // urt -> Latn
{0xDA340000u, 46u}, // urw -> Latn
@@ -1356,10 +1358,10 @@
{0x9EB40000u, 46u}, // uvh -> Latn
{0xAEB40000u, 46u}, // uvl -> Latn
{0x757A0000u, 46u}, // uz -> Latn
- {0x757A4146u, 1u}, // uz-AF -> Arab
- {0x757A434Eu, 17u}, // uz-CN -> Cyrl
+ {0x757A4146u, 2u}, // uz-AF -> Arab
+ {0x757A434Eu, 18u}, // uz-CN -> Cyrl
{0x98150000u, 46u}, // vag -> Latn
- {0xA0150000u, 93u}, // vai -> Vaii
+ {0xA0150000u, 94u}, // vai -> Vaii
{0xB4150000u, 46u}, // van -> Latn
{0x76650000u, 46u}, // ve -> Latn
{0x88950000u, 46u}, // vec -> Latn
@@ -1378,12 +1380,12 @@
{0x77610000u, 46u}, // wa -> Latn
{0x90160000u, 46u}, // wae -> Latn
{0xA4160000u, 46u}, // waj -> Latn
- {0xAC160000u, 20u}, // wal -> Ethi
+ {0xAC160000u, 21u}, // wal -> Ethi
{0xB4160000u, 46u}, // wan -> Latn
{0xC4160000u, 46u}, // war -> Latn
{0xBC360000u, 46u}, // wbp -> Latn
- {0xC0360000u, 87u}, // wbq -> Telu
- {0xC4360000u, 18u}, // wbr -> Deva
+ {0xC0360000u, 88u}, // wbq -> Telu
+ {0xC4360000u, 19u}, // wbr -> Deva
{0xA0560000u, 46u}, // wci -> Latn
{0xC4960000u, 46u}, // wer -> Latn
{0xA0D60000u, 46u}, // wgi -> Latn
@@ -1396,40 +1398,40 @@
{0xC9760000u, 46u}, // wls -> Latn
{0xB9960000u, 46u}, // wmo -> Latn
{0x89B60000u, 46u}, // wnc -> Latn
- {0xA1B60000u, 1u}, // wni -> Arab
+ {0xA1B60000u, 2u}, // wni -> Arab
{0xD1B60000u, 46u}, // wnu -> Latn
{0x776F0000u, 46u}, // wo -> Latn
{0x85D60000u, 46u}, // wob -> Latn
{0xC9D60000u, 46u}, // wos -> Latn
{0xCA360000u, 46u}, // wrs -> Latn
- {0x9A560000u, 22u}, // wsg -> Gong
+ {0x9A560000u, 23u}, // wsg -> Gong
{0xAA560000u, 46u}, // wsk -> Latn
- {0xB2760000u, 18u}, // wtm -> Deva
- {0xD2960000u, 28u}, // wuu -> Hans
+ {0xB2760000u, 19u}, // wtm -> Deva
+ {0xD2960000u, 29u}, // wuu -> Hans
{0xD6960000u, 46u}, // wuv -> Latn
{0x82D60000u, 46u}, // wwa -> Latn
{0xD4170000u, 46u}, // xav -> Latn
{0xA0370000u, 46u}, // xbi -> Latn
- {0xB8570000u, 14u}, // xco -> Chrs
- {0xC4570000u, 11u}, // xcr -> Cari
+ {0xB8570000u, 15u}, // xco -> Chrs
+ {0xC4570000u, 12u}, // xcr -> Cari
{0xC8970000u, 46u}, // xes -> Latn
{0x78680000u, 46u}, // xh -> Latn
{0x81770000u, 46u}, // xla -> Latn
{0x89770000u, 50u}, // xlc -> Lyci
{0x8D770000u, 51u}, // xld -> Lydi
- {0x95970000u, 21u}, // xmf -> Geor
+ {0x95970000u, 22u}, // xmf -> Geor
{0xB5970000u, 53u}, // xmn -> Mani
- {0xC5970000u, 54u}, // xmr -> Merc
- {0x81B70000u, 59u}, // xna -> Narb
- {0xC5B70000u, 18u}, // xnr -> Deva
+ {0xC5970000u, 55u}, // xmr -> Merc
+ {0x81B70000u, 60u}, // xna -> Narb
+ {0xC5B70000u, 19u}, // xnr -> Deva
{0x99D70000u, 46u}, // xog -> Latn
{0xB5D70000u, 46u}, // xon -> Latn
- {0xC5F70000u, 71u}, // xpr -> Prti
+ {0xC5F70000u, 72u}, // xpr -> Prti
{0x86370000u, 46u}, // xrb -> Latn
- {0x82570000u, 74u}, // xsa -> Sarb
+ {0x82570000u, 75u}, // xsa -> Sarb
{0xA2570000u, 46u}, // xsi -> Latn
{0xB2570000u, 46u}, // xsm -> Latn
- {0xC6570000u, 18u}, // xsr -> Deva
+ {0xC6570000u, 19u}, // xsr -> Deva
{0x92D70000u, 46u}, // xwe -> Latn
{0xB0180000u, 46u}, // yam -> Latn
{0xB8180000u, 46u}, // yao -> Latn
@@ -1458,33 +1460,33 @@
{0xAE380000u, 46u}, // yrl -> Latn
{0xCA580000u, 46u}, // yss -> Latn
{0x82980000u, 46u}, // yua -> Latn
- {0x92980000u, 29u}, // yue -> Hant
- {0x9298434Eu, 28u}, // yue-CN -> Hans
+ {0x92980000u, 30u}, // yue -> Hant
+ {0x9298434Eu, 29u}, // yue-CN -> Hans
{0xA6980000u, 46u}, // yuj -> Latn
{0xCE980000u, 46u}, // yut -> Latn
{0xDA980000u, 46u}, // yuw -> Latn
{0x7A610000u, 46u}, // za -> Latn
{0x98190000u, 46u}, // zag -> Latn
- {0xA4790000u, 1u}, // zdj -> Arab
+ {0xA4790000u, 2u}, // zdj -> Arab
{0x80990000u, 46u}, // zea -> Latn
- {0x9CD90000u, 88u}, // zgh -> Tfng
- {0x7A680000u, 28u}, // zh -> Hans
- {0x7A684155u, 29u}, // zh-AU -> Hant
- {0x7A68424Eu, 29u}, // zh-BN -> Hant
- {0x7A684742u, 29u}, // zh-GB -> Hant
- {0x7A684746u, 29u}, // zh-GF -> Hant
- {0x7A68484Bu, 29u}, // zh-HK -> Hant
- {0x7A684944u, 29u}, // zh-ID -> Hant
- {0x7A684D4Fu, 29u}, // zh-MO -> Hant
- {0x7A685041u, 29u}, // zh-PA -> Hant
- {0x7A685046u, 29u}, // zh-PF -> Hant
- {0x7A685048u, 29u}, // zh-PH -> Hant
- {0x7A685352u, 29u}, // zh-SR -> Hant
- {0x7A685448u, 29u}, // zh-TH -> Hant
- {0x7A685457u, 29u}, // zh-TW -> Hant
- {0x7A685553u, 29u}, // zh-US -> Hant
- {0x7A68564Eu, 29u}, // zh-VN -> Hant
- {0xDCF90000u, 61u}, // zhx -> Nshu
+ {0x9CD90000u, 89u}, // zgh -> Tfng
+ {0x7A680000u, 29u}, // zh -> Hans
+ {0x7A684155u, 30u}, // zh-AU -> Hant
+ {0x7A68424Eu, 30u}, // zh-BN -> Hant
+ {0x7A684742u, 30u}, // zh-GB -> Hant
+ {0x7A684746u, 30u}, // zh-GF -> Hant
+ {0x7A68484Bu, 30u}, // zh-HK -> Hant
+ {0x7A684944u, 30u}, // zh-ID -> Hant
+ {0x7A684D4Fu, 30u}, // zh-MO -> Hant
+ {0x7A685041u, 30u}, // zh-PA -> Hant
+ {0x7A685046u, 30u}, // zh-PF -> Hant
+ {0x7A685048u, 30u}, // zh-PH -> Hant
+ {0x7A685352u, 30u}, // zh-SR -> Hant
+ {0x7A685448u, 30u}, // zh-TH -> Hant
+ {0x7A685457u, 30u}, // zh-TW -> Hant
+ {0x7A685553u, 30u}, // zh-US -> Hant
+ {0x7A68564Eu, 30u}, // zh-VN -> Hant
+ {0xDCF90000u, 62u}, // zhx -> Nshu
{0x81190000u, 46u}, // zia -> Latn
{0xCD590000u, 41u}, // zkt -> Kits
{0xB1790000u, 46u}, // zlm -> Latn
@@ -1642,6 +1644,7 @@
0xB48343414C61746ELLU, // den_Latn_CA
0xC4C343414C61746ELLU, // dgr_Latn_CA
0x91234E454C61746ELLU, // dje_Latn_NE
+ 0x95834E474D656466LLU, // dmf_Medf_NG
0xA5A343494C61746ELLU, // dnj_Latn_CI
0xA1C3494E44657661LLU, // doi_Deva_IN
0x9E23434E4D6F6E67LLU, // drh_Mong_CN
@@ -1917,8 +1920,6 @@
0x6D684D484C61746ELLU, // mh_Latn_MH
0x6D694E5A4C61746ELLU, // mi_Latn_NZ
0xB50C49444C61746ELLU, // min_Latn_ID
- 0xC90C495148617472LLU, // mis_Hatr_IQ
- 0xC90C4E474D656466LLU, // mis_Medf_NG
0x6D6B4D4B4379726CLLU, // mk_Cyrl_MK
0x6D6C494E4D6C796DLLU, // ml_Mlym_IN
0xC96C53444C61746ELLU, // mls_Latn_SD
@@ -2174,6 +2175,7 @@
0x747950464C61746ELLU, // ty_Latn_PF
0xD71352554379726CLLU, // tyv_Cyrl_RU
0xB3334D414C61746ELLU, // tzm_Latn_MA
+ 0xA074525541676862LLU, // udi_Aghb_RU
0xB07452554379726CLLU, // udm_Cyrl_RU
0x7567434E41726162LLU, // ug_Arab_CN
0x75674B5A4379726CLLU, // ug_Cyrl_KZ
@@ -2382,6 +2384,8 @@
{0x65735553u, 0x6573A424u}, // es-US -> es-419
{0x65735559u, 0x6573A424u}, // es-UY -> es-419
{0x65735645u, 0x6573A424u}, // es-VE -> es-419
+ {0x6E620000u, 0x6E6F0000u}, // nb -> no
+ {0x6E6E0000u, 0x6E6F0000u}, // nn -> no
{0x7074414Fu, 0x70745054u}, // pt-AO -> pt-PT
{0x70744348u, 0x70745054u}, // pt-CH -> pt-PT
{0x70744356u, 0x70745054u}, // pt-CV -> pt-PT
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 6db7170..1439656 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -101,59 +101,58 @@
paint.setBlendMode(layer->getMode());
paint.setColorFilter(layer->getColorFilter());
const SkMatrix& totalMatrix = canvas->getTotalMatrix();
- if (srcRect || dstRect) {
- SkRect skiaSrcRect;
- if (srcRect && !srcRect->isEmpty()) {
- skiaSrcRect = *srcRect;
- } else {
- skiaSrcRect = SkRect::MakeIWH(imageWidth, imageHeight);
- }
- SkRect skiaDestRect;
- if (dstRect && !dstRect->isEmpty()) {
- skiaDestRect = *dstRect;
- } else {
- skiaDestRect = (windowTransform & NATIVE_WINDOW_TRANSFORM_ROT_90)
- ? SkRect::MakeIWH(layerHeight, layerWidth)
- : SkRect::MakeIWH(layerWidth, layerHeight);
- }
-
- const float px = skiaDestRect.centerX();
- const float py = skiaDestRect.centerY();
- SkMatrix m;
- if (windowTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
- m.postScale(-1.f, 1.f, px, py);
- }
- if (windowTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
- m.postScale(1.f, -1.f, px, py);
- }
- if (windowTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
- m.postRotate(90, 0, 0);
- m.postTranslate(skiaDestRect.height(), 0);
- }
- auto constraint = SkCanvas::kFast_SrcRectConstraint;
- if (srcRect && !srcRect->isEmpty()) {
- constraint = SkCanvas::kStrict_SrcRectConstraint;
- }
-
- canvas->save();
- canvas->concat(m);
-
- // If (matrix is a rect-to-rect transform)
- // and (src/dst buffers size match in screen coordinates)
- // and (src/dst corners align fractionally),
- // then use nearest neighbor, otherwise use bilerp sampling.
- // Skia TextureOp has the above logic build-in, but not NonAAFillRectOp. TextureOp works
- // only for SrcOver blending and without color filter (readback uses Src blending).
- SkSamplingOptions sampling(SkFilterMode::kNearest);
- if (layer->getForceFilter() ||
- shouldFilterRect(totalMatrix, skiaSrcRect, skiaDestRect)) {
- sampling = SkSamplingOptions(SkFilterMode::kLinear);
- }
-
- canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, sampling, &paint,
- constraint);
- canvas->restore();
+ SkRect skiaSrcRect;
+ if (srcRect && !srcRect->isEmpty()) {
+ skiaSrcRect = *srcRect;
+ } else {
+ skiaSrcRect = SkRect::MakeIWH(imageWidth, imageHeight);
}
+ SkRect skiaDestRect;
+ if (dstRect && !dstRect->isEmpty()) {
+ skiaDestRect = (windowTransform & NATIVE_WINDOW_TRANSFORM_ROT_90)
+ ? SkRect::MakeIWH(dstRect->height(), dstRect->width())
+ : SkRect::MakeIWH(dstRect->width(), dstRect->height());
+ } else {
+ skiaDestRect = (windowTransform & NATIVE_WINDOW_TRANSFORM_ROT_90)
+ ? SkRect::MakeIWH(layerHeight, layerWidth)
+ : SkRect::MakeIWH(layerWidth, layerHeight);
+ }
+
+ const float px = skiaDestRect.centerX();
+ const float py = skiaDestRect.centerY();
+ SkMatrix m;
+ if (windowTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
+ m.postScale(-1.f, 1.f, px, py);
+ }
+ if (windowTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
+ m.postScale(1.f, -1.f, px, py);
+ }
+ if (windowTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+ m.postRotate(90, 0, 0);
+ m.postTranslate(skiaDestRect.height(), 0);
+ }
+ auto constraint = SkCanvas::kFast_SrcRectConstraint;
+ if (srcRect && !srcRect->isEmpty()) {
+ constraint = SkCanvas::kStrict_SrcRectConstraint;
+ }
+
+ canvas->save();
+ canvas->concat(m);
+
+ // If (matrix is a rect-to-rect transform)
+ // and (src/dst buffers size match in screen coordinates)
+ // and (src/dst corners align fractionally),
+ // then use nearest neighbor, otherwise use bilerp sampling.
+ // Skia TextureOp has the above logic build-in, but not NonAAFillRectOp. TextureOp works
+ // only for SrcOver blending and without color filter (readback uses Src blending).
+ SkSamplingOptions sampling(SkFilterMode::kNearest);
+ if (layer->getForceFilter() || shouldFilterRect(totalMatrix, skiaSrcRect, skiaDestRect)) {
+ sampling = SkSamplingOptions(SkFilterMode::kLinear);
+ }
+
+ canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, sampling, &paint,
+ constraint);
+ canvas->restore();
// restore the original matrix
if (useLayerTransform) {
canvas->restore();
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index 209903c..1fcb194 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -18,11 +18,12 @@
import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import android.annotation.FloatRange;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -30,48 +31,51 @@
import android.util.Printer;
import android.util.TimeUtils;
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.text.DecimalFormat;
import java.util.Locale;
import java.util.Objects;
import java.util.StringTokenizer;
/**
- * A data class representing a geographic location.
- *
- * <p>A location may consist of a latitude, longitude, timestamp, and other information such as
- * bearing, altitude and velocity.
+ * A data class representing a geographic location. A location consists of a latitude, longitude,
+ * timestamp, accuracy, and other information such as bearing, altitude and velocity.
*
* <p>All locations generated through {@link LocationManager} are guaranteed to have a valid
- * latitude, longitude, and timestamp (both UTC time and elapsed real-time since boot). All other
- * parameters are optional.
+ * latitude, longitude, timestamp (both UTC time and elapsed real-time since boot), and accuracy.
+ * All other parameters are optional.
*/
public class Location implements Parcelable {
/**
- * Constant used to specify formatting of a latitude or longitude
- * in the form "[+-]DDD.DDDDD where D indicates degrees.
+ * Constant used to specify formatting of a latitude or longitude in the form "[+-]DDD.DDDDD
+ * where D indicates degrees.
*/
public static final int FORMAT_DEGREES = 0;
/**
- * Constant used to specify formatting of a latitude or longitude
- * in the form "[+-]DDD:MM.MMMMM" where D indicates degrees and
- * M indicates minutes of arc (1 minute = 1/60th of a degree).
+ * Constant used to specify formatting of a latitude or longitude in the form "[+-]DDD:MM.MMMMM"
+ * where D indicates degrees and M indicates minutes of arc (1 minute = 1/60th of a degree).
*/
public static final int FORMAT_MINUTES = 1;
/**
- * Constant used to specify formatting of a latitude or longitude
- * in the form "DDD:MM:SS.SSSSS" where D indicates degrees, M
- * indicates minutes of arc, and S indicates seconds of arc (1
+ * Constant used to specify formatting of a latitude or longitude in the form "DDD:MM:SS.SSSSS"
+ * where D indicates degrees, M indicates minutes of arc, and S indicates seconds of arc (1
* minute = 1/60th of a degree, 1 second = 1/3600th of a degree).
*/
public static final int FORMAT_SECONDS = 2;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({FORMAT_DEGREES, FORMAT_MINUTES, FORMAT_SECONDS})
+ public @interface Format {}
+
/**
* Bundle key for a version of the location containing no GPS data.
- * Allows location providers to flag locations as being safe to
- * feed to LocationFudger.
*
* @hide
* @deprecated As of Android R, this extra is longer in use, since it is not necessary to keep
@@ -87,7 +91,7 @@
private static final int HAS_BEARING_MASK = 1 << 2;
private static final int HAS_HORIZONTAL_ACCURACY_MASK = 1 << 3;
private static final int HAS_MOCK_PROVIDER_MASK = 1 << 4;
- private static final int HAS_VERTICAL_ACCURACY_MASK = 1 << 5;
+ private static final int HAS_ALTITUDE_ACCURACY_MASK = 1 << 5;
private static final int HAS_SPEED_ACCURACY_MASK = 1 << 6;
private static final int HAS_BEARING_ACCURACY_MASK = 1 << 7;
private static final int HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK = 1 << 8;
@@ -98,358 +102,86 @@
private static final ThreadLocal<BearingDistanceCache> sBearingDistanceCache =
ThreadLocal.withInitial(BearingDistanceCache::new);
- private String mProvider;
- private long mTime = 0;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R,
- publicAlternatives = "{@link #getElapsedRealtimeNanos()}")
- private long mElapsedRealtimeNanos = 0;
- // Estimate of the relative precision of the alignment of this SystemClock
- // timestamp, with the reported measurements in nanoseconds (68% confidence).
- private double mElapsedRealtimeUncertaintyNanos = 0.0f;
- private double mLatitude = 0.0;
- private double mLongitude = 0.0;
- private double mAltitude = 0.0f;
- private float mSpeed = 0.0f;
- private float mBearing = 0.0f;
- private float mHorizontalAccuracyMeters = 0.0f;
- private float mVerticalAccuracyMeters = 0.0f;
- private float mSpeedAccuracyMetersPerSecond = 0.0f;
- private float mBearingAccuracyDegrees = 0.0f;
-
- private Bundle mExtras = null;
-
// A bitmask of fields present in this object (see HAS_* constants defined above).
private int mFieldsMask = 0;
+ private @Nullable String mProvider;
+ private long mTimeMs;
+ private long mElapsedRealtimeNs;
+ private double mElapsedRealtimeUncertaintyNs;
+ private double mLatitudeDegrees;
+ private double mLongitudeDegrees;
+ private float mHorizontalAccuracyMeters;
+ private double mAltitudeMeters;
+ private float mAltitudeAccuracyMeters;
+ private float mSpeedMetersPerSecond;
+ private float mSpeedAccuracyMetersPerSecond;
+ private float mBearingDegrees;
+ private float mBearingAccuracyDegrees;
+
+ private Bundle mExtras = null;
+
/**
- * Construct a new Location with a named provider.
+ * Constructs a new location with a named provider. By default all values are zero, and no
+ * optional values are present.
*
- * <p>By default time, latitude and longitude are 0, and the location
- * has no bearing, altitude, speed, accuracy or extras.
- *
- * @param provider the source that provides the location. It can be of type
- * {@link LocationManager#GPS_PROVIDER}, {@link LocationManager#NETWORK_PROVIDER},
- * or {@link LocationManager#PASSIVE_PROVIDER}. You can also define your own
- * provider string, in which case an empty string is a valid provider.
+ * @param provider the location provider name associated with this location
*/
- public Location(String provider) {
+ public Location(@Nullable String provider) {
mProvider = provider;
}
/**
* Construct a new Location object that is copied from an existing one.
*/
- public Location(Location l) {
+ public Location(@NonNull Location l) {
set(l);
}
/**
- * Sets the contents of the location to the values from the given location.
+ * Turns this location into a copy of the given location.
*/
- public void set(Location l) {
- mProvider = l.mProvider;
- mTime = l.mTime;
- mElapsedRealtimeNanos = l.mElapsedRealtimeNanos;
- mElapsedRealtimeUncertaintyNanos = l.mElapsedRealtimeUncertaintyNanos;
+ public void set(@NonNull Location l) {
mFieldsMask = l.mFieldsMask;
- mLatitude = l.mLatitude;
- mLongitude = l.mLongitude;
- mAltitude = l.mAltitude;
- mSpeed = l.mSpeed;
- mBearing = l.mBearing;
+ mProvider = l.mProvider;
+ mTimeMs = l.mTimeMs;
+ mElapsedRealtimeNs = l.mElapsedRealtimeNs;
+ mElapsedRealtimeUncertaintyNs = l.mElapsedRealtimeUncertaintyNs;
+ mLatitudeDegrees = l.mLatitudeDegrees;
+ mLongitudeDegrees = l.mLongitudeDegrees;
mHorizontalAccuracyMeters = l.mHorizontalAccuracyMeters;
- mVerticalAccuracyMeters = l.mVerticalAccuracyMeters;
+ mAltitudeMeters = l.mAltitudeMeters;
+ mAltitudeAccuracyMeters = l.mAltitudeAccuracyMeters;
+ mSpeedMetersPerSecond = l.mSpeedMetersPerSecond;
mSpeedAccuracyMetersPerSecond = l.mSpeedAccuracyMetersPerSecond;
+ mBearingDegrees = l.mBearingDegrees;
mBearingAccuracyDegrees = l.mBearingAccuracyDegrees;
mExtras = (l.mExtras == null) ? null : new Bundle(l.mExtras);
}
/**
- * Clears the contents of the location.
+ * Sets the provider to null, removes all optional fields, and sets the values of all other
+ * fields to zero.
*/
public void reset() {
mProvider = null;
- mTime = 0;
- mElapsedRealtimeNanos = 0;
- mElapsedRealtimeUncertaintyNanos = 0.0;
+ mTimeMs = 0;
+ mElapsedRealtimeNs = 0;
+ mElapsedRealtimeUncertaintyNs = 0.0;
mFieldsMask = 0;
- mLatitude = 0;
- mLongitude = 0;
- mAltitude = 0;
- mSpeed = 0;
- mBearing = 0;
+ mLatitudeDegrees = 0;
+ mLongitudeDegrees = 0;
+ mAltitudeMeters = 0;
+ mSpeedMetersPerSecond = 0;
+ mBearingDegrees = 0;
mHorizontalAccuracyMeters = 0;
- mVerticalAccuracyMeters = 0;
+ mAltitudeAccuracyMeters = 0;
mSpeedAccuracyMetersPerSecond = 0;
mBearingAccuracyDegrees = 0;
mExtras = null;
}
/**
- * Converts a coordinate to a String representation. The outputType
- * may be one of FORMAT_DEGREES, FORMAT_MINUTES, or FORMAT_SECONDS.
- * The coordinate must be a valid double between -180.0 and 180.0.
- * This conversion is performed in a method that is dependent on the
- * default locale, and so is not guaranteed to round-trip with
- * {@link #convert(String)}.
- *
- * @throws IllegalArgumentException if coordinate is less than
- * -180.0, greater than 180.0, or is not a number.
- * @throws IllegalArgumentException if outputType is not one of
- * FORMAT_DEGREES, FORMAT_MINUTES, or FORMAT_SECONDS.
- */
- public static String convert(double coordinate, int outputType) {
- if (coordinate < -180.0 || coordinate > 180.0 || Double.isNaN(coordinate)) {
- throw new IllegalArgumentException("coordinate=" + coordinate);
- }
- if ((outputType != FORMAT_DEGREES) &&
- (outputType != FORMAT_MINUTES) &&
- (outputType != FORMAT_SECONDS)) {
- throw new IllegalArgumentException("outputType=" + outputType);
- }
-
- StringBuilder sb = new StringBuilder();
-
- // Handle negative values
- if (coordinate < 0) {
- sb.append('-');
- coordinate = -coordinate;
- }
-
- DecimalFormat df = new DecimalFormat("###.#####");
- if (outputType == FORMAT_MINUTES || outputType == FORMAT_SECONDS) {
- int degrees = (int) Math.floor(coordinate);
- sb.append(degrees);
- sb.append(':');
- coordinate -= degrees;
- coordinate *= 60.0;
- if (outputType == FORMAT_SECONDS) {
- int minutes = (int) Math.floor(coordinate);
- sb.append(minutes);
- sb.append(':');
- coordinate -= minutes;
- coordinate *= 60.0;
- }
- }
- sb.append(df.format(coordinate));
- return sb.toString();
- }
-
- /**
- * Converts a String in one of the formats described by
- * FORMAT_DEGREES, FORMAT_MINUTES, or FORMAT_SECONDS into a
- * double. This conversion is performed in a locale agnostic
- * method, and so is not guaranteed to round-trip with
- * {@link #convert(double, int)}.
- *
- * @throws NullPointerException if coordinate is null
- * @throws IllegalArgumentException if the coordinate is not
- * in one of the valid formats.
- */
- public static double convert(String coordinate) {
- // IllegalArgumentException if bad syntax
- if (coordinate == null) {
- throw new NullPointerException("coordinate");
- }
-
- boolean negative = false;
- if (coordinate.charAt(0) == '-') {
- coordinate = coordinate.substring(1);
- negative = true;
- }
-
- StringTokenizer st = new StringTokenizer(coordinate, ":");
- int tokens = st.countTokens();
- if (tokens < 1) {
- throw new IllegalArgumentException("coordinate=" + coordinate);
- }
- try {
- String degrees = st.nextToken();
- double val;
- if (tokens == 1) {
- val = Double.parseDouble(degrees);
- return negative ? -val : val;
- }
-
- String minutes = st.nextToken();
- int deg = Integer.parseInt(degrees);
- double min;
- double sec = 0.0;
- boolean secPresent = false;
-
- if (st.hasMoreTokens()) {
- min = Integer.parseInt(minutes);
- String seconds = st.nextToken();
- sec = Double.parseDouble(seconds);
- secPresent = true;
- } else {
- min = Double.parseDouble(minutes);
- }
-
- boolean isNegative180 = negative && (deg == 180) &&
- (min == 0) && (sec == 0);
-
- // deg must be in [0, 179] except for the case of -180 degrees
- if ((deg < 0.0) || (deg > 179 && !isNegative180)) {
- throw new IllegalArgumentException("coordinate=" + coordinate);
- }
-
- // min must be in [0, 59] if seconds are present, otherwise [0.0, 60.0)
- if (min < 0 || min >= 60 || (secPresent && (min > 59))) {
- throw new IllegalArgumentException("coordinate=" +
- coordinate);
- }
-
- // sec must be in [0.0, 60.0)
- if (sec < 0 || sec >= 60) {
- throw new IllegalArgumentException("coordinate=" +
- coordinate);
- }
-
- val = deg*3600.0 + min*60.0 + sec;
- val /= 3600.0;
- return negative ? -val : val;
- } catch (NumberFormatException nfe) {
- throw new IllegalArgumentException("coordinate=" + coordinate);
- }
- }
-
- private static void computeDistanceAndBearing(double lat1, double lon1,
- double lat2, double lon2, BearingDistanceCache results) {
- // Based on http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf
- // using the "Inverse Formula" (section 4)
-
- int MAXITERS = 20;
- // Convert lat/long to radians
- lat1 *= Math.PI / 180.0;
- lat2 *= Math.PI / 180.0;
- lon1 *= Math.PI / 180.0;
- lon2 *= Math.PI / 180.0;
-
- double a = 6378137.0; // WGS84 major axis
- double b = 6356752.3142; // WGS84 semi-major axis
- double f = (a - b) / a;
- double aSqMinusBSqOverBSq = (a * a - b * b) / (b * b);
-
- double L = lon2 - lon1;
- double A = 0.0;
- double U1 = Math.atan((1.0 - f) * Math.tan(lat1));
- double U2 = Math.atan((1.0 - f) * Math.tan(lat2));
-
- double cosU1 = Math.cos(U1);
- double cosU2 = Math.cos(U2);
- double sinU1 = Math.sin(U1);
- double sinU2 = Math.sin(U2);
- double cosU1cosU2 = cosU1 * cosU2;
- double sinU1sinU2 = sinU1 * sinU2;
-
- double sigma = 0.0;
- double deltaSigma = 0.0;
- double cosSqAlpha;
- double cos2SM;
- double cosSigma;
- double sinSigma;
- double cosLambda = 0.0;
- double sinLambda = 0.0;
-
- double lambda = L; // initial guess
- for (int iter = 0; iter < MAXITERS; iter++) {
- double lambdaOrig = lambda;
- cosLambda = Math.cos(lambda);
- sinLambda = Math.sin(lambda);
- double t1 = cosU2 * sinLambda;
- double t2 = cosU1 * sinU2 - sinU1 * cosU2 * cosLambda;
- double sinSqSigma = t1 * t1 + t2 * t2; // (14)
- sinSigma = Math.sqrt(sinSqSigma);
- cosSigma = sinU1sinU2 + cosU1cosU2 * cosLambda; // (15)
- sigma = Math.atan2(sinSigma, cosSigma); // (16)
- double sinAlpha = (sinSigma == 0) ? 0.0 :
- cosU1cosU2 * sinLambda / sinSigma; // (17)
- cosSqAlpha = 1.0 - sinAlpha * sinAlpha;
- cos2SM = (cosSqAlpha == 0) ? 0.0 :
- cosSigma - 2.0 * sinU1sinU2 / cosSqAlpha; // (18)
-
- double uSquared = cosSqAlpha * aSqMinusBSqOverBSq; // defn
- A = 1 + (uSquared / 16384.0) * // (3)
- (4096.0 + uSquared *
- (-768 + uSquared * (320.0 - 175.0 * uSquared)));
- double B = (uSquared / 1024.0) * // (4)
- (256.0 + uSquared *
- (-128.0 + uSquared * (74.0 - 47.0 * uSquared)));
- double C = (f / 16.0) *
- cosSqAlpha *
- (4.0 + f * (4.0 - 3.0 * cosSqAlpha)); // (10)
- double cos2SMSq = cos2SM * cos2SM;
- deltaSigma = B * sinSigma * // (6)
- (cos2SM + (B / 4.0) *
- (cosSigma * (-1.0 + 2.0 * cos2SMSq) -
- (B / 6.0) * cos2SM *
- (-3.0 + 4.0 * sinSigma * sinSigma) *
- (-3.0 + 4.0 * cos2SMSq)));
-
- lambda = L +
- (1.0 - C) * f * sinAlpha *
- (sigma + C * sinSigma *
- (cos2SM + C * cosSigma *
- (-1.0 + 2.0 * cos2SM * cos2SM))); // (11)
-
- double delta = (lambda - lambdaOrig) / lambda;
- if (Math.abs(delta) < 1.0e-12) {
- break;
- }
- }
-
- results.mDistance = (float) (b * A * (sigma - deltaSigma));
- float initialBearing = (float) Math.atan2(cosU2 * sinLambda,
- cosU1 * sinU2 - sinU1 * cosU2 * cosLambda);
- initialBearing *= 180.0 / Math.PI;
- results.mInitialBearing = initialBearing;
- float finalBearing = (float) Math.atan2(cosU1 * sinLambda,
- -sinU1 * cosU2 + cosU1 * sinU2 * cosLambda);
- finalBearing *= 180.0 / Math.PI;
- results.mFinalBearing = finalBearing;
- results.mLat1 = lat1;
- results.mLat2 = lat2;
- results.mLon1 = lon1;
- results.mLon2 = lon2;
- }
-
- /**
- * Computes the approximate distance in meters between two
- * locations, and optionally the initial and final bearings of the
- * shortest path between them. Distance and bearing are defined using the
- * WGS84 ellipsoid.
- *
- * <p> The computed distance is stored in results[0]. If results has length
- * 2 or greater, the initial bearing is stored in results[1]. If results has
- * length 3 or greater, the final bearing is stored in results[2].
- *
- * @param startLatitude the starting latitude
- * @param startLongitude the starting longitude
- * @param endLatitude the ending latitude
- * @param endLongitude the ending longitude
- * @param results an array of floats to hold the results
- *
- * @throws IllegalArgumentException if results is null or has length < 1
- */
- public static void distanceBetween(double startLatitude, double startLongitude,
- double endLatitude, double endLongitude, float[] results) {
- if (results == null || results.length < 1) {
- throw new IllegalArgumentException("results is null or has length < 1");
- }
- BearingDistanceCache cache = sBearingDistanceCache.get();
- computeDistanceAndBearing(startLatitude, startLongitude,
- endLatitude, endLongitude, cache);
- results[0] = cache.mDistance;
- if (results.length > 1) {
- results[1] = cache.mInitialBearing;
- if (results.length > 2) {
- results[2] = cache.mFinalBearing;
- }
- }
- }
-
- /**
* Returns the approximate distance in meters between this
* location and the given location. Distance is defined using
* the WGS84 ellipsoid.
@@ -457,13 +189,13 @@
* @param dest the destination location
* @return the approximate distance in meters
*/
- public float distanceTo(Location dest) {
+ public @FloatRange float distanceTo(@NonNull Location dest) {
BearingDistanceCache cache = sBearingDistanceCache.get();
// See if we already have the result
- if (mLatitude != cache.mLat1 || mLongitude != cache.mLon1 ||
- dest.mLatitude != cache.mLat2 || dest.mLongitude != cache.mLon2) {
- computeDistanceAndBearing(mLatitude, mLongitude,
- dest.mLatitude, dest.mLongitude, cache);
+ if (mLatitudeDegrees != cache.mLat1 || mLongitudeDegrees != cache.mLon1
+ || dest.mLatitudeDegrees != cache.mLat2 || dest.mLongitudeDegrees != cache.mLon2) {
+ computeDistanceAndBearing(mLatitudeDegrees, mLongitudeDegrees,
+ dest.mLatitudeDegrees, dest.mLongitudeDegrees, cache);
}
return cache.mDistance;
}
@@ -478,30 +210,32 @@
* @param dest the destination location
* @return the initial bearing in degrees
*/
- public float bearingTo(Location dest) {
+ public float bearingTo(@NonNull Location dest) {
BearingDistanceCache cache = sBearingDistanceCache.get();
// See if we already have the result
- if (mLatitude != cache.mLat1 || mLongitude != cache.mLon1 ||
- dest.mLatitude != cache.mLat2 || dest.mLongitude != cache.mLon2) {
- computeDistanceAndBearing(mLatitude, mLongitude,
- dest.mLatitude, dest.mLongitude, cache);
+ if (mLatitudeDegrees != cache.mLat1 || mLongitudeDegrees != cache.mLon1
+ || dest.mLatitudeDegrees != cache.mLat2 || dest.mLongitudeDegrees != cache.mLon2) {
+ computeDistanceAndBearing(mLatitudeDegrees, mLongitudeDegrees,
+ dest.mLatitudeDegrees, dest.mLongitudeDegrees, cache);
}
return cache.mInitialBearing;
}
/**
- * Returns the name of the provider that generated this fix.
+ * Returns the name of the provider associated with this location.
*
- * @return the provider, or null if it has not been set
+ * @return the name of the provider
*/
- public String getProvider() {
+ public @Nullable String getProvider() {
return mProvider;
}
/**
- * Sets the name of the provider that generated this fix.
+ * Sets the name of the provider associated with this location
+ *
+ * @param provider the name of the provider
*/
- public void setProvider(String provider) {
+ public void setProvider(@Nullable String provider) {
mProvider = provider;
}
@@ -526,19 +260,19 @@
* set, however remember that the device clock may have changed since the location was
* generated.
*
- * @return UTC time of fix, in milliseconds since January 1, 1970.
+ * @return UTC time of this location
*/
- public long getTime() {
- return mTime;
+ public @IntRange long getTime() {
+ return mTimeMs;
}
/**
- * Set the UTC time of this fix, in milliseconds since epoch (January 1, 1970).
+ * Set the UTC time of this location, in milliseconds since epoch (January 1, 1970).
*
- * @param time UTC time of this fix, in milliseconds since January 1, 1970
+ * @param timeMs UTC time of this location
*/
- public void setTime(long time) {
- mTime = time;
+ public void setTime(@IntRange long timeMs) {
+ mTimeMs = timeMs;
}
/**
@@ -548,732 +282,413 @@
* reliably order or compare locations. This is reliable because elapsed realtime is guaranteed
* to be monotonic and continues to increment even when the system is in deep sleep (unlike
* {@link #getTime}). However, since elapsed realtime is with reference to system boot, it does
- * not make sense to use this value to order or compare locations across boot cycles.
+ * not make sense to use this value to order or compare locations across boot cycles or devices.
*
* <p>All locations generated by the {@link LocationManager} are guaranteed to have a valid
* elapsed realtime set.
*
- * @return elapsed realtime of fix, in nanoseconds since system boot.
+ * @return elapsed realtime of this location in nanoseconds
*/
- public long getElapsedRealtimeNanos() {
- return mElapsedRealtimeNanos;
+ public @IntRange long getElapsedRealtimeNanos() {
+ return mElapsedRealtimeNs;
}
/**
* Return the time of this fix in milliseconds of elapsed realtime since system boot.
*
+ * @return elapsed realtime of this location in milliseconds
* @see #getElapsedRealtimeNanos()
- *
- * @hide
*/
- public long getElapsedRealtimeMillis() {
- return NANOSECONDS.toMillis(getElapsedRealtimeNanos());
+ public @IntRange long getElapsedRealtimeMillis() {
+ return NANOSECONDS.toMillis(mElapsedRealtimeNs);
}
/**
- * Returns the age of this fix with respect to the current elapsed realtime.
+ * A convenience methods that returns the age of this location in milliseconds with respect to
+ * the current elapsed realtime.
*
- * @see #getElapsedRealtimeNanos()
- *
- * @hide
+ * @return age of this location in milliseconds
*/
- public long getElapsedRealtimeAgeMillis() {
+ public @IntRange long getElapsedRealtimeAgeMillis() {
return getElapsedRealtimeAgeMillis(SystemClock.elapsedRealtime());
}
/**
- * Returns the age of this fix with respect to the given elapsed realtime.
+ * A convenience method that returns the age of this location with respect to the given
+ * reference elapsed realtime.
*
- * @see #getElapsedRealtimeNanos()
- *
- * @hide
+ * @param referenceRealtimeMs reference realtime in milliseconds
+ * @return age of this location in milliseconds
*/
- public long getElapsedRealtimeAgeMillis(long referenceRealtimeMs) {
- return referenceRealtimeMs - NANOSECONDS.toMillis(mElapsedRealtimeNanos);
+ public @IntRange long getElapsedRealtimeAgeMillis(@IntRange long referenceRealtimeMs) {
+ return referenceRealtimeMs - getElapsedRealtimeMillis();
}
/**
- * Set the time of this fix, in elapsed realtime since system boot.
+ * Set the time of this location in nanoseconds of elapsed realtime since system boot.
*
- * @param time elapsed realtime of fix, in nanoseconds since system boot.
+ * @param elapsedRealtimeNs elapsed realtime in nanoseconds
*/
- public void setElapsedRealtimeNanos(long time) {
- mElapsedRealtimeNanos = time;
+ public void setElapsedRealtimeNanos(@IntRange long elapsedRealtimeNs) {
+ mElapsedRealtimeNs = elapsedRealtimeNs;
}
/**
- * Get estimate of the relative precision of the alignment of the
- * ElapsedRealtimeNanos timestamp, with the reported measurements in
- * nanoseconds (68% confidence).
+ * Get the uncertainty in nanoseconds of the precision of {@link #getElapsedRealtimeNanos()} at
+ * the 68th percentile confidence level. This means that there is 68% chance that the true
+ * elapsed realtime of this location is within {@link #getElapsedRealtimeNanos()} +/- this
+ * uncertainty.
*
- * This means that we have 68% confidence that the true timestamp of the
- * event is within ElapsedReatimeNanos +/- uncertainty.
+ * <p>This is only valid if {@link #hasElapsedRealtimeUncertaintyNanos()} is true.
*
- * Example :
- * - getElapsedRealtimeNanos() returns 10000000
- * - getElapsedRealtimeUncertaintyNanos() returns 1000000 (equivalent to 1millisecond)
- * This means that the event most likely happened between 9000000 and 11000000.
- *
- * @return uncertainty of elapsed real-time of fix, in nanoseconds.
+ * @return uncertainty in nanoseconds of the elapsed realtime of this location
*/
- public double getElapsedRealtimeUncertaintyNanos() {
- return mElapsedRealtimeUncertaintyNanos;
+ public @FloatRange double getElapsedRealtimeUncertaintyNanos() {
+ return mElapsedRealtimeUncertaintyNs;
}
/**
- * Set estimate of the relative precision of the alignment of the
- * ElapsedRealtimeNanos timestamp, with the reported measurements in
- * nanoseconds (68% confidence).
+ * Sets the uncertainty in nanoseconds of the precision of the elapsed realtime timestamp at a
+ * 68% confidence level.
*
- * @param time uncertainty of the elapsed real-time of fix, in nanoseconds.
+ * @param elapsedRealtimeUncertaintyNs uncertainty in nanoseconds of the elapsed realtime of
+ * this location
*/
- public void setElapsedRealtimeUncertaintyNanos(double time) {
- mElapsedRealtimeUncertaintyNanos = time;
+ public void setElapsedRealtimeUncertaintyNanos(
+ @FloatRange double elapsedRealtimeUncertaintyNs) {
+ mElapsedRealtimeUncertaintyNs = elapsedRealtimeUncertaintyNs;
mFieldsMask |= HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK;
}
/**
- * True if this location has a elapsed realtime accuracy.
+ * True if this location has a elapsed realtime uncertainty, false otherwise.
*/
public boolean hasElapsedRealtimeUncertaintyNanos() {
return (mFieldsMask & HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK) != 0;
}
-
/**
- * Get the latitude, in degrees.
- *
- * <p>All locations generated by the {@link LocationManager}
- * will have a valid latitude.
+ * Removes the elapsed realtime uncertainy from this location.
*/
- public double getLatitude() {
- return mLatitude;
+ public void removeElapsedRealtimeUncertaintyNanos() {
+ mFieldsMask &= ~HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK;
}
/**
- * Set the latitude, in degrees.
+ * Get the latitude in degrees. All locations generated by the {@link LocationManager} will have
+ * a valid latitude.
+ *
+ * @return latitude of this location
*/
- public void setLatitude(double latitude) {
- mLatitude = latitude;
+ public @FloatRange double getLatitude() {
+ return mLatitudeDegrees;
}
/**
- * Get the longitude, in degrees.
+ * Set the latitude of this location.
*
- * <p>All locations generated by the {@link LocationManager}
- * will have a valid longitude.
+ * @param latitudeDegrees latitude in degrees
*/
- public double getLongitude() {
- return mLongitude;
+ public void setLatitude(@FloatRange double latitudeDegrees) {
+ mLatitudeDegrees = latitudeDegrees;
}
/**
- * Set the longitude, in degrees.
+ * Get the longitude in degrees. All locations generated by the {@link LocationManager} will
+ * have a valid longitude.
+ *
+ * @return longitude of this location
*/
- public void setLongitude(double longitude) {
- mLongitude = longitude;
+ public @FloatRange double getLongitude() {
+ return mLongitudeDegrees;
}
/**
- * True if this location has an altitude.
+ * Set the longitude of this location.
+ *
+ * @param longitudeDegrees longitude in degrees
*/
- public boolean hasAltitude() {
- return (mFieldsMask & HAS_ALTITUDE_MASK) != 0;
+ public void setLongitude(@FloatRange double longitudeDegrees) {
+ mLongitudeDegrees = longitudeDegrees;
}
/**
- * Get the altitude if available, in meters above the WGS 84 reference
- * ellipsoid.
+ * Returns the estimated horizontal accuracy radius in meters of this location at the 68th
+ * percentile confidence level. This means that there is a 68% chance that the true location of
+ * the device is within a distance of this uncertainty of the reported location. Another way of
+ * putting this is that if a circle with a radius equal to this accuracy is drawn around the
+ * reported location, there is a 68% chance that the true location falls within this circle.
+ * This accuracy value is only valid for horizontal positioning, and not vertical positioning.
*
- * <p>If this location does not have an altitude then 0.0 is returned.
+ * <p>This is only valid if {@link #hasSpeed()} is true. All locations generated by the
+ * {@link LocationManager} include horizontal accuracy.
+ *
+ * @return horizontal accuracy of this location
*/
- public double getAltitude() {
- return mAltitude;
+ public @FloatRange float getAccuracy() {
+ return mHorizontalAccuracyMeters;
}
/**
- * Set the altitude, in meters above the WGS 84 reference ellipsoid.
+ * Set the horizontal accuracy in meters of this location.
*
- * <p>Following this call {@link #hasAltitude} will return true.
+ * @param horizontalAccuracyMeters horizontal altitude in meters
*/
- public void setAltitude(double altitude) {
- mAltitude = altitude;
- mFieldsMask |= HAS_ALTITUDE_MASK;
+ public void setAccuracy(@FloatRange float horizontalAccuracyMeters) {
+ mHorizontalAccuracyMeters = horizontalAccuracyMeters;
+ mFieldsMask |= HAS_HORIZONTAL_ACCURACY_MASK;
}
/**
- * Remove the altitude from this location.
- *
- * <p>Following this call {@link #hasAltitude} will return false,
- * and {@link #getAltitude} will return 0.0.
- *
- * @deprecated use a new Location object for location updates.
- */
- @Deprecated
- public void removeAltitude() {
- mAltitude = 0.0f;
- mFieldsMask &= ~HAS_ALTITUDE_MASK;
- }
-
- /**
- * True if this location has a speed.
- */
- public boolean hasSpeed() {
- return (mFieldsMask & HAS_SPEED_MASK) != 0;
- }
-
- /**
- * Get the speed if it is available, in meters/second over ground.
- *
- * <p>If this location does not have a speed then 0.0 is returned.
- */
- public float getSpeed() {
- return mSpeed;
- }
-
- /**
- * Set the speed, in meters/second over ground.
- *
- * <p>Following this call {@link #hasSpeed} will return true.
- */
- public void setSpeed(float speed) {
- mSpeed = speed;
- mFieldsMask |= HAS_SPEED_MASK;
- }
-
- /**
- * Remove the speed from this location.
- *
- * <p>Following this call {@link #hasSpeed} will return false,
- * and {@link #getSpeed} will return 0.0.
- *
- * @deprecated use a new Location object for location updates.
- */
- @Deprecated
- public void removeSpeed() {
- mSpeed = 0.0f;
- mFieldsMask &= ~HAS_SPEED_MASK;
- }
-
- /**
- * True if this location has a bearing.
- */
- public boolean hasBearing() {
- return (mFieldsMask & HAS_BEARING_MASK) != 0;
- }
-
- /**
- * Get the bearing, in degrees.
- *
- * <p>Bearing is the horizontal direction of travel of this device,
- * and is not related to the device orientation. It is guaranteed to
- * be in the range (0.0, 360.0] if the device has a bearing.
- *
- * <p>If this location does not have a bearing then 0.0 is returned.
- */
- public float getBearing() {
- return mBearing;
- }
-
- /**
- * Set the bearing, in degrees.
- *
- * <p>Bearing is the horizontal direction of travel of this device,
- * and is not related to the device orientation.
- *
- * <p>The input will be wrapped into the range (0.0, 360.0].
- */
- public void setBearing(float bearing) {
- while (bearing < 0.0f) {
- bearing += 360.0f;
- }
- while (bearing >= 360.0f) {
- bearing -= 360.0f;
- }
- mBearing = bearing;
- mFieldsMask |= HAS_BEARING_MASK;
- }
-
- /**
- * Remove the bearing from this location.
- *
- * <p>Following this call {@link #hasBearing} will return false,
- * and {@link #getBearing} will return 0.0.
- *
- * @deprecated use a new Location object for location updates.
- */
- @Deprecated
- public void removeBearing() {
- mBearing = 0.0f;
- mFieldsMask &= ~HAS_BEARING_MASK;
- }
-
- /**
- * True if this location has a horizontal accuracy.
- *
- * <p>All locations generated by the {@link LocationManager} have an horizontal accuracy.
+ * Returns true if this location has a horizontal accuracy, false otherwise.
*/
public boolean hasAccuracy() {
return (mFieldsMask & HAS_HORIZONTAL_ACCURACY_MASK) != 0;
}
/**
- * Get the estimated horizontal accuracy of this location, radial, in meters.
- *
- * <p>We define horizontal accuracy as the radius of 68% confidence. In other
- * words, if you draw a circle centered at this location's
- * latitude and longitude, and with a radius equal to the accuracy,
- * then there is a 68% probability that the true location is inside
- * the circle.
- *
- * <p>This accuracy estimation is only concerned with horizontal
- * accuracy, and does not indicate the accuracy of bearing,
- * velocity or altitude if those are included in this Location.
- *
- * <p>If this location does not have a horizontal accuracy, then 0.0 is returned.
- * All locations generated by the {@link LocationManager} include horizontal accuracy.
- */
- public float getAccuracy() {
- return mHorizontalAccuracyMeters;
- }
-
- /**
- * Set the estimated horizontal accuracy of this location, meters.
- *
- * <p>See {@link #getAccuracy} for the definition of horizontal accuracy.
- *
- * <p>Following this call {@link #hasAccuracy} will return true.
- */
- public void setAccuracy(float horizontalAccuracy) {
- mHorizontalAccuracyMeters = horizontalAccuracy;
- mFieldsMask |= HAS_HORIZONTAL_ACCURACY_MASK;
- }
-
- /**
* Remove the horizontal accuracy from this location.
- *
- * <p>Following this call {@link #hasAccuracy} will return false, and
- * {@link #getAccuracy} will return 0.0.
- *
- * @deprecated use a new Location object for location updates.
*/
- @Deprecated
public void removeAccuracy() {
- mHorizontalAccuracyMeters = 0.0f;
mFieldsMask &= ~HAS_HORIZONTAL_ACCURACY_MASK;
}
/**
- * True if this location has a vertical accuracy.
+ * The altitude of this location in meters above the WGS84 reference ellipsoid.
+ *
+ * <p>This is only valid if {@link #hasAltitude()} is true.
+ *
+ * @return altitude of this location
+ */
+ public @FloatRange double getAltitude() {
+ return mAltitudeMeters;
+ }
+
+ /**
+ * Set the altitude of this location in meters above the WGS84 reference ellipsoid.
+ *
+ * @param altitudeMeters altitude in meters
+ */
+ public void setAltitude(@FloatRange double altitudeMeters) {
+ mAltitudeMeters = altitudeMeters;
+ mFieldsMask |= HAS_ALTITUDE_MASK;
+ }
+
+ /**
+ * Returns true if this location has an altitude, false otherwise.
+ */
+ public boolean hasAltitude() {
+ return (mFieldsMask & HAS_ALTITUDE_MASK) != 0;
+ }
+
+ /**
+ * Removes the altitude from this location.
+ */
+ public void removeAltitude() {
+ mFieldsMask &= ~HAS_ALTITUDE_MASK;
+ }
+
+ /**
+ * Returns the estimated altitude accuracy in meters of this location at the 68th percentile
+ * confidence level. This means that there is 68% chance that the true altitude of this location
+ * falls within {@link #getAltitude()} ()} +/- this uncertainty.
+ *
+ * <p>This is only valid if {@link #hasVerticalAccuracy()} is true.
+ *
+ * @return vertical accuracy of this location
+ */
+ public @FloatRange float getVerticalAccuracyMeters() {
+ return mAltitudeAccuracyMeters;
+ }
+
+ /**
+ * Set the altitude accuracy of this location in meters.
+ *
+ * @param altitudeAccuracyMeters altitude accuracy in meters
+ */
+ public void setVerticalAccuracyMeters(@FloatRange float altitudeAccuracyMeters) {
+ mAltitudeAccuracyMeters = altitudeAccuracyMeters;
+ mFieldsMask |= HAS_ALTITUDE_ACCURACY_MASK;
+ }
+
+ /**
+ * Returns true if this location has a vertical accuracy, false otherwise.
*/
public boolean hasVerticalAccuracy() {
- return (mFieldsMask & HAS_VERTICAL_ACCURACY_MASK) != 0;
- }
-
- /**
- * Get the estimated vertical accuracy of this location, in meters.
- *
- * <p>We define vertical accuracy at 68% confidence. Specifically, as 1-side of the 2-sided
- * range above and below the estimated altitude reported by {@link #getAltitude()}, within which
- * there is a 68% probability of finding the true altitude.
- *
- * <p>In the case where the underlying distribution is assumed Gaussian normal, this would be
- * considered 1 standard deviation.
- *
- * <p>For example, if {@link #getAltitude()} returns 150m, and this method returns 20m then
- * there is a 68% probability of the true altitude being between 130m and 170m.
- */
- public float getVerticalAccuracyMeters() {
- return mVerticalAccuracyMeters;
- }
-
- /**
- * Set the estimated vertical accuracy of this location, meters.
- *
- * <p>See {@link #getVerticalAccuracyMeters} for the definition of vertical accuracy.
- *
- * <p>Following this call {@link #hasVerticalAccuracy} will return true.
- */
- public void setVerticalAccuracyMeters(float verticalAccuracyMeters) {
- mVerticalAccuracyMeters = verticalAccuracyMeters;
- mFieldsMask |= HAS_VERTICAL_ACCURACY_MASK;
+ return (mFieldsMask & HAS_ALTITUDE_ACCURACY_MASK) != 0;
}
/**
* Remove the vertical accuracy from this location.
- *
- * <p>Following this call {@link #hasVerticalAccuracy} will return false, and
- * {@link #getVerticalAccuracyMeters} will return 0.0.
- *
- * @deprecated use a new Location object for location updates.
- * @removed
*/
- @Deprecated
public void removeVerticalAccuracy() {
- mVerticalAccuracyMeters = 0.0f;
- mFieldsMask &= ~HAS_VERTICAL_ACCURACY_MASK;
+ mFieldsMask &= ~HAS_ALTITUDE_ACCURACY_MASK;
}
/**
- * True if this location has a speed accuracy.
+ * Returns the speed at the time of this location in meters per second. Note that the speed
+ * returned here may be more accurate than would be obtained simply by calculating
+ * {@code distance / time} for sequential positions, such as if the Doppler measurements from
+ * GNSS satellites are taken into account.
+ *
+ * <p>This is only valid if {@link #hasSpeed()} is true.
+ *
+ * @return speed at the time of this location
+ */
+ public @FloatRange float getSpeed() {
+ return mSpeedMetersPerSecond;
+ }
+
+ /**
+ * Set the speed at the time of this location, in meters per second. Prefer not to set negative
+ * speeds.
+ *
+ * @param speedMetersPerSecond speed in meters per second
+ */
+ public void setSpeed(@FloatRange float speedMetersPerSecond) {
+ mSpeedMetersPerSecond = speedMetersPerSecond;
+ mFieldsMask |= HAS_SPEED_MASK;
+ }
+
+ /**
+ * True if this location has a speed, false otherwise.
+ */
+ public boolean hasSpeed() {
+ return (mFieldsMask & HAS_SPEED_MASK) != 0;
+ }
+
+ /**
+ * Remove the speed from this location.
+ */
+ public void removeSpeed() {
+ mFieldsMask &= ~HAS_SPEED_MASK;
+ }
+
+ /**
+ * Returns the estimated speed accuracy in meters per second of this location at the 68th
+ * percentile confidence level. This means that there is 68% chance that the true speed at the
+ * time of this location falls within {@link #getSpeed()} ()} +/- this uncertainty.
+ *
+ * <p>This is only valid if {@link #hasSpeedAccuracy()} is true.
+ *
+ * @return vertical accuracy of this location
+ */
+ public @FloatRange float getSpeedAccuracyMetersPerSecond() {
+ return mSpeedAccuracyMetersPerSecond;
+ }
+
+ /**
+ * Set the speed accuracy of this location in meters per second.
+ *
+ * @param speedAccuracyMeterPerSecond speed accuracy in meters per second
+ */
+ public void setSpeedAccuracyMetersPerSecond(@FloatRange float speedAccuracyMeterPerSecond) {
+ mSpeedAccuracyMetersPerSecond = speedAccuracyMeterPerSecond;
+ mFieldsMask |= HAS_SPEED_ACCURACY_MASK;
+ }
+
+ /**
+ * Returns true if this location has a speed accuracy, false otherwise.
*/
public boolean hasSpeedAccuracy() {
return (mFieldsMask & HAS_SPEED_ACCURACY_MASK) != 0;
}
/**
- * Get the estimated speed accuracy of this location, in meters per second.
- *
- * <p>We define speed accuracy at 68% confidence. Specifically, as 1-side of the 2-sided range
- * above and below the estimated speed reported by {@link #getSpeed()}, within which there is a
- * 68% probability of finding the true speed.
- *
- * <p>In the case where the underlying distribution is assumed Gaussian normal, this would be
- * considered 1 standard deviation.
- *
- * <p>For example, if {@link #getSpeed()} returns 5m/s, and this method returns 1m/s, then there
- * is a 68% probability of the true speed being between 4m/s and 6m/s.
- *
- * <p>Note that the speed and speed accuracy may be more accurate than would be obtained simply
- * from differencing sequential positions, such as when the Doppler measurements from GNSS
- * satellites are taken into account.
- */
- public float getSpeedAccuracyMetersPerSecond() {
- return mSpeedAccuracyMetersPerSecond;
- }
-
- /**
- * Set the estimated speed accuracy of this location, meters per second.
- *
- * <p>See {@link #getSpeedAccuracyMetersPerSecond} for the definition of speed accuracy.
- *
- * <p>Following this call {@link #hasSpeedAccuracy} will return true.
- */
- public void setSpeedAccuracyMetersPerSecond(float speedAccuracyMeterPerSecond) {
- mSpeedAccuracyMetersPerSecond = speedAccuracyMeterPerSecond;
- mFieldsMask |= HAS_SPEED_ACCURACY_MASK;
- }
-
- /**
* Remove the speed accuracy from this location.
- *
- * <p>Following this call {@link #hasSpeedAccuracy} will return false, and
- * {@link #getSpeedAccuracyMetersPerSecond} will return 0.0.
- *
- * @deprecated use a new Location object for location updates.
- * @removed
*/
- @Deprecated
public void removeSpeedAccuracy() {
- mSpeedAccuracyMetersPerSecond = 0.0f;
mFieldsMask &= ~HAS_SPEED_ACCURACY_MASK;
}
/**
- * True if this location has a bearing accuracy.
+ * Returns the bearing at the time of this location in degrees. Bearing is the horizontal
+ * direction of travel of this device and is unrelated to the device orientation. The bearing
+ * is guaranteed to be in the range [0, 360).
+ *
+ * <p>This is only valid if {@link #hasBearing()} is true.
+ *
+ * @return bearing at the time of this location
+ */
+ public @FloatRange(from = 0f, to = 360f, toInclusive = false) float getBearing() {
+ return mBearingDegrees;
+ }
+
+ /**
+ * Set the bearing at the time of this location, in degrees. The given bearing will be converted
+ * into the range [0, 360).
+ *
+ * <p class="note">Note: passing in extremely high or low floating point values to this function
+ * may produce strange results due to the intricacies of floating point math.
+ *
+ * @param bearingDegrees bearing in degrees
+ */
+ public void setBearing(
+ @FloatRange(fromInclusive = false, toInclusive = false) float bearingDegrees) {
+ Preconditions.checkArgument(Float.isFinite(bearingDegrees));
+
+ // final addition of zero is to remove -0 results. while these are technically within the
+ // range [0, 360) according to IEEE semantics, this eliminates possible user confusion.
+ float modBearing = bearingDegrees % 360f + 0f;
+ if (modBearing < 0) {
+ modBearing += 360f;
+ }
+ mBearingDegrees = modBearing;
+ mFieldsMask |= HAS_BEARING_MASK;
+ }
+
+ /**
+ * True if this location has a bearing, false otherwise.
+ */
+ public boolean hasBearing() {
+ return (mFieldsMask & HAS_BEARING_MASK) != 0;
+ }
+
+ /**
+ * Remove the bearing from this location.
+ */
+ public void removeBearing() {
+ mFieldsMask &= ~HAS_BEARING_MASK;
+ }
+
+ /**
+ * Returns the estimated bearing accuracy in degrees of this location at the 68th percentile
+ * confidence level. This means that there is 68% chance that the true bearing at the
+ * time of this location falls within {@link #getBearing()} ()} +/- this uncertainty.
+ *
+ * <p>This is only valid if {@link #hasBearingAccuracy()} ()} is true.
+ *
+ * @return bearing accuracy in degrees of this location
+ */
+ public @FloatRange float getBearingAccuracyDegrees() {
+ return mBearingAccuracyDegrees;
+ }
+
+ /**
+ * Set the bearing accuracy in degrees of this location.
+ *
+ * @param bearingAccuracyDegrees bearing accuracy in degrees
+ */
+ public void setBearingAccuracyDegrees(@FloatRange float bearingAccuracyDegrees) {
+ mBearingAccuracyDegrees = bearingAccuracyDegrees;
+ mFieldsMask |= HAS_BEARING_ACCURACY_MASK;
+ }
+
+ /**
+ * Returns true if this location has a bearing accuracy, false otherwise.
*/
public boolean hasBearingAccuracy() {
return (mFieldsMask & HAS_BEARING_ACCURACY_MASK) != 0;
}
/**
- * Get the estimated bearing accuracy of this location, in degrees.
- *
- * <p>We define bearing accuracy at 68% confidence. Specifically, as 1-side of the 2-sided range
- * on each side of the estimated bearing reported by {@link #getBearing()}, within which there
- * is a 68% probability of finding the true bearing.
- *
- * <p>In the case where the underlying distribution is assumed Gaussian normal, this would be
- * considered 1 standard deviation.
- *
- * <p>For example, if {@link #getBearing()} returns 60°, and this method returns 10°, then there
- * is a 68% probability of the true bearing being between 50° and 70°.
- */
- public float getBearingAccuracyDegrees() {
- return mBearingAccuracyDegrees;
- }
-
- /**
- * Set the estimated bearing accuracy of this location, degrees.
- *
- * <p>See {@link #getBearingAccuracyDegrees} for the definition of bearing accuracy.
- *
- * <p>Following this call {@link #hasBearingAccuracy} will return true.
- */
- public void setBearingAccuracyDegrees(float bearingAccuracyDegrees) {
- mBearingAccuracyDegrees = bearingAccuracyDegrees;
- mFieldsMask |= HAS_BEARING_ACCURACY_MASK;
- }
-
- /**
* Remove the bearing accuracy from this location.
- *
- * <p>Following this call {@link #hasBearingAccuracy} will return false, and
- * {@link #getBearingAccuracyDegrees} will return 0.0.
- *
- * @deprecated use a new Location object for location updates.
- * @removed
*/
- @Deprecated
public void removeBearingAccuracy() {
- mBearingAccuracyDegrees = 0.0f;
mFieldsMask &= ~HAS_BEARING_ACCURACY_MASK;
}
/**
- * Return true if this Location object is complete.
- *
- * <p>A location object is currently considered complete if it has
- * a valid provider, accuracy, wall-clock time and elapsed real-time.
- *
- * <p>All locations supplied by the {@link LocationManager} to
- * applications must be complete.
- *
- * @see #makeComplete
- * @hide
- */
- @SystemApi
- public boolean isComplete() {
- return mProvider != null && hasAccuracy() && mTime != 0 && mElapsedRealtimeNanos != 0;
- }
-
- /**
- * Helper to fill incomplete fields.
- *
- * <p>Used to assist in backwards compatibility with
- * Location objects received from applications.
- *
- * @see #isComplete
- * @hide
- */
- @SystemApi
- public void makeComplete() {
- if (mProvider == null) mProvider = "?";
- if (!hasAccuracy()) {
- mFieldsMask |= HAS_HORIZONTAL_ACCURACY_MASK;
- mHorizontalAccuracyMeters = 100.0f;
- }
- if (mTime == 0) mTime = System.currentTimeMillis();
- if (mElapsedRealtimeNanos == 0) mElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
- }
-
- /**
- * Returns additional provider-specific information about the location fix as a Bundle. The keys
- * and values are determined by the provider. If no additional information is available, null
- * is returned.
- *
- * <p> A number of common key/value pairs are listed
- * below. Providers that use any of the keys on this list must
- * provide the corresponding value as described below.
- *
- * <ul>
- * <li> satellites - the number of satellites used to derive the fix
- * </ul>
- */
- public Bundle getExtras() {
- return mExtras;
- }
-
- /**
- * Sets the extra information associated with this fix to the given Bundle.
- *
- * <p>Note this stores a copy of the given extras, so any changes to extras after calling this
- * method won't be reflected in the location bundle.
- */
- public void setExtras(@Nullable Bundle extras) {
- mExtras = (extras == null) ? null : new Bundle(extras);
- }
-
- /**
- * Location equality is provided primarily for test purposes. Comparing locations for equality
- * in production may indicate incorrect assumptions, and should be avoided whenever possible.
- *
- * <p>{@inheritDoc}
- */
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- Location location = (Location) o;
- return mTime == location.mTime
- && mElapsedRealtimeNanos == location.mElapsedRealtimeNanos
- && hasElapsedRealtimeUncertaintyNanos()
- == location.hasElapsedRealtimeUncertaintyNanos()
- && (!hasElapsedRealtimeUncertaintyNanos() || Double.compare(
- location.mElapsedRealtimeUncertaintyNanos, mElapsedRealtimeUncertaintyNanos) == 0)
- && Double.compare(location.mLatitude, mLatitude) == 0
- && Double.compare(location.mLongitude, mLongitude) == 0
- && hasAltitude() == location.hasAltitude()
- && (!hasAltitude() || Double.compare(location.mAltitude, mAltitude) == 0)
- && hasSpeed() == location.hasSpeed()
- && (!hasSpeed() || Float.compare(location.mSpeed, mSpeed) == 0)
- && hasBearing() == location.hasBearing()
- && (!hasBearing() || Float.compare(location.mBearing, mBearing) == 0)
- && hasAccuracy() == location.hasAccuracy()
- && (!hasAccuracy() || Float.compare(location.mHorizontalAccuracyMeters,
- mHorizontalAccuracyMeters) == 0)
- && hasVerticalAccuracy() == location.hasVerticalAccuracy()
- && (!hasVerticalAccuracy() || Float.compare(location.mVerticalAccuracyMeters,
- mVerticalAccuracyMeters) == 0)
- && hasSpeedAccuracy() == location.hasSpeedAccuracy()
- && (!hasSpeedAccuracy() || Float.compare(location.mSpeedAccuracyMetersPerSecond,
- mSpeedAccuracyMetersPerSecond) == 0)
- && hasBearingAccuracy() == location.hasBearingAccuracy()
- && (!hasBearingAccuracy() || Float.compare(location.mBearingAccuracyDegrees,
- mBearingAccuracyDegrees) == 0)
- && Objects.equals(mProvider, location.mProvider)
- && areExtrasEqual(mExtras, location.mExtras);
- }
-
- private static boolean areExtrasEqual(@Nullable Bundle extras1, @Nullable Bundle extras2) {
- if ((extras1 == null || extras1.isEmpty()) && (extras2 == null || extras2.isEmpty())) {
- return true;
- } else if (extras1 == null || extras2 == null) {
- return false;
- } else {
- return extras1.kindofEquals(extras2);
- }
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mProvider, mElapsedRealtimeNanos, mLatitude, mLongitude);
- }
-
- @Override
- public String toString() {
- StringBuilder s = new StringBuilder();
- s.append("Location[");
- s.append(mProvider);
- s.append(" ").append(String.format(Locale.ROOT, "%.6f,%.6f", mLatitude, mLongitude));
- if (hasAccuracy()) {
- s.append(" hAcc=").append(mHorizontalAccuracyMeters);
- }
- s.append(" et=");
- TimeUtils.formatDuration(getElapsedRealtimeMillis(), s);
- if (hasAltitude()) {
- s.append(" alt=").append(mAltitude);
- if (hasVerticalAccuracy()) {
- s.append(" vAcc=").append(mVerticalAccuracyMeters);
- }
- }
- if (hasSpeed()) {
- s.append(" vel=").append(mSpeed);
- if (hasSpeedAccuracy()) {
- s.append(" sAcc=").append(mSpeedAccuracyMetersPerSecond);
- }
- }
- if (hasBearing()) {
- s.append(" bear=").append(mBearing);
- if (hasBearingAccuracy()) {
- s.append(" bAcc=").append(mBearingAccuracyDegrees);
- }
- }
- if (isMock()) {
- s.append(" mock");
- }
-
- if (mExtras != null) {
- s.append(" {").append(mExtras).append('}');
- }
- s.append(']');
- return s.toString();
- }
-
- public void dump(Printer pw, String prefix) {
- pw.println(prefix + toString());
- }
-
- public static final @NonNull Parcelable.Creator<Location> CREATOR =
- new Parcelable.Creator<Location>() {
- @Override
- public Location createFromParcel(Parcel in) {
- Location l = new Location(in.readString());
- l.mFieldsMask = in.readInt();
- l.mTime = in.readLong();
- l.mElapsedRealtimeNanos = in.readLong();
- if (l.hasElapsedRealtimeUncertaintyNanos()) {
- l.mElapsedRealtimeUncertaintyNanos = in.readDouble();
- }
- l.mLatitude = in.readDouble();
- l.mLongitude = in.readDouble();
- if (l.hasAltitude()) {
- l.mAltitude = in.readDouble();
- }
- if (l.hasSpeed()) {
- l.mSpeed = in.readFloat();
- }
- if (l.hasBearing()) {
- l.mBearing = in.readFloat();
- }
- if (l.hasAccuracy()) {
- l.mHorizontalAccuracyMeters = in.readFloat();
- }
- if (l.hasVerticalAccuracy()) {
- l.mVerticalAccuracyMeters = in.readFloat();
- }
- if (l.hasSpeedAccuracy()) {
- l.mSpeedAccuracyMetersPerSecond = in.readFloat();
- }
- if (l.hasBearingAccuracy()) {
- l.mBearingAccuracyDegrees = in.readFloat();
- }
- l.mExtras = Bundle.setDefusable(in.readBundle(), true);
- return l;
- }
-
- @Override
- public Location[] newArray(int size) {
- return new Location[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeString(mProvider);
- parcel.writeInt(mFieldsMask);
- parcel.writeLong(mTime);
- parcel.writeLong(mElapsedRealtimeNanos);
- if (hasElapsedRealtimeUncertaintyNanos()) {
- parcel.writeDouble(mElapsedRealtimeUncertaintyNanos);
- }
- parcel.writeDouble(mLatitude);
- parcel.writeDouble(mLongitude);
- if (hasAltitude()) {
- parcel.writeDouble(mAltitude);
- }
- if (hasSpeed()) {
- parcel.writeFloat(mSpeed);
- }
- if (hasBearing()) {
- parcel.writeFloat(mBearing);
- }
- if (hasAccuracy()) {
- parcel.writeFloat(mHorizontalAccuracyMeters);
- }
- if (hasVerticalAccuracy()) {
- parcel.writeFloat(mVerticalAccuracyMeters);
- }
- if (hasSpeedAccuracy()) {
- parcel.writeFloat(mSpeedAccuracyMetersPerSecond);
- }
- if (hasBearingAccuracy()) {
- parcel.writeFloat(mBearingAccuracyDegrees);
- }
- parcel.writeBundle(mExtras);
- }
-
- /**
* Returns true if the Location came from a mock provider.
*
* @return true if this Location came from a mock provider, false otherwise
@@ -1320,9 +735,497 @@
}
/**
- * Caches data used to compute distance and bearing (so successive calls to {@link #distanceTo}
- * and {@link #bearingTo} don't duplicate work.
+ * Returns an optional bundle of additional information associated with this location. The keys
+ * and values within the bundle are determined by the location provider.
+ *
+ * <p> Common key/value pairs are listed below. There is no guarantee that these key/value pairs
+ * will be present for any location.
+ *
+ * <ul>
+ * <li> satellites - the number of satellites used to derive the GNSS fix
+ * </ul>
*/
+ public @Nullable Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
+ * Sets the extra information associated with this fix to the given Bundle.
+ *
+ * <p>Note this stores a copy of the given extras, so any changes to extras after calling this
+ * method won't be reflected in the location bundle.
+ */
+ public void setExtras(@Nullable Bundle extras) {
+ mExtras = (extras == null) ? null : new Bundle(extras);
+ }
+
+ /**
+ * Return true if this location is considered complete. A location is considered complete if it
+ * has a non-null provider, accuracy, and non-zero time and elapsed realtime. The exact
+ * definition of completeness may change over time.
+ *
+ * <p>All locations supplied by the {@link LocationManager} are guaranteed to be complete.
+ */
+ public boolean isComplete() {
+ return mProvider != null && hasAccuracy() && mTimeMs != 0 && mElapsedRealtimeNs != 0;
+ }
+
+ /**
+ * Helper to fill incomplete fields with valid (but likely nonsensical) values.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void makeComplete() {
+ if (mProvider == null) {
+ mProvider = "";
+ }
+ if (!hasAccuracy()) {
+ mFieldsMask |= HAS_HORIZONTAL_ACCURACY_MASK;
+ mHorizontalAccuracyMeters = 100.0f;
+ }
+ if (mTimeMs == 0) {
+ mTimeMs = System.currentTimeMillis();
+ }
+ if (mElapsedRealtimeNs == 0) {
+ mElapsedRealtimeNs = SystemClock.elapsedRealtimeNanos();
+ }
+ }
+
+ /**
+ * Location equality is provided primarily for test purposes. Comparing locations for equality
+ * in production may indicate incorrect assumptions, and should be avoided whenever possible.
+ *
+ * <p>{@inheritDoc}
+ */
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Location)) {
+ return false;
+ }
+
+ Location location = (Location) o;
+ return mTimeMs == location.mTimeMs
+ && mElapsedRealtimeNs == location.mElapsedRealtimeNs
+ && hasElapsedRealtimeUncertaintyNanos()
+ == location.hasElapsedRealtimeUncertaintyNanos()
+ && (!hasElapsedRealtimeUncertaintyNanos() || Double.compare(
+ location.mElapsedRealtimeUncertaintyNs, mElapsedRealtimeUncertaintyNs) == 0)
+ && Double.compare(location.mLatitudeDegrees, mLatitudeDegrees) == 0
+ && Double.compare(location.mLongitudeDegrees, mLongitudeDegrees) == 0
+ && hasAltitude() == location.hasAltitude()
+ && (!hasAltitude() || Double.compare(location.mAltitudeMeters, mAltitudeMeters)
+ == 0)
+ && hasSpeed() == location.hasSpeed()
+ && (!hasSpeed() || Float.compare(location.mSpeedMetersPerSecond,
+ mSpeedMetersPerSecond) == 0)
+ && hasBearing() == location.hasBearing()
+ && (!hasBearing() || Float.compare(location.mBearingDegrees, mBearingDegrees) == 0)
+ && hasAccuracy() == location.hasAccuracy()
+ && (!hasAccuracy() || Float.compare(location.mHorizontalAccuracyMeters,
+ mHorizontalAccuracyMeters) == 0)
+ && hasVerticalAccuracy() == location.hasVerticalAccuracy()
+ && (!hasVerticalAccuracy() || Float.compare(location.mAltitudeAccuracyMeters,
+ mAltitudeAccuracyMeters) == 0)
+ && hasSpeedAccuracy() == location.hasSpeedAccuracy()
+ && (!hasSpeedAccuracy() || Float.compare(location.mSpeedAccuracyMetersPerSecond,
+ mSpeedAccuracyMetersPerSecond) == 0)
+ && hasBearingAccuracy() == location.hasBearingAccuracy()
+ && (!hasBearingAccuracy() || Float.compare(location.mBearingAccuracyDegrees,
+ mBearingAccuracyDegrees) == 0)
+ && Objects.equals(mProvider, location.mProvider)
+ && areExtrasEqual(mExtras, location.mExtras);
+ }
+
+ private static boolean areExtrasEqual(@Nullable Bundle extras1, @Nullable Bundle extras2) {
+ if ((extras1 == null || extras1.isEmpty()) && (extras2 == null || extras2.isEmpty())) {
+ return true;
+ } else if (extras1 == null || extras2 == null) {
+ return false;
+ } else {
+ return extras1.kindofEquals(extras2);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mProvider, mElapsedRealtimeNs, mLatitudeDegrees, mLongitudeDegrees);
+ }
+
+ @Override
+ public @NonNull String toString() {
+ StringBuilder s = new StringBuilder();
+ s.append("Location[");
+ s.append(mProvider);
+ s.append(" ").append(String.format(Locale.ROOT, "%.6f,%.6f", mLatitudeDegrees,
+ mLongitudeDegrees));
+ if (hasAccuracy()) {
+ s.append(" hAcc=").append(mHorizontalAccuracyMeters);
+ }
+ s.append(" et=");
+ TimeUtils.formatDuration(getElapsedRealtimeMillis(), s);
+ if (hasAltitude()) {
+ s.append(" alt=").append(mAltitudeMeters);
+ if (hasVerticalAccuracy()) {
+ s.append(" vAcc=").append(mAltitudeAccuracyMeters);
+ }
+ }
+ if (hasSpeed()) {
+ s.append(" vel=").append(mSpeedMetersPerSecond);
+ if (hasSpeedAccuracy()) {
+ s.append(" sAcc=").append(mSpeedAccuracyMetersPerSecond);
+ }
+ }
+ if (hasBearing()) {
+ s.append(" bear=").append(mBearingDegrees);
+ if (hasBearingAccuracy()) {
+ s.append(" bAcc=").append(mBearingAccuracyDegrees);
+ }
+ }
+ if (isMock()) {
+ s.append(" mock");
+ }
+
+ if (mExtras != null && !mExtras.isEmpty()) {
+ s.append(" {").append(mExtras).append('}');
+ }
+ s.append(']');
+ return s.toString();
+ }
+
+ /** Dumps location. */
+ public void dump(@NonNull Printer pw, @Nullable String prefix) {
+ pw.println(prefix + this);
+ }
+
+ public static final @NonNull Parcelable.Creator<Location> CREATOR =
+ new Parcelable.Creator<Location>() {
+ @Override
+ public Location createFromParcel(Parcel in) {
+ Location l = new Location(in.readString8());
+ l.mFieldsMask = in.readInt();
+ l.mTimeMs = in.readLong();
+ l.mElapsedRealtimeNs = in.readLong();
+ if (l.hasElapsedRealtimeUncertaintyNanos()) {
+ l.mElapsedRealtimeUncertaintyNs = in.readDouble();
+ }
+ l.mLatitudeDegrees = in.readDouble();
+ l.mLongitudeDegrees = in.readDouble();
+ if (l.hasAltitude()) {
+ l.mAltitudeMeters = in.readDouble();
+ }
+ if (l.hasSpeed()) {
+ l.mSpeedMetersPerSecond = in.readFloat();
+ }
+ if (l.hasBearing()) {
+ l.mBearingDegrees = in.readFloat();
+ }
+ if (l.hasAccuracy()) {
+ l.mHorizontalAccuracyMeters = in.readFloat();
+ }
+ if (l.hasVerticalAccuracy()) {
+ l.mAltitudeAccuracyMeters = in.readFloat();
+ }
+ if (l.hasSpeedAccuracy()) {
+ l.mSpeedAccuracyMetersPerSecond = in.readFloat();
+ }
+ if (l.hasBearingAccuracy()) {
+ l.mBearingAccuracyDegrees = in.readFloat();
+ }
+ l.mExtras = Bundle.setDefusable(in.readBundle(), true);
+ return l;
+ }
+
+ @Override
+ public Location[] newArray(int size) {
+ return new Location[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeString8(mProvider);
+ parcel.writeInt(mFieldsMask);
+ parcel.writeLong(mTimeMs);
+ parcel.writeLong(mElapsedRealtimeNs);
+ if (hasElapsedRealtimeUncertaintyNanos()) {
+ parcel.writeDouble(mElapsedRealtimeUncertaintyNs);
+ }
+ parcel.writeDouble(mLatitudeDegrees);
+ parcel.writeDouble(mLongitudeDegrees);
+ if (hasAltitude()) {
+ parcel.writeDouble(mAltitudeMeters);
+ }
+ if (hasSpeed()) {
+ parcel.writeFloat(mSpeedMetersPerSecond);
+ }
+ if (hasBearing()) {
+ parcel.writeFloat(mBearingDegrees);
+ }
+ if (hasAccuracy()) {
+ parcel.writeFloat(mHorizontalAccuracyMeters);
+ }
+ if (hasVerticalAccuracy()) {
+ parcel.writeFloat(mAltitudeAccuracyMeters);
+ }
+ if (hasSpeedAccuracy()) {
+ parcel.writeFloat(mSpeedAccuracyMetersPerSecond);
+ }
+ if (hasBearingAccuracy()) {
+ parcel.writeFloat(mBearingAccuracyDegrees);
+ }
+ parcel.writeBundle(mExtras);
+ }
+
+ /**
+ * Converts a latitude/longitude coordinate to a String representation. The outputType must be
+ * one of {@link #FORMAT_DEGREES}, {@link #FORMAT_MINUTES}, or {@link #FORMAT_SECONDS}. The
+ * coordinate must be a number between -180.0 and 180.0, inclusive. This conversion is performed
+ * in a method that is dependent on the default locale, and so is not guaranteed to round-trip
+ * with {@link #convert(String)}.
+ *
+ * @throws IllegalArgumentException if coordinate is less than -180.0, greater than 180.0, or is
+ * not a number.
+ * @throws IllegalArgumentException if outputType is not a recognized value.
+ */
+ public static @NonNull String convert(@FloatRange double coordinate, @Format int outputType) {
+ Preconditions.checkArgumentInRange(coordinate, -180D, 180D, "coordinate");
+ Preconditions.checkArgument(outputType == FORMAT_DEGREES || outputType == FORMAT_MINUTES
+ || outputType == FORMAT_SECONDS, "%d is an unrecognized format", outputType);
+
+ StringBuilder sb = new StringBuilder();
+
+ if (coordinate < 0) {
+ sb.append('-');
+ coordinate = -coordinate;
+ }
+
+ DecimalFormat df = new DecimalFormat("###.#####");
+ if (outputType == FORMAT_MINUTES || outputType == FORMAT_SECONDS) {
+ int degrees = (int) Math.floor(coordinate);
+ sb.append(degrees);
+ sb.append(':');
+ coordinate -= degrees;
+ coordinate *= 60.0;
+ if (outputType == FORMAT_SECONDS) {
+ int minutes = (int) Math.floor(coordinate);
+ sb.append(minutes);
+ sb.append(':');
+ coordinate -= minutes;
+ coordinate *= 60.0;
+ }
+ }
+ sb.append(df.format(coordinate));
+ return sb.toString();
+ }
+
+ /**
+ * Converts a String in one of the formats described by {@link #FORMAT_DEGREES},
+ * {@link #FORMAT_MINUTES}, or {@link #FORMAT_SECONDS} into a double. This conversion is
+ * performed in a locale agnostic method, and so is not guaranteed to round-trip with
+ * {@link #convert(double, int)}.
+ *
+ * @throws NullPointerException if coordinate is null
+ * @throws IllegalArgumentException if the coordinate is not
+ * in one of the valid formats.
+ */
+ public static @FloatRange double convert(@NonNull String coordinate) {
+ Objects.requireNonNull(coordinate);
+
+ boolean negative = false;
+ if (coordinate.charAt(0) == '-') {
+ coordinate = coordinate.substring(1);
+ negative = true;
+ }
+
+ StringTokenizer st = new StringTokenizer(coordinate, ":");
+ int tokens = st.countTokens();
+ if (tokens < 1) {
+ throw new IllegalArgumentException("coordinate=" + coordinate);
+ }
+ try {
+ String degrees = st.nextToken();
+ double val;
+ if (tokens == 1) {
+ val = Double.parseDouble(degrees);
+ return negative ? -val : val;
+ }
+
+ String minutes = st.nextToken();
+ int deg = Integer.parseInt(degrees);
+ double min;
+ double sec = 0.0;
+ boolean secPresent = false;
+
+ if (st.hasMoreTokens()) {
+ min = Integer.parseInt(minutes);
+ String seconds = st.nextToken();
+ sec = Double.parseDouble(seconds);
+ secPresent = true;
+ } else {
+ min = Double.parseDouble(minutes);
+ }
+
+ boolean isNegative180 = negative && deg == 180 && min == 0 && sec == 0;
+
+ // deg must be in [0, 179] except for the case of -180 degrees
+ if (deg < 0.0 || (deg > 179 && !isNegative180)) {
+ throw new IllegalArgumentException("coordinate=" + coordinate);
+ }
+
+ // min must be in [0, 59] if seconds are present, otherwise [0.0, 60.0)
+ if (min < 0 || min >= 60 || (secPresent && min > 59)) {
+ throw new IllegalArgumentException("coordinate=" + coordinate);
+ }
+
+ // sec must be in [0.0, 60.0)
+ if (sec < 0 || sec >= 60) {
+ throw new IllegalArgumentException("coordinate=" + coordinate);
+ }
+
+ val = deg * 3600.0 + min * 60.0 + sec;
+ val /= 3600.0;
+ return negative ? -val : val;
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("coordinate=" + coordinate, e);
+ }
+ }
+
+ private static void computeDistanceAndBearing(double lat1, double lon1,
+ double lat2, double lon2, BearingDistanceCache results) {
+ // Based on http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf
+ // using the "Inverse Formula" (section 4)
+
+ // Convert lat/long to radians
+ lat1 *= Math.PI / 180.0;
+ lat2 *= Math.PI / 180.0;
+ lon1 *= Math.PI / 180.0;
+ lon2 *= Math.PI / 180.0;
+
+ double a = 6378137.0; // WGS84 major axis
+ double b = 6356752.3142; // WGS84 semi-major axis
+ double f = (a - b) / a;
+ double aSqMinusBSqOverBSq = (a * a - b * b) / (b * b);
+
+ double l = lon2 - lon1;
+ double aA = 0.0;
+ double u1 = Math.atan((1.0 - f) * Math.tan(lat1));
+ double u2 = Math.atan((1.0 - f) * Math.tan(lat2));
+
+ double cosU1 = Math.cos(u1);
+ double cosU2 = Math.cos(u2);
+ double sinU1 = Math.sin(u1);
+ double sinU2 = Math.sin(u2);
+ double cosU1cosU2 = cosU1 * cosU2;
+ double sinU1sinU2 = sinU1 * sinU2;
+
+ double sigma = 0.0;
+ double deltaSigma = 0.0;
+ double cosSqAlpha;
+ double cos2SM;
+ double cosSigma;
+ double sinSigma;
+ double cosLambda = 0.0;
+ double sinLambda = 0.0;
+
+ double lambda = l; // initial guess
+ for (int iter = 0; iter < 20; iter++) {
+ double lambdaOrig = lambda;
+ cosLambda = Math.cos(lambda);
+ sinLambda = Math.sin(lambda);
+ double t1 = cosU2 * sinLambda;
+ double t2 = cosU1 * sinU2 - sinU1 * cosU2 * cosLambda;
+ double sinSqSigma = t1 * t1 + t2 * t2;
+ sinSigma = Math.sqrt(sinSqSigma);
+ cosSigma = sinU1sinU2 + cosU1cosU2 * cosLambda;
+ sigma = Math.atan2(sinSigma, cosSigma);
+ double sinAlpha = (sinSigma == 0) ? 0.0 :
+ cosU1cosU2 * sinLambda / sinSigma;
+ cosSqAlpha = 1.0 - sinAlpha * sinAlpha;
+ cos2SM = (cosSqAlpha == 0) ? 0.0 : cosSigma - 2.0 * sinU1sinU2 / cosSqAlpha;
+
+ double uSquared = cosSqAlpha * aSqMinusBSqOverBSq;
+ aA = 1 + (uSquared / 16384.0) * (4096.0 + uSquared * (-768 + uSquared * (320.0
+ - 175.0 * uSquared)));
+ double bB = (uSquared / 1024.0) * (256.0 + uSquared * (-128.0 + uSquared * (74.0
+ - 47.0 * uSquared)));
+ double cC = (f / 16.0) * cosSqAlpha * (4.0 + f * (4.0 - 3.0 * cosSqAlpha));
+ double cos2SMSq = cos2SM * cos2SM;
+ deltaSigma = bB * sinSigma * (cos2SM + (bB / 4.0) * (cosSigma * (-1.0 + 2.0 * cos2SMSq)
+ - (bB / 6.0) * cos2SM * (-3.0 + 4.0 * sinSigma * sinSigma) * (-3.0
+ + 4.0 * cos2SMSq)));
+
+ lambda = l + (1.0 - cC) * f * sinAlpha * (sigma + cC * sinSigma * (cos2SM
+ + cC * cosSigma * (-1.0 + 2.0 * cos2SM * cos2SM)));
+
+ double delta = (lambda - lambdaOrig) / lambda;
+ if (Math.abs(delta) < 1.0e-12) {
+ break;
+ }
+ }
+
+ results.mDistance = (float) (b * aA * (sigma - deltaSigma));
+ float initialBearing = (float) Math.atan2(cosU2 * sinLambda,
+ cosU1 * sinU2 - sinU1 * cosU2 * cosLambda);
+ initialBearing = (float) (initialBearing * (180.0 / Math.PI));
+ results.mInitialBearing = initialBearing;
+ float finalBearing = (float) Math.atan2(cosU1 * sinLambda,
+ -sinU1 * cosU2 + cosU1 * sinU2 * cosLambda);
+ finalBearing = (float) (finalBearing * (180.0 / Math.PI));
+ results.mFinalBearing = finalBearing;
+ results.mLat1 = lat1;
+ results.mLat2 = lat2;
+ results.mLon1 = lon1;
+ results.mLon2 = lon2;
+ }
+
+ /**
+ * Computes the approximate distance in meters between two
+ * locations, and optionally the initial and final bearings of the
+ * shortest path between them. Distance and bearing are defined using the
+ * WGS84 ellipsoid.
+ *
+ * <p> The computed distance is stored in results[0]. If results has length
+ * 2 or greater, the initial bearing is stored in results[1]. If results has
+ * length 3 or greater, the final bearing is stored in results[2].
+ *
+ * @param startLatitude the starting latitude
+ * @param startLongitude the starting longitude
+ * @param endLatitude the ending latitude
+ * @param endLongitude the ending longitude
+ * @param results an array of floats to hold the results
+ *
+ * @throws IllegalArgumentException if results is null or has length < 1
+ */
+ public static void distanceBetween(
+ @FloatRange double startLatitude,
+ @FloatRange double startLongitude,
+ @FloatRange double endLatitude,
+ @FloatRange double endLongitude,
+ float[] results) {
+ if (results == null || results.length < 1) {
+ throw new IllegalArgumentException("results is null or has length < 1");
+ }
+ BearingDistanceCache cache = sBearingDistanceCache.get();
+ computeDistanceAndBearing(startLatitude, startLongitude,
+ endLatitude, endLongitude, cache);
+ results[0] = cache.mDistance;
+ if (results.length > 1) {
+ results[1] = cache.mInitialBearing;
+ if (results.length > 2) {
+ results[2] = cache.mFinalBearing;
+ }
+ }
+ }
+
private static class BearingDistanceCache {
double mLat1 = 0.0;
double mLon1 = 0.0;
diff --git a/media/Android.bp b/media/Android.bp
index 82d6160..ce62b03 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -54,6 +54,8 @@
srcs: [
"aidl/android/media/audio/common/AudioChannelLayout.aidl",
"aidl/android/media/audio/common/AudioConfig.aidl",
+ "aidl/android/media/audio/common/AudioConfigBase.aidl",
+ "aidl/android/media/audio/common/AudioEncapsulationMode.aidl",
"aidl/android/media/audio/common/AudioFormatDescription.aidl",
"aidl/android/media/audio/common/AudioFormatType.aidl",
"aidl/android/media/audio/common/AudioOffloadInfo.aidl",
diff --git a/media/aidl/android/media/audio/common/AudioConfig.aidl b/media/aidl/android/media/audio/common/AudioConfig.aidl
index 4a264d8..8d71e8d 100644
--- a/media/aidl/android/media/audio/common/AudioConfig.aidl
+++ b/media/aidl/android/media/audio/common/AudioConfig.aidl
@@ -16,8 +16,7 @@
package android.media.audio.common;
-import android.media.audio.common.AudioChannelLayout;
-import android.media.audio.common.AudioFormatDescription;
+import android.media.audio.common.AudioConfigBase;
import android.media.audio.common.AudioOffloadInfo;
/**
@@ -28,9 +27,8 @@
@JavaDerive(equals = true, toString = true)
@VintfStability
parcelable AudioConfig {
- int sampleRateHz;
- AudioChannelLayout channelMask;
- AudioFormatDescription format;
+ AudioConfigBase base;
AudioOffloadInfo offloadInfo;
+ /** I/O buffer size in frames. */
long frameCount;
}
diff --git a/media/aidl/android/media/audio/common/AudioConfigBase.aidl b/media/aidl/android/media/audio/common/AudioConfigBase.aidl
new file mode 100644
index 0000000..63f12f4
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioConfigBase.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audio.common;
+
+import android.media.audio.common.AudioChannelLayout;
+import android.media.audio.common.AudioFormatDescription;
+
+/**
+ * Base configuration attributes applicable to any stream of audio.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals = true, toString = true)
+@VintfStability
+parcelable AudioConfigBase {
+ int sampleRate;
+ AudioChannelLayout channelMask;
+ AudioFormatDescription format;
+}
diff --git a/media/aidl/android/media/audio/common/AudioEncapsulationMode.aidl b/media/aidl/android/media/audio/common/AudioEncapsulationMode.aidl
new file mode 100644
index 0000000..6f8e9e1
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioEncapsulationMode.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.audio.common;
+
+/**
+ * Encapsulation mode used for sending audio compressed data.
+ *
+ * {@hide}
+ */
+@VintfStability
+@Backing(type="byte")
+enum AudioEncapsulationMode {
+ /**
+ * Used as default value in parcelables to indicate that a value was not
+ * set. Should never be considered a valid setting, except for backward
+ * compatibility scenarios.
+ */
+ INVALID = -1,
+ /** No encapsulation mode for metadata. */
+ NONE = 0,
+ /** Elementary stream payload with metadata. */
+ ELEMENTARY_STREAM = 1,
+ /** Handle-based payload with metadata. */
+ HANDLE = 2,
+}
diff --git a/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl b/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl
index b6b5487..46496c3 100644
--- a/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl
+++ b/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl
@@ -16,8 +16,8 @@
package android.media.audio.common;
-import android.media.audio.common.AudioChannelLayout;
-import android.media.audio.common.AudioFormatDescription;
+import android.media.audio.common.AudioConfigBase;
+import android.media.audio.common.AudioEncapsulationMode;
import android.media.audio.common.AudioStreamType;
import android.media.audio.common.AudioUsage;
@@ -29,15 +29,28 @@
@JavaDerive(equals = true, toString = true)
@VintfStability
parcelable AudioOffloadInfo {
- int sampleRateHz;
- AudioChannelLayout channelMask;
- AudioFormatDescription format;
+ /** Base audio configuration. */
+ AudioConfigBase base;
+ /** Stream type. Intended for use by the system only. */
AudioStreamType streamType = AudioStreamType.INVALID;
+ /** Bit rate in bits per second. */
int bitRatePerSecond;
- long durationMicroseconds;
+ /** Duration in microseconds, -1 if unknown. */
+ long durationUs;
+ /** True if the stream is tied to a video stream. */
boolean hasVideo;
+ /** True if streaming, false if local playback. */
boolean isStreaming;
- int bitWidth;
- int bufferSize;
+ /** Sample bit width. */
+ int bitWidth = 16;
+ /** Offload fragment size. */
+ int offloadBufferSize;
+ /** See the documentation of AudioUsage. */
AudioUsage usage = AudioUsage.INVALID;
+ /** See the documentation of AudioEncapsulationMode. */
+ AudioEncapsulationMode encapsulationMode = AudioEncapsulationMode.INVALID;
+ /** Content id from tuner HAL (0 if none). */
+ int contentId;
+ /** Sync id from tuner HAL (0 if none). */
+ int syncId;
}
diff --git a/media/aidl/android/media/audio/common/AudioStreamType.aidl b/media/aidl/android/media/audio/common/AudioStreamType.aidl
index 8b70367..1fa3a9c 100644
--- a/media/aidl/android/media/audio/common/AudioStreamType.aidl
+++ b/media/aidl/android/media/audio/common/AudioStreamType.aidl
@@ -14,15 +14,13 @@
* limitations under the License.
*/
- // This file has been semi-automatically generated using hidl2aidl from its counterpart in
- // hardware/interfaces/audio/common/5.0/types.hal
-
package android.media.audio.common;
/**
- * Audio streams
- *
- * Audio stream type describing the intended use case of a stream.
+ * Audio stream type describing the intended use case of a stream. Streams
+ * must be used in the context of volume management only. For playback type
+ * identification purposes, AudioContentType and AudioUsage must be used,
+ * similar to how it's done in the SDK.
*
* {@hide}
*/
@@ -30,21 +28,71 @@
@Backing(type="int")
enum AudioStreamType {
/**
- * Used as default value in parcelables to indicate that a value was not set.
- * Should never be considered a valid setting, except for backward compatibility scenarios.
+ * Used as default value in parcelables to indicate that a value was not
+ * set. Should never be considered a valid setting, except for backward
+ * compatibility scenarios.
*/
INVALID = -2,
+ /**
+ * Indicates that the operation is applied to the "default" stream
+ * in this context, e.g. MUSIC in normal device state, or RING if the
+ * phone is ringing.
+ */
DEFAULT = -1,
- MIN = 0,
+ /** Used to identify the volume of audio streams for phone calls. */
VOICE_CALL = 0,
+ /** Used to identify the volume of audio streams for system sounds. */
SYSTEM = 1,
+ /**
+ * Used to identify the volume of audio streams for the phone ring and
+ * message alerts.
+ */
RING = 2,
+ /** Used to identify the volume of audio streams for music playback. */
MUSIC = 3,
+ /** Used to identify the volume of audio streams for alarms. */
ALARM = 4,
+ /** Used to identify the volume of audio streams for notifications. */
NOTIFICATION = 5,
+ /**
+ * Used to identify the volume of audio streams for phone calls when
+ * connected via Bluetooth.
+ */
BLUETOOTH_SCO = 6,
+ /**
+ * Used to identify the volume of audio streams for enforced system sounds
+ * in certain countries (e.g camera in Japan).
+ */
ENFORCED_AUDIBLE = 7,
+ /** Used to identify the volume of audio streams for DTMF tones. */
DTMF = 8,
+ /**
+ * Used to identify the volume of audio streams exclusively transmitted
+ * through the speaker (TTS) of the device.
+ */
TTS = 9,
+ /**
+ * Used to identify the volume of audio streams for accessibility prompts.
+ */
ACCESSIBILITY = 10,
+ /**
+ * Used to identify the volume of audio streams for virtual assistant.
+ */
+ ASSISTANT = 11,
+ /**
+ * Used for dynamic policy output mixes. Only used by the audio policy.
+ *
+ * Value reserved for system use only. HALs must never return this value to
+ * the system or accept it from the system.
+ */
+ SYS_RESERVED_REROUTING = 12,
+ /**
+ * Used for audio flinger tracks volume. Only used by the audioflinger.
+ *
+ * Value reserved for system use only. HALs must never return this value to
+ * the system or accept it from the system.
+ */
+ SYS_RESERVED_PATCH = 13,
+ /** Used for the stream corresponding to the call assistant usage. */
+ CALL_ASSISTANT = 14,
}
diff --git a/media/aidl/android/media/audio/common/AudioUsage.aidl b/media/aidl/android/media/audio/common/AudioUsage.aidl
index 028eefe..34a7185 100644
--- a/media/aidl/android/media/audio/common/AudioUsage.aidl
+++ b/media/aidl/android/media/audio/common/AudioUsage.aidl
@@ -14,9 +14,6 @@
* limitations under the License.
*/
- // This file has been semi-automatically generated using hidl2aidl from its counterpart in
- // hardware/interfaces/audio/common/5.0/types.hal
-
package android.media.audio.common;
/**
@@ -26,21 +23,119 @@
@Backing(type="int")
enum AudioUsage {
/**
- * Used as default value in parcelables to indicate that a value was not set.
- * Should never be considered a valid setting, except for backward compatibility scenarios.
+ * Used as default value in parcelables to indicate that a value was not
+ * set. Should never be considered a valid setting, except for backward
+ * compatibility scenarios.
*/
INVALID = -1,
+ /**
+ * Usage value to use when the usage is unknown.
+ */
UNKNOWN = 0,
+ /**
+ * Usage value to use when the usage is media, such as music, or movie
+ * soundtracks.
+ */
MEDIA = 1,
+ /**
+ * Usage value to use when the usage is voice communications, such as
+ * telephony or VoIP.
+ */
VOICE_COMMUNICATION = 2,
+ /**
+ * Usage value to use when the usage is in-call signalling, such as with
+ * a "busy" beep, or DTMF tones.
+ */
VOICE_COMMUNICATION_SIGNALLING = 3,
+ /**
+ * Usage value to use when the usage is an alarm (e.g. wake-up alarm).
+ */
ALARM = 4,
+ /**
+ * Usage value to use when the usage is notification. See other notification
+ * usages for more specialized uses.
+ */
NOTIFICATION = 5,
+ /**
+ * Usage value to use when the usage is telephony ringtone.
+ */
NOTIFICATION_TELEPHONY_RINGTONE = 6,
+ /**
+ * Usage value to use when the usage is a request to enter/end a
+ * communication, such as a VoIP communication or video-conference.
+ *
+ * Value reserved for system use only. HALs must never return this value to
+ * the system or accept it from the system.
+ */
+ SYS_RESERVED_NOTIFICATION_COMMUNICATION_REQUEST = 7,
+ /**
+ * Usage value to use when the usage is notification for an "instant"
+ * communication such as a chat, or SMS.
+ *
+ * Value reserved for system use only. HALs must never return this value to
+ * the system or accept it from the system.
+ */
+ SYS_RESERVED_NOTIFICATION_COMMUNICATION_INSTANT = 8,
+ /**
+ * Usage value to use when the usage is notification for a
+ * non-immediate type of communication such as e-mail.
+ *
+ * Value reserved for system use only. HALs must never return this value to
+ * the system or accept it from the system.
+ */
+ SYS_RESERVED_NOTIFICATION_COMMUNICATION_DELAYED = 9,
+ /**
+ * Usage value to use when the usage is to attract the user's attention,
+ * such as a reminder or low battery warning.
+ */
+ NOTIFICATION_EVENT = 10,
+ /**
+ * Usage value to use when the usage is for accessibility, such as with
+ * a screen reader.
+ */
ASSISTANCE_ACCESSIBILITY = 11,
+ /**
+ * Usage value to use when the usage is driving or navigation directions.
+ */
ASSISTANCE_NAVIGATION_GUIDANCE = 12,
+ /**
+ * Usage value to use when the usage is sonification, such as with user
+ * interface sounds.
+ */
ASSISTANCE_SONIFICATION = 13,
+ /**
+ * Usage value to use when the usage is for game audio.
+ */
GAME = 14,
+ /**
+ * Usage value to use when feeding audio to the platform and replacing
+ * "traditional" audio source, such as audio capture devices.
+ */
VIRTUAL_SOURCE = 15,
+ /**
+ * Usage value to use for audio responses to user queries, audio
+ * instructions or help utterances.
+ */
ASSISTANT = 16,
+ /**
+ * Usage value to use for assistant voice interaction with remote caller on
+ * Cell and VoIP calls.
+ */
+ CALL_ASSISTANT = 17,
+ /**
+ * Usage value to use when the usage is an emergency.
+ */
+ EMERGENCY = 1000,
+ /**
+ * Usage value to use when the usage is a safety sound.
+ */
+ SAFETY = 1001,
+ /**
+ * Usage value to use when the usage is a vehicle status.
+ */
+ VEHICLE_STATUS = 1002,
+ /**
+ * Usage value to use when the usage is an announcement.
+ */
+ ANNOUNCEMENT = 1003,
}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfig.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfig.aidl
index 5fdeb4c..6b8686c 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfig.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfig.aidl
@@ -35,9 +35,7 @@
/* @hide */
@JavaDerive(equals=true, toString=true) @VintfStability
parcelable AudioConfig {
- int sampleRateHz;
- android.media.audio.common.AudioChannelLayout channelMask;
- android.media.audio.common.AudioFormatDescription format;
+ android.media.audio.common.AudioConfigBase base;
android.media.audio.common.AudioOffloadInfo offloadInfo;
long frameCount;
}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfigBase.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfigBase.aidl
new file mode 100644
index 0000000..f3e716b
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfigBase.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioConfigBase {
+ int sampleRate;
+ android.media.audio.common.AudioChannelLayout channelMask;
+ android.media.audio.common.AudioFormatDescription format;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioEncapsulationMode.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioEncapsulationMode.aidl
new file mode 100644
index 0000000..0cf2f31
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioEncapsulationMode.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="byte") @VintfStability
+enum AudioEncapsulationMode {
+ INVALID = -1,
+ NONE = 0,
+ ELEMENTARY_STREAM = 1,
+ HANDLE = 2,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOffloadInfo.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOffloadInfo.aidl
index 9404a4b..40bd53b2 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOffloadInfo.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOffloadInfo.aidl
@@ -35,15 +35,16 @@
/* @hide */
@JavaDerive(equals=true, toString=true) @VintfStability
parcelable AudioOffloadInfo {
- int sampleRateHz;
- android.media.audio.common.AudioChannelLayout channelMask;
- android.media.audio.common.AudioFormatDescription format;
+ android.media.audio.common.AudioConfigBase base;
android.media.audio.common.AudioStreamType streamType = android.media.audio.common.AudioStreamType.INVALID;
int bitRatePerSecond;
- long durationMicroseconds;
+ long durationUs;
boolean hasVideo;
boolean isStreaming;
- int bitWidth;
- int bufferSize;
+ int bitWidth = 16;
+ int offloadBufferSize;
android.media.audio.common.AudioUsage usage = android.media.audio.common.AudioUsage.INVALID;
+ android.media.audio.common.AudioEncapsulationMode encapsulationMode = android.media.audio.common.AudioEncapsulationMode.INVALID;
+ int contentId;
+ int syncId;
}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl
index 915c668..cbca6c5 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl
@@ -12,8 +12,7 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
-// hardware/interfaces/audio/common/5.0/types.hal
+ */
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
@@ -38,7 +37,6 @@
enum AudioStreamType {
INVALID = -2,
DEFAULT = -1,
- MIN = 0,
VOICE_CALL = 0,
SYSTEM = 1,
RING = 2,
@@ -50,4 +48,8 @@
DTMF = 8,
TTS = 9,
ACCESSIBILITY = 10,
+ ASSISTANT = 11,
+ SYS_RESERVED_REROUTING = 12,
+ SYS_RESERVED_PATCH = 13,
+ CALL_ASSISTANT = 14,
}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUsage.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUsage.aidl
index f5130a4..4c72455 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUsage.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUsage.aidl
@@ -12,8 +12,7 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
-// hardware/interfaces/audio/common/5.0/types.hal
+ */
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
@@ -44,10 +43,19 @@
ALARM = 4,
NOTIFICATION = 5,
NOTIFICATION_TELEPHONY_RINGTONE = 6,
+ SYS_RESERVED_NOTIFICATION_COMMUNICATION_REQUEST = 7,
+ SYS_RESERVED_NOTIFICATION_COMMUNICATION_INSTANT = 8,
+ SYS_RESERVED_NOTIFICATION_COMMUNICATION_DELAYED = 9,
+ NOTIFICATION_EVENT = 10,
ASSISTANCE_ACCESSIBILITY = 11,
ASSISTANCE_NAVIGATION_GUIDANCE = 12,
ASSISTANCE_SONIFICATION = 13,
GAME = 14,
VIRTUAL_SOURCE = 15,
ASSISTANT = 16,
+ CALL_ASSISTANT = 17,
+ EMERGENCY = 1000,
+ SAFETY = 1001,
+ VEHICLE_STATUS = 1002,
+ ANNOUNCEMENT = 1003,
}
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 347a9b1..f263c28 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -139,19 +139,28 @@
*/
public final static int USAGE_NOTIFICATION_RINGTONE = 6;
/**
+ * @deprecated Use {@link #USAGE_NOTIFICATION} which is handled
+ * the same way as this usage by the audio framework
* Usage value to use when the usage is a request to enter/end a
* communication, such as a VoIP communication or video-conference.
*/
+ @Deprecated
public final static int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7;
/**
+ * @deprecated Use {@link #USAGE_NOTIFICATION} which is handled
+ * the same way as this usage by the audio framework
* Usage value to use when the usage is notification for an "instant"
* communication such as a chat, or SMS.
*/
+ @Deprecated
public final static int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8;
/**
+ * @deprecated Use {@link #USAGE_NOTIFICATION} which is handled
+ * the same way as this usage by the audio framework
* Usage value to use when the usage is notification for a
* non-immediate type of communication such as e-mail.
*/
+ @Deprecated
public final static int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9;
/**
* Usage value to use when the usage is to attract the user's attention,
@@ -786,6 +795,17 @@
}
}
+ // handle deprecation of notification usages by remapping to USAGE_NOTIFICATION
+ switch (aa.mUsage) {
+ case USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
+ case USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
+ case USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
+ aa.mUsage = USAGE_NOTIFICATION;
+ break;
+ default:
+ break;
+ }
+
aa.mSource = mSource;
aa.mFlags = mFlags;
if (mMuteHapticChannels) {
@@ -823,7 +843,6 @@
&& (mFlags & FLAG_HW_HOTWORD) == FLAG_HW_HOTWORD) {
aa.mFlags &= ~FLAG_HW_HOTWORD;
}
-
return aa;
}
@@ -836,9 +855,6 @@
* {@link AttributeSdkUsage#USAGE_VOICE_COMMUNICATION_SIGNALLING},
* {@link AttributeSdkUsage#USAGE_ALARM}, {@link AudioAttributes#USAGE_NOTIFICATION},
* {@link AttributeSdkUsage#USAGE_NOTIFICATION_RINGTONE},
- * {@link AttributeSdkUsage#USAGE_NOTIFICATION_COMMUNICATION_REQUEST},
- * {@link AttributeSdkUsage#USAGE_NOTIFICATION_COMMUNICATION_INSTANT},
- * {@link AttributeSdkUsage#USAGE_NOTIFICATION_COMMUNICATION_DELAYED},
* {@link AttributeSdkUsage#USAGE_NOTIFICATION_EVENT},
* {@link AttributeSdkUsage#USAGE_ASSISTANT},
* {@link AttributeSdkUsage#USAGE_ASSISTANCE_ACCESSIBILITY},
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index a73b436..31ab09e 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2456,145 +2456,21 @@
// Immersive audio
/**
- * @hide
- * Returns the level of support for immersive audio from the {@link Spatializer} if
- * available.
- * @return the level of immersive audio support through spatialization
- * @see Spatializer#getImmersiveAudioLevel()
- */
- @Spatializer.ImmersiveAudioLevel int getSpatializerImmersiveAudioLevel() {
- final IAudioService service = getService();
- try {
- return service.getSpatializerImmersiveAudioLevel();
- } catch (RemoteException e) {
- return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
- }
- }
-
- /**
* Return a handle to the optional platform's {@link Spatializer}
* @return {@code null} if spatialization is not supported, the {@code Spatializer} instance
* otherwise.
*/
public @Nullable Spatializer getSpatializer() {
- if (getSpatializerImmersiveAudioLevel() == Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE) {
+ int level = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ try {
+ level = getService().getSpatializerImmersiveAudioLevel();
+ } catch (Exception e) { /* using NONE */ }
+ if (level == Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE) {
return null;
}
return new Spatializer(this);
}
- /**
- * @hide
- * @see Spatializer#isEnabled()
- * @return {@code true} if spatialization is enabled
- */
- boolean isSpatializerEnabled() {
- final IAudioService service = getService();
- try {
- return service.isSpatializerEnabled();
- } catch (RemoteException e) {
- Log.e(TAG, "Error querying isSpatializerEnabled, returning false", e);
- return false;
- }
- }
-
- /**
- * @hide
- * @see Spatializer#setEnabled(boolean)
- * Enable/disable the spatialization wherever supported.
- * @param enabled {@code true} to enable
- */
- void setSpatializerFeatureEnabled(boolean enabled) {
- final IAudioService service = getService();
- try {
- service.setSpatializerFeatureEnabled(enabled);
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling setSpatializerFeatureEnabled", e);
- }
- }
-
- /**
- * @hide
- * @see Spatializer#setEnabledForDevice(boolean, AudioDeviceAttributes)
- * @see Spatializer#setEnabled(boolean)
- * @param enabled enable/disable for a specific device.
- * @param device the device concerned with spatializer functionality.
- */
- void setSpatializerEnabledForDevice(boolean enabled,
- @NonNull AudioDeviceAttributes device) {
- final IAudioService service = getService();
- try {
- service.setSpatializerEnabledForDevice(enabled, device);
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling setSpatializerEnabledForDevice", e);
- }
- }
-
- /**
- * @hide
- * @see Spatializer#canBeSpatialized(AudioAttributes, AudioFormat)
- * @param attributes the {@code AudioAttributes} of the content as used for playback
- * @param format the {@code AudioFormat} of the content as used for playback
- * @return true if the device is capable of spatializing the combination of audio
- * format and attributes.
- */
- boolean canBeSpatialized(
- @NonNull AudioAttributes attributes, @NonNull AudioFormat format) {
- final IAudioService service = getService();
- try {
- return service.canBeSpatialized(attributes, format);
- } catch (RemoteException e) {
- Log.e(TAG, "Error querying canBeSpatialized for attr:" + attributes
- + " format:" + format + " returning false", e);
- return false;
- }
- }
-
- /**
- * @hide
- * @see Spatializer#getCompatibleAudioDevices()
- * @return a non-null list of the spatialization-compatible audio devices
- */
- @NonNull List<AudioDeviceAttributes> getSpatializerCompatibleAudioDevices() {
- final IAudioService service = getService();
- try {
- return service.getSpatializerCompatibleAudioDevices();
- } catch (RemoteException e) {
- Log.e(TAG, "Error querying getSpatializerCompatibleAudioDevices(), "
- + " returning empty list", e);
- return new ArrayList<AudioDeviceAttributes>(0);
- }
- }
-
- /**
- * @hide
- * @see Spatializer#addCompatibleAudioDevice(AudioDeviceAttributes)
- * @param ada the audio device compatible with spatialization
- */
- void addSpatializerCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
- final IAudioService service = getService();
- try {
- service.addSpatializerCompatibleAudioDevice(ada);
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling addSpatializerCompatibleAudioDevice()", e);
- }
- }
-
- /**
- * @hide
- * @see Spatializer#removeCompatibleAudioDevice(AudioDeviceAttributes)
- * @param ada the audio device incompatible with spatialization
- */
- void removeSpatializerCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
- final IAudioService service = getService();
- try {
- service.removeSpatializerCompatibleAudioDevice(ada);
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling removeSpatializerCompatibleAudioDevice()", e);
- }
- }
-
-
//====================================================================
// Bluetooth SCO control
/**
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 28b4143..12b06bf 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -403,9 +403,9 @@
boolean isSpatializerEnabled();
- void setSpatializerFeatureEnabled(boolean enabled);
+ boolean isSpatializerAvailable();
- void setSpatializerEnabledForDevice(boolean enabled, in AudioDeviceAttributes device);
+ void setSpatializerEnabled(boolean enabled);
boolean canBeSpatialized(in AudioAttributes aa, in AudioFormat af);
diff --git a/media/java/android/media/ISpatializerCallback.aidl b/media/java/android/media/ISpatializerCallback.aidl
index 50c5a6b8..50f91e7 100644
--- a/media/java/android/media/ISpatializerCallback.aidl
+++ b/media/java/android/media/ISpatializerCallback.aidl
@@ -23,6 +23,7 @@
*/
oneway interface ISpatializerCallback {
- void dispatchSpatializerStateChanged(boolean enabled);
+ void dispatchSpatializerEnabledChanged(boolean enabled);
+ void dispatchSpatializerAvailableChanged(boolean available);
}
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index ac19c21..b8e7930e 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -69,17 +69,14 @@
<h4>Metadata Track</h4>
<p>
- Per-frame metadata is useful in carrying extra information that correlated with video or audio to
- facilitate offline processing, e.g. gyro signals from the sensor could help video stabilization when
- doing offline processing. Metadata track is only supported in MP4 container. When adding a new
- metadata track, track's mime format must start with prefix "application/", e.g. "applicaton/gyro".
- Metadata's format/layout will be defined by the application. Writing metadata is nearly the same as
- writing video/audio data except that the data will not be from mediacodec. Application just needs
- to pass the bytebuffer that contains the metadata and also the associated timestamp to the
- {@link #writeSampleData} api. The timestamp must be in the same time base as video and audio. The
- generated MP4 file uses TextMetaDataSampleEntry defined in section 12.3.3.2 of the ISOBMFF to signal
- the metadata's mime format. When using{@link android.media.MediaExtractor} to extract the file with
- metadata track, the mime format of the metadata will be extracted into {@link android.media.MediaFormat}.
+ Per-frame metadata carries information that correlates with video or audio to facilitate offline
+ processing. For example, gyro signals from the sensor can help video stabilization when doing
+ offline processing. Metadata tracks are only supported when multiplexing to the MP4 container
+ format. When adding a new metadata track, the MIME type format must start with prefix
+ "application/" (for example, "application/gyro"). The format of the metadata is
+ application-defined. Metadata timestamps must be in the same time base as video and audio
+ timestamps. The generated MP4 file uses TextMetaDataSampleEntry (defined in section 12.3.3.2 of
+ the ISOBMFF specification) to signal the metadata's MIME type.
<pre class=prettyprint>
MediaMuxer muxer = new MediaMuxer("temp.mp4", OutputFormat.MUXER_OUTPUT_MPEG_4);
diff --git a/media/java/android/media/Spatializer.java b/media/java/android/media/Spatializer.java
index a2267cc..3ed8b58 100644
--- a/media/java/android/media/Spatializer.java
+++ b/media/java/android/media/Spatializer.java
@@ -25,6 +25,7 @@
import android.media.permission.ClearCallingIdentityContext;
import android.media.permission.SafeCloseable;
import android.os.RemoteException;
+import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -50,6 +51,8 @@
private final Object mStateListenerLock = new Object();
+ private static final String TAG = "Spatializer";
+
/**
* List of listeners for state listener and their associated Executor.
* List is lazy-initialized on first registration
@@ -71,13 +74,42 @@
/**
* Returns whether spatialization is enabled or not.
- * A false value can originate from a number of sources, examples are the user electing to
- * disable the feature, or the use of an audio device that is not compatible with multichannel
- * audio spatialization (for instance playing audio over a monophonic speaker).
+ * A false value can originate for instance from the user electing to
+ * disable the feature.<br>
+ * Note that this state reflects a platform-wide state of the "desire" to use spatialization,
+ * but availability of the audio processing is still dictated by the compatibility between
+ * the effect and the hardware configuration, as indicated by {@link #isAvailable()}.
* @return {@code true} if spatialization is enabled
+ * @see #isAvailable()
*/
public boolean isEnabled() {
- return mAm.isSpatializerEnabled();
+ try {
+ return mAm.getService().isSpatializerEnabled();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error querying isSpatializerEnabled, returning false", e);
+ return false;
+ }
+ }
+
+ /**
+ * Returns whether spatialization is available.
+ * Reasons for spatialization being unavailable include situations where audio output is
+ * incompatible with sound spatialization, such as playback on a monophonic speaker.<br>
+ * Note that spatialization can be available, but disabled by the user, in which case this
+ * method would still return {@code true}, whereas {@link #isEnabled()}
+ * would return {@code false}.
+ * @return {@code true} if the spatializer effect is available and capable
+ * of processing the audio for the current configuration of the device,
+ * {@code false} otherwise.
+ * @see #isEnabled()
+ */
+ public boolean isAvailable() {
+ try {
+ return mAm.getService().isSpatializerAvailable();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error querying isSpatializerAvailable, returning false", e);
+ return false;
+ }
}
/** @hide */
@@ -103,43 +135,46 @@
*/
public static final int SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL = 1;
-
/**
* @hide
- * @param enabled
- * @param device
- */
- //TODO make as API if needed for UX, remove otherwise
- //@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
- //@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public void setEnabledForDevice(boolean enabled,
- @NonNull AudioDeviceAttributes device) {
- Objects.requireNonNull(device);
- mAm.setSpatializerEnabledForDevice(enabled, device);
- }
-
- /**
- * @hide
- * Enables / disables the spatializer effect
+ * Enables / disables the spatializer effect.
+ * Changing the enabled state will trigger the public
+ * {@link OnSpatializerStateChangedListener#onSpatializerEnabledChanged(Spatializer, boolean)}
+ * registered listeners.
* @param enabled {@code true} for enabling the effect
*/
- //TODO make as API if needed for UX, remove otherwise
- //@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
public void setEnabled(boolean enabled) {
- mAm.setSpatializerFeatureEnabled(enabled);
+ try {
+ mAm.getService().setSpatializerEnabled(enabled);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling setSpatializerEnabled", e);
+ }
}
/**
* An interface to be notified of changes to the state of the spatializer.
*/
- public interface OnSpatializerEnabledChangedListener {
+ public interface OnSpatializerStateChangedListener {
/**
- * Called when the enabled state of the Spatializer changes
- * @param enabled {@code true} if the Spatializer effect is enabled on the device,
+ * Called when the enabled state of the spatializer effect changes
+ * @param spat the {@code Spatializer} instance whose state changed
+ * @param enabled {@code true} if the spatializer effect is enabled on the device,
* {@code false} otherwise
+ * @see #isEnabled()
*/
- void onSpatializerEnabledChanged(boolean enabled);
+ void onSpatializerEnabledChanged(@NonNull Spatializer spat, boolean enabled);
+
+ /**
+ * Called when the availability of the spatializer effect changes
+ * @param spat the {@code Spatializer} instance whose state changed
+ * @param available {@code true} if the spatializer effect is available and capable
+ * of processing the audio for the current configuration of the device,
+ * {@code false} otherwise.
+ * @see #isAvailable()
+ */
+ void onSpatializerAvailableChanged(@NonNull Spatializer spat, boolean available);
}
/**
@@ -150,31 +185,37 @@
* The result is independent from whether spatialization processing is enabled or not.
* @param attributes the {@code AudioAttributes} of the content as used for playback
* @param format the {@code AudioFormat} of the content as used for playback
- * @return true if the device is capable of spatializing the combination of audio format and
- * attributes.
+ * @return {@code true} if the device is capable of spatializing the combination of audio format
+ * and attributes, {@code false} otherwise.
*/
public boolean canBeSpatialized(
@NonNull AudioAttributes attributes, @NonNull AudioFormat format) {
- return mAm.canBeSpatialized(
- Objects.requireNonNull(attributes), Objects.requireNonNull(format));
+ try {
+ return mAm.getService().canBeSpatialized(
+ Objects.requireNonNull(attributes), Objects.requireNonNull(format));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error querying canBeSpatialized for attr:" + attributes
+ + " format:" + format + " returning false", e);
+ return false;
+ }
}
/**
* Adds a listener to be notified of changes to the enabled state of the
* {@code Spatializer}.
- * @see #isEnabled()
* @param executor the {@code Executor} handling the callback
* @param listener the listener to receive enabled state updates
+ * @see #isEnabled()
*/
- public void addOnSpatializerEnabledChangedListener(
+ public void addOnSpatializerStateChangedListener(
@NonNull @CallbackExecutor Executor executor,
- @NonNull OnSpatializerEnabledChangedListener listener) {
+ @NonNull OnSpatializerStateChangedListener listener) {
Objects.requireNonNull(executor);
Objects.requireNonNull(listener);
synchronized (mStateListenerLock) {
if (hasSpatializerStateListener(listener)) {
throw new IllegalArgumentException(
- "Called addOnSpatializerEnabledChangedListener() "
+ "Called addOnSpatializerStateChangedListener() "
+ "on a previously registered listener");
}
// lazy initialization of the list of strategy-preferred device listener
@@ -201,16 +242,16 @@
/**
* Removes a previously added listener for changes to the enabled state of the
* {@code Spatializer}.
- * @see #isEnabled()
* @param listener the listener to receive enabled state updates
+ * @see #isEnabled()
*/
- public void removeOnSpatializerEnabledChangedListener(
- @NonNull OnSpatializerEnabledChangedListener listener) {
+ public void removeOnSpatializerStateChangedListener(
+ @NonNull OnSpatializerStateChangedListener listener) {
Objects.requireNonNull(listener);
synchronized (mStateListenerLock) {
if (!removeStateListener(listener)) {
throw new IllegalArgumentException(
- "Called removeOnSpatializerEnabledChangedListener() "
+ "Called removeOnSpatializerStateChangedListener() "
+ "on an unregistered listener");
}
if (mStateListeners.size() == 0) {
@@ -234,9 +275,15 @@
* @return a list of devices. An empty list indicates virtualization is not supported.
*/
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
public @NonNull List<AudioDeviceAttributes> getCompatibleAudioDevices() {
- return mAm.getSpatializerCompatibleAudioDevices();
+ try {
+ return mAm.getService().getSpatializerCompatibleAudioDevices();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error querying getSpatializerCompatibleAudioDevices(), "
+ + " returning empty list", e);
+ return new ArrayList<AudioDeviceAttributes>(0);
+ }
}
/**
@@ -247,9 +294,13 @@
* @param ada the audio device compatible with spatialization
*/
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
public void addCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
- mAm.addSpatializerCompatibleAudioDevice(Objects.requireNonNull(ada));
+ try {
+ mAm.getService().addSpatializerCompatibleAudioDevice(Objects.requireNonNull(ada));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling addSpatializerCompatibleAudioDevice(), ", e);
+ }
}
/**
@@ -260,15 +311,18 @@
* @param ada the audio device incompatible with spatialization
*/
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
- @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
public void removeCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
- mAm.removeSpatializerCompatibleAudioDevice(Objects.requireNonNull(ada));
+ try {
+ mAm.getService().removeSpatializerCompatibleAudioDevice(Objects.requireNonNull(ada));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling removeSpatializerCompatibleAudioDevice(), ", e);
+ }
}
- private final class SpatializerInfoDispatcherStub
- extends ISpatializerCallback.Stub {
+ private final class SpatializerInfoDispatcherStub extends ISpatializerCallback.Stub {
@Override
- public void dispatchSpatializerStateChanged(boolean enabled) {
+ public void dispatchSpatializerEnabledChanged(boolean enabled) {
// make a shallow copy of listeners so callback is not executed under lock
final ArrayList<StateListenerInfo> stateListeners;
synchronized (mStateListenerLock) {
@@ -280,17 +334,36 @@
try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
for (StateListenerInfo info : stateListeners) {
info.mExecutor.execute(() ->
- info.mListener.onSpatializerEnabledChanged(enabled));
+ info.mListener.onSpatializerEnabledChanged(Spatializer.this, enabled));
+ }
+ }
+ }
+
+ @Override
+ public void dispatchSpatializerAvailableChanged(boolean available) {
+ // make a shallow copy of listeners so callback is not executed under lock
+ final ArrayList<StateListenerInfo> stateListeners;
+ synchronized (mStateListenerLock) {
+ if (mStateListeners == null || mStateListeners.size() == 0) {
+ return;
+ }
+ stateListeners = (ArrayList<StateListenerInfo>) mStateListeners.clone();
+ }
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ for (StateListenerInfo info : stateListeners) {
+ info.mExecutor.execute(() ->
+ info.mListener.onSpatializerAvailableChanged(
+ Spatializer.this, available));
}
}
}
}
private static class StateListenerInfo {
- final @NonNull OnSpatializerEnabledChangedListener mListener;
+ final @NonNull OnSpatializerStateChangedListener mListener;
final @NonNull Executor mExecutor;
- StateListenerInfo(@NonNull OnSpatializerEnabledChangedListener listener,
+ StateListenerInfo(@NonNull OnSpatializerStateChangedListener listener,
@NonNull Executor exe) {
mListener = listener;
mExecutor = exe;
@@ -298,13 +371,13 @@
}
@GuardedBy("mStateListenerLock")
- private boolean hasSpatializerStateListener(OnSpatializerEnabledChangedListener listener) {
+ private boolean hasSpatializerStateListener(OnSpatializerStateChangedListener listener) {
return getStateListenerInfo(listener) != null;
}
@GuardedBy("mStateListenerLock")
private @Nullable StateListenerInfo getStateListenerInfo(
- OnSpatializerEnabledChangedListener listener) {
+ OnSpatializerStateChangedListener listener) {
if (mStateListeners == null) {
return null;
}
@@ -320,7 +393,7 @@
/**
* @return true if the listener was removed from the list
*/
- private boolean removeStateListener(OnSpatializerEnabledChangedListener listener) {
+ private boolean removeStateListener(OnSpatializerStateChangedListener listener) {
final StateListenerInfo infoToRemove = getStateListenerInfo(listener);
if (infoToRemove != null) {
mStateListeners.remove(infoToRemove);
diff --git a/media/java/android/media/audio/common/AidlConversion.java b/media/java/android/media/audio/common/AidlConversion.java
index 3b58934..f96ba8c 100644
--- a/media/java/android/media/audio/common/AidlConversion.java
+++ b/media/java/android/media/audio/common/AidlConversion.java
@@ -35,18 +35,25 @@
* aidl2api_AIDL-type-name_SDK-type-name
* api2aidl_SDK-type-name_AIDL-type-name
*
+ * Since the range of the SDK values is generally narrower than
+ * the range of AIDL values, when a match can't be found, the
+ * conversion function returns a corresponding 'INVALID' value.
+ *
* Methods that convert between AIDL and legacy types are called
* using the following pattern:
*
* aidl2legacy_AIDL-type-name_native-type-name
* legacy2aidl_native-type-name_AIDL-type-name
*
+ * In general, there is a 1:1 mapping between AIDL and framework
+ * types, and a failure to convert a value indicates a programming
+ * error. Thus, the conversion functions may throw an IllegalArgumentException.
+ *
* @hide
*/
@VisibleForTesting
public class AidlConversion {
/** Convert from AIDL AudioChannelLayout to legacy audio_channel_mask_t. */
- @VisibleForTesting
public static int /*audio_channel_mask_t*/ aidl2legacy_AudioChannelLayout_audio_channel_mask_t(
@NonNull AudioChannelLayout aidl, boolean isInput) {
Parcel out = Parcel.obtain();
@@ -60,7 +67,6 @@
}
/** Convert from legacy audio_channel_mask_t to AIDL AudioChannelLayout. */
- @VisibleForTesting
public static AudioChannelLayout legacy2aidl_audio_channel_mask_t_AudioChannelLayout(
int /*audio_channel_mask_t*/ legacy, boolean isInput) {
Parcel in = legacy2aidl_audio_channel_mask_t_AudioChannelLayout_Parcel(legacy, isInput);
@@ -71,12 +77,11 @@
in.recycle();
}
}
- throw new IllegalArgumentException("Invalid legacy audio "
- + (isInput ? "input" : "output") + " channel mask: " + legacy);
+ throw new IllegalArgumentException("Failed to convert legacy audio "
+ + (isInput ? "input" : "output") + " audio_channel_mask_t " + legacy + " value");
}
/** Convert from AIDL AudioFormatDescription to legacy audio_format_t. */
- @VisibleForTesting
public static int /*audio_format_t*/ aidl2legacy_AudioFormatDescription_audio_format_t(
@NonNull AudioFormatDescription aidl) {
Parcel out = Parcel.obtain();
@@ -90,7 +95,6 @@
}
/** Convert from legacy audio_format_t to AIDL AudioFormatDescription. */
- @VisibleForTesting
public static @NonNull AudioFormatDescription legacy2aidl_audio_format_t_AudioFormatDescription(
int /*audio_format_t*/ legacy) {
Parcel in = legacy2aidl_audio_format_t_AudioFormatDescription_Parcel(legacy);
@@ -101,28 +105,33 @@
in.recycle();
}
}
- throw new IllegalArgumentException("Invalid legacy audio format: " + legacy);
+ throw new IllegalArgumentException(
+ "Failed to convert legacy audio_format_t value " + legacy);
}
+ /** Convert from AIDL AudioEncapsulationMode to legacy audio_encapsulation_mode_t. */
+ public static native int aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t(
+ int /*AudioEncapsulationMode.* */ aidl);
+
+ /** Convert from legacy audio_encapsulation_mode_t to AIDL AudioEncapsulationMode. */
+ public static native int legacy2aidl_audio_encapsulation_mode_t_AudioEncapsulationMode(
+ int /*audio_encapsulation_mode_t*/ legacy);
+
+ /** Convert from AIDL AudioStreamType to legacy audio_stream_type_t. */
+ public static native int aidl2legacy_AudioStreamType_audio_stream_type_t(
+ int /*AudioStreamType.* */ aidl);
+
/** Convert from legacy audio_stream_type_t to AIDL AudioStreamType. */
- @VisibleForTesting
- public static int legacy2aidl_audio_stream_type_t_AudioStreamType(
- int /*audio_stream_type_t*/ legacy) {
- // Relies on the fact that AudioStreamType was converted from
- // the HIDL definition which uses the same constant values as system/audio.h
- return legacy;
- }
+ public static native int legacy2aidl_audio_stream_type_t_AudioStreamType(
+ int /*audio_stream_type_t*/ legacy);
+
+ /** Convert from AIDL AudioUsage to legacy audio_usage_t. */
+ public static native int aidl2legacy_AudioUsage_audio_usage_t(int /*AudioUsage.* */ aidl);
/** Convert from legacy audio_usage_t to AIDL AudioUsage. */
- @VisibleForTesting
- public static int legacy2aidl_audio_usage_t_AudioUsage(int /*audio_usage_t*/ legacy) {
- // Relies on the fact that AudioUsage was converted from
- // the HIDL definition which uses the same constant values as system/audio.h
- return legacy;
- }
+ public static native int legacy2aidl_audio_usage_t_AudioUsage(int /*audio_usage_t*/ legacy);
/** Convert from AIDL AudioChannelLayout to SDK AudioFormat.CHANNEL_*. */
- @VisibleForTesting
public static int aidl2api_AudioChannelLayout_AudioFormatChannelMask(
@NonNull AudioChannelLayout aidlMask, boolean isInput) {
switch (aidlMask.getTag()) {
@@ -283,29 +292,34 @@
}
/** Convert from AIDL AudioConfig to SDK AudioFormat. */
- @VisibleForTesting
public static @NonNull AudioFormat aidl2api_AudioConfig_AudioFormat(
- @NonNull AudioConfig audioConfig, boolean isInput) {
+ @NonNull AudioConfig aidl, boolean isInput) {
+ // Only information from the encapsulated AudioConfigBase is used.
+ return aidl2api_AudioConfigBase_AudioFormat(aidl.base, isInput);
+ }
+
+ /** Convert from AIDL AudioConfigBase to SDK AudioFormat. */
+ public static @NonNull AudioFormat aidl2api_AudioConfigBase_AudioFormat(
+ @NonNull AudioConfigBase aidl, boolean isInput) {
AudioFormat.Builder apiBuilder = new AudioFormat.Builder();
- apiBuilder.setSampleRate(audioConfig.sampleRateHz);
- if (audioConfig.channelMask.getTag() != AudioChannelLayout.indexMask) {
+ apiBuilder.setSampleRate(aidl.sampleRate);
+ if (aidl.channelMask.getTag() != AudioChannelLayout.indexMask) {
apiBuilder.setChannelMask(aidl2api_AudioChannelLayout_AudioFormatChannelMask(
- audioConfig.channelMask, isInput));
+ aidl.channelMask, isInput));
} else {
apiBuilder.setChannelIndexMask(aidl2api_AudioChannelLayout_AudioFormatChannelMask(
- audioConfig.channelMask, isInput));
+ aidl.channelMask, isInput));
}
- apiBuilder.setEncoding(aidl2api_AudioFormat_AudioFormatEncoding(audioConfig.format));
+ apiBuilder.setEncoding(aidl2api_AudioFormat_AudioFormatEncoding(aidl.format));
return apiBuilder.build();
}
/** Convert from AIDL AudioFormat to SDK AudioFormat.ENCODING_*. */
- @VisibleForTesting
public static int aidl2api_AudioFormat_AudioFormatEncoding(
- @NonNull AudioFormatDescription aidlFormat) {
- switch (aidlFormat.type) {
+ @NonNull AudioFormatDescription aidl) {
+ switch (aidl.type) {
case AudioFormatType.PCM:
- switch (aidlFormat.pcm) {
+ switch (aidl.pcm) {
case PcmType.UINT_8_BIT:
return AudioFormat.ENCODING_PCM_8BIT;
case PcmType.INT_16_BIT:
@@ -321,54 +335,54 @@
return AudioFormat.ENCODING_INVALID;
}
case AudioFormatType.NON_PCM: // same as DEFAULT
- if (aidlFormat.encoding != null && !aidlFormat.encoding.isEmpty()) {
- if (MediaFormat.MIMETYPE_AUDIO_AC3.equals(aidlFormat.encoding)) {
+ if (aidl.encoding != null && !aidl.encoding.isEmpty()) {
+ if (MediaFormat.MIMETYPE_AUDIO_AC3.equals(aidl.encoding)) {
return AudioFormat.ENCODING_AC3;
- } else if (MediaFormat.MIMETYPE_AUDIO_EAC3.equals(aidlFormat.encoding)) {
+ } else if (MediaFormat.MIMETYPE_AUDIO_EAC3.equals(aidl.encoding)) {
return AudioFormat.ENCODING_E_AC3;
- } else if (MediaFormat.MIMETYPE_AUDIO_DTS.equals(aidlFormat.encoding)) {
+ } else if (MediaFormat.MIMETYPE_AUDIO_DTS.equals(aidl.encoding)) {
return AudioFormat.ENCODING_DTS;
- } else if (MediaFormat.MIMETYPE_AUDIO_DTS_HD.equals(aidlFormat.encoding)) {
+ } else if (MediaFormat.MIMETYPE_AUDIO_DTS_HD.equals(aidl.encoding)) {
return AudioFormat.ENCODING_DTS_HD;
- } else if (MediaFormat.MIMETYPE_AUDIO_MPEG.equals(aidlFormat.encoding)) {
+ } else if (MediaFormat.MIMETYPE_AUDIO_MPEG.equals(aidl.encoding)) {
return AudioFormat.ENCODING_MP3;
- } else if (MediaFormat.MIMETYPE_AUDIO_AAC_LC.equals(aidlFormat.encoding)) {
+ } else if (MediaFormat.MIMETYPE_AUDIO_AAC_LC.equals(aidl.encoding)) {
return AudioFormat.ENCODING_AAC_LC;
- } else if (MediaFormat.MIMETYPE_AUDIO_AAC_HE_V1.equals(aidlFormat.encoding)) {
+ } else if (MediaFormat.MIMETYPE_AUDIO_AAC_HE_V1.equals(aidl.encoding)) {
return AudioFormat.ENCODING_AAC_HE_V1;
- } else if (MediaFormat.MIMETYPE_AUDIO_AAC_HE_V2.equals(aidlFormat.encoding)) {
+ } else if (MediaFormat.MIMETYPE_AUDIO_AAC_HE_V2.equals(aidl.encoding)) {
return AudioFormat.ENCODING_AAC_HE_V2;
- } else if (MediaFormat.MIMETYPE_AUDIO_IEC61937.equals(aidlFormat.encoding)
- && aidlFormat.pcm == PcmType.INT_16_BIT) {
+ } else if (MediaFormat.MIMETYPE_AUDIO_IEC61937.equals(aidl.encoding)
+ && aidl.pcm == PcmType.INT_16_BIT) {
return AudioFormat.ENCODING_IEC61937;
} else if (MediaFormat.MIMETYPE_AUDIO_DOLBY_TRUEHD.equals(
- aidlFormat.encoding)) {
+ aidl.encoding)) {
return AudioFormat.ENCODING_DOLBY_TRUEHD;
- } else if (MediaFormat.MIMETYPE_AUDIO_AAC_ELD.equals(aidlFormat.encoding)) {
+ } else if (MediaFormat.MIMETYPE_AUDIO_AAC_ELD.equals(aidl.encoding)) {
return AudioFormat.ENCODING_AAC_ELD;
- } else if (MediaFormat.MIMETYPE_AUDIO_AAC_XHE.equals(aidlFormat.encoding)) {
+ } else if (MediaFormat.MIMETYPE_AUDIO_AAC_XHE.equals(aidl.encoding)) {
return AudioFormat.ENCODING_AAC_XHE;
- } else if (MediaFormat.MIMETYPE_AUDIO_AC4.equals(aidlFormat.encoding)) {
+ } else if (MediaFormat.MIMETYPE_AUDIO_AC4.equals(aidl.encoding)) {
return AudioFormat.ENCODING_AC4;
- } else if (MediaFormat.MIMETYPE_AUDIO_EAC3_JOC.equals(aidlFormat.encoding)) {
+ } else if (MediaFormat.MIMETYPE_AUDIO_EAC3_JOC.equals(aidl.encoding)) {
return AudioFormat.ENCODING_E_AC3_JOC;
- } else if (MediaFormat.MIMETYPE_AUDIO_DOLBY_MAT.equals(aidlFormat.encoding)
- || aidlFormat.encoding.startsWith(
+ } else if (MediaFormat.MIMETYPE_AUDIO_DOLBY_MAT.equals(aidl.encoding)
+ || aidl.encoding.startsWith(
MediaFormat.MIMETYPE_AUDIO_DOLBY_MAT + ".")) {
return AudioFormat.ENCODING_DOLBY_MAT;
- } else if (MediaFormat.MIMETYPE_AUDIO_OPUS.equals(aidlFormat.encoding)) {
+ } else if (MediaFormat.MIMETYPE_AUDIO_OPUS.equals(aidl.encoding)) {
return AudioFormat.ENCODING_OPUS;
- } else if (MediaFormat.MIMETYPE_AUDIO_MPEGH_BL_L3.equals(aidlFormat.encoding)) {
+ } else if (MediaFormat.MIMETYPE_AUDIO_MPEGH_BL_L3.equals(aidl.encoding)) {
return AudioFormat.ENCODING_MPEGH_BL_L3;
- } else if (MediaFormat.MIMETYPE_AUDIO_MPEGH_BL_L4.equals(aidlFormat.encoding)) {
+ } else if (MediaFormat.MIMETYPE_AUDIO_MPEGH_BL_L4.equals(aidl.encoding)) {
return AudioFormat.ENCODING_MPEGH_BL_L4;
- } else if (MediaFormat.MIMETYPE_AUDIO_MPEGH_LC_L3.equals(aidlFormat.encoding)) {
+ } else if (MediaFormat.MIMETYPE_AUDIO_MPEGH_LC_L3.equals(aidl.encoding)) {
return AudioFormat.ENCODING_MPEGH_LC_L3;
- } else if (MediaFormat.MIMETYPE_AUDIO_MPEGH_LC_L4.equals(aidlFormat.encoding)) {
+ } else if (MediaFormat.MIMETYPE_AUDIO_MPEGH_LC_L4.equals(aidl.encoding)) {
return AudioFormat.ENCODING_MPEGH_LC_L4;
- } else if (MediaFormat.MIMETYPE_AUDIO_DTS_UHD.equals(aidlFormat.encoding)) {
+ } else if (MediaFormat.MIMETYPE_AUDIO_DTS_UHD.equals(aidl.encoding)) {
return AudioFormat.ENCODING_DTS_UHD;
- } else if (MediaFormat.MIMETYPE_AUDIO_DRA.equals(aidlFormat.encoding)) {
+ } else if (MediaFormat.MIMETYPE_AUDIO_DRA.equals(aidl.encoding)) {
return AudioFormat.ENCODING_DRA;
} else {
return AudioFormat.ENCODING_INVALID;
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index 72cddc9..353556d 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -50,6 +50,13 @@
private final Context mContext;
private final Map<Callback, CallbackRecord> mCallbacks;
+ /**
+ * Store the WindowContext in a field. If it is a local variable, and it is garbage collected
+ * during a MediaProjection session, the WindowContainer listener no longer exists.
+ */
+ @Nullable
+ private Context mWindowContext;
+
/** @hide */
public MediaProjection(Context context, IMediaProjection impl) {
mCallbacks = new ArrayMap<Callback, CallbackRecord>();
@@ -162,11 +169,11 @@
*/
private VirtualDisplayConfig.Builder buildMirroredVirtualDisplay(@NonNull String name,
int width, int height, int dpi) {
- Context windowContext = mContext.createWindowContext(mContext.getDisplayNoVerify(),
+ mWindowContext = mContext.createWindowContext(mContext.getDisplayNoVerify(),
TYPE_APPLICATION, null /* options */);
final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
height, dpi);
- builder.setWindowTokenClientToMirror(windowContext.getWindowContextToken());
+ builder.setWindowTokenClientToMirror(mWindowContext.getWindowContextToken());
return builder;
}
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index 0d506f0..728424e 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -26,6 +26,7 @@
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
+import android.media.MediaCommunicationManager;
import android.media.MediaMetadata;
import android.media.MediaMetadataEditor;
import android.media.MediaMetadataRetriever;
@@ -53,6 +54,7 @@
private Context mContext;
private MediaSessionManager mSessionManager;
+ private MediaCommunicationManager mCommunicationManager;
private Handler mHandler = new Handler(Looper.getMainLooper());
// The legacy APIs use PendingIntents to register/unregister media button
// receivers and these are associated with RCC.
@@ -63,6 +65,7 @@
mContext = context;
mSessionManager = (MediaSessionManager) context
.getSystemService(Context.MEDIA_SESSION_SERVICE);
+ mCommunicationManager = context.getSystemService(MediaCommunicationManager.class);
}
@UnsupportedAppUsage
@@ -171,7 +174,7 @@
Log.w(TAG, "Tried to send a null key event. Ignoring.");
return;
}
- mSessionManager.dispatchMediaKeyEvent(keyEvent, needWakeLock);
+ mCommunicationManager.dispatchMediaKeyEvent(keyEvent, needWakeLock);
if (DEBUG) {
Log.d(TAG, "dispatched media key " + keyEvent);
}
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 6a50a98..bf264f8f 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -607,7 +607,7 @@
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public void dispatchMediaKeyEventAsSystemService(@NonNull KeyEvent keyEvent) {
- dispatchMediaKeyEventInternal(keyEvent, /*asSystemService=*/true, /*needWakeLock=*/false);
+ dispatchMediaKeyEventInternal(keyEvent, /*asSystemService=*/true, /*needWakeLock=*/true);
}
private void dispatchMediaKeyEventInternal(KeyEvent keyEvent, boolean asSystemService,
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index ed5e6ee..b4ae1fb 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -53,7 +53,6 @@
import android.media.tv.tunerresourcemanager.TunerLnbRequest;
import android.media.tv.tunerresourcemanager.TunerResourceManager;
import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
@@ -354,7 +353,7 @@
profile.tvInputSessionId = tvInputSessionId;
profile.useCase = useCase;
mTunerResourceManager.registerClientProfile(
- profile, new HandlerExecutor(mHandler), mResourceListener, clientId);
+ profile, Runnable::run, mResourceListener, clientId);
mClientId = clientId[0];
mUserId = Process.myUid();
@@ -1146,13 +1145,13 @@
}
}
- private void onFrequenciesReport(int[] frequency) {
+ private void onFrequenciesReport(long[] frequencies) {
synchronized (mScanCallbackLock) {
if (mScanCallbackExecutor != null && mScanCallback != null) {
mScanCallbackExecutor.execute(() -> {
synchronized (mScanCallbackLock) {
if (mScanCallback != null) {
- mScanCallback.onFrequenciesReported(frequency);
+ mScanCallback.onFrequenciesLongReported(frequencies);
}
}
});
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index e7612bc..bd860d9 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -333,6 +333,7 @@
/**
* Gets the filter Id in 32-bit. For any Tuner SoC that supports 64-bit filter architecture,
* use {@link #getIdLong()}.
+ * @deprecated Use {@link #getIdLong()} for both 32-bit and 64-bit filter architectures.
*/
public int getId() {
synchronized (mLock) {
@@ -342,8 +343,7 @@
}
/**
- * Gets the 64-bit filter Id. For any Tuner SoC that supports 32-bit filter architecture,
- * use {@link #getId()}.
+ * Gets the filter Id.
*/
public long getIdLong() {
synchronized (mLock) {
diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
index 768f1d3..e0405ef 100644
--- a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
@@ -229,7 +229,7 @@
return new Builder();
}
- private AnalogFrontendSettings(int frequency, int signalType, int sifStandard, int aftFlag) {
+ private AnalogFrontendSettings(long frequency, int signalType, int sifStandard, int aftFlag) {
super(frequency);
mSignalType = signalType;
mSifStandard = sifStandard;
@@ -240,7 +240,7 @@
* Builder for {@link AnalogFrontendSettings}.
*/
public static class Builder {
- private int mFrequency = 0;
+ private long mFrequency = 0;
private int mSignalType = SIGNAL_TYPE_UNDEFINED;
private int mSifStandard = SIF_UNDEFINED;
private int mAftFlag = AFT_FLAG_UNDEFINED;
@@ -251,10 +251,23 @@
* Sets frequency in Hz.
*
* <p>Default value is 0.
+ * @deprecated Use {@link #setFrequencyLong(long)}
*/
@NonNull
@IntRange(from = 1)
+ @Deprecated
public Builder setFrequency(int frequency) {
+ return setFrequencyLong((long) frequency);
+ }
+
+ /**
+ * Sets frequency in Hz.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequencyLong(long frequency) {
mFrequency = frequency;
return this;
}
diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
index 52a20cb..a7157e2 100644
--- a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
@@ -276,8 +276,8 @@
private final int mDemodOutputFormat;
private final Atsc3PlpSettings[] mPlpSettings;
- private Atsc3FrontendSettings(int frequency, int bandwidth, int demodOutputFormat,
- Atsc3PlpSettings[] plpSettings) {
+ private Atsc3FrontendSettings(
+ long frequency, int bandwidth, int demodOutputFormat, Atsc3PlpSettings[] plpSettings) {
super(frequency);
mBandwidth = bandwidth;
mDemodOutputFormat = demodOutputFormat;
@@ -319,7 +319,7 @@
* Builder for {@link Atsc3FrontendSettings}.
*/
public static class Builder {
- private int mFrequency = 0;
+ private long mFrequency = 0;
private int mBandwidth = BANDWIDTH_UNDEFINED;
private int mDemodOutputFormat = DEMOD_OUTPUT_FORMAT_UNDEFINED;
private Atsc3PlpSettings[] mPlpSettings = {};
@@ -331,10 +331,23 @@
* Sets frequency in Hz.
*
* <p>Default value is 0.
+ * @deprecated Use {@link #setFrequencyLong(long)}
*/
@NonNull
@IntRange(from = 1)
+ @Deprecated
public Builder setFrequency(int frequency) {
+ return setFrequencyLong((long) frequency);
+ }
+
+ /**
+ * Sets frequency in Hz.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequencyLong(long frequency) {
mFrequency = frequency;
return this;
}
diff --git a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
index 042bba8..3071ce8 100644
--- a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
@@ -61,7 +61,7 @@
private final int mModulation;
- private AtscFrontendSettings(int frequency, int modulation) {
+ private AtscFrontendSettings(long frequency, int modulation) {
super(frequency);
mModulation = modulation;
}
@@ -86,7 +86,7 @@
* Builder for {@link AtscFrontendSettings}.
*/
public static class Builder {
- private int mFrequency = 0;
+ private long mFrequency = 0;
private int mModulation = MODULATION_UNDEFINED;
private Builder() {
@@ -96,10 +96,23 @@
* Sets frequency in Hz.
*
* <p>Default value is 0.
+ * @deprecated Use {@link #setFrequencyLong(long)}
*/
@NonNull
@IntRange(from = 1)
+ @Deprecated
public Builder setFrequency(int frequency) {
+ return setFrequencyLong((long) frequency);
+ }
+
+ /**
+ * Sets frequency in Hz.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequencyLong(long frequency) {
mFrequency = frequency;
return this;
}
diff --git a/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java
index 9ba41d5..91102d4 100644
--- a/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java
@@ -247,7 +247,7 @@
private final int mGuardInterval;
private final int mTimeInterleaveMode;
- private DtmbFrontendSettings(int frequency, int modulation, int codeRate, int transmissionMode,
+ private DtmbFrontendSettings(long frequency, int modulation, int codeRate, int transmissionMode,
int guardInterval, int timeInterleaveMode, int bandwidth) {
super(frequency);
mModulation = modulation;
@@ -319,7 +319,7 @@
* Builder for {@link AtscFrontendSettings}.
*/
public static final class Builder {
- private int mFrequency = 0;
+ private long mFrequency = 0;
private int mModulation = MODULATION_CONSTELLATION_UNDEFINED;
private int mCodeRate = CODERATE_UNDEFINED;
private int mTransmissionMode = TRANSMISSION_MODE_UNDEFINED;
@@ -334,11 +334,24 @@
* Sets frequency in Hz.
*
* <p>Default value is 0.
+ * @deprecated Use {@link #setFrequencyLong(long)}
+ */
+ @NonNull
+ @IntRange(from = 1)
+ @Deprecated
+ public Builder setFrequency(int frequency) {
+ return setFrequencyLong((long) frequency);
+ }
+
+ /**
+ * Sets frequency in Hz.
+ *
+ * <p>Default value is 0.
*/
@NonNull
@IntRange(from = 1)
@SuppressLint("MissingGetterMatchingBuilder")
- public Builder setFrequency(int frequency) {
+ public Builder setFrequencyLong(long frequency) {
mFrequency = frequency;
return this;
}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
index b209d97..afe953d 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
@@ -266,7 +266,7 @@
// Dvbc bandwidth is only supported in Tuner 1.1 or higher.
private final int mBandwidth;
- private DvbcFrontendSettings(int frequency, int modulation, long innerFec, int symbolRate,
+ private DvbcFrontendSettings(long frequency, int modulation, long innerFec, int symbolRate,
int outerFec, int annex, int spectralInversion, int interleaveMode, int bandwidth) {
super(frequency);
mModulation = modulation;
@@ -347,7 +347,7 @@
* Builder for {@link DvbcFrontendSettings}.
*/
public static class Builder {
- private int mFrequency = 0;
+ private long mFrequency = 0;
private int mModulation = MODULATION_UNDEFINED;
private long mInnerFec = FEC_UNDEFINED;
private int mSymbolRate = 0;
@@ -364,10 +364,23 @@
* Sets frequency in Hz.
*
* <p>Default value is 0.
+ * @deprecated Use {@link #setFrequencyLong(long)}
*/
@NonNull
@IntRange(from = 1)
+ @Deprecated
public Builder setFrequency(int frequency) {
+ return setFrequencyLong((long) frequency);
+ }
+
+ /**
+ * Sets frequency in Hz.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequencyLong(long frequency) {
mFrequency = frequency;
return this;
}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
index 6e3d98a..e16f192 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
@@ -263,7 +263,7 @@
// isDiseqcRxMessage is only supported in Tuner 1.1 or higher.
private final boolean mIsDiseqcRxMessage;
- private DvbsFrontendSettings(int frequency, int modulation, DvbsCodeRate codeRate,
+ private DvbsFrontendSettings(long frequency, int modulation, DvbsCodeRate codeRate,
int symbolRate, int rolloff, int pilot, int inputStreamId, int standard, int vcm,
int scanType, boolean isDiseqcRxMessage) {
super(frequency);
@@ -363,7 +363,7 @@
* Builder for {@link DvbsFrontendSettings}.
*/
public static class Builder {
- private int mFrequency = 0;
+ private long mFrequency = 0;
private int mModulation = MODULATION_UNDEFINED;
private DvbsCodeRate mCodeRate = null;
private int mSymbolRate = 0;
@@ -382,10 +382,23 @@
* Sets frequency in Hz.
*
* <p>Default value is 0.
+ * @deprecated Use {@link #setFrequencyLong(long)}
*/
@NonNull
@IntRange(from = 1)
+ @Deprecated
public Builder setFrequency(int frequency) {
+ return setFrequencyLong((long) frequency);
+ }
+
+ /**
+ * Sets frequency in Hz.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequencyLong(long frequency) {
mFrequency = frequency;
return this;
}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
index 5735b39..d86e9a8 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
@@ -399,7 +399,7 @@
private final int mPlpId;
private final int mPlpGroupId;
- private DvbtFrontendSettings(int frequency, int transmissionMode, int bandwidth,
+ private DvbtFrontendSettings(long frequency, int transmissionMode, int bandwidth,
int constellation, int hierarchy, int hpCodeRate, int lpCodeRate, int guardInterval,
boolean isHighPriority, int standard, boolean isMiso, int plpMode, int plpId,
int plpGroupId) {
@@ -532,7 +532,7 @@
* Builder for {@link DvbtFrontendSettings}.
*/
public static class Builder {
- private int mFrequency = 0;
+ private long mFrequency = 0;
private int mTransmissionMode = TRANSMISSION_MODE_UNDEFINED;
private int mBandwidth = BANDWIDTH_UNDEFINED;
private int mConstellation = CONSTELLATION_UNDEFINED;
@@ -554,10 +554,23 @@
* Sets frequency in Hz.
*
* <p>Default value is 0.
+ * @deprecated Use {@link #setFrequencyLong(long)}
*/
@NonNull
@IntRange(from = 1)
+ @Deprecated
public Builder setFrequency(int frequency) {
+ return setFrequencyLong((long) frequency);
+ }
+
+ /**
+ * Sets frequency in Hz.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequencyLong(long frequency) {
mFrequency = frequency;
return this;
}
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
index e96cae6..5cabb71 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
@@ -34,15 +34,15 @@
public class FrontendInfo {
private final int mId;
private final int mType;
- private final Range<Integer> mFrequencyRange;
+ private final Range<Long> mFrequencyRange;
private final Range<Integer> mSymbolRateRange;
- private final int mAcquireRange;
+ private final long mAcquireRange;
private final int mExclusiveGroupId;
private final int[] mStatusCaps;
private final FrontendCapabilities mFrontendCap;
- private FrontendInfo(int id, int type, int minFrequency, int maxFrequency, int minSymbolRate,
- int maxSymbolRate, int acquireRange, int exclusiveGroupId, int[] statusCaps,
+ private FrontendInfo(int id, int type, long minFrequency, long maxFrequency, int minSymbolRate,
+ int maxSymbolRate, long acquireRange, int exclusiveGroupId, int[] statusCaps,
FrontendCapabilities frontendCap) {
mId = id;
mType = type;
@@ -77,9 +77,21 @@
/**
* Gets supported frequency range in Hz.
+ *
+ * @deprecated Use {@link #getFrequencyRangeLong()}
*/
+ @Deprecated
@NonNull
public Range<Integer> getFrequencyRange() {
+ return new Range<>(
+ (int) (long) mFrequencyRange.getLower(), (int) (long) mFrequencyRange.getUpper());
+ }
+
+ /**
+ * Gets supported frequency range in Hz.
+ */
+ @NonNull
+ public Range<Long> getFrequencyRangeLong() {
return mFrequencyRange;
}
@@ -95,10 +107,22 @@
* Gets acquire range in Hz.
*
* <p>The maximum frequency difference the frontend can detect.
+ @deprecated Use {@link #getAcquireRangeLong(long)}
*/
+ @Deprecated
public int getAcquireRange() {
+ return (int) getAcquireRangeLong();
+ }
+
+ /**
+ * Gets acquire range in Hz.
+ *
+ * <p>The maximum frequency difference the frontend can detect.
+ */
+ public long getAcquireRangeLong() {
return mAcquireRange;
}
+
/**
* Gets exclusive group ID.
*
@@ -108,6 +132,7 @@
public int getExclusiveGroupId() {
return mExclusiveGroupId;
}
+
/**
* Gets status capabilities.
*
@@ -118,6 +143,7 @@
public int[] getStatusCapabilities() {
return mStatusCaps;
}
+
/**
* Gets frontend capabilities.
*/
@@ -126,7 +152,6 @@
return mFrontendCap;
}
-
/** @hide */
@Override
public boolean equals(Object o) {
@@ -139,9 +164,9 @@
// TODO: compare FrontendCapabilities
FrontendInfo info = (FrontendInfo) o;
return mId == info.getId() && mType == info.getType()
- && Objects.equals(mFrequencyRange, info.getFrequencyRange())
+ && Objects.equals(mFrequencyRange, info.getFrequencyRangeLong())
&& Objects.equals(mSymbolRateRange, info.getSymbolRateRange())
- && mAcquireRange == info.getAcquireRange()
+ && mAcquireRange == info.getAcquireRangeLong()
&& mExclusiveGroupId == info.getExclusiveGroupId()
&& Arrays.equals(mStatusCaps, info.getStatusCapabilities());
}
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
index 4a31686..38bffec 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
@@ -271,17 +271,13 @@
public static final int FRONTEND_SPECTRAL_INVERSION_INVERTED =
android.hardware.tv.tuner.FrontendSpectralInversion.INVERTED;
-
-
- private final int mFrequency;
+ private final long mFrequency;
// End frequency is only supported in Tuner 1.1 or higher.
- private int mEndFrequency = Tuner.INVALID_FRONTEND_SETTING_FREQUENCY;
+ private long mEndFrequency = Tuner.INVALID_FRONTEND_SETTING_FREQUENCY;
// General spectral inversion is only supported in Tuner 1.1 or higher.
private int mSpectralInversion = FRONTEND_SPECTRAL_INVERSION_UNDEFINED;
- FrontendSettings(int frequency) {
- mFrequency = frequency;
- }
+ FrontendSettings(long frequency) { mFrequency = frequency; }
/**
* Returns the frontend type.
@@ -293,8 +289,19 @@
* Gets the frequency.
*
* @return the frequency in Hz.
+ * @deprecated Use {@link #getFrequencyLong()}
*/
+ @Deprecated
public int getFrequency() {
+ return (int) getFrequencyLong();
+ }
+
+ /**
+ * Gets the frequency.
+ *
+ * @return the frequency in Hz.
+ */
+ public long getFrequencyLong() {
return mFrequency;
}
@@ -302,9 +309,21 @@
* Get the end frequency.
*
* @return the end frequency in Hz.
+ * @deprecated Use {@link #getEndFrequencyLong()}
+ */
+ @Deprecated
+ @IntRange(from = 1)
+ public int getEndFrequency() {
+ return (int) getEndFrequencyLong();
+ }
+
+ /**
+ * Get the end frequency.
+ *
+ * @return the end frequency in Hz.
*/
@IntRange(from = 1)
- public int getEndFrequency() {
+ public long getEndFrequencyLong() {
return mEndFrequency;
}
@@ -344,9 +363,27 @@
* @param endFrequency the end frequency used during blind scan. The default value is
* {@link android.media.tv.tuner.Tuner#INVALID_FRONTEND_SETTING_FREQUENCY}.
* @throws IllegalArgumentException if the {@code endFrequency} is not greater than 0.
+ * @deprecated Use {@link #setFrequencyLong(long)}
*/
@IntRange(from = 1)
- public void setEndFrequency(int endFrequency) {
+ @Deprecated
+ public void setEndFrequency(int frequency) {
+ setEndFrequencyLong((long) frequency);
+ }
+
+ /**
+ * Set End Frequency. This API is only supported with Tuner HAL 1.1 or higher. Otherwise it
+ * would be no-op.
+ *
+ * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
+ * no-op. Use {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ *
+ * @param endFrequency the end frequency used during blind scan. The default value is
+ * {@link android.media.tv.tuner.Tuner#INVALID_FRONTEND_SETTING_FREQUENCY}.
+ * @throws IllegalArgumentException if the {@code endFrequency} is not greater than 0.
+ */
+ @IntRange(from = 1)
+ public void setEndFrequencyLong(long endFrequency) {
if (TunerVersionChecker.checkHigherOrEqualVersionTo(
TunerVersionChecker.TUNER_VERSION_1_1, "setEndFrequency")) {
if (endFrequency < 1) {
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index 36fd942..53adc75 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -447,7 +447,7 @@
private Boolean mIsLnaOn;
private boolean[] mIsLayerErrors;
private Integer mMer;
- private Integer mFreqOffset;
+ private Long mFreqOffset;
private Integer mHierarchy;
private Boolean mIsRfLocked;
private Atsc3PlpTuningInfo[] mPlpInfo;
@@ -651,8 +651,18 @@
* Gets the current frequency difference in Hz.
*
* <p>Difference between tuning frequency and actual locked frequency.
+ * @deprecated Use {@link #getFreqOffsetLong()}
*/
+ @Deprecated
public int getFreqOffset() {
+ return (int) getFreqOffsetLong();
+ }
+ /**
+ * Gets the current frequency difference in Hz.
+ *
+ * <p>Difference between tuning frequency and actual locked frequency.
+ */
+ public long getFreqOffsetLong() {
if (mFreqOffset == null) {
throw new IllegalStateException("FreqOffset status is empty");
}
diff --git a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
index 14b0b02..726fe15 100644
--- a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
@@ -158,7 +158,7 @@
private final int mSymbolRate;
private final int mRolloff;
- private Isdbs3FrontendSettings(int frequency, int streamId, int streamIdType, int modulation,
+ private Isdbs3FrontendSettings(long frequency, int streamId, int streamIdType, int modulation,
int codeRate, int symbolRate, int rolloff) {
super(frequency);
mStreamId = streamId;
@@ -222,7 +222,7 @@
* Builder for {@link Isdbs3FrontendSettings}.
*/
public static class Builder {
- private int mFrequency = 0;
+ private long mFrequency = 0;
private int mStreamId = Tuner.INVALID_STREAM_ID;
private int mStreamIdType = IsdbsFrontendSettings.STREAM_ID_TYPE_ID;
private int mModulation = MODULATION_UNDEFINED;
@@ -237,10 +237,23 @@
* Sets frequency in Hz.
*
* <p>Default value is 0.
+ * @deprecated Use {@link #setFrequencyLong(long)}
*/
@NonNull
@IntRange(from = 1)
+ @Deprecated
public Builder setFrequency(int frequency) {
+ return setFrequencyLong((long) frequency);
+ }
+
+ /**
+ * Sets frequency in Hz.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequencyLong(long frequency) {
mFrequency = frequency;
return this;
}
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
index aab6408..51ec5ae 100644
--- a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
@@ -143,7 +143,7 @@
private final int mSymbolRate;
private final int mRolloff;
- private IsdbsFrontendSettings(int frequency, int streamId, int streamIdType, int modulation,
+ private IsdbsFrontendSettings(long frequency, int streamId, int streamIdType, int modulation,
int codeRate, int symbolRate, int rolloff) {
super(frequency);
mStreamId = streamId;
@@ -207,7 +207,7 @@
* Builder for {@link IsdbsFrontendSettings}.
*/
public static class Builder {
- private int mFrequency = 0;
+ private long mFrequency = 0;
private int mStreamId = Tuner.INVALID_STREAM_ID;
private int mStreamIdType = STREAM_ID_TYPE_ID;
private int mModulation = MODULATION_UNDEFINED;
@@ -222,10 +222,23 @@
* Sets frequency in Hz.
*
* <p>Default value is 0.
+ * @deprecated Use {@link #setFrequencyLong(long)}
*/
@NonNull
@IntRange(from = 1)
+ @Deprecated
public Builder setFrequency(int frequency) {
+ return setFrequencyLong((long) frequency);
+ }
+
+ /**
+ * Sets frequency in Hz.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequencyLong(long frequency) {
mFrequency = frequency;
return this;
}
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
index de2476a..d34b643 100644
--- a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
@@ -134,7 +134,7 @@
private final int mGuardInterval;
private final int mServiceAreaId;
- private IsdbtFrontendSettings(int frequency, int modulation, int bandwidth, int mode,
+ private IsdbtFrontendSettings(long frequency, int modulation, int bandwidth, int mode,
int codeRate, int guardInterval, int serviceAreaId) {
super(frequency);
mModulation = modulation;
@@ -199,7 +199,7 @@
* Builder for {@link IsdbtFrontendSettings}.
*/
public static class Builder {
- private int mFrequency = 0;
+ private long mFrequency = 0;
private int mModulation = MODULATION_UNDEFINED;
private int mBandwidth = BANDWIDTH_UNDEFINED;
private int mMode = MODE_UNDEFINED;
@@ -214,10 +214,23 @@
* Sets frequency in Hz.
*
* <p>Default value is 0.
+ * @deprecated Use {@link #setFrequencyLong(long)}
*/
@NonNull
@IntRange(from = 1)
+ @Deprecated
public Builder setFrequency(int frequency) {
+ return setFrequencyLong((long) frequency);
+ }
+
+ /**
+ * Sets frequency in Hz.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequencyLong(long frequency) {
mFrequency = frequency;
return this;
}
diff --git a/media/java/android/media/tv/tuner/frontend/ScanCallback.java b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
index 27627d7..f61bd52 100644
--- a/media/java/android/media/tv/tuner/frontend/ScanCallback.java
+++ b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
@@ -37,8 +37,19 @@
/** scan progress percent (0..100) */
void onProgress(@IntRange(from = 0, to = 100) int percent);
+ /**
+ * @deprecated Use {@link #onFrequenciesLongReported(long[])}
+ */
+ @Deprecated void onFrequenciesReported(@NonNull int[] frequencies);
+
/** Signal frequencies in Hertz */
- void onFrequenciesReported(@NonNull int[] frequency);
+ default void onFrequenciesLongReported(@NonNull long[] frequencies) {
+ final int[] intFrequencies = new int[frequencies.length];
+ for (int i = 0; i < frequencies.length; i++) {
+ intFrequencies[i] = (int) frequencies[i];
+ }
+ onFrequenciesReported(intFrequencies);
+ }
/** Symbols per second */
void onSymbolRatesReported(@NonNull int[] rate);
diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/IResourcesReclaimListener.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/IResourcesReclaimListener.aidl
index 1a4eb29..feb68dc 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/IResourcesReclaimListener.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/IResourcesReclaimListener.aidl
@@ -21,7 +21,7 @@
*
* @hide
*/
-oneway interface IResourcesReclaimListener {
+interface IResourcesReclaimListener {
/*
* TRM invokes this method when the client's resources need to be reclaimed.
*
@@ -30,4 +30,4 @@
* then grant the resource.
*/
void onReclaimResources();
-}
\ No newline at end of file
+}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index f1c72f8..4bee485 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -978,24 +978,16 @@
}
case FrontendScanMessageType::FREQUENCY: {
std::vector<int64_t> v = message.get<FrontendScanMessage::Tag::frequencies>();
- std::vector<uint32_t> jintV;
- for (int i = 0; i < v.size(); i++) {
- jintV.push_back(static_cast<uint32_t>(v[i]));
- }
- jintArray freqs = env->NewIntArray(jintV.size());
- env->SetIntArrayRegion(freqs, 0, v.size(), reinterpret_cast<jint *>(&jintV[0]));
-
- env->CallVoidMethod(
- frontend,
- env->GetMethodID(clazz, "onFrequenciesReport", "([I)V"),
- freqs);
+ jlongArray freqs = env->NewLongArray(v.size());
+ env->SetLongArrayRegion(freqs, 0, v.size(), reinterpret_cast<jlong *>(&v[0]));
+ env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onFrequenciesReport", "([J)V"),
+ freqs);
break;
}
case FrontendScanMessageType::SYMBOL_RATE: {
std::vector<int32_t> v = message.get<FrontendScanMessage::Tag::symbolRates>();
jintArray symbolRates = env->NewIntArray(v.size());
env->SetIntArrayRegion(symbolRates, 0, v.size(), reinterpret_cast<jint *>(&v[0]));
-
env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onSymbolRates", "([I)V"),
symbolRates);
break;
@@ -1384,15 +1376,16 @@
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendInfo");
- jmethodID infoInit = env->GetMethodID(clazz, "<init>",
- "(IIIIIIII[ILandroid/media/tv/tuner/frontend/FrontendCapabilities;)V");
+ jmethodID infoInit =
+ env->GetMethodID(clazz, "<init>",
+ "(IIJJIIJI[ILandroid/media/tv/tuner/frontend/FrontendCapabilities;)V");
jint type = (jint)feInfo->type;
- jint minFrequency = static_cast<uint32_t>(feInfo->minFrequency);
- jint maxFrequency = static_cast<uint32_t>(feInfo->maxFrequency);
+ jlong minFrequency = feInfo->minFrequency;
+ jlong maxFrequency = feInfo->maxFrequency;
jint minSymbolRate = feInfo->minSymbolRate;
jint maxSymbolRate = feInfo->maxSymbolRate;
- jint acquireRange = feInfo->acquireRange;
+ jlong acquireRange = feInfo->acquireRange;
jint exclusiveGroupId = feInfo->exclusiveGroupId;
jintArray statusCaps = env->NewIntArray(feInfo->statusCaps.size());
env->SetIntArrayRegion(
@@ -1854,6 +1847,8 @@
jmethodID initInt = env->GetMethodID(intClazz, "<init>", "(I)V");
jclass booleanClazz = env->FindClass("java/lang/Boolean");
jmethodID initBoolean = env->GetMethodID(booleanClazz, "<init>", "(Z)V");
+ jclass longClazz = env->FindClass("java/lang/Long");
+ jmethodID initLong = env->GetMethodID(longClazz, "<init>", "(J)V");
for (int i = 0; i < status.size(); i++) {
const FrontendStatus &s = status[i];
@@ -2033,12 +2028,10 @@
break;
}
case FrontendStatus::Tag::freqOffset: {
- jfieldID field = env->GetFieldID(clazz, "mFreqOffset", "Ljava/lang/Integer;");
- jobject newIntegerObj =
- env->NewObject(intClazz, initInt,
- static_cast<uint32_t>(
- s.get<FrontendStatus::Tag::freqOffset>()));
- env->SetObjectField(statusObj, field, newIntegerObj);
+ jfieldID field = env->GetFieldID(clazz, "mFreqOffset", "Ljava/lang/Long;");
+ jobject newLongObj = env->NewObject(longClazz, initLong,
+ s.get<FrontendStatus::Tag::freqOffset>());
+ env->SetObjectField(statusObj, field, newLongObj);
break;
}
case FrontendStatus::Tag::hierarchy: {
@@ -2480,14 +2473,14 @@
static int64_t getFrontendSettingsFreq(JNIEnv *env, const jobject &settings) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendSettings");
- jfieldID freqField = env->GetFieldID(clazz, "mFrequency", "I");
- return static_cast<uint32_t>(env->GetIntField(settings, freqField));
+ jfieldID freqField = env->GetFieldID(clazz, "mFrequency", "J");
+ return env->GetLongField(settings, freqField);
}
static int64_t getFrontendSettingsEndFreq(JNIEnv *env, const jobject &settings) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendSettings");
- jfieldID endFreqField = env->GetFieldID(clazz, "mEndFrequency", "I");
- return static_cast<uint32_t>(env->GetIntField(settings, endFreqField));
+ jfieldID endFreqField = env->GetFieldID(clazz, "mEndFrequency", "J");
+ return env->GetLongField(settings, endFreqField);
}
static FrontendSpectralInversion getFrontendSettingsSpectralInversion(
diff --git a/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java b/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java
index c9e79e6..5f64d20 100644
--- a/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java
+++ b/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java
@@ -16,19 +16,19 @@
package android.media.audio.common;
+import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioSystem;
+import android.media.AudioTrack;
import android.media.MediaFormat;
import android.platform.test.annotations.Presubmit;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import androidx.test.runner.AndroidJUnit4;
+
+import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
/**
* Unit tests for AidlConversion utilities.
@@ -36,10 +36,15 @@
* Run with "atest AidlConversionUnitTests".
*/
@Presubmit
-@RunWith(JUnit4.class)
+@RunWith(AndroidJUnit4.class)
public final class AidlConversionUnitTests {
private static final String TAG = "AidlConvTests";
+ // Negative values are considered to be "invalid" as a general rule.
+ // However, '-1' is sometimes used as "system invalid" value, and thus
+ // does not cause an exception to be thrown during conversion.
+ private static int sInvalidValue = -2;
+ private static byte sInvalidValueByte = -2;
@Test
public void testAudioChannelConversionApiDefault() {
@@ -92,11 +97,12 @@
}
@Test
- public void testAudioConfigConfersionApiIndex() {
+ public void testAudioConfigConversionApiIndex() {
final AudioConfig aidl = new AudioConfig();
- aidl.sampleRateHz = 8000;
- aidl.channelMask = AudioChannelLayout.indexMask(AudioChannelLayout.INDEX_MASK_1);
- aidl.format = createPcm16FormatAidl();
+ aidl.base = new AudioConfigBase();
+ aidl.base.sampleRate = 8000;
+ aidl.base.channelMask = AudioChannelLayout.indexMask(AudioChannelLayout.INDEX_MASK_1);
+ aidl.base.format = createPcm16FormatAidl();
// Other fields in AudioConfig are irrelevant.
final AudioFormat api = AidlConversion.aidl2api_AudioConfig_AudioFormat(
aidl, false /*isInput*/);
@@ -109,11 +115,12 @@
}
@Test
- public void testAudioConfigConfersionApiLayout() {
+ public void testAudioConfigConversionApiLayout() {
final AudioConfig aidl = new AudioConfig();
- aidl.sampleRateHz = 8000;
- aidl.channelMask = AudioChannelLayout.layoutMask(AudioChannelLayout.LAYOUT_MONO);
- aidl.format = createPcm16FormatAidl();
+ aidl.base = new AudioConfigBase();
+ aidl.base.sampleRate = 8000;
+ aidl.base.channelMask = AudioChannelLayout.layoutMask(AudioChannelLayout.LAYOUT_MONO);
+ aidl.base.format = createPcm16FormatAidl();
// Other fields in AudioConfig are irrelevant.
final AudioFormat api = AidlConversion.aidl2api_AudioConfig_AudioFormat(
aidl, false /*isInput*/);
@@ -128,6 +135,40 @@
}
@Test
+ public void testAudioConfigBaseConversionApiIndex() {
+ final AudioConfigBase aidl = new AudioConfigBase();
+ aidl.sampleRate = 8000;
+ aidl.channelMask = AudioChannelLayout.indexMask(AudioChannelLayout.INDEX_MASK_1);
+ aidl.format = createPcm16FormatAidl();
+ final AudioFormat api = AidlConversion.aidl2api_AudioConfigBase_AudioFormat(
+ aidl, false /*isInput*/);
+ final AudioFormat apiInput = AidlConversion.aidl2api_AudioConfigBase_AudioFormat(
+ aidl, true /*isInput*/);
+ assertEquals(api, apiInput);
+ assertEquals(8000, api.getSampleRate());
+ assertEquals(1, api.getChannelIndexMask());
+ assertEquals(AudioFormat.ENCODING_PCM_16BIT, api.getEncoding());
+ }
+
+ @Test
+ public void testAudioConfigBaseConversionApiLayout() {
+ final AudioConfigBase aidl = new AudioConfigBase();
+ aidl.sampleRate = 8000;
+ aidl.channelMask = AudioChannelLayout.layoutMask(AudioChannelLayout.LAYOUT_MONO);
+ aidl.format = createPcm16FormatAidl();
+ final AudioFormat api = AidlConversion.aidl2api_AudioConfigBase_AudioFormat(
+ aidl, false /*isInput*/);
+ assertEquals(8000, api.getSampleRate());
+ assertEquals(AudioFormat.CHANNEL_OUT_MONO, api.getChannelMask());
+ assertEquals(AudioFormat.ENCODING_PCM_16BIT, api.getEncoding());
+ final AudioFormat apiInput = AidlConversion.aidl2api_AudioConfigBase_AudioFormat(
+ aidl, true /*isInput*/);
+ assertEquals(8000, apiInput.getSampleRate());
+ assertEquals(AudioFormat.CHANNEL_IN_MONO, apiInput.getChannelMask());
+ assertEquals(AudioFormat.ENCODING_PCM_16BIT, apiInput.getEncoding());
+ }
+
+ @Test
public void testAudioFormatConversionApiDefault() {
final AudioFormatDescription aidl = new AudioFormatDescription();
final int api = AidlConversion.aidl2api_AudioFormat_AudioFormatEncoding(aidl);
@@ -236,6 +277,22 @@
}
@Test
+ public void testAudioChannelConversionLegacyInvalid() {
+ assertThrows(IllegalArgumentException.class,
+ () -> AidlConversion.aidl2legacy_AudioChannelLayout_audio_channel_mask_t(
+ AudioChannelLayout.voiceMask(sInvalidValue), false /*isInput*/));
+ assertThrows(IllegalArgumentException.class,
+ () -> AidlConversion.aidl2legacy_AudioChannelLayout_audio_channel_mask_t(
+ AudioChannelLayout.voiceMask(sInvalidValue), true /*isInput*/));
+ assertThrows(IllegalArgumentException.class,
+ () -> AidlConversion.legacy2aidl_audio_channel_mask_t_AudioChannelLayout(
+ sInvalidValue, false /*isInput*/));
+ assertThrows(IllegalArgumentException.class,
+ () -> AidlConversion.legacy2aidl_audio_channel_mask_t_AudioChannelLayout(
+ sInvalidValue, true /*isInput*/));
+ }
+
+ @Test
public void testAudioFormatConversionLegacyDefault() {
final int legacy = AudioSystem.AUDIO_FORMAT_DEFAULT;
final AudioFormatDescription aidl =
@@ -273,7 +330,67 @@
assertEquals(legacy, legacyBack);
}
- private AudioFormatDescription createPcm16FormatAidl() {
+ @Test
+ public void testAudioFormatConversionLegacyInvalid() {
+ final AudioFormatDescription aidl = new AudioFormatDescription();
+ aidl.type = sInvalidValueByte;
+ assertThrows(IllegalArgumentException.class,
+ () -> AidlConversion.aidl2legacy_AudioFormatDescription_audio_format_t(aidl));
+ assertThrows(IllegalArgumentException.class,
+ () -> AidlConversion.legacy2aidl_audio_format_t_AudioFormatDescription(
+ sInvalidValue));
+ }
+
+ @Test
+ public void testAudioEncapsulationModeConversionLegacy() {
+ // AIDL values are synchronized with SDK, so we can use the SDK values as AIDL.
+ final int aidl = AudioTrack.ENCAPSULATION_MODE_ELEMENTARY_STREAM;
+ final int legacy =
+ AidlConversion.aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t(aidl);
+ final int aidlBack =
+ AidlConversion.legacy2aidl_audio_encapsulation_mode_t_AudioEncapsulationMode(
+ legacy);
+ assertEquals(aidl, aidlBack);
+
+ assertThrows(IllegalArgumentException.class,
+ () -> AidlConversion.aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t(
+ sInvalidValue));
+ assertThrows(IllegalArgumentException.class,
+ () -> AidlConversion.legacy2aidl_audio_encapsulation_mode_t_AudioEncapsulationMode(
+ sInvalidValue));
+ }
+
+ @Test
+ public void testAudioStreamTypeConversionLegacy() {
+ // AIDL values are synchronized with SDK, so we can use the SDK values as AIDL.
+ final int aidl = AudioSystem.STREAM_MUSIC;
+ final int legacy = AidlConversion.aidl2legacy_AudioStreamType_audio_stream_type_t(aidl);
+ final int aidlBack = AidlConversion.legacy2aidl_audio_stream_type_t_AudioStreamType(legacy);
+ assertEquals(aidl, aidlBack);
+
+ assertThrows(IllegalArgumentException.class,
+ () -> AidlConversion.aidl2legacy_AudioStreamType_audio_stream_type_t(
+ sInvalidValue));
+ assertThrows(IllegalArgumentException.class,
+ () -> AidlConversion.legacy2aidl_audio_stream_type_t_AudioStreamType(
+ sInvalidValue));
+ }
+
+ @Test
+ public void testAudioUsageConversionLegacy() {
+ // AIDL values are synchronized with SDK, so we can use the SDK values as AIDL.
+ final int aidl = AudioAttributes.USAGE_MEDIA;
+ final int legacy = AidlConversion.aidl2legacy_AudioUsage_audio_usage_t(aidl);
+ final int aidlBack = AidlConversion.legacy2aidl_audio_usage_t_AudioUsage(legacy);
+ assertEquals(aidl, aidlBack);
+
+ assertThrows(IllegalArgumentException.class,
+ () -> AidlConversion.aidl2legacy_AudioUsage_audio_usage_t(sInvalidValue));
+ assertThrows(IllegalArgumentException.class,
+ () -> AidlConversion.legacy2aidl_audio_usage_t_AudioUsage(sInvalidValue));
+ }
+
+ private static AudioFormatDescription createPcm16FormatAidl() {
final AudioFormatDescription aidl = new AudioFormatDescription();
aidl.type = AudioFormatType.PCM;
aidl.pcm = PcmType.INT_16_BIT;
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/mdnsFilter/MDNSFilterPlugin.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/mdnsFilter/MDNSFilterPlugin.java
index 5f107d6..34e7e3d 100644
--- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/mdnsFilter/MDNSFilterPlugin.java
+++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/mdnsFilter/MDNSFilterPlugin.java
@@ -84,8 +84,7 @@
*/
public MDNSFilterPlugin(@NonNull Context context, @NonNull String name,
@NonNull CharSequence packageName, @NonNull List<String> mDNSNames) {
- mName = context.getResources().getIdentifier(name, null,
- "com.android.printservice.recommendation");
+ mName = context.getResources().getIdentifier(name, null, context.getPackageName());
mPackageName = packageName;
mMDNSFilteredDiscovery = new MDNSFilteredDiscovery(context, PRINTER_SERVICE_TYPES,
new VendorNameFilter(new HashSet<>(mDNSNames)));
diff --git a/packages/PrintSpooler/res/values-as/strings.xml b/packages/PrintSpooler/res/values-as/strings.xml
index b6b287f..a93fceb 100644
--- a/packages/PrintSpooler/res/values-as/strings.xml
+++ b/packages/PrintSpooler/res/values-as/strings.xml
@@ -47,7 +47,7 @@
<string name="savetopdf_button" msgid="2976186791686924743">"PDFৰ জৰিয়তে ছেভ কৰক"</string>
<string name="print_options_expanded" msgid="6944679157471691859">"প্ৰিণ্ট বিকল্পসমূহ বিস্তাৰ কৰা হ’ল"</string>
<string name="print_options_collapsed" msgid="7455930445670414332">"প্ৰিণ্ট বিকল্পসমূহ সংকুচিত কৰা হ’ল"</string>
- <string name="search" msgid="5421724265322228497">"Search"</string>
+ <string name="search" msgid="5421724265322228497">"সন্ধান কৰক"</string>
<string name="all_printers_label" msgid="3178848870161526399">"সকলো প্ৰিণ্টাৰ"</string>
<string name="add_print_service_label" msgid="5356702546188981940">"সেৱা যোগ কৰক"</string>
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"সন্ধান বাকচটো দেখুওৱা হ’ল"</string>
diff --git a/packages/PrintSpooler/res/values-kn/strings.xml b/packages/PrintSpooler/res/values-kn/strings.xml
index 261fe4b..150ede4 100644
--- a/packages/PrintSpooler/res/values-kn/strings.xml
+++ b/packages/PrintSpooler/res/values-kn/strings.xml
@@ -47,7 +47,7 @@
<string name="savetopdf_button" msgid="2976186791686924743">"PDF ಗೆ ಉಳಿಸು"</string>
<string name="print_options_expanded" msgid="6944679157471691859">"ಪ್ರಿಂಟ್ ಆಯ್ಕೆಗಳನ್ನು ವಿಸ್ತರಿಸಲಾಗಿದೆ"</string>
<string name="print_options_collapsed" msgid="7455930445670414332">"ಪ್ರಿಂಟ್ ಆಯ್ಕೆಗಳನ್ನು ಮುಚ್ಚಲಾಗಿದೆ"</string>
- <string name="search" msgid="5421724265322228497">"Search"</string>
+ <string name="search" msgid="5421724265322228497">"ಹುಡುಕಿ"</string>
<string name="all_printers_label" msgid="3178848870161526399">"ಎಲ್ಲಾ ಪ್ರಿಂಟರ್ಗಳು"</string>
<string name="add_print_service_label" msgid="5356702546188981940">"ಸೇವೆಯನ್ನು ಸೇರಿಸು"</string>
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"ಹುಡುಕಾಟ ಪೆಟ್ಟಿಗೆಯನ್ನು ತೋರಿಸಲಾಗಿದೆ"</string>
diff --git a/packages/PrintSpooler/res/values-ml/strings.xml b/packages/PrintSpooler/res/values-ml/strings.xml
index 73af95d..dbcd34b 100644
--- a/packages/PrintSpooler/res/values-ml/strings.xml
+++ b/packages/PrintSpooler/res/values-ml/strings.xml
@@ -47,7 +47,7 @@
<string name="savetopdf_button" msgid="2976186791686924743">"PDF-ൽ സംരക്ഷിക്കുക"</string>
<string name="print_options_expanded" msgid="6944679157471691859">"പ്രിന്റ് ചെയ്യാനുള്ള ഓപ്ഷനുകൾ വിപുലീകരിച്ചു"</string>
<string name="print_options_collapsed" msgid="7455930445670414332">"പ്രിന്റ് ചെയ്യാനുള്ള ഓപ്ഷനുകൾ ചുരുക്കി"</string>
- <string name="search" msgid="5421724265322228497">"Search"</string>
+ <string name="search" msgid="5421724265322228497">"തിരയൽ"</string>
<string name="all_printers_label" msgid="3178848870161526399">"എല്ലാ പ്രിന്ററുകളും"</string>
<string name="add_print_service_label" msgid="5356702546188981940">"സേവനം ചേർക്കുക"</string>
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"തിരയൽ ബോക്സ് ദൃശ്യമാക്കിയിരിക്കുന്നു"</string>
diff --git a/packages/PrintSpooler/res/values-mr/strings.xml b/packages/PrintSpooler/res/values-mr/strings.xml
index 8119439..e1fa390 100644
--- a/packages/PrintSpooler/res/values-mr/strings.xml
+++ b/packages/PrintSpooler/res/values-mr/strings.xml
@@ -47,7 +47,7 @@
<string name="savetopdf_button" msgid="2976186791686924743">"पीडीएफ वर सेव्ह करा"</string>
<string name="print_options_expanded" msgid="6944679157471691859">"प्रिंट पर्याय विस्तृत झाले"</string>
<string name="print_options_collapsed" msgid="7455930445670414332">"प्रिंट पर्याय संक्षिप्त झाले"</string>
- <string name="search" msgid="5421724265322228497">"Search"</string>
+ <string name="search" msgid="5421724265322228497">"शोधा"</string>
<string name="all_printers_label" msgid="3178848870161526399">"सर्व प्रिंटर"</string>
<string name="add_print_service_label" msgid="5356702546188981940">"सेवा जोडा"</string>
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"शोध बॉक्स दर्शविला"</string>
diff --git a/packages/PrintSpooler/res/values-or/strings.xml b/packages/PrintSpooler/res/values-or/strings.xml
index d6f920f..fa10909 100644
--- a/packages/PrintSpooler/res/values-or/strings.xml
+++ b/packages/PrintSpooler/res/values-or/strings.xml
@@ -47,7 +47,7 @@
<string name="savetopdf_button" msgid="2976186791686924743">"PDFରେ ସେଭ୍ କରନ୍ତୁ"</string>
<string name="print_options_expanded" msgid="6944679157471691859">"ପ୍ରିଣ୍ଟ ବିକଳ୍ପକୁ ବଡ଼ କରାଯାଇଛି"</string>
<string name="print_options_collapsed" msgid="7455930445670414332">"ପ୍ରିଣ୍ଟ ବିକଳ୍ପକୁ ଛୋଟ କରାଯାଇଛି"</string>
- <string name="search" msgid="5421724265322228497">"Search"</string>
+ <string name="search" msgid="5421724265322228497">"ସନ୍ଧାନ କରନ୍ତୁ"</string>
<string name="all_printers_label" msgid="3178848870161526399">"ସମସ୍ତ ପ୍ରିଣ୍ଟର୍"</string>
<string name="add_print_service_label" msgid="5356702546188981940">"ସେବା ଯୋଗ କରନ୍ତୁ"</string>
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"ସର୍ଚ୍ଚ ବକ୍ସ ଦେଖାଯାଇଛି"</string>
diff --git a/packages/SettingsLib/res/values-te/arrays.xml b/packages/SettingsLib/res/values-te/arrays.xml
index e0ba520..67decc9 100644
--- a/packages/SettingsLib/res/values-te/arrays.xml
+++ b/packages/SettingsLib/res/values-te/arrays.xml
@@ -76,7 +76,7 @@
<item msgid="1963366694959681026">"avrcp16"</item>
</string-array>
<string-array name="bluetooth_map_versions">
- <item msgid="8786402640610987099">"MAP 1.2 (డిఫాల్ట్)"</item>
+ <item msgid="8786402640610987099">"MAP 1.2 (ఆటోమేటిక్)"</item>
<item msgid="6817922176194686449">"MAP 1.3"</item>
<item msgid="3423518690032737851">"MAP 1.4"</item>
</string-array>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 2928abc..20c5edb 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -143,7 +143,7 @@
<string name="bluetooth_pairing_accept" msgid="2054232610815498004">"జత చేయి"</string>
<string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"జత చేయి"</string>
<string name="bluetooth_pairing_decline" msgid="6483118841204885890">"రద్దు చేయి"</string>
- <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"జత చేయడం వలన కనెక్ట్ చేయబడినప్పుడు మీ పరిచయాలకు మరియు కాల్ చరిత్రకు యాక్సెస్ను మంజూరు చేస్తుంది."</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"పెయిర్ చేయడం వలన కనెక్ట్ చేయబడినప్పుడు మీ కాంటాక్ట్లకు అలాగే కాల్ హిస్టరీకి యాక్సెస్ను మంజూరు చేస్తుంది."</string>
<string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>తో జత చేయడం సాధ్యపడలేదు."</string>
<string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"పిన్ లేదా పాస్కీ చెల్లని కారణంగా <xliff:g id="DEVICE_NAME">%1$s</xliff:g>తో పెయిర్ చేయడం సాధ్యపడలేదు."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>తో కమ్యూనికేట్ చేయడం సాధ్యపడదు."</string>
@@ -180,7 +180,7 @@
<string name="user_guest" msgid="6939192779649870792">"గెస్ట్"</string>
<string name="unknown" msgid="3544487229740637809">"తెలియదు"</string>
<string name="running_process_item_user_label" msgid="3988506293099805796">"యూజర్: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
- <string name="launch_defaults_some" msgid="3631650616557252926">"కొన్ని డిఫాల్ట్లు సెట్ చేయబడ్డాయి"</string>
+ <string name="launch_defaults_some" msgid="3631650616557252926">"కొన్ని ఆటోమేటిక్ సెట్టింగ్లు సెట్ చేయబడ్డాయి"</string>
<string name="launch_defaults_none" msgid="8049374306261262709">"ఆటోమేటిక్ ఆప్షన్లు ఏవీ సెట్ చేయలేదు"</string>
<string name="tts_settings" msgid="8130616705989351312">"వచనం నుండి ప్రసంగం సెట్టింగ్లు"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"టెక్స్ట్-టు-స్పీచ్ అవుట్పుట్"</string>
@@ -273,8 +273,8 @@
<string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"OEM అన్లాకింగ్ను అనుమతించాలా?"</string>
<string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"హెచ్చరిక: ఈ సెట్టింగ్ ఆన్ చేయబడినప్పుడు పరికరం రక్షణ లక్షణాలు ఈ పరికరంలో పని చేయవు."</string>
<string name="mock_location_app" msgid="6269380172542248304">"డమ్మీ లొకేషన్ యాప్ను ఎంచుకోండి"</string>
- <string name="mock_location_app_not_set" msgid="6972032787262831155">"అనుకృత స్థాన యాప్ ఏదీ సెట్ చేయబడలేదు"</string>
- <string name="mock_location_app_set" msgid="4706722469342913843">"కృత్రిమ స్థాన యాప్: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="mock_location_app_not_set" msgid="6972032787262831155">"డమ్మీ లొకేషన్ యాప్ ఏదీ సెట్ చేయబడలేదు"</string>
+ <string name="mock_location_app_set" msgid="4706722469342913843">"డమ్మీ లొకేషన్ యాప్: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="debug_networking_category" msgid="6829757985772659599">"నెట్వర్కింగ్"</string>
<string name="wifi_display_certification" msgid="1805579519992520381">"వైర్లెస్ డిస్ప్లే సర్టిఫికేషన్"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Wi‑Fi విశదీకృత లాగింగ్ను ప్రారంభించండి"</string>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 20735cb..4ac1938 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1476,6 +1476,9 @@
Settings.Global.USE_OPEN_WIFI_PACKAGE,
GlobalSettingsProto.USE_OPEN_WIFI_PACKAGE);
dumpSetting(s, p,
+ Settings.Global.UWB_ENABLED,
+ GlobalSettingsProto.UWB_ENABLED);
+ dumpSetting(s, p,
Settings.Global.VT_IMS_ENABLED,
GlobalSettingsProto.VT_IMS_ENABLED);
dumpSetting(s, p,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index b0647fa..df7bf20 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -524,6 +524,7 @@
Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLIST,
Settings.Global.UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST,
Settings.Global.UPDATABLE_DRIVER_SPHAL_LIBRARIES,
+ Settings.Global.UWB_ENABLED,
Settings.Global.SHOW_ANGLE_IN_USE_DIALOG_BOX,
Settings.Global.GPU_DEBUG_LAYER_APP,
Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
diff --git a/packages/Shell/res/values-te/strings.xml b/packages/Shell/res/values-te/strings.xml
index 50b5c85..2f86232 100644
--- a/packages/Shell/res/values-te/strings.xml
+++ b/packages/Shell/res/values-te/strings.xml
@@ -23,9 +23,9 @@
<string name="bugreport_updating_title" msgid="4423539949559634214">"బగ్ రిపోర్ట్కు వివరాలను జోడిస్తోంది"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"దయచేసి వేచి ఉండండి..."</string>
<string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"బగ్ రిపోర్ట్ త్వరలో ఫోన్లో కనిపిస్తుంది"</string>
- <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"మీ బగ్ రిపోర్ట్ను భాగస్వామ్యం చేయడానికి ఎంచుకోండి"</string>
+ <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"మీ బగ్ రిపోర్ట్ను షేర్ చేయడానికి ఎంచుకోండి"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"మీ బగ్ రిపోర్ట్ను షేర్ చేయడానికి నొక్కండి"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"స్క్రీన్షాట్ లేకుండా మీ బగ్ రిపోర్ట్ను భాగస్వామ్యం చేయడానికి ఎంచుకోండి లేదా స్క్రీన్షాట్ ముగిసేదాకా వేచి ఉండండి"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"స్క్రీన్షాట్ లేకుండా మీ బగ్ రిపోర్ట్ను షేర్ చేయడానికి ఎంచుకోండి లేదా స్క్రీన్షాట్ ముగిసేదాకా వేచి ఉండండి"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"స్క్రీన్షాట్ లేకుండా మీ బగ్ నివే. భాగ. చేయడానికి నొక్కండి లేదా స్క్రీన్షాట్ ముగిసేదాకా వేచి ఉండండి"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"స్క్రీన్షాట్ లేకుండా మీ బగ్ నివే. భాగ. చేయడానికి నొక్కండి లేదా స్క్రీన్షాట్ ముగిసేదాకా వేచి ఉండండి"</string>
<string name="bugreport_confirm" msgid="5917407234515812495">"బగ్ రిపోర్ట్స్లో మీరు గోప్యమైనదిగా పరిగణించే (యాప్ వినియోగం, లొకేషన్ డేటా వంటి) డేటాతో పాటు సిస్టమ్కు సంబంధించిన విభిన్న లాగ్ ఫైళ్ల డేటా ఉంటుంది. బగ్ రిపోర్ట్లను మీరు విశ్వసించే యాప్లు, వ్యక్తులతో మాత్రమే షేర్ చేయండి."</string>
diff --git a/packages/SoundPicker/res/values-te/strings.xml b/packages/SoundPicker/res/values-te/strings.xml
index 10b4e7c..8f5c34a 100644
--- a/packages/SoundPicker/res/values-te/strings.xml
+++ b/packages/SoundPicker/res/values-te/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="ringtone_default" msgid="798836092118824500">"డిఫాల్ట్ రింగ్టోన్"</string>
+ <string name="ringtone_default" msgid="798836092118824500">"ఆటోమేటిక్ రింగ్టోన్"</string>
<string name="notification_sound_default" msgid="8133121186242636840">"నోటిఫికేషన్ ఆటోమేటిక్ సౌండ్"</string>
<string name="alarm_sound_default" msgid="4787646764557462649">"అలారం ఆటోమేటిక్ సౌండ్"</string>
<string name="add_ringtone_text" msgid="6642389991738337529">"రింగ్టోన్ను జోడించు"</string>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index adf441b..a4b4680 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -463,6 +463,7 @@
<!-- started from SensoryPrivacyService -->
<activity android:name=".sensorprivacy.television.TvUnblockSensorActivity"
android:exported="true"
+ android:launchMode="singleTop"
android:permission="android.permission.MANAGE_SENSOR_PRIVACY"
android:theme="@style/BottomSheet"
android:finishOnCloseSystemDialogs="true">
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/donottranslate.xml b/packages/SystemUI/res-keyguard/values-sw600dp-land/donottranslate.xml
new file mode 100644
index 0000000..1a52e93
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/values-sw600dp-land/donottranslate.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Don't use the smaller PIN pad keys if we have the screen space to support it. -->
+ <string name="num_pad_key_ratio">1.0</string>
+</resources>
diff --git a/packages/SystemUI/res/color/docked_divider_background.xml b/packages/SystemUI/res/color/docked_divider_background.xml
new file mode 100644
index 0000000..85ede9a
--- /dev/null
+++ b/packages/SystemUI/res/color/docked_divider_background.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral2_500" android:lStar="35" />
+</selector>
diff --git a/packages/SystemUI/res/color/settingslib_state_off.xml b/packages/SystemUI/res/color/settingslib_state_off.xml
new file mode 100644
index 0000000..e821825
--- /dev/null
+++ b/packages/SystemUI/res/color/settingslib_state_off.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item android:color="?androidprv:attr/colorAccentSecondaryVariant"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/settingslib_state_on.xml b/packages/SystemUI/res/color/settingslib_state_on.xml
new file mode 100644
index 0000000..6d2133c
--- /dev/null
+++ b/packages/SystemUI/res/color/settingslib_state_on.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item android:color="?androidprv:attr/colorAccentPrimary"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/settingslib_track_off.xml b/packages/SystemUI/res/color/settingslib_track_off.xml
new file mode 100644
index 0000000..21d1dcc
--- /dev/null
+++ b/packages/SystemUI/res/color/settingslib_track_off.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item android:color="?androidprv:attr/colorAccentSecondary"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/settingslib_track_on.xml b/packages/SystemUI/res/color/settingslib_track_on.xml
new file mode 100644
index 0000000..ba7848a
--- /dev/null
+++ b/packages/SystemUI/res/color/settingslib_track_on.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item android:color="?androidprv:attr/colorAccentPrimaryVariant"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/communal_host_view.xml b/packages/SystemUI/res/layout/communal_host_view.xml
index 4406a46..cd9c260 100644
--- a/packages/SystemUI/res/layout/communal_host_view.xml
+++ b/packages/SystemUI/res/layout/communal_host_view.xml
@@ -18,13 +18,6 @@
<!-- This is a view that shows general status information in Keyguard. -->
<com.android.systemui.communal.CommunalHostView
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/communal_host"
- android:orientation="vertical"
- systemui:layout_constraintEnd_toEndOf="parent"
- systemui:layout_constraintStart_toStartOf="parent"
- systemui:layout_constraintTop_toTopOf="parent"
- systemui:layout_constraintBottom_toBottomOf="parent"
- systemui:layout_constraintHeight_percent="@dimen/communal_source_height_percentage"
- android:layout_width="0dp"
- android:layout_height="0dp"/>
\ No newline at end of file
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_screenshot_static.xml b/packages/SystemUI/res/layout/global_screenshot_static.xml
index e4a9694..6a9254c 100644
--- a/packages/SystemUI/res/layout/global_screenshot_static.xml
+++ b/packages/SystemUI/res/layout/global_screenshot_static.xml
@@ -36,7 +36,6 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/screenshot_action_container_margin_horizontal"
- android:layout_marginBottom="@dimen/screenshot_action_container_offset_y"
android:paddingEnd="@dimen/screenshot_action_container_padding_right"
android:paddingVertical="@dimen/screenshot_action_container_padding_vertical"
android:elevation="1dp"
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
index cc44b5e..b1e8c38 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
@@ -49,6 +49,11 @@
/>
</FrameLayout>
+ <!-- We want this to be centered (to align with notches). In order to do that, the following
+ has to hold (in portrait):
+ * date_container and privacy_container must have the same width and weight
+ * header_text_container must be gone
+ -->
<android.widget.Space
android:id="@+id/space"
android:layout_width="0dp"
@@ -75,7 +80,7 @@
android:layout_weight="1"
android:gravity="center_vertical|end" >
- <include layout="@layout/ongoing_privacy_chip" />
+ <include layout="@layout/ongoing_privacy_chip" />
</FrameLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index a2b4ac122..9821fb0 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -25,6 +25,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent">
+
+ <include layout="@layout/communal_host_view"
+ android:visibility="gone"/>
+
<FrameLayout
android:id="@+id/big_clock_container"
android:layout_width="match_parent"
@@ -120,13 +124,11 @@
android:layout_width="@dimen/notification_panel_width"
android:layout_height="match_parent"
android:layout_marginBottom="@dimen/close_handle_underlap"
+ android:importantForAccessibility="no"
systemui:layout_constraintStart_toStartOf="parent"
systemui:layout_constraintEnd_toEndOf="parent"
/>
- <include layout="@layout/communal_host_view"
- android:visibility="gone"/>
-
<include layout="@layout/ambient_indication"
android:id="@+id/ambient_indication_container" />
diff --git a/packages/SystemUI/res/layout/tv_ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/tv_ongoing_privacy_chip.xml
index 6218a5e..45b9f25 100644
--- a/packages/SystemUI/res/layout/tv_ongoing_privacy_chip.xml
+++ b/packages/SystemUI/res/layout/tv_ongoing_privacy_chip.xml
@@ -26,7 +26,7 @@
<ImageView
android:id="@+id/chip_drawable"
- android:layout_width="51dp"
+ android:layout_width="@dimen/privacy_chip_max_width"
android:layout_height="@dimen/privacy_chip_height"
android:minWidth="@dimen/privacy_chip_dot_bg_width"
android:minHeight="@dimen/privacy_chip_dot_bg_height"
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index b63d97c..7d3ae96 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -133,7 +133,7 @@
<string name="accessibility_accessibility_button" msgid="4089042473497107709">"দিব্যাংগসকলৰ বাবে থকা সুবিধাসমূহ"</string>
<string name="accessibility_rotate_button" msgid="1238584767612362586">"স্ক্ৰীণ ঘূৰাওক"</string>
<string name="accessibility_recent" msgid="901641734769533575">"অৱলোকন"</string>
- <string name="accessibility_search_light" msgid="524741790416076988">"Search"</string>
+ <string name="accessibility_search_light" msgid="524741790416076988">"সন্ধান কৰক"</string>
<string name="accessibility_camera_button" msgid="2938898391716647247">"কেমেৰা"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"ফ\'ন"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"কণ্ঠধ্বনিৰে সহায়"</string>
@@ -442,7 +442,7 @@
<string name="expanded_header_battery_charging_with_time" msgid="757991461445765011">"বেটাৰিৰ চ্চাৰ্জ সম্পূর্ণ হ\'বলৈ <xliff:g id="CHARGING_TIME">%s</xliff:g> বাকী"</string>
<string name="expanded_header_battery_not_charging" msgid="809409140358955848">"চ্চার্জ কৰি থকা নাই"</string>
<string name="ssl_ca_cert_warning" msgid="8373011375250324005">"নেটৱৰ্ক \nনিৰীক্ষণ কৰা হ\'ব পাৰে"</string>
- <string name="description_target_search" msgid="3875069993128855865">"Search"</string>
+ <string name="description_target_search" msgid="3875069993128855865">"সন্ধান কৰক"</string>
<string name="description_direction_up" msgid="3632251507574121434">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>ৰ বাবে ওপৰলৈ শ্লাইড কৰক।"</string>
<string name="description_direction_left" msgid="4762708739096907741">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>ৰ বাবে বাওঁফাললৈ শ্লাইড কৰক।"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"আপুনি নিৰ্দিষ্ট কৰা এলাৰ্ম, ৰিমাইণ্ডাৰ, ইভেন্ট আৰু কল কৰোঁতাৰ বাহিৰে আন কোনো শব্দৰ পৰা আপুনি অসুবিধা নাপাব। কিন্তু, সংগীত, ভিডিঅ\' আৰু খেলসমূহকে ধৰি আপুনি প্লে কৰিব খোজা যিকোনো বস্তু তথাপি শুনিব পাৰিব।"</string>
@@ -1014,7 +1014,7 @@
<string name="ongoing_privacy_dialog_enterprise" msgid="3003314125311966061">"(কৰ্মস্থান)"</string>
<string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"ফ’ন কল"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="4738795925380373994">"(<xliff:g id="APPLICATION_NAME_S_">%s</xliff:g>ৰ জৰিয়তে)"</string>
- <string name="privacy_type_camera" msgid="7974051382167078332">"কেমেৰা"</string>
+ <string name="privacy_type_camera" msgid="7974051382167078332">"Camera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"অৱস্থান"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"মাইক্ৰ\'ফ\'ন"</string>
<string name="sensor_privacy_mode" msgid="4462866919026513692">"ছেন্সৰ অফ হৈ আছে"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index c4ff99e..bd2d05a 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -77,8 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"স্ক্রীণ পূরণ করতে জুম করুন"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"ফুল স্ক্রিন করুন"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"স্ক্রিনশট নিন"</string>
- <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
- <skip />
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock বন্ধ করা হয়েছে"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"একটি ছবি পাঠানো হয়েছে"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"স্ক্রিনশট সেভ করা হচ্ছে..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"স্ক্রিনশট সেভ করা হচ্ছে..."</string>
@@ -1178,10 +1177,7 @@
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"নেটওয়ার্ক সার্চ করা হচ্ছে…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"নেটওয়ার্কে কানেক্ট করা যায়নি"</string>
<string name="see_all_networks" msgid="3773666844913168122">"সবকটি দেখুন"</string>
- <!-- no translation found for qs_tile_request_dialog_text (3501359944139877694) -->
- <skip />
- <!-- no translation found for qs_tile_request_dialog_add (4888460910694986304) -->
- <skip />
- <!-- no translation found for qs_tile_request_dialog_not_add (4168716573114067296) -->
- <skip />
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> নিম্নলিখিত টাইল দ্রুত সেটিংস মেনুতে যোগ করতে চায়"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"টাইল যোগ করুন"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"টাইল যোগ করবেন না"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 619d690..f5539e3 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -77,8 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoomer pour remplir l\'écran"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Étirer pour remplir l\'écran"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Capture d\'écran"</string>
- <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
- <skip />
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Fonctionnalité Smart Lock désactivée"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"a envoyé une image"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Enregistrement capture écran…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Enregistrement capture écran…"</string>
@@ -1178,10 +1177,7 @@
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Recherche de réseaux en cours…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Échec de la connexion au réseau"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Tout afficher"</string>
- <!-- no translation found for qs_tile_request_dialog_text (3501359944139877694) -->
- <skip />
- <!-- no translation found for qs_tile_request_dialog_add (4888460910694986304) -->
- <skip />
- <!-- no translation found for qs_tile_request_dialog_not_add (4168716573114067296) -->
- <skip />
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"L\'application <xliff:g id="APPNAME">%1$s</xliff:g> veut ajouter la tuile suivante au menu Paramètres rapides"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Ajouter la tuile"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ne pas ajouter tuile"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index dba4aba..a2295ab 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -77,8 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"સ્ક્રીન ભરવા માટે ઝૂમ કરો"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"સ્ક્રીન ભરવા માટે ખેંચો"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"સ્ક્રીનશૉટ"</string>
- <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
- <skip />
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock બંધ કરેલું છે"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"છબી મોકલી"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"સ્ક્રીનશોટ સાચવી રહ્યું છે…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"સ્ક્રીનશોટ સાચવી રહ્યું છે…"</string>
@@ -135,7 +134,7 @@
<string name="accessibility_rotate_button" msgid="1238584767612362586">"સ્ક્રીન ફેરવો"</string>
<string name="accessibility_recent" msgid="901641734769533575">"ઝલક"</string>
<string name="accessibility_search_light" msgid="524741790416076988">"શોધ"</string>
- <string name="accessibility_camera_button" msgid="2938898391716647247">"કૅમેરો"</string>
+ <string name="accessibility_camera_button" msgid="2938898391716647247">"કૅમેરા"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"ફોન"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"વૉઇસ સહાય"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"વૉલેટ"</string>
@@ -984,7 +983,7 @@
<string name="qs_dnd_prompt_app" msgid="4027984447935396820">"ખલેલ પાડશો નહીં એક ઍપ્લિકેશન દ્વારા ચાલુ કરાયું હતું (<xliff:g id="ID_1">%s</xliff:g>)."</string>
<string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"ખલેલ પાડશો નહીં એક સ્વચાલિત નિયમ અથવા ઍપ્લિકેશન દ્વારા ચાલુ કરાયું હતું."</string>
<string name="qs_dnd_until" msgid="7844269319043747955">"<xliff:g id="ID_1">%s</xliff:g> સુધી"</string>
- <string name="qs_dnd_keep" msgid="3829697305432866434">"Keep"</string>
+ <string name="qs_dnd_keep" msgid="3829697305432866434">"રાખો"</string>
<string name="qs_dnd_replace" msgid="7712119051407052689">"બદલો"</string>
<string name="running_foreground_services_title" msgid="5137313173431186685">"પૃષ્ઠભૂમિમાં ચાલી રહેલ ઍપ્લિકેશનો"</string>
<string name="running_foreground_services_msg" msgid="3009459259222695385">"બૅટરી અને ડેટા વપરાશ વિશેની વિગતો માટે ટૅપ કરો"</string>
@@ -1178,10 +1177,7 @@
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"નેટવર્ક શોધી રહ્યાં છીએ…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"નેટવર્ક સાથે કનેક્ટ કરવામાં નિષ્ફળ થયાં"</string>
<string name="see_all_networks" msgid="3773666844913168122">"બધા જુઓ"</string>
- <!-- no translation found for qs_tile_request_dialog_text (3501359944139877694) -->
- <skip />
- <!-- no translation found for qs_tile_request_dialog_add (4888460910694986304) -->
- <skip />
- <!-- no translation found for qs_tile_request_dialog_not_add (4168716573114067296) -->
- <skip />
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"ઝડપી સેટિંગમાં <xliff:g id="APPNAME">%1$s</xliff:g> નીચે જણાવેલા ટાઇલ ઉમેરવા માગે છે"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ટાઇલ ઉમેરો"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ટાઇલ ઉમેરશો નહીં"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index e123544..ce0f9b9 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -848,7 +848,7 @@
<string name="keyboard_shortcut_group_applications_sms" msgid="6912633831752843566">"मैसेज (एसएमएस) करें"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="9032078456666204025">"संगीत"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="5078136084632450333">"YouTube"</string>
- <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"कैलेंडर"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
<string name="tuner_full_zen_title" msgid="5120366354224404511">"वॉल्यूम नियंत्रणों के साथ दिखाएं"</string>
<string name="volume_and_do_not_disturb" msgid="502044092739382832">"परेशान न करें"</string>
<string name="volume_dnd_silent" msgid="4154597281458298093">"वॉल्यूम बटन का शॉर्टकट"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index c1f1305..cd2df4e 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -77,8 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"הגדלת התצוגה למילוי המסך"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"מתיחה למילוי של המסך"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"צילום מסך"</string>
- <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
- <skip />
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"השבתת את Smart Lock"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"נשלחה תמונה"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"צילום המסך נשמר..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"המערכת שומרת את צילום המסך..."</string>
@@ -1190,10 +1189,7 @@
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"בתהליך חיפוש רשתות…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"נכשל הניסיון להתחבר לרשת"</string>
<string name="see_all_networks" msgid="3773666844913168122">"הצגת הכול"</string>
- <!-- no translation found for qs_tile_request_dialog_text (3501359944139877694) -->
- <skip />
- <!-- no translation found for qs_tile_request_dialog_add (4888460910694986304) -->
- <skip />
- <!-- no translation found for qs_tile_request_dialog_not_add (4168716573114067296) -->
- <skip />
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"האפליקציה <xliff:g id="APPNAME">%1$s</xliff:g> מבקשת להוסיף להגדרות המהירות את האריח הבא"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"הוספת אריח"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"אין להוסיף אריח"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 235fc72..06db06d 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -77,8 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"ಪರದೆ ತುಂಬಿಸಲು ಝೂಮ್ ಮಾಡು"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"ಪರದೆ ತುಂಬಿಸಲು ವಿಸ್ತಾರಗೊಳಿಸು"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"ಸ್ಕ್ರೀನ್ಶಾಟ್"</string>
- <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
- <skip />
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ಚಿತ್ರವನ್ನು ಕಳುಹಿಸಲಾಗಿದೆ"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಉಳಿಸಲಾಗುತ್ತಿದೆ…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಉಳಿಸಲಾಗುತ್ತಿದೆ…"</string>
@@ -134,7 +133,7 @@
<string name="accessibility_accessibility_button" msgid="4089042473497107709">"ಪ್ರವೇಶಿಸುವಿಕೆ"</string>
<string name="accessibility_rotate_button" msgid="1238584767612362586">"ಪರದೆಯನ್ನು ತಿರುಗಿಸಿ"</string>
<string name="accessibility_recent" msgid="901641734769533575">"ಸಮಗ್ರ ನೋಟ"</string>
- <string name="accessibility_search_light" msgid="524741790416076988">"Search"</string>
+ <string name="accessibility_search_light" msgid="524741790416076988">"ಹುಡುಕಿ"</string>
<string name="accessibility_camera_button" msgid="2938898391716647247">"ಕ್ಯಾಮರಾ"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"ಫೋನ್"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ಧ್ವನಿ ಸಹಾಯಕ"</string>
@@ -443,7 +442,7 @@
<string name="expanded_header_battery_charging_with_time" msgid="757991461445765011">"<xliff:g id="CHARGING_TIME">%s</xliff:g> ಪೂರ್ಣಗೊಳ್ಳುವವರೆಗೆ"</string>
<string name="expanded_header_battery_not_charging" msgid="809409140358955848">"ಚಾರ್ಜ್ ಆಗುತ್ತಿಲ್ಲ"</string>
<string name="ssl_ca_cert_warning" msgid="8373011375250324005">"ನೆಟ್ವರ್ಕ್\n ವೀಕ್ಷಿಸಬಹುದಾಗಿರುತ್ತದೆ"</string>
- <string name="description_target_search" msgid="3875069993128855865">"Search"</string>
+ <string name="description_target_search" msgid="3875069993128855865">"ಹುಡುಕಿ"</string>
<string name="description_direction_up" msgid="3632251507574121434">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ಗಾಗಿ ಮೇಲಕ್ಕೆ ಸ್ಲೈಡ್ ಮಾಡಿ."</string>
<string name="description_direction_left" msgid="4762708739096907741">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ಗಾಗಿ ಎಡಕ್ಕೆ ಸ್ಲೈಡ್ ಮಾಡಿ."</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"ಅಲಾರಾಂಗಳು, ಜ್ಞಾಪನೆಗಳು, ಈವೆಂಟ್ಗಳು ಹಾಗೂ ನೀವು ಸೂಚಿಸಿರುವ ಕರೆದಾರರನ್ನು ಹೊರತುಪಡಿಸಿ ಬೇರಾವುದೇ ಸದ್ದುಗಳು ಅಥವಾ ವೈಬ್ರೇಶನ್ಗಳು ನಿಮಗೆ ತೊಂದರೆ ನೀಡುವುದಿಲ್ಲ. ಹಾಗಿದ್ದರೂ, ನೀವು ಪ್ಲೇ ಮಾಡುವ ಸಂಗೀತ, ವೀಡಿಯೊಗಳು ಮತ್ತು ಆಟಗಳ ಆಡಿಯೊವನ್ನು ನೀವು ಕೇಳಿಸಿಕೊಳ್ಳುತ್ತೀರಿ."</string>
@@ -1178,10 +1177,7 @@
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ನೆಟ್ವರ್ಕ್ಗಳನ್ನು ಹುಡುಕಲಾಗುತ್ತಿದೆ…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"ನೆಟ್ವರ್ಕ್ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲು ವಿಫಲವಾಗಿದೆ"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ಎಲ್ಲವನ್ನೂ ನೋಡಿ"</string>
- <!-- no translation found for qs_tile_request_dialog_text (3501359944139877694) -->
- <skip />
- <!-- no translation found for qs_tile_request_dialog_add (4888460910694986304) -->
- <skip />
- <!-- no translation found for qs_tile_request_dialog_not_add (4168716573114067296) -->
- <skip />
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ಈ ಕೆಳಗಿನ ಟೈಲ್ ಅನ್ನು ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳಿಗೆ ಸೇರಿಸಲು ಬಯಸುತ್ತದೆ"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ಟೈಲ್ ಅನ್ನು ಸೇರಿಸಿ"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ಟೈಲ್ ಅನ್ನು ಸೇರಿಸಬೇಡಿ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 9c6a698..986141b 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -133,7 +133,7 @@
<string name="accessibility_accessibility_button" msgid="4089042473497107709">"ഉപയോഗസഹായി"</string>
<string name="accessibility_rotate_button" msgid="1238584767612362586">"സ്ക്രീൻ തിരിക്കുക"</string>
<string name="accessibility_recent" msgid="901641734769533575">"അവലോകനം"</string>
- <string name="accessibility_search_light" msgid="524741790416076988">"Search"</string>
+ <string name="accessibility_search_light" msgid="524741790416076988">"തിരയൽ"</string>
<string name="accessibility_camera_button" msgid="2938898391716647247">"ക്യാമറ"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"ഫോണ്"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"വോയ്സ് സഹായം"</string>
@@ -442,7 +442,7 @@
<string name="expanded_header_battery_charging_with_time" msgid="757991461445765011">"ഫുൾ ചാർജാകാൻ, <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
<string name="expanded_header_battery_not_charging" msgid="809409140358955848">"ചാർജ്ജുചെയ്യുന്നില്ല"</string>
<string name="ssl_ca_cert_warning" msgid="8373011375250324005">"നെറ്റ്വർക്ക്\nനിരീക്ഷിക്കപ്പെടാം"</string>
- <string name="description_target_search" msgid="3875069993128855865">"Search"</string>
+ <string name="description_target_search" msgid="3875069993128855865">"തിരയൽ"</string>
<string name="description_direction_up" msgid="3632251507574121434">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> എന്നതിനായി മുകളിലേയ്ക്ക് സ്ലൈഡുചെയ്യുക."</string>
<string name="description_direction_left" msgid="4762708739096907741">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> എന്നതിനായി ഇടത്തേയ്ക്ക് സ്ലൈഡുചെയ്യുക."</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"നിങ്ങൾ സജ്ജീകരിച്ച അലാറങ്ങൾ, റിമൈൻഡറുകൾ, ഇവന്റുകൾ, കോളർമാർ എന്നിവയിൽ നിന്നുള്ള ശബ്ദങ്ങളും വൈബ്രേഷനുകളുമൊഴികെ മറ്റൊന്നും നിങ്ങളെ ശല്യപ്പെടുത്തുകയില്ല. സംഗീതം, വീഡിയോകൾ, ഗെയിമുകൾ എന്നിവയുൾപ്പെടെ പ്ലേ ചെയ്യുന്നതെന്തും നിങ്ങൾക്ക് തുടർന്നും കേൾക്കാൻ കഴിയും."</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 5fb26e0..ae48d21 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -1177,7 +1177,7 @@
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Сүлжээ хайж байна…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Сүлжээнд холбогдож чадсангүй"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Бүгдийг харах"</string>
- <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> нь дараах хавтанг Шуурхай тохиргоонд нэмэх хүмэлтэй байна"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> нь дараах хавтанг Шуурхай тохиргоонд нэмэх хүсэлтэй байна"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Хавтан нэмэх"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Хавтанг бүү нэм"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 87d7b40..dd75e9a 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -77,8 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"स्क्रीन भरण्यासाठी झूम करा"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"स्क्रीन भरण्यासाठी ताणा"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"स्क्रीनशॉट"</string>
- <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
- <skip />
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock बंद केले"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"इमेज पाठवली आहे"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"स्क्रीनशॉट सेव्ह करत आहे…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"स्क्रीनशॉट सेव्ह करत आहे…"</string>
@@ -134,7 +133,7 @@
<string name="accessibility_accessibility_button" msgid="4089042473497107709">"अॅक्सेसिबिलिटी"</string>
<string name="accessibility_rotate_button" msgid="1238584767612362586">"स्क्रीन फिरवा"</string>
<string name="accessibility_recent" msgid="901641734769533575">"अवलोकन"</string>
- <string name="accessibility_search_light" msgid="524741790416076988">"Search"</string>
+ <string name="accessibility_search_light" msgid="524741790416076988">"शोधा"</string>
<string name="accessibility_camera_button" msgid="2938898391716647247">"कॅमेरा"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"फोन"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"व्हॉइस सहाय्य"</string>
@@ -443,7 +442,7 @@
<string name="expanded_header_battery_charging_with_time" msgid="757991461445765011">"<xliff:g id="CHARGING_TIME">%s</xliff:g> पूर्ण होईपर्यंत"</string>
<string name="expanded_header_battery_not_charging" msgid="809409140358955848">"चार्ज होत नाही"</string>
<string name="ssl_ca_cert_warning" msgid="8373011375250324005">"नेटवर्कचे परीक्षण\nकेले जाऊ शकते"</string>
- <string name="description_target_search" msgid="3875069993128855865">"Search"</string>
+ <string name="description_target_search" msgid="3875069993128855865">"शोध"</string>
<string name="description_direction_up" msgid="3632251507574121434">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> साठी वर स्लाइड करा."</string>
<string name="description_direction_left" msgid="4762708739096907741">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> साठी डावीकडे स्लाइड करा."</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"अलार्म, रिमाइंडर, इव्हेंट आणि तुम्ही निश्चित केलेल्या कॉलर व्यतिरिक्त तुम्हाला कोणत्याही आवाज आणि कंपनांचा व्यत्त्यय आणला जाणार नाही. तरीही तुम्ही प्ले करायचे ठरवलेले कोणतेही संगीत, व्हिडिओ आणि गेमचे आवाज ऐकू शकतात."</string>
@@ -1015,7 +1014,7 @@
<string name="ongoing_privacy_dialog_enterprise" msgid="3003314125311966061">"(ऑफिस)"</string>
<string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"फोन कॉल"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="4738795925380373994">"(<xliff:g id="APPLICATION_NAME_S_">%s</xliff:g> द्वारे)"</string>
- <string name="privacy_type_camera" msgid="7974051382167078332">"कॅमेरा"</string>
+ <string name="privacy_type_camera" msgid="7974051382167078332">"camera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"स्थान"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"मायक्रोफोन"</string>
<string name="sensor_privacy_mode" msgid="4462866919026513692">"सेन्सर बंद आहेत"</string>
@@ -1178,10 +1177,7 @@
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"नेटवर्क शोधत आहे…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"नेटवर्कशी कनेक्ट करता आले नाही"</string>
<string name="see_all_networks" msgid="3773666844913168122">"सर्व पहा"</string>
- <!-- no translation found for qs_tile_request_dialog_text (3501359944139877694) -->
- <skip />
- <!-- no translation found for qs_tile_request_dialog_add (4888460910694986304) -->
- <skip />
- <!-- no translation found for qs_tile_request_dialog_not_add (4168716573114067296) -->
- <skip />
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ला क्विक सेटिंग्जमध्ये पुढील टाइल जोडायची आहे"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"टाइल जोडा"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"टाइल जोडू नका"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index d90ea35..eb16281 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -1177,7 +1177,7 @@
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Mencari rangkaian…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Gagal menyambung kepada rangkaian"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Lihat semua"</string>
- <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> mahu menambah jubin berikut kepada Tetapan Pantas"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> mahu menambah jubin yang berikut kepada Tetapan Pantas"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Tambahkan jubin"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Jangan tambah jubin"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index f19f8de..400b997 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -77,8 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"स्क्रिन भर्न जुम गर्नुहोस्"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"स्क्रिन भर्न तन्काउनुहोस्"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"स्क्रिनसट"</string>
- <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
- <skip />
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"स्मार्ट लक अफ गरिएको छ"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"कुनै छवि पठाइयो"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"स्क्रिनसट बचत गर्दै…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"स्क्रिनसट बचत गर्दै…"</string>
@@ -1178,10 +1177,7 @@
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"नेटवर्कहरू खोजिँदै छन्…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"नेटवर्कमा कनेक्ट गर्न सकिएन"</string>
<string name="see_all_networks" msgid="3773666844913168122">"सबै नेटवर्क हेर्नुहोस्"</string>
- <!-- no translation found for qs_tile_request_dialog_text (3501359944139877694) -->
- <skip />
- <!-- no translation found for qs_tile_request_dialog_add (4888460910694986304) -->
- <skip />
- <!-- no translation found for qs_tile_request_dialog_not_add (4168716573114067296) -->
- <skip />
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> द्रुत सेटिङमा निम्न टाइल हाल्न चाहन्छ"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"टाइल हाल्नुहोस्"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"टाइल नहाल्नुहोस्"</string>
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 10b3c4c..2aafac4 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -77,8 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"ସ୍କ୍ରୀନ ଭରିବା ପାଇଁ ଜୁମ୍ କରନ୍ତୁ"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"ସ୍କ୍ରୀନ୍କୁ ଭରିବା ପାଇଁ ଟାଣନ୍ତୁ"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"ସ୍କ୍ରିନ୍ସଟ୍ ନିଅନ୍ତୁ"</string>
- <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
- <skip />
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"ସ୍ମାର୍ଟ ଲକ୍ ଅକ୍ଷମ କରାଯାଇଛି"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ଏକ ଛବି ପଠାଯାଇଛି"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"ସ୍କ୍ରୀନଶଟ୍ ସେଭ୍ କରାଯାଉଛି…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"ସ୍କ୍ରୀନଶଟ୍ ସେଭ୍ କରାଯାଉଛି…"</string>
@@ -134,7 +133,7 @@
<string name="accessibility_accessibility_button" msgid="4089042473497107709">"ଆକ୍ସେସିବିଲିଟୀ"</string>
<string name="accessibility_rotate_button" msgid="1238584767612362586">"ସ୍କ୍ରୀନ୍କୁ ଘୁରାନ୍ତୁ"</string>
<string name="accessibility_recent" msgid="901641734769533575">"ଓଭରଭିଉ"</string>
- <string name="accessibility_search_light" msgid="524741790416076988">"Search"</string>
+ <string name="accessibility_search_light" msgid="524741790416076988">"ସନ୍ଧାନ କରନ୍ତୁ"</string>
<string name="accessibility_camera_button" msgid="2938898391716647247">"କ୍ୟାମେରା"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"ଫୋନ୍"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ଭଏସ୍ ସହାୟକ"</string>
@@ -443,7 +442,7 @@
<string name="expanded_header_battery_charging_with_time" msgid="757991461445765011">"ପୂର୍ଣ୍ଣ ଚାର୍ଜ ହେବାକୁ ଆଉ <xliff:g id="CHARGING_TIME">%s</xliff:g> ଅଛି"</string>
<string name="expanded_header_battery_not_charging" msgid="809409140358955848">"ଚାର୍ଜ ହେଉନାହିଁ"</string>
<string name="ssl_ca_cert_warning" msgid="8373011375250324005">"ନେଟ୍ୱର୍କ\nମନିଟର୍ କରାଯାଇପାରେ"</string>
- <string name="description_target_search" msgid="3875069993128855865">"Search"</string>
+ <string name="description_target_search" msgid="3875069993128855865">"ସନ୍ଧାନ କରନ୍ତୁ"</string>
<string name="description_direction_up" msgid="3632251507574121434">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ପାଇଁ ଉପରକୁ ସ୍ଲାଇଡ୍ କରନ୍ତୁ।"</string>
<string name="description_direction_left" msgid="4762708739096907741">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ପାଇଁ ବାମକୁ ସ୍ଲାଇଡ୍ କରନ୍ତୁ"</string>
<string name="zen_priority_introduction" msgid="3159291973383796646">"ଆଲାର୍ମ, ରିମାଇଣ୍ଡର୍, ଇଭେଣ୍ଟ ଏବଂ ଆପଣ ନିର୍ଦ୍ଦିଷ୍ଟ କରିଥିବା କଲର୍ଙ୍କ ବ୍ୟତୀତ ଆପଣଙ୍କ ଧ୍ୟାନ ଅନ୍ୟ କୌଣସି ଧ୍ୱନୀ ଏବଂ ଭାଇବ୍ରେଶନ୍ରେ ଆକର୍ଷଣ କରାଯିବନାହିଁ। ମ୍ୟୁଜିକ୍, ଭିଡିଓ ଏବଂ ଗେମ୍ ସମେତ ନିଜେ ଚଲାଇବାକୁ ବାଛିଥିବା ଅନ୍ୟ ସବୁକିଛି ଆପଣ ଶୁଣିପାରିବେ।"</string>
@@ -1178,10 +1177,7 @@
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ନେଟୱାର୍କଗୁଡ଼ିକ ସନ୍ଧାନ କରାଯାଉଛି…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"ନେଟୱାର୍କକୁ ସଂଯୋଗ କରିବାରେ ବିଫଳ ହୋଇଛି"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ସବୁ ଦେଖନ୍ତୁ"</string>
- <!-- no translation found for qs_tile_request_dialog_text (3501359944139877694) -->
- <skip />
- <!-- no translation found for qs_tile_request_dialog_add (4888460910694986304) -->
- <skip />
- <!-- no translation found for qs_tile_request_dialog_not_add (4168716573114067296) -->
- <skip />
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> କ୍ୱିକ୍ ସେଟିଂସରେ ନିମ୍ନୋକ୍ତ ଟାଇଲ୍ ଯୋଗ କରିବାକୁ ଚାହେଁ"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ଟାଇଲ୍ ଯୋଗ କରନ୍ତୁ"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ଟାଇଲ୍ ଯୋଗ କର ନାହିଁ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index f4ed008..4459ae1c 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -77,8 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"ਸਕ੍ਰੀਨ ਭਰਨ ਲਈ ਜ਼ੂਮ ਕਰੋ"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"ਸਕ੍ਰੀਨ ਭਰਨ ਲਈ ਸਟ੍ਰੈਚ ਕਰੋ"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"ਸਕ੍ਰੀਨਸ਼ਾਟ"</string>
- <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
- <skip />
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ਚਿੱਤਰ ਭੇਜਿਆ ਗਿਆ"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਸੁਰੱਖਿਅਤ ਕਰ ਰਿਹਾ ਹੈ…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਸੁਰੱਖਿਅਤ ਕਰ ਰਿਹਾ ਹੈ…"</string>
@@ -1178,10 +1177,7 @@
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ਨੈੱਟਵਰਕ ਖੋਜੇ ਜਾ ਰਹੇ ਹਨ…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ਸਭ ਦੇਖੋ"</string>
- <!-- no translation found for qs_tile_request_dialog_text (3501359944139877694) -->
- <skip />
- <!-- no translation found for qs_tile_request_dialog_add (4888460910694986304) -->
- <skip />
- <!-- no translation found for qs_tile_request_dialog_not_add (4168716573114067296) -->
- <skip />
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ਅੱਗੇ ਦਿੱਤੀ ਟਾਇਲ ਨੂੰ ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰਨਾ ਚਾਹੁੰਦੀ ਹੈ"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ਟਾਇਲ ਸ਼ਾਮਲ ਕਰੋ"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ਟਾਇਲ ਸ਼ਾਮਲ ਨਾ ਕਰੋ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index f89d50d..52ee8c6 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -77,8 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zmadho për të mbushur ekranin"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Shtrije për të mbushur ekranin"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Pamja e ekranit"</string>
- <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
- <skip />
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock është çaktivizuar"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"dërgoi një imazh"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Po ruan pamjen e ekranit..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Po ruan pamjen e ekranit…"</string>
@@ -1178,10 +1177,7 @@
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Po kërkon për rrjete…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Lidhja me rrjetin dështoi"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Shiko të gjitha"</string>
- <!-- no translation found for qs_tile_request_dialog_text (3501359944139877694) -->
- <skip />
- <!-- no translation found for qs_tile_request_dialog_add (4888460910694986304) -->
- <skip />
- <!-- no translation found for qs_tile_request_dialog_not_add (4168716573114067296) -->
- <skip />
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> dëshiron të shtojë pllakëzën e mëposhtme te \"Cilësimet e shpejta\""</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Shto një pllakëz"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Mos e shto pllakëzën"</string>
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 3f0d65d..8c1d5b9 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -77,8 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"స్క్రీన్కు నింపేలా జూమ్ చేయండి"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"స్క్రీన్కు నింపేలా విస్తరించండి"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"స్క్రీన్షాట్"</string>
- <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
- <skip />
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock డిజేబుల్ చేయబడింది"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ఇమేజ్ను పంపారు"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"స్క్రీన్షాట్ను సేవ్ చేస్తోంది…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"స్క్రీన్షాట్ను సేవ్ చేస్తోంది…"</string>
@@ -276,8 +275,8 @@
<string name="accessibility_quick_settings_bluetooth_connected" msgid="5237625393869747261">"బ్లూటూత్ కనెక్ట్ చేయబడింది."</string>
<string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"బ్లూటూత్ ఆఫ్ చేయబడింది."</string>
<string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"బ్లూటూత్ ఆన్ చేయబడింది."</string>
- <string name="accessibility_quick_settings_location_off" msgid="6122523378294740598">"స్థాన నివేదన ఆఫ్లో ఉంది."</string>
- <string name="accessibility_quick_settings_location_on" msgid="6869947200325467243">"స్థాన నివేదన ఆన్లో ఉంది."</string>
+ <string name="accessibility_quick_settings_location_off" msgid="6122523378294740598">"లొకేషన్ రిపోర్టింగ్ ఆఫ్లో ఉంది."</string>
+ <string name="accessibility_quick_settings_location_on" msgid="6869947200325467243">"లొకేషన్ రిపోర్టింగ్ ఆన్లో ఉంది."</string>
<string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"స్థాన నివేదన ఆఫ్ చేయబడింది."</string>
<string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"స్థాన నివేదన ఆన్ చేయబడింది."</string>
<string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"<xliff:g id="TIME">%s</xliff:g>కి అలారం సెట్ చేయబడింది."</string>
@@ -310,7 +309,7 @@
<string name="data_usage_disabled_dialog_enable" msgid="2796648546086408937">"పునఃప్రారంభించు"</string>
<string name="gps_notification_searching_text" msgid="231304732649348313">"GPS కోసం శోధిస్తోంది"</string>
<string name="gps_notification_found_text" msgid="3145873880174658526">"స్థానం GPS ద్వారా సెట్ చేయబడింది"</string>
- <string name="accessibility_location_active" msgid="2845747916764660369">"స్థాన రిక్వెస్ట్లు సక్రియంగా ఉన్నాయి"</string>
+ <string name="accessibility_location_active" msgid="2845747916764660369">"లొకేషన్ రిక్వెస్ట్లు యాక్టివ్గా ఉన్నాయి"</string>
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"సెన్సార్లు ఆఫ్ యాక్టివ్లో ఉంది"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"అన్ని నోటిఫికేషన్లను క్లియర్ చేయండి."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
@@ -570,7 +569,7 @@
<string name="monitoring_description_personal_profile_named_vpn" msgid="8179722332380953673">"మీ వ్యక్తిగత ప్రొఫైల్ ఇమెయిల్లు, యాప్లు మరియు వెబ్సైట్లతో సహా మీ నెట్వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడింది."</string>
<string name="monitoring_description_do_header_generic" msgid="6130190408164834986">"మీ పరికరం <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> ద్వారా నిర్వహించబడుతోంది."</string>
<string name="monitoring_description_do_header_with_name" msgid="2696255132542779511">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> మీ పరికరాన్ని నిర్వహించడానికి <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>ని ఉపయోగిస్తుంది."</string>
- <string name="monitoring_description_do_body" msgid="7700878065625769970">"మీ పరికరంతో అనుబంధించబడిన సెట్టింగ్లు, కార్పొరేట్ యాక్సెస్, యాప్లు, డేటా మరియు మీ పరికరం యొక్క స్థాన సమాచారాన్ని మీ నిర్వాహకులు పర్యవేక్షించగలరు మరియు నిర్వహించగలరు."</string>
+ <string name="monitoring_description_do_body" msgid="7700878065625769970">"మీ పరికరంతో అనుబంధించబడిన సెట్టింగ్లు, కార్పొరేట్ యాక్సెస్, యాప్లు, డేటా మరియు మీ పరికరం యొక్క లొకేషన్ సమాచారాన్ని మీ అడ్మిన్ పర్యవేక్షించగలరు, మేనేజ్ చేయగలరు."</string>
<string name="monitoring_description_do_learn_more_separator" msgid="1467280496376492558">" "</string>
<string name="monitoring_description_do_learn_more" msgid="645149183455573790">"మరింత తెలుసుకోండి"</string>
<string name="monitoring_description_do_body_vpn" msgid="7699280130070502303">"మీరు <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఇమెయిల్లు, యాప్లు మరియు వెబ్సైట్లతో సహా మీ వ్యక్తిగత నెట్వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
@@ -871,7 +870,7 @@
<string name="nav_bar_layout" msgid="4716392484772899544">"లేఅవుట్"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"అత్యంత ఎడమ వైపు ఉన్న బటన్ రకం"</string>
<string name="right_nav_bar_button_type" msgid="4472566498647364715">"అత్యంత కుడివైపు ఉన్న బటన్ రకం"</string>
- <string name="nav_bar_default" msgid="8386559913240761526">"(డిఫాల్ట్)"</string>
+ <string name="nav_bar_default" msgid="8386559913240761526">"(ఆటోమేటిక్)"</string>
<string-array name="nav_bar_buttons">
<item msgid="2681220472659720036">"క్లిప్బోర్డ్"</item>
<item msgid="4795049793625565683">"కీకోడ్"</item>
@@ -902,12 +901,12 @@
<string name="tuner_time" msgid="2450785840990529997">"సమయం"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"గంటలు, నిమిషాలు మరియు సెకన్లను చూపు"</item>
- <item msgid="1271006222031257266">"గంటలు మరియు నిమిషాలను చూపు (డిఫాల్ట్)"</item>
+ <item msgid="1271006222031257266">"గంటలు, నిమిషాలను చూపు (ఆటోమేటిక్)"</item>
<item msgid="6135970080453877218">"ఈ చిహ్నాన్ని చూపవద్దు"</item>
</string-array>
<string-array name="battery_options">
<item msgid="7714004721411852551">"ఎల్లప్పుడూ శాతాన్ని చూపు"</item>
- <item msgid="3805744470661798712">"ఛార్జ్ అవుతున్నప్పుడు శాతాన్ని చూపు (డిఫాల్ట్)"</item>
+ <item msgid="3805744470661798712">"ఛార్జ్ అవుతున్నప్పుడు శాతాన్ని చూపు (ఆటోమేటిక్)"</item>
<item msgid="8619482474544321778">"ఈ చిహ్నాన్ని చూపవద్దు"</item>
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"తక్కువ ప్రాధాన్యత నోటిఫికేషన్ చిహ్నాలను చూపించు"</string>
@@ -1178,10 +1177,7 @@
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"నెట్వర్క్ల కోసం సెర్చ్ చేస్తోంది…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"నెట్వర్క్కు కనెక్ట్ చేయడం విఫలమైంది"</string>
<string name="see_all_networks" msgid="3773666844913168122">"అన్నీ చూడండి"</string>
- <!-- no translation found for qs_tile_request_dialog_text (3501359944139877694) -->
- <skip />
- <!-- no translation found for qs_tile_request_dialog_add (4888460910694986304) -->
- <skip />
- <!-- no translation found for qs_tile_request_dialog_not_add (4168716573114067296) -->
- <skip />
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"కింది టైల్ను క్విక్ సెట్టింగ్లకు జోడించడానికి <xliff:g id="APPNAME">%1$s</xliff:g> అనుమతి కోరుతోంది"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"టైల్ను జోడించండి"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"టైల్ను జోడించవద్దు"</string>
</resources>
diff --git a/packages/SystemUI/res/values-television/dimens.xml b/packages/SystemUI/res/values-television/dimens.xml
index 76c620d..d4bfc4d 100644
--- a/packages/SystemUI/res/values-television/dimens.xml
+++ b/packages/SystemUI/res/values-television/dimens.xml
@@ -44,6 +44,7 @@
<dimen name="privacy_chip_icon_margin_in_between">9dp</dimen>
<dimen name="privacy_chip_padding_horizontal">9dp</dimen>
<dimen name="privacy_chip_icon_size">12dp</dimen>
+ <dimen name="privacy_chip_max_width">51dp</dimen>
<dimen name="privacy_chip_height">24dp</dimen>
<dimen name="privacy_chip_min_width">30dp</dimen>
<dimen name="privacy_chip_radius">12dp</dimen>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index bbaaf20..59d2acf 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -1177,7 +1177,7 @@
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"نیٹ ورکس تلاش کیے جا رہے ہیں…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"نیٹ ورک سے منسلک ہونے میں ناکام ہو گیا"</string>
<string name="see_all_networks" msgid="3773666844913168122">"سبھی دیکھیں"</string>
- <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> درج ذیل ٹائل کو فوری ترتیبات میں شامل کرنا چاہتا ہے"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> درج ذیل ٹائل کو فوری ترتیبات میں شامل کرنا چاہتی ہے"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ٹائل شامل کریں"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ٹائل شامل نہ کریں"</string>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 38af659..2c19212 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -152,7 +152,6 @@
<!-- Chosen so fill over background matches single tone -->
<color name="dark_mode_qs_icon_color_dual_tone_fill">#99000000</color>
- <color name="docked_divider_background">#ff000000</color>
<color name="docked_divider_handle">#ffffff</color>
<drawable name="forced_resizable_background">#59000000</drawable>
<color name="minimize_dock_shadow_start">#60000000</color>
@@ -190,7 +189,8 @@
<!-- UDFPS colors -->
<color name="udfps_enroll_icon">#000000</color> <!-- 100% black -->
<color name="udfps_moving_target_fill">#cc4285f4</color> <!-- 80% blue -->
- <color name="udfps_enroll_progress">#ff669DF6</color> <!-- 100% blue -->
+ <color name="udfps_enroll_progress">#ff669DF6</color> <!-- blue 400 -->
+ <color name="udfps_enroll_progress_help">#ffEE675C</color> <!-- red 400 -->
<!-- Logout button -->
<color name="logout_button_bg_color">#ccffffff</color>
@@ -287,13 +287,13 @@
<!-- Internet Dialog -->
<!-- Material next state on color-->
- <color name="settingslib_state_on_color">?androidprv:attr/colorAccentPrimary</color>
+ <color name="settingslib_state_on_color">@color/settingslib_state_on</color>
<!-- Material next state off color-->
- <color name="settingslib_state_off_color">?androidprv:attr/colorAccentSecondary</color>
+ <color name="settingslib_state_off_color">@color/settingslib_state_off</color>
<!-- Material next track on color-->
- <color name="settingslib_track_on_color">?androidprv:attr/colorAccentPrimaryVariant</color>
+ <color name="settingslib_track_on_color">@color/settingslib_track_on</color>
<!-- Material next track off color-->
- <color name="settingslib_track_off_color">?androidprv:attr/colorAccentSecondaryVariant</color>
+ <color name="settingslib_track_off_color">@color/settingslib_track_off</color>
<color name="connected_network_primary_color">#191C18</color>
<color name="connected_network_secondary_color">#41493D</color>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 1d30273..e309994 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -310,11 +310,6 @@
<!-- Whether to show the full screen user switcher. -->
<bool name="config_enableFullscreenUserSwitcher">false</bool>
- <!-- Whether the multi-user switch on the keyguard opens QS user panel. If false, clicking the
- user switch on the keyguard will replace the notifications and status area with the user
- switcher. The multi-user switch is only shown if config_keyguardUserSwitcher=false. -->
- <bool name="config_keyguard_user_switch_opens_qs_details">false</bool>
-
<!-- SystemUIFactory component -->
<string name="config_systemUIFactoryComponent" translatable="false">com.android.systemui.SystemUIFactory</string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 4f857ed..4d19c07 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -328,11 +328,10 @@
<dimen name="global_screenshot_x_scale">80dp</dimen>
<dimen name="screenshot_bg_protection_height">242dp</dimen>
<dimen name="screenshot_preview_elevation">4dp</dimen>
- <dimen name="screenshot_offset_y">24dp</dimen>
+ <dimen name="screenshot_offset_y">8dp</dimen>
<dimen name="screenshot_offset_x">16dp</dimen>
<dimen name="screenshot_dismiss_button_tappable_size">48dp</dimen>
<dimen name="screenshot_dismiss_button_margin">8dp</dimen>
- <dimen name="screenshot_action_container_offset_y">16dp</dimen>
<dimen name="screenshot_action_container_corner_radius">18dp</dimen>
<dimen name="screenshot_action_container_padding_vertical">4dp</dimen>
<dimen name="screenshot_action_container_margin_horizontal">8dp</dimen>
@@ -1148,7 +1147,7 @@
<!-- The maximum offset in either direction that elements are moved vertically to prevent
burn-in on AOD. -->
- <dimen name="burn_in_prevention_offset_y_large_clock">42dp</dimen>
+ <dimen name="burn_in_prevention_offset_y_clock">42dp</dimen>
<!-- Clock maximum font size (dp is intentional, to prevent any further scaling) -->
<dimen name="large_clock_text_size">150dp</dimen>
@@ -1339,6 +1338,7 @@
<dimen name="magnifier_up_down_controls_height">40dp</dimen>
<!-- The extra padding to show the whole outer border -->
<dimen name="magnifier_drag_handle_padding">3dp</dimen>
+ <dimen name="magnification_max_frame_size">300dp</dimen>
<!-- Home Controls -->
<dimen name="controls_header_side_margin">4dp</dimen>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index c2b87a5..a9cc6ac 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -34,9 +34,14 @@
<bool name="flag_lockscreen_animations">true</bool>
<!-- The new swipe to unlock animation, which shows the app/launcher behind the keyguard during
- the swipe. -->
+ the swipe. -->
<bool name="flag_new_unlock_swipe_animation">true</bool>
+ <!-- Whether the multi-user icon on the lockscreen opens the QS user detail. If false, clicking
+ the multi-user icon will display a list of users directly on the lockscreen. The multi-user
+ icon is only shown if config_keyguardUserSwitcher=false in the frameworks config. -->
+ <bool name="flag_lockscreen_qs_user_detail_shortcut">false</bool>
+
<!-- The shared-element transition between lockscreen smartspace and launcher smartspace. -->
<bool name="flag_smartspace_shared_element_transition">false</bool>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6df34c4..fd31f3f9 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2990,8 +2990,6 @@
<!-- Content description for a chip in the status bar showing that the user is currently on a phone call. [CHAR LIMIT=NONE] -->
<string name="ongoing_phone_call_content_description">Ongoing phone call</string>
- <!-- Provider Model: Title of the airplane mode in the internet dialog. [CHAR LIMIT=50] -->
- <string name="airplane_mode">Airplane mode</string>
<!-- Provider Model: Default title of the mobile network in the mobile layout. [CHAR LIMIT=50] -->
<string name="mobile_data_settings_title">Mobile data</string>
<!-- Provider Model: Summary text separator for preferences including a short description
@@ -3002,7 +3000,7 @@
<string name="mobile_data_connection_active">Connected</string>
<!-- Provider Model:
Summary indicating that a SIM has no mobile data connection [CHAR LIMIT=50] -->
- <string name="mobile_data_off_summary">Internet won\u0027t auto\u2011connect</string>
+ <string name="mobile_data_off_summary">Mobile data won\u0027t auto\u2011connect</string>
<!-- Provider Model:
Summary indicating that a active SIM and no network available [CHAR LIMIT=50] -->
<string name="mobile_data_no_connection">No connection</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
index bcfb774..9808374 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
@@ -72,6 +72,8 @@
private boolean mWindowVisible;
private boolean mWindowHasBlurs;
private SurfaceControl mRegisteredStopLayer = null;
+ // A copy of mRegisteredStopLayer where we own the life cycle and can access from a bg thread.
+ private SurfaceControl mWrappedStopLayer = null;
private ViewTreeObserver.OnDrawListener mUpdateOnDraw = new ViewTreeObserver.OnDrawListener() {
@Override
public void onDraw() {
@@ -184,16 +186,21 @@
}
if (!mSamplingRequestBounds.equals(mRegisteredSamplingBounds)
|| mRegisteredStopLayer != stopLayerControl) {
- // We only want to reregister if something actually changed
+ // We only want to re-register if something actually changed
unregisterSamplingListener();
mSamplingListenerRegistered = true;
- SurfaceControl registeredStopLayer = stopLayerControl;
+ SurfaceControl wrappedStopLayer = stopLayerControl == null
+ ? null : new SurfaceControl(stopLayerControl, "regionSampling");
mBackgroundExecutor.execute(() -> {
+ if (wrappedStopLayer != null && !wrappedStopLayer.isValid()) {
+ return;
+ }
CompositionSamplingListener.register(mSamplingListener, DEFAULT_DISPLAY,
- registeredStopLayer, mSamplingRequestBounds);
+ wrappedStopLayer, mSamplingRequestBounds);
});
mRegisteredSamplingBounds.set(mSamplingRequestBounds);
- mRegisteredStopLayer = registeredStopLayer;
+ mRegisteredStopLayer = stopLayerControl;
+ mWrappedStopLayer = wrappedStopLayer;
}
mFirstSamplingAfterStart = false;
} else {
@@ -204,10 +211,14 @@
private void unregisterSamplingListener() {
if (mSamplingListenerRegistered) {
mSamplingListenerRegistered = false;
+ SurfaceControl wrappedStopLayer = mWrappedStopLayer;
mRegisteredStopLayer = null;
mRegisteredSamplingBounds.setEmpty();
mBackgroundExecutor.execute(() -> {
CompositionSamplingListener.unregister(mSamplingListener);
+ if (wrappedStopLayer != null && wrappedStopLayer.isValid()) {
+ wrappedStopLayer.release();
+ }
});
}
}
@@ -262,6 +273,7 @@
pw.println(" mWindowHasBlurs: " + mWindowHasBlurs);
pw.println(" mWaitingOnDraw: " + mWaitingOnDraw);
pw.println(" mRegisteredStopLayer: " + mRegisteredStopLayer);
+ pw.println(" mWrappedStopLayer: " + mWrappedStopLayer);
pw.println(" mIsDestroyed: " + mIsDestroyed);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index 0a14657..30db136 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -71,11 +71,11 @@
public final boolean allowEnterPip;
public final int rotationChange;
public final int windowType;
+ public final WindowConfiguration windowConfiguration;
private final SurfaceControl mStartLeash;
// Fields used only to unrap into RemoteAnimationTarget
- private final WindowConfiguration windowConfiguration;
private final Rect startBounds;
public RemoteAnimationTargetCompat(RemoteAnimationTarget app) {
@@ -235,8 +235,9 @@
rotationChange = change.getEndRotation() - change.getStartRotation();
windowType = INVALID_WINDOW_TYPE;
- // TODO this probably isn't right but it's unused for now /shrug
- windowConfiguration = new WindowConfiguration();
+ windowConfiguration = change.getTaskInfo() != null
+ ? change.getTaskInfo().configuration.windowConfiguration
+ : new WindowConfiguration();
startBounds = change.getStartAbsBounds();
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index b38270c..85d5de0 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -27,10 +27,12 @@
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
+import android.view.InsetsController;
import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import android.view.animation.Interpolator;
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
import com.android.systemui.shared.recents.view.RecentsTransition;
@@ -89,6 +91,9 @@
public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT =
InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
+ public static final int ANIMATION_DURATION_RESIZE = InsetsController.ANIMATION_DURATION_RESIZE;
+ public static final Interpolator RESIZE_INTERPOLATOR = InsetsController.RESIZE_INTERPOLATOR;
+
private static final WindowManagerWrapper sInstance = new WindowManagerWrapper();
public static WindowManagerWrapper getInstance() {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index 9b5eae8..49cd279 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -29,6 +29,7 @@
import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
import com.android.systemui.unfold.updates.DeviceFoldStateProvider
import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
+import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider
import com.android.systemui.unfold.updates.hinge.RotationSensorHingeAngleProvider
import java.lang.IllegalStateException
import java.util.concurrent.Executor
@@ -50,7 +51,13 @@
val hingeAngleProvider =
if (config.mode == ANIMATION_MODE_HINGE_ANGLE) {
- RotationSensorHingeAngleProvider(sensorManager)
+ // TODO: after removing temporary "config.mode" we should just
+ // switch between fixed timing and hinge sensor based on this flag
+ if (config.isHingeAngleEnabled) {
+ HingeSensorAngleProvider(sensorManager)
+ } else {
+ RotationSensorHingeAngleProvider(sensorManager)
+ }
} else {
EmptyHingeAngleProvider()
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
index fa6b5de..e7c6998a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
@@ -25,6 +25,9 @@
override val isEnabled: Boolean
get() = readIsEnabled() && mode != ANIMATION_MODE_DISABLED
+ override val isHingeAngleEnabled: Boolean
+ get() = readIsHingeAngleEnabled()
+
@AnimationMode
override val mode: Int
get() = SystemProperties.getInt(UNFOLD_TRANSITION_MODE_PROPERTY_NAME,
@@ -32,6 +35,9 @@
private fun readIsEnabled(): Boolean = context.resources
.getBoolean(com.android.internal.R.bool.config_unfoldTransitionEnabled)
+
+ private fun readIsHingeAngleEnabled(): Boolean = context.resources
+ .getBoolean(com.android.internal.R.bool.config_unfoldTransitionHingeAngle)
}
/**
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
index 75d9dc3..a569757 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
@@ -19,6 +19,7 @@
interface UnfoldTransitionConfig {
val isEnabled: Boolean
+ val isHingeAngleEnabled: Boolean
@AnimationMode
val mode: Int
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
index b111892..10e6c2b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
@@ -16,6 +16,7 @@
package com.android.systemui.unfold.progress
import android.os.Handler
+import android.util.MathUtils.saturate
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.dynamicanimation.animation.SpringAnimation
@@ -24,6 +25,7 @@
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE
import com.android.systemui.unfold.updates.FoldStateProvider
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
@@ -33,7 +35,6 @@
* Maps fold updates to unfold transition progress using DynamicAnimation.
*
* TODO(b/193793338) Current limitations:
- * - doesn't handle folding transition
* - doesn't handle postures
*/
internal class PhysicsBasedUnfoldTransitionProgressProvider(
@@ -75,14 +76,20 @@
override fun onHingeAngleUpdate(angle: Float) {
if (!isTransitionRunning || isAnimatedCancelRunning) return
- springAnimation.animateToFinalPosition(angle / 180f)
+ val progress = saturate(angle / FINAL_HINGE_ANGLE_POSITION)
+ springAnimation.animateToFinalPosition(progress)
}
override fun onFoldUpdate(@FoldUpdate update: Int) {
when (update) {
FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> {
- onStartTransition()
startTransition(startValue = 0f)
+
+ // Stop the animation if the device has already opened by the time when
+ // the display is available as we won't receive the full open event anymore
+ if (foldStateProvider.isFullyOpened) {
+ cancelTransition(endValue = 1f, animate = true)
+ }
}
FOLD_UPDATE_FINISH_FULL_OPEN -> {
cancelTransition(endValue = 1f, animate = true)
@@ -90,13 +97,16 @@
FOLD_UPDATE_FINISH_CLOSED -> {
cancelTransition(endValue = 0f, animate = false)
}
+ FOLD_UPDATE_START_CLOSING -> {
+ startTransition(startValue = 1f)
+ }
}
}
private fun cancelTransition(endValue: Float, animate: Boolean) {
handler.removeCallbacks(timeoutRunnable)
- if (animate) {
+ if (isTransitionRunning && animate) {
isAnimatedCancelRunning = true
springAnimation.animateToFinalPosition(endValue)
} else {
@@ -182,3 +192,4 @@
private const val TRANSITION_TIMEOUT_MILLIS = 2000L
private const val SPRING_STIFFNESS = 200.0f
private const val MINIMAL_VISIBLE_CHANGE = 0.001f
+private const val FINAL_HINGE_ANGLE_POSITION = 165f
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 949652b..c37ab06 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -68,20 +68,29 @@
outputListeners.remove(listener)
}
+ override val isFullyOpened: Boolean
+ get() = !isFolded && lastFoldUpdate == FOLD_UPDATE_FINISH_FULL_OPEN
+
private fun onHingeAngle(angle: Float) {
when (lastFoldUpdate) {
FOLD_UPDATE_FINISH_FULL_OPEN -> {
- if (FULLY_OPEN_DEGREES - angle > MOVEMENT_THRESHOLD_DEGREES) {
+ if (FULLY_OPEN_DEGREES - angle > START_CLOSING_THRESHOLD_DEGREES) {
lastFoldUpdate = FOLD_UPDATE_START_CLOSING
outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_START_CLOSING) }
}
}
- FOLD_UPDATE_START_OPENING, FOLD_UPDATE_START_CLOSING -> {
+ FOLD_UPDATE_START_OPENING -> {
if (FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES) {
lastFoldUpdate = FOLD_UPDATE_FINISH_FULL_OPEN
outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }
}
}
+ FOLD_UPDATE_START_CLOSING -> {
+ if (FULLY_OPEN_DEGREES - angle < START_CLOSING_THRESHOLD_DEGREES) {
+ lastFoldUpdate = FOLD_UPDATE_FINISH_FULL_OPEN
+ outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }
+ }
+ }
}
outputListeners.forEach { it.onHingeAngleUpdate(angle) }
@@ -120,5 +129,5 @@
}
}
-private const val MOVEMENT_THRESHOLD_DEGREES = 10f
-private const val FULLY_OPEN_THRESHOLD_DEGREES = 10f
\ No newline at end of file
+private const val START_CLOSING_THRESHOLD_DEGREES = 95f
+private const val FULLY_OPEN_THRESHOLD_DEGREES = 15f
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
index 4c6d241..11984b93 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
@@ -28,6 +28,8 @@
fun start()
fun stop()
+ val isFullyOpened: Boolean
+
interface FoldUpdatesListener {
fun onHingeAngleUpdate(@FloatRange(from = 0.0, to = 180.0) angle: Float)
fun onFoldUpdate(@FoldUpdate update: Int)
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
index 8549913..2520d35 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
@@ -3,6 +3,12 @@
import androidx.core.util.Consumer
import com.android.systemui.statusbar.policy.CallbackController
+/**
+ * Emits device hinge angle values (angle between two integral parts of the device).
+ * The hinge angle could be from 0 to 360 degrees inclusive.
+ * For foldable devices usually 0 corresponds to fully closed (folded) state and
+ * 180 degrees corresponds to fully open (flat) state
+ */
internal interface HingeAngleProvider : CallbackController<Consumer<Float>> {
fun start()
fun stop()
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
new file mode 100644
index 0000000..a42ebef
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
@@ -0,0 +1,42 @@
+package com.android.systemui.unfold.updates.hinge
+
+import android.hardware.Sensor
+import android.hardware.SensorEvent
+import android.hardware.SensorEventListener
+import android.hardware.SensorManager
+import androidx.core.util.Consumer
+
+internal class HingeSensorAngleProvider(
+ private val sensorManager: SensorManager
+) : HingeAngleProvider {
+
+ private val sensorListener = HingeAngleSensorListener()
+ private val listeners: MutableList<Consumer<Float>> = arrayListOf()
+
+ override fun start() {
+ val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_HINGE_ANGLE)
+ sensorManager.registerListener(sensorListener, sensor, SensorManager.SENSOR_DELAY_FASTEST)
+ }
+
+ override fun stop() {
+ sensorManager.unregisterListener(sensorListener)
+ }
+
+ override fun removeCallback(listener: Consumer<Float>) {
+ listeners.remove(listener)
+ }
+
+ override fun addCallback(listener: Consumer<Float>) {
+ listeners.add(listener)
+ }
+
+ private inner class HingeAngleSensorListener : SensorEventListener {
+
+ override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
+ }
+
+ override fun onSensorChanged(event: SensorEvent) {
+ listeners.forEach { it.accept(event.values[0]) }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 840e8c8..26059068 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -317,7 +317,8 @@
mRunningOneHandedAnimator = null;
}
- int targetTranslation = mIsSecurityViewLeftAligned ? 0 : (int) (getMeasuredWidth() / 2f);
+ int targetTranslation = mIsSecurityViewLeftAligned
+ ? 0 : (int) (getMeasuredWidth() - mSecurityViewFlipper.getWidth());
if (animate) {
mRunningOneHandedAnimator =
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index 56fdb89..23438a9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -147,8 +147,7 @@
.alpha(1f)
.withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable)
.start();
- } else if (mUnlockedScreenOffAnimationController
- .isScreenOffLightRevealAnimationPlaying()) {
+ } else if (mUnlockedScreenOffAnimationController.shouldAnimateInKeyguard()) {
mKeyguardViewVisibilityAnimating = true;
// Ask the screen off animation controller to animate the keyguard visibility for us
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 47f0714..2a4022c 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -501,8 +501,10 @@
if (!wasClickableOnDownEvent()) {
return;
}
+ mDetectedLongPress = true;
- if (mVibrator != null) {
+ if (onAffordanceClick() && mVibrator != null) {
+ // only vibrate if the click went through and wasn't intercepted by falsing
mVibrator.vibrate(
Process.myUid(),
getContext().getOpPackageName(),
@@ -510,8 +512,6 @@
"lockIcon-onLongPress",
VIBRATION_SONIFICATION_ATTRIBUTES);
}
- mDetectedLongPress = true;
- onAffordanceClick();
}
public boolean onSingleTapUp(MotionEvent e) {
@@ -535,9 +535,14 @@
return mDownDetected;
}
- private void onAffordanceClick() {
+ /**
+ * Whether we tried to launch the affordance.
+ *
+ * If falsing intercepts the click, returns false.
+ */
+ private boolean onAffordanceClick() {
if (mFalsingManager.isFalseTouch(LOCK_ICON)) {
- return;
+ return false;
}
// pre-emptively set to true to hide view
@@ -547,6 +552,7 @@
}
updateVisibility();
mKeyguardViewController.showBouncer(/* scrim */ true);
+ return true;
}
});
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java
index 49a617e..153da4b 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java
@@ -19,6 +19,7 @@
import com.android.keyguard.KeyguardStatusViewController;
import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import dagger.BindsInstance;
import dagger.Subcomponent;
@@ -34,7 +35,10 @@
/** Simple factory for {@link KeyguardStatusBarViewComponent}. */
@Subcomponent.Factory
interface Factory {
- KeyguardStatusBarViewComponent build(@BindsInstance KeyguardStatusBarView view);
+ KeyguardStatusBarViewComponent build(
+ @BindsInstance KeyguardStatusBarView view,
+ @BindsInstance NotificationPanelViewController.NotificationPanelViewStateProvider
+ notificationPanelViewStateProvider);
}
/** Builds a {@link KeyguardStatusViewController}. */
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index a51e3fc..0893e89 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -499,9 +499,12 @@
}
private void setMagnificationFrameWith(Rect windowBounds, int centerX, int centerY) {
- // Sets the initial frame area for the mirror and places it in the center of the display.
- final int initSize = Math.min(windowBounds.width(), windowBounds.height()) / 2
- + 2 * mMirrorSurfaceMargin;
+ // Sets the initial frame area for the mirror and place it to the given center on the
+ // display.
+ int initSize = Math.min(windowBounds.width(), windowBounds.height()) / 2;
+ initSize = Math.min(mResources.getDimensionPixelSize(R.dimen.magnification_max_frame_size),
+ initSize);
+ initSize += 2 * mMirrorSurfaceMargin;
final int initX = centerX - initSize / 2;
final int initY = centerY - initSize / 2;
mMagnificationFrame.set(initX, initY, initX + initSize, initY + initSize);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 3f61d3c..fd37b35 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -126,6 +126,7 @@
boolean mCredentialAllowed;
boolean mSkipIntro;
long mOperationId;
+ long mRequestId;
@BiometricMultiSensorMode int mMultiSensorConfig;
}
@@ -172,6 +173,12 @@
return this;
}
+ /** Unique id for this request. */
+ public Builder setRequestId(long requestId) {
+ mConfig.mRequestId = requestId;
+ return this;
+ }
+
/** The multi-sensor mode. */
public Builder setMultiSensorConfig(@BiometricMultiSensorMode int multiSensorConfig) {
mConfig.mMultiSensorConfig = multiSensorConfig;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index ab5f2b828..bcc0530 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -501,7 +501,7 @@
@Override
public void showAuthenticationDialog(PromptInfo promptInfo, IBiometricSysuiReceiver receiver,
int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation,
- int userId, String opPackageName, long operationId,
+ int userId, long operationId, String opPackageName, long requestId,
@BiometricMultiSensorMode int multiSensorConfig) {
@Authenticators.Types final int authenticators = promptInfo.getAuthenticators();
@@ -515,6 +515,7 @@
+ ", credentialAllowed: " + credentialAllowed
+ ", requireConfirmation: " + requireConfirmation
+ ", operationId: " + operationId
+ + ", requestId: " + requestId
+ ", multiSensorConfig: " + multiSensorConfig);
}
SomeArgs args = SomeArgs.obtain();
@@ -526,6 +527,7 @@
args.argi1 = userId;
args.arg6 = opPackageName;
args.arg7 = operationId;
+ args.arg8 = requestId;
args.argi2 = multiSensorConfig;
boolean skipAnimation = false;
@@ -629,6 +631,7 @@
if (mCurrentDialog == null) {
// Could be possible if the caller canceled authentication after credential success
// but before the client was notified.
+ if (DEBUG) Log.d(TAG, "dialog already gone");
return;
}
@@ -683,6 +686,7 @@
final int userId = args.argi1;
final String opPackageName = (String) args.arg6;
final long operationId = (long) args.arg7;
+ final long requestId = (long) args.arg8;
final @BiometricMultiSensorMode int multiSensorConfig = args.argi2;
// Create a new dialog but do not replace the current one yet.
@@ -695,6 +699,7 @@
opPackageName,
skipAnimation,
operationId,
+ requestId,
multiSensorConfig);
if (newDialog == null) {
@@ -772,7 +777,7 @@
protected AuthDialog buildDialog(PromptInfo promptInfo, boolean requireConfirmation,
int userId, int[] sensorIds, boolean credentialAllowed, String opPackageName,
- boolean skipIntro, long operationId,
+ boolean skipIntro, long operationId, long requestId,
@BiometricMultiSensorMode int multiSensorConfig) {
return new AuthContainerView.Builder(mContext)
.setCallback(this)
@@ -782,6 +787,7 @@
.setOpPackageName(opPackageName)
.setSkipIntro(skipIntro)
.setOperationId(operationId)
+ .setRequestId(requestId)
.setMultiSensorConfig(multiSensorConfig)
.build(sensorIds, credentialAllowed, mFpProps, mFaceProps);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index c323bf7..e13ae4c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -163,7 +163,8 @@
public static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES =
new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
- .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+ // vibration will bypass battery saver mode:
+ .setUsage(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY)
.build();
public static final VibrationEffect EFFECT_CLICK =
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
index ea69b1d..b5273ab 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
@@ -143,6 +143,10 @@
mProgressDrawable.onLastStepAcquired();
}
+ void onEnrollmentHelp() {
+ mProgressDrawable.onEnrollmentHelp();
+ }
+
@Override
public void draw(@NonNull Canvas canvas) {
mProgressDrawable.draw(canvas);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
index 6a918a6..19148e3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
@@ -50,6 +50,7 @@
interface Listener {
void onEnrollmentProgress(int remaining, int totalSteps);
void onLastStepAcquired();
+ void onEnrollmentHelp();
}
@NonNull private final Context mContext;
@@ -138,7 +139,9 @@
}
void onEnrollmentHelp() {
-
+ if (mListener != null) {
+ mListener.onEnrollmentHelp();
+ }
}
void setListener(Listener listener) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
index 4195009..9c486b9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
@@ -16,7 +16,9 @@
package com.android.systemui.biometrics;
+import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
+import android.annotation.ColorInt;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
@@ -46,6 +48,11 @@
@NonNull private final Paint mProgressPaint;
@Nullable private ValueAnimator mProgressAnimator;
+ @Nullable private ValueAnimator mProgressShowingHelpAnimator;
+ @Nullable private ValueAnimator mProgressHidingHelpAnimator;
+ @ColorInt private final int mProgressColor;
+ @ColorInt private final int mProgressHelpColor;
+ private final int mShortAnimationDuration;
private float mProgress;
private int mRotation; // After last step, rotate the progress bar once
private boolean mLastStepAcquired;
@@ -55,6 +62,11 @@
mContext = context;
mParent = parent;
+ mShortAnimationDuration = context.getResources()
+ .getInteger(com.android.internal.R.integer.config_shortAnimTime);
+ mProgressColor = context.getColor(R.color.udfps_enroll_progress);
+ mProgressHelpColor = context.getColor(R.color.udfps_enroll_progress_help);
+
mBackgroundCirclePaint = new Paint();
mBackgroundCirclePaint.setStrokeWidth(Utils.dpToPixels(context, PROGRESS_BAR_THICKNESS_DP));
mBackgroundCirclePaint.setColor(context.getColor(R.color.white_disabled));
@@ -74,7 +86,7 @@
// Progress should not be color extracted
mProgressPaint = new Paint();
mProgressPaint.setStrokeWidth(Utils.dpToPixels(context, PROGRESS_BAR_THICKNESS_DP));
- mProgressPaint.setColor(context.getColor(R.color.udfps_enroll_progress));
+ mProgressPaint.setColor(mProgressColor);
mProgressPaint.setAntiAlias(true);
mProgressPaint.setStyle(Paint.Style.STROKE);
mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
@@ -92,7 +104,9 @@
return;
}
- long animationDuration = 150;
+ long animationDuration = mShortAnimationDuration;
+
+ hideEnrollmentHelp();
if (progress == 1.f) {
animationDuration = 400;
@@ -128,6 +142,47 @@
mLastStepAcquired = true;
}
+ void onEnrollmentHelp() {
+ if (mProgressShowingHelpAnimator != null || mProgressAnimator == null) {
+ return; // already showing or at 0% (no progress bar visible)
+ }
+
+ if (mProgressHidingHelpAnimator != null && mProgressHidingHelpAnimator.isRunning()) {
+ mProgressHidingHelpAnimator.cancel();
+ }
+ mProgressHidingHelpAnimator = null;
+
+ mProgressShowingHelpAnimator = getProgressColorAnimator(
+ mProgressPaint.getColor(), mProgressHelpColor);
+ mProgressShowingHelpAnimator.start();
+ }
+
+ private void hideEnrollmentHelp() {
+ if (mProgressHidingHelpAnimator != null || mProgressShowingHelpAnimator == null) {
+ return; // already hidden or help never shown
+ }
+
+ if (mProgressShowingHelpAnimator != null && mProgressShowingHelpAnimator.isRunning()) {
+ mProgressShowingHelpAnimator.cancel();
+ }
+ mProgressShowingHelpAnimator = null;
+
+ mProgressHidingHelpAnimator = getProgressColorAnimator(
+ mProgressPaint.getColor(), mProgressColor);
+ mProgressHidingHelpAnimator.start();
+ }
+
+ private ValueAnimator getProgressColorAnimator(@ColorInt int from, @ColorInt int to) {
+ final ValueAnimator animator = ValueAnimator.ofObject(
+ ArgbEvaluator.getInstance(), from, to);
+ animator.setDuration(mShortAnimationDuration);
+ animator.addUpdateListener(animation -> {
+ mProgressPaint.setColor((int) animation.getAnimatedValue());
+ mParent.invalidateSelf();
+ });
+ return animator;
+ }
+
@Override
public void draw(@NonNull Canvas canvas) {
canvas.save();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
index 2cdf49d..56d5b31 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
@@ -64,4 +64,8 @@
void onLastStepAcquired() {
mHandler.post(mFingerprintDrawable::onLastStepAcquired);
}
+
+ void onEnrollmentHelp() {
+ mHandler.post(mFingerprintDrawable::onEnrollmentHelp);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
index 54244a1..33fbe7b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
@@ -44,6 +44,11 @@
public void onLastStepAcquired() {
mView.onLastStepAcquired();
}
+
+ @Override
+ public void onEnrollmentHelp() {
+ mView.onEnrollmentHelp();
+ }
};
protected UdfpsEnrollViewController(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
index d46426a..9015396 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -155,6 +155,13 @@
updateAlpha();
}
+ /**
+ * @return alpha between 0 and 255
+ */
+ int getUnpausedAlpha() {
+ return mAlpha;
+ }
+
@Override
protected int updateAlpha() {
int alpha = super.updateAlpha();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 4d9675c..44ab69f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -168,7 +168,7 @@
pw.println("mIsBouncerVisible=" + mIsBouncerVisible);
pw.println("mInputBouncerHiddenAmount=" + mInputBouncerHiddenAmount);
pw.println("mStatusBarExpansion=" + mStatusBarExpansion);
- pw.println("mAlpha=" + mView.getAlpha());
+ pw.println("unpausedAlpha=" + mView.getUnpausedAlpha());
pw.println("mUdfpsRequested=" + mUdfpsRequested);
pw.println("mView.mUdfpsRequested=" + mView.mUdfpsRequested);
pw.println("mLaunchTransitionFadingAway=" + mLaunchTransitionFadingAway);
@@ -183,13 +183,13 @@
return false;
}
+ boolean udfpsAffordanceWasNotShowing = shouldPauseAuth();
mShowingUdfpsBouncer = show;
if (mShowingUdfpsBouncer) {
mLastUdfpsBouncerShowTime = mSystemClock.uptimeMillis();
}
- updatePauseAuth();
if (mShowingUdfpsBouncer) {
- if (mStatusBarState == StatusBarState.SHADE_LOCKED) {
+ if (udfpsAffordanceWasNotShowing) {
mView.animateInUdfpsBouncer(null);
}
@@ -202,6 +202,8 @@
} else {
mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
}
+ updateAlpha();
+ updatePauseAuth();
return true;
}
@@ -335,6 +337,7 @@
public void requestUdfps(boolean request, int color) {
mUdfpsRequested = request;
mView.requestUdfps(request, color);
+ updateAlpha();
updatePauseAuth();
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
index e1349f2..40c28fa 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
@@ -21,6 +21,7 @@
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_PRIMARY_DEVIANCE;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_SECONDARY_DEVIANCE;
import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
+import static com.android.systemui.classifier.Classifier.LOCK_ICON;
import static com.android.systemui.classifier.Classifier.SHADE_DRAG;
import android.graphics.Point;
@@ -89,7 +90,9 @@
Result calculateFalsingResult(
@Classifier.InteractionType int interactionType,
double historyBelief, double historyConfidence) {
- if (interactionType == BRIGHTNESS_SLIDER || interactionType == SHADE_DRAG) {
+ if (interactionType == BRIGHTNESS_SLIDER
+ || interactionType == SHADE_DRAG
+ || interactionType == LOCK_ICON) {
return Result.passed(0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java
index 1680e42..73e5afe 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java
@@ -27,6 +27,11 @@
import com.android.keyguard.KeyguardVisibilityHelper;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.PropertyAnimator;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -37,6 +42,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
+import java.util.Objects;
+import java.util.Optional;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -48,13 +55,16 @@
private static final String TAG = "CommunalController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final String STATE_LIST_FORMAT = "[%s]";
+ private static final AnimationProperties COMMUNAL_ANIMATION_PROPERTIES =
+ new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
private final Executor mMainExecutor;
private final CommunalStateController mCommunalStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final KeyguardStateController mKeyguardStateController;
private final StatusBarStateController mStatusBarStateController;
- private WeakReference<CommunalSource> mLastSource;
+ private WeakReference<CommunalSource> mCurrentSource;
+ private Optional<ShowRequest> mLastRequest = Optional.empty();
private int mState;
private float mQsExpansion;
private float mShadeExpansion;
@@ -71,12 +81,43 @@
// Only show communal view when keyguard is showing and not dozing.
private static final int SHOW_COMMUNAL_VIEW_REQUIRED_STATES = STATE_KEYGUARD_SHOWING;
private static final int SHOW_COMMUNAL_VIEW_INVALID_STATES =
- STATE_DOZING | STATE_BOUNCER_SHOWING | STATE_KEYGUARD_OCCLUDED;
+ STATE_DOZING | STATE_KEYGUARD_OCCLUDED;
private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
private ViewController<? extends View> mCommunalViewController;
+ private static class ShowRequest {
+ private boolean mShouldShow;
+ private WeakReference<CommunalSource> mSource;
+
+ ShowRequest(boolean shouldShow, WeakReference<CommunalSource> source) {
+ mShouldShow = shouldShow;
+ mSource = source;
+ }
+
+ CommunalSource getSource() {
+ return mSource != null ? mSource.get() : null;
+ }
+
+ boolean shouldShow() {
+ return mShouldShow;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ShowRequest)) return false;
+ ShowRequest that = (ShowRequest) o;
+ return mShouldShow == that.mShouldShow && Objects.equals(getSource(), that.getSource());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mShouldShow, mSource);
+ }
+ }
+
private KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
new KeyguardUpdateMonitorCallback() {
@Override
@@ -121,6 +162,11 @@
setState(STATE_DOZING, isDozing);
}
+
+ @Override
+ public void onStateChanged(int newState) {
+ updateCommunalViewOccluded();
+ }
};
@Inject
@@ -154,10 +200,23 @@
statusBarState, keyguardFadingAway, goingToFullShade, oldStatusBarState);
}
- @Override
- public void init() {
- super.init();
+ /**
+ * Set keyguard status view alpha.
+ */
+ public void setAlpha(float alpha) {
+ if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) {
+ mView.setAlpha(alpha);
+ // Some communal view implementations, such as SurfaceViews, do not behave correctly
+ // inheriting the alpha of their parent. Directly set child alpha here to work around
+ // this.
+ for (int i = mView.getChildCount() - 1; i >= 0; --i) {
+ mView.getChildAt(i).setAlpha(alpha);
+ }
+ }
+ }
+ @Override
+ public void onInit() {
setState(STATE_KEYGUARD_SHOWING, mKeyguardStateController.isShowing());
setState(STATE_DOZING, mStatusBarStateController.isDozing());
}
@@ -195,6 +254,8 @@
if (existingState != mState) {
showSource();
}
+
+ updateCommunalViewOccluded();
}
private String describeState(@State int stateFlag) {
@@ -230,18 +291,26 @@
}
private void showSource() {
+ final ShowRequest request = new ShowRequest(
+ (mState & SHOW_COMMUNAL_VIEW_REQUIRED_STATES) == SHOW_COMMUNAL_VIEW_REQUIRED_STATES
+ && (mState & SHOW_COMMUNAL_VIEW_INVALID_STATES) == 0
+ && mCurrentSource != null,
+ mCurrentSource);
+
+ if (mLastRequest.isPresent() && Objects.equals(mLastRequest.get(), request)) {
+ return;
+ }
+
+ mLastRequest = Optional.of(request);
+
// Make sure all necessary states are present for showing communal and all invalid states
// are absent
mMainExecutor.execute(() -> {
- final CommunalSource currentSource = mLastSource != null ? mLastSource.get() : null;
-
if (DEBUG) {
- Log.d(TAG, "showSource. currentSource:" + currentSource);
+ Log.d(TAG, "showSource. currentSource:" + request.getSource());
}
- if ((mState & SHOW_COMMUNAL_VIEW_REQUIRED_STATES) == SHOW_COMMUNAL_VIEW_REQUIRED_STATES
- && (mState & SHOW_COMMUNAL_VIEW_INVALID_STATES) == 0
- && currentSource != null) {
+ if (request.shouldShow()) {
mView.removeAllViews();
// Make view visible.
@@ -250,7 +319,7 @@
final Context context = mView.getContext();
final ListenableFuture<CommunalSource.CommunalViewResult> listenableFuture =
- currentSource.requestCommunalView(context);
+ request.getSource().requestCommunalView(context);
if (listenableFuture == null) {
Log.e(TAG, "could not request communal view");
@@ -285,11 +354,19 @@
* @param source The new {@link CommunalSource}, {@code null} if not set.
*/
public void show(WeakReference<CommunalSource> source) {
- mLastSource = source;
+ mCurrentSource = source;
showSource();
}
/**
+ * Update position of the view with an optional animation
+ */
+ public void updatePosition(int y, boolean animate) {
+ PropertyAnimator.setProperty(mView, AnimatableProperty.Y, y, COMMUNAL_ANIMATION_PROPERTIES,
+ animate);
+ }
+
+ /**
* Invoked when the quick settings is expanded.
* @param expansionFraction the percentage the QS shade has been expanded.
*/
@@ -308,7 +385,12 @@
}
private void updateCommunalViewOccluded() {
+ final boolean bouncerShowing = (mState & STATE_BOUNCER_SHOWING) == STATE_BOUNCER_SHOWING;
+ final int statusBarState = mStatusBarStateController.getState();
+ final boolean shadeExpanded = statusBarState == StatusBarState.SHADE
+ || statusBarState == StatusBarState.SHADE_LOCKED;
+
mCommunalStateController.setCommunalViewOccluded(
- mQsExpansion > 0.0f || mShadeExpansion > 0.0f);
+ bouncerShowing || shadeExpanded || mQsExpansion > 0.0f || mShadeExpansion > 0.0f);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewPositionAlgorithm.java
new file mode 100644
index 0000000..424da0b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewPositionAlgorithm.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal;
+
+import android.util.Log;
+
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
+
+/**
+ * {@link CommunalHostViewPositionAlgorithm} calculates the position of the communal view given
+ * input such as the notification panel position.
+ */
+public class CommunalHostViewPositionAlgorithm {
+ private static final String TAG = "CommunalPositionAlg";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ /**
+ * @see NotificationPanelViewController#getExpandedFraction()
+ */
+ private float mPanelExpansion;
+
+ /**
+ * Height of {@link CommunalHostView}.
+ */
+ private int mCommunalHeight;
+
+ /**
+ * A data container for the result of the position algorithm.
+ */
+ public static class Result {
+ /**
+ * The y translation of the clock.
+ */
+ public int communalY;
+ }
+
+ /**
+ * Sets the conditions under which the result should be calculated from.
+ * @param panelExpansion The percentage the keyguard panel has been moved upwards.
+ * @param communalHeight The height of the communal panel.
+ */
+ public void setup(float panelExpansion, int communalHeight) {
+ if (DEBUG) {
+ Log.d(TAG, "setup. panelExpansion:" + panelExpansion);
+ }
+ mPanelExpansion = panelExpansion;
+ mCommunalHeight = communalHeight;
+ }
+
+ /**
+ * Calculates the position based on factors input through {link {@link #setup(float, int)}}.
+ * @param result The resulting calculations.
+ */
+ public void run(Result result) {
+ // The panel expansion relates to the keyguard expansion. At full expansion, the communal
+ // view should be aligned at the top (0). Otherwise, it should be shifted offscreen by the
+ // unexpanded amount.
+ result.communalY = (int) ((1 - mPanelExpansion) * -mCommunalHeight);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSourceImpl.java b/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSourceImpl.java
index df368c3..b8070ab 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSourceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSourceImpl.java
@@ -17,6 +17,7 @@
package com.android.systemui.communal.service;
import android.content.Context;
+import android.content.res.Resources;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
@@ -30,12 +31,14 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.shared.communal.ICommunalSource;
import com.android.systemui.shared.communal.ICommunalSurfaceCallback;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.google.android.collect.Lists;
import com.google.common.util.concurrent.ListenableFuture;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Objects;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -47,23 +50,71 @@
*/
public class CommunalSourceImpl implements CommunalSource {
private static final String TAG = "CommunalSourceImpl";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final ICommunalSource mSourceProxy;
+ private final Resources mResources;
private final Executor mMainExecutor;
+ private final NotificationShadeWindowController mNotificationShadeWindowController;
private final CommunalStateController mCommunalStateController;
static class Factory {
private final Executor mExecutor;
+ private final Resources mResources;
private final CommunalStateController mCommunalStateController;
+ private final NotificationShadeWindowController mNotificationShadeWindowController;
@Inject
- Factory(@Main Executor executor, CommunalStateController communalStateController) {
+ Factory(@Main Executor executor, @Main Resources resources,
+ NotificationShadeWindowController notificationShadeWindowController,
+ CommunalStateController communalStateController) {
mExecutor = executor;
+ mResources = resources;
+ mNotificationShadeWindowController = notificationShadeWindowController;
mCommunalStateController = communalStateController;
}
public CommunalSource create(ICommunalSource source) {
- return new CommunalSourceImpl(mExecutor, mCommunalStateController, source);
+ return new CommunalSourceImpl(mExecutor, mResources, mCommunalStateController,
+ mNotificationShadeWindowController, source);
+ }
+ }
+
+ static class Request {
+ private final int mWidth;
+ private final int mHeight;
+ private final int mDisplayId;
+ private final IBinder mHostToken;
+
+ Request(int width, int height, int displayId, IBinder hostToken) {
+ mWidth = width;
+ mHeight = height;
+ mDisplayId = displayId;
+ mHostToken = hostToken;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Request)) return false;
+ Request request = (Request) o;
+ return mWidth == request.mWidth && mHeight == request.mHeight
+ && mDisplayId == request.mDisplayId && Objects.equals(mHostToken,
+ request.mHostToken);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mWidth, mHeight, mDisplayId, mHostToken);
+ }
+
+ @Override
+ public String toString() {
+ return "Request{"
+ + "mWidth=" + mWidth
+ + ", mHeight=" + mHeight
+ + ", mDisplayId=" + mDisplayId
+ + ", mHostToken=" + mHostToken
+ + '}';
}
}
@@ -75,10 +126,14 @@
// A list of {@link Callback} that have registered to receive updates.
private final ArrayList<WeakReference<Callback>> mCallbacks = Lists.newArrayList();
- public CommunalSourceImpl(Executor mainExecutor,
- CommunalStateController communalStateController, ICommunalSource sourceProxy) {
+ public CommunalSourceImpl(Executor mainExecutor, Resources resources,
+ CommunalStateController communalStateController,
+ NotificationShadeWindowController notificationShadeWindowController,
+ ICommunalSource sourceProxy) {
mMainExecutor = mainExecutor;
mCommunalStateController = communalStateController;
+ mNotificationShadeWindowController = notificationShadeWindowController;
+ mResources = resources;
mSourceProxy = sourceProxy;
try {
@@ -120,8 +175,9 @@
CallbackToFutureAdapter.getFuture(completer -> {
final SurfaceView view = new SurfaceView(context);
completer.set(new CommunalViewResult(view,
- new CommunalSurfaceViewController(view, mMainExecutor,
- mCommunalStateController, this)));
+ new CommunalSurfaceViewController(view, mResources, mMainExecutor,
+ mCommunalStateController, mNotificationShadeWindowController,
+ this)));
return "CommunalSourceImpl::requestCommunalSurface::getCommunalSurface";
});
@@ -132,18 +188,15 @@
* Called internally to request a new {@link android.view.SurfaceControlViewHost.SurfacePackage}
* for showing communal content.
*
- * @param hostToken The HostToken necessary to generate a {@link SurfaceControlViewHost}.
- * @param displayId The id of the display the surface will be shown on.
- * @param width The width of the surface.
- * @param height The height of the surface.
+ * @param request A request with the parameters for the new communal surface.
* @return A future that returns the resulting
* {@link android.view.SurfaceControlViewHost.SurfacePackage}.
*/
protected ListenableFuture<SurfaceControlViewHost.SurfacePackage> requestCommunalSurface(
- IBinder hostToken, int displayId, int width, int height) {
+ Request request) {
return CallbackToFutureAdapter.getFuture(completer -> {
- mSourceProxy.getCommunalSurface(hostToken, width, height, displayId,
- new ICommunalSurfaceCallback.Stub() {
+ mSourceProxy.getCommunalSurface(request.mHostToken, request.mWidth, request.mHeight,
+ request.mDisplayId, new ICommunalSurfaceCallback.Stub() {
@Override
public void onSurface(
SurfaceControlViewHost.SurfacePackage surfacePackage) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSurfaceViewController.java b/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSurfaceViewController.java
index 8a67744..0de5029 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSurfaceViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSurfaceViewController.java
@@ -17,18 +17,27 @@
package com.android.systemui.communal.service;
import android.annotation.IntDef;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.graphics.Region;
import android.util.Log;
+import android.view.IWindow;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
+import android.view.View;
import androidx.annotation.NonNull;
+import com.android.systemui.R;
import com.android.systemui.communal.CommunalStateController;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.util.Utils;
import com.android.systemui.util.ViewController;
import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Optional;
import java.util.concurrent.Executor;
/**
@@ -40,7 +49,10 @@
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final Executor mMainExecutor;
private final CommunalStateController mCommunalStateController;
+ private final NotificationShadeWindowController mNotificationShadeWindowController;
private final CommunalSourceImpl mSource;
+ private final Resources mResources;
+ private final Region mSurfaceViewTouchableRegion;
@IntDef({STATE_SURFACE_CREATED, STATE_SURFACE_VIEW_ATTACHED})
private @interface State {}
@@ -53,6 +65,8 @@
private int mCurrentState;
+ private Optional<CommunalSourceImpl.Request> mLastRequest = Optional.empty();
+
// The current in-flight request for a surface package.
private ListenableFuture<SurfaceControlViewHost.SurfacePackage> mCurrentSurfaceFuture;
@@ -73,18 +87,54 @@
}
};
- protected CommunalSurfaceViewController(SurfaceView view, Executor executor,
- CommunalStateController communalStateController, CommunalSourceImpl source) {
+ private final View.OnLayoutChangeListener mOnLayoutChangeListener =
+ new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
+ int oldTop, int oldRight, int oldBottom) {
+ // The margin for the status bar and keyguard indication are excluded from the tap
+ // exclusion to preserve vertical swipes in this region.
+ final int topMargin = mResources.getDimensionPixelSize(
+ Utils.shouldUseSplitNotificationShade(mResources)
+ ? R.dimen.split_shade_header_height
+ : R.dimen.notification_panel_margin_top);
+ final int bottomMargin = mResources.getDimensionPixelSize(
+ R.dimen.keyguard_indication_bottom_padding);
+
+ mSurfaceViewTouchableRegion.set(left, top + topMargin, right, bottom - bottomMargin);
+ updateTouchExclusion();
+
+ // Trigger showing (or hiding) surface based on new dimensions.
+ showSurface();
+ }
+ };
+
+ private CommunalStateController.Callback mCommunalStateCallback =
+ new CommunalStateController.Callback() {
+ @Override
+ public void onCommunalViewOccludedChanged() {
+ updateTouchExclusion();
+ }
+ };
+
+ protected CommunalSurfaceViewController(SurfaceView view, Resources resources,
+ Executor executor, CommunalStateController communalStateController,
+ NotificationShadeWindowController notificationShadeWindowController,
+ CommunalSourceImpl source) {
super(view);
mCommunalStateController = communalStateController;
mSource = source;
+ mResources = resources;
mMainExecutor = executor;
+ mNotificationShadeWindowController = notificationShadeWindowController;
+ mSurfaceViewTouchableRegion = new Region();
}
@Override
- public void init() {
- super.init();
+ protected void onInit() {
+ mView.getHolder().setFormat(PixelFormat.TRANSPARENT);
mView.getHolder().addCallback(mSurfaceHolderCallback);
+ mView.addOnLayoutChangeListener(mOnLayoutChangeListener);
}
private void setState(@State int state, boolean enabled) {
@@ -105,28 +155,52 @@
mCurrentState = newState;
- showSurface(newState == STATE_CAN_SHOW_SURFACE);
+ showSurface();
+
+ updateTouchExclusion();
}
- private void showSurface(boolean show) {
+ private void updateTouchExclusion() {
+ final IWindow window = IWindow.Stub.asInterface(mView.getWindowToken());
+ final boolean excludeTouches = (mCurrentState & STATE_SURFACE_VIEW_ATTACHED) != 0
+ && !mCommunalStateController.getCommunalViewOccluded();
+ if (excludeTouches) {
+ mNotificationShadeWindowController.setTouchExclusionRegion(mSurfaceViewTouchableRegion);
+ } else {
+ final Region emptyRegion = Region.obtain();
+ mNotificationShadeWindowController.setTouchExclusionRegion(emptyRegion);
+ emptyRegion.recycle();
+ }
+ }
+
+ private void showSurface() {
mView.setWillNotDraw(false);
- if (!show) {
+ if (mCurrentState != STATE_CAN_SHOW_SURFACE) {
// If the surface is no longer showing, cancel any in-flight requests.
if (mCurrentSurfaceFuture != null) {
mCurrentSurfaceFuture.cancel(true);
mCurrentSurfaceFuture = null;
}
+ mLastRequest = Optional.empty();
mView.setWillNotDraw(true);
return;
}
+ final CommunalSourceImpl.Request request = new CommunalSourceImpl.Request(
+ mView.getMeasuredWidth(), mView.getMeasuredHeight(),
+ mView.getDisplay().getDisplayId(), mView.getHostToken());
+
+ if (mLastRequest.isPresent() && mLastRequest.get().equals(request)) {
+ return;
+ }
+
+ mLastRequest = Optional.of(request);
+
// Since this method is only called when the state has changed, mCurrentSurfaceFuture should
// be null here.
- mCurrentSurfaceFuture = mSource.requestCommunalSurface(mView.getHostToken(),
- mView.getDisplay().getDisplayId(), mView.getMeasuredWidth(),
- mView.getMeasuredHeight());
+ mCurrentSurfaceFuture = mSource.requestCommunalSurface(request);
mCurrentSurfaceFuture.addListener(new Runnable() {
@Override
@@ -147,7 +221,6 @@
if (surfacePackage != null) {
mView.setChildSurfacePackage(surfacePackage);
- mView.setZOrderOnTop(true);
mView.postInvalidate();
mCommunalStateController.setCommunalViewShowing(true);
} else {
@@ -163,10 +236,12 @@
@Override
protected void onViewAttached() {
setState(STATE_SURFACE_VIEW_ATTACHED, true);
+ mCommunalStateController.addCallback(mCommunalStateCallback);
}
@Override
protected void onViewDetached() {
+ mCommunalStateController.removeCallback(mCommunalStateCallback);
setState(STATE_SURFACE_VIEW_ATTACHED, false);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index c093219..e7974bc 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -196,9 +196,11 @@
@SysUISingleton
@Provides
static ThemeOverlayApplier provideThemeOverlayManager(Context context,
- @Background Executor bgExecutor, OverlayManager overlayManager,
+ @Background Executor bgExecutor,
+ @Main Executor mainExecutor,
+ OverlayManager overlayManager,
DumpManager dumpManager) {
- return new ThemeOverlayApplier(overlayManager, bgExecutor,
+ return new ThemeOverlayApplier(overlayManager, bgExecutor, mainExecutor,
context.getString(R.string.launcher_overlayable_package),
context.getString(R.string.themepicker_overlayable_package), dumpManager);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 7450eb6..f593ed2 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -18,6 +18,7 @@
import android.app.INotificationManager;
import android.content.Context;
+import android.view.LayoutInflater;
import androidx.annotation.Nullable;
@@ -26,6 +27,7 @@
import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.BootCompleteCache;
import com.android.systemui.BootCompleteCacheImpl;
+import com.android.systemui.R;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.appops.dagger.AppOpsModule;
import com.android.systemui.assist.AssistModule;
@@ -62,6 +64,7 @@
import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarWindowView;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -70,6 +73,7 @@
import com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule;
import com.android.systemui.tuner.dagger.TunerModule;
import com.android.systemui.user.UserModule;
+import com.android.systemui.util.InjectionInflationController;
import com.android.systemui.util.concurrency.SysUIConcurrencyModule;
import com.android.systemui.util.dagger.UtilModule;
import com.android.systemui.util.sensors.SensorModule;
@@ -200,4 +204,19 @@
groupManager, entryManager, notifPipeline, sysUiState, featureFlags, dumpManager,
sysuiMainExecutor));
}
+
+ @Provides
+ @SysUISingleton
+ static StatusBarWindowView providesStatusBarWindowView(Context context,
+ InjectionInflationController injectionInflationController) {
+ StatusBarWindowView view =
+ (StatusBarWindowView) injectionInflationController.injectable(
+ LayoutInflater.from(context)).inflate(R.layout.super_status_bar,
+ /* root= */ null);
+ if (view == null) {
+ throw new IllegalStateException(
+ "R.layout.super_status_bar could not be properly inflated");
+ }
+ return view;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
index e51f90f..1a6d6ae 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
@@ -173,6 +173,10 @@
return mFlagReader.isEnabled(R.bool.flag_new_unlock_swipe_animation);
}
+ public boolean isKeyguardQsUserDetailsShortcutEnabled() {
+ return mFlagReader.isEnabled(R.bool.flag_lockscreen_qs_user_detail_shortcut);
+ }
+
public boolean isSmartSpaceSharedElementTransitionEnabled() {
return mFlagReader.isEnabled(R.bool.flag_smartspace_shared_element_transition);
}
diff --git a/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java b/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java
index 97d32c3..192c4184a 100644
--- a/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java
@@ -39,6 +39,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.ViewController;
@@ -139,6 +140,9 @@
// Monitor for tracking touches for activity.
private InputMonitorCompat mInputMonitor;
+ // Input receiver of touch activities.
+ private InputChannelCompat.InputEventReceiver mInputEventReceiver;
+
// Intent filter for receiving dream broadcasts.
private IntentFilter mDreamIntentFilter;
@@ -335,7 +339,7 @@
}
private void enableIdleMonitoring(boolean enable) {
- if (enable && mInputMonitor == null) {
+ if (enable && mInputMonitor == null && mInputEventReceiver == null) {
if (DEBUG) {
Log.d(TAG, "enable idle monitoring");
}
@@ -345,7 +349,7 @@
// Monitor - any input should reset timer
mInputMonitor = mInputMonitorFactory.getInputMonitor(INPUT_MONITOR_IDENTIFIER);
- mInputMonitor.getInputReceiver(mLooper, mChoreographer,
+ mInputEventReceiver = mInputMonitor.getInputReceiver(mLooper, mChoreographer,
v -> {
if (DEBUG) {
Log.d(TAG, "touch detected, resetting timeout");
@@ -358,7 +362,7 @@
mCancelEnableIdling = mDelayableExecutor.executeDelayed(
mEnableIdlingCallback, mIdleTimeout);
});
- } else if (!enable && mInputMonitor != null) {
+ } else if (!enable && mInputMonitor != null && mInputEventReceiver != null) {
if (DEBUG) {
Log.d(TAG, "disable idle monitoring");
}
@@ -368,7 +372,9 @@
mCancelEnableIdling = null;
}
+ mInputEventReceiver.dispose();
mInputMonitor.dispose();
+ mInputEventReceiver = null;
mInputMonitor = null;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 93388f9..0f888cb 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -19,9 +19,7 @@
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SEARCH_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode;
@@ -79,9 +77,9 @@
import com.android.systemui.navigationbar.buttons.RotationContextButton;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.navigationbar.gestural.FloatingRotationButton;
-import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
+import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog;
@@ -841,7 +839,6 @@
public void onStatusBarPanelStateChanged() {
updateSlippery();
- updatePanelSystemUiStateFlags();
}
public void updateDisabledSystemUiStateFlags() {
@@ -858,21 +855,12 @@
.commitUpdate(displayId);
}
- public void updatePanelSystemUiStateFlags() {
- int displayId = mContext.getDisplayId();
+ private void updatePanelSystemUiStateFlags() {
if (SysUiState.DEBUG) {
Log.d(TAG, "Updating panel sysui state flags: panelView=" + mPanelView);
}
if (mPanelView != null) {
- if (SysUiState.DEBUG) {
- Log.d(TAG, "Updating panel sysui state flags: fullyExpanded="
- + mPanelView.isFullyExpanded() + " inQs=" + mPanelView.isInSettings());
- }
- mSysUiFlagContainer.setFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
- mPanelView.isFullyExpanded() && !mPanelView.isInSettings())
- .setFlag(SYSUI_STATE_QUICK_SETTINGS_EXPANDED,
- mPanelView.isInSettings())
- .commitUpdate(displayId);
+ mPanelView.updateSystemUiStateFlags();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
index d94cd28..8750976 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
+++ b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
@@ -24,13 +24,17 @@
import android.annotation.IntDef;
import android.annotation.UiThread;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
+import android.os.RemoteException;
import android.util.Log;
import android.view.Gravity;
+import android.view.IWindowManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -98,6 +102,10 @@
private final Context mContext;
private final PrivacyItemController mPrivacyItemController;
+ private final IWindowManager mIWindowManager;
+ private final Rect[] mBounds = new Rect[4];
+ private boolean mIsRtl;
+
private ViewGroup mIndicatorView;
private boolean mViewAndWindowAdded;
private ObjectAnimator mAnimator;
@@ -124,17 +132,23 @@
private int mState = STATE_NOT_SHOWN;
@Inject
- public TvOngoingPrivacyChip(Context context, PrivacyItemController privacyItemController) {
+ public TvOngoingPrivacyChip(Context context, PrivacyItemController privacyItemController,
+ IWindowManager iWindowManager) {
super(context);
if (DEBUG) Log.d(TAG, "Privacy chip running");
mContext = context;
mPrivacyItemController = privacyItemController;
+ mIWindowManager = iWindowManager;
Resources res = mContext.getResources();
mIconMarginStart = Math.round(
res.getDimension(R.dimen.privacy_chip_icon_margin_in_between));
mIconSize = res.getDimensionPixelSize(R.dimen.privacy_chip_icon_size);
+ mIsRtl = mContext.getResources().getConfiguration().getLayoutDirection()
+ == View.LAYOUT_DIRECTION_RTL;
+ updateStaticPrivacyIndicatorBounds();
+
mAnimationDurationMs = res.getInteger(R.integer.privacy_chip_animation_millis);
mMicCameraIndicatorFlagEnabled = privacyItemController.getMicCameraAvailable();
@@ -147,6 +161,24 @@
}
@Override
+ public void onConfigurationChanged(Configuration config) {
+ boolean updatedRtl = config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+ if (mIsRtl == updatedRtl) {
+ return;
+ }
+ mIsRtl = updatedRtl;
+
+ updateStaticPrivacyIndicatorBounds();
+
+ // Update privacy chip location.
+ if (mState == STATE_NOT_SHOWN || mIndicatorView == null) {
+ return;
+ }
+ fadeOutIndicator();
+ createAndShowIndicator();
+ }
+
+ @Override
public void start() {
mPrivacyItemController.addCallback(this);
}
@@ -181,6 +213,32 @@
updateChip();
}
+ private void updateStaticPrivacyIndicatorBounds() {
+ Resources res = mContext.getResources();
+ int mMaxExpandedWidth = res.getDimensionPixelSize(R.dimen.privacy_chip_max_width);
+ int mMaxExpandedHeight = res.getDimensionPixelSize(R.dimen.privacy_chip_height);
+ int mChipMarginTotal = 2 * res.getDimensionPixelSize(R.dimen.privacy_chip_margin);
+
+ final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
+ Rect screenBounds = windowManager.getCurrentWindowMetrics().getBounds();
+ mBounds[0] = new Rect(
+ mIsRtl ? screenBounds.left
+ : screenBounds.right - mChipMarginTotal - mMaxExpandedWidth,
+ screenBounds.top,
+ mIsRtl ? screenBounds.left + mChipMarginTotal + mMaxExpandedWidth
+ : screenBounds.right,
+ screenBounds.top + mChipMarginTotal + mMaxExpandedHeight
+ );
+
+ if (DEBUG) Log.v(TAG, "privacy indicator bounds: " + mBounds[0].toShortString());
+
+ try {
+ mIWindowManager.updateStaticPrivacyIndicatorBounds(mContext.getDisplayId(), mBounds);
+ } catch (RemoteException e) {
+ Log.w(TAG, "could not update privacy indicator bounds");
+ }
+ }
+
private void updateChip() {
if (DEBUG) Log.d(TAG, mPrivacyItems.size() + " privacy items");
@@ -316,13 +374,9 @@
}
});
- final boolean isRtl = mContext.getResources().getConfiguration().getLayoutDirection()
- == View.LAYOUT_DIRECTION_RTL;
- if (DEBUG) Log.d(TAG, "is RTL: " + isRtl);
-
mChipDrawable = new PrivacyChipDrawable(mContext);
mChipDrawable.setListener(this);
- mChipDrawable.setRtl(isRtl);
+ mChipDrawable.setRtl(mIsRtl);
ImageView chipBackground = mIndicatorView.findViewById(R.id.chip_drawable);
if (chipBackground != null) {
chipBackground.setImageDrawable(mChipDrawable);
@@ -332,17 +386,21 @@
mIconsContainer.setAlpha(0f);
updateIcons();
+ final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
+ windowManager.addView(mIndicatorView, getWindowLayoutParams());
+ }
+
+ private WindowManager.LayoutParams getWindowLayoutParams() {
final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
WRAP_CONTENT,
WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
- layoutParams.gravity = Gravity.TOP | (isRtl ? Gravity.LEFT : Gravity.RIGHT);
+ layoutParams.gravity = Gravity.TOP | (mIsRtl ? Gravity.LEFT : Gravity.RIGHT);
layoutParams.setTitle(LAYOUT_PARAMS_TITLE);
layoutParams.packageName = mContext.getPackageName();
- final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
- windowManager.addView(mIndicatorView, layoutParams);
+ return layoutParams;
}
private void updateIcons() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
index bedb330..4f87cad 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
@@ -213,5 +213,13 @@
mView.setKeyguardShowing()
}
+ fun refreshVisibility(shouldBeVisible: Boolean) {
+ if (shouldBeVisible) {
+ showFooter()
+ } else {
+ hideFooter()
+ }
+ }
+
private fun isTunerEnabled() = tunerService.isTunerEnabled
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 0da4814..42323e3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -84,15 +84,15 @@
public void onConfigurationChange(Configuration newConfig) {
mShouldUseSplitNotificationShade =
Utils.shouldUseSplitNotificationShade(getResources());
+ onConfigurationChanged();
if (newConfig.orientation != mLastOrientation) {
mLastOrientation = newConfig.orientation;
- onScreenRotated();
switchTileLayout(false);
}
}
};
- protected void onScreenRotated() { }
+ protected void onConfigurationChanged() { }
private final Function1<Boolean, Unit> mMediaHostVisibilityListener = (visible) -> {
if (mMediaVisibilityChangedListener != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 921ee35..92690c7d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -81,7 +81,7 @@
mMediaHost.init(MediaHierarchyManager.LOCATION_QQS);
mBrightnessController.init(mShouldUseSplitNotificationShade);
mFooterActionsController.init();
- refreshFooterVisibility();
+ mFooterActionsController.refreshVisibility(mShouldUseSplitNotificationShade);
}
@Override
@@ -109,14 +109,6 @@
return mView.isListening();
}
- private void refreshFooterVisibility() {
- if (mShouldUseSplitNotificationShade) {
- mFooterActionsController.showFooter();
- } else {
- mFooterActionsController.hideFooter();
- }
- }
-
private void setMaxTiles(int parseNumTiles) {
mView.setMaxTiles(parseNumTiles);
setTiles();
@@ -129,9 +121,9 @@
}
@Override
- protected void onScreenRotated() {
+ protected void onConfigurationChanged() {
mBrightnessController.refreshVisibility(mShouldUseSplitNotificationShade);
- refreshFooterVisibility();
+ mFooterActionsController.refreshVisibility(mShouldUseSplitNotificationShade);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index a85800b..7eedb3a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -140,6 +140,10 @@
mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon);
updateResources();
+ Configuration config = mContext.getResources().getConfiguration();
+ setDatePrivacyContainersWidth(config.orientation == Configuration.ORIENTATION_LANDSCAPE);
+ setSecurityHeaderContainerVisibility(
+ config.orientation == Configuration.ORIENTATION_LANDSCAPE);
// QS will always show the estimate, and BatteryMeterView handles the case where
// it's unavailable or charging
@@ -189,6 +193,8 @@
super.onConfigurationChanged(newConfig);
updateResources();
setDatePrivacyContainersWidth(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE);
+ setSecurityHeaderContainerVisibility(
+ newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE);
}
@Override
@@ -209,6 +215,10 @@
mPrivacyContainer.setLayoutParams(lp);
}
+ private void setSecurityHeaderContainerVisibility(boolean landscape) {
+ mSecurityHeaderView.setVisibility(landscape ? VISIBLE : GONE);
+ }
+
private void updateBatteryMode() {
if (mConfigShowBatteryEstimate && !mHasCenterCutout) {
mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TEST_MAPPING b/packages/SystemUI/src/com/android/systemui/qs/TEST_MAPPING
new file mode 100644
index 0000000..55da203
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/TEST_MAPPING
@@ -0,0 +1,21 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsAppTestCases",
+ "options": [
+ {
+ "include-filter": "android.app.cts.TileServiceTest"
+ },
+ {
+ "include-filter": "android.app.cts.BooleanTileServiceTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 396eca5..c5fa76e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -63,6 +63,7 @@
import com.android.systemui.qs.tileimpl.QSTileImpl;
import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.inject.Inject;
@@ -98,6 +99,8 @@
private final TileServiceKey mKey;
+ private final AtomicBoolean mInitialDefaultIconFetched = new AtomicBoolean(false);
+
private CustomTile(
QSHost host,
Looper backgroundLooper,
@@ -128,6 +131,12 @@
@Override
protected void handleInitialize() {
updateDefaultTileAndIcon();
+ if (mInitialDefaultIconFetched.compareAndSet(false, true)) {
+ if (mDefaultIcon == null) {
+ mQSLogger.logTileDestroyed(getTileSpec(),
+ "Custom tile default icon not available");
+ }
+ }
if (mServiceManager.isToggleableTile()) {
// Replace states with BooleanState
resetStates();
@@ -213,9 +222,18 @@
mHandler.post(this::updateDefaultTileAndIcon);
}
+ /**
+ * Custom tile is considered available if there is a default icon (obtained from PM).
+ *
+ * It will return {@code true} before initialization, so tiles are not destroyed prematurely.
+ */
@Override
public boolean isAvailable() {
- return mDefaultIcon != null;
+ if (mInitialDefaultIconFetched.get()) {
+ return mDefaultIcon != null;
+ } else {
+ return true;
+ }
}
public int getUser() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index 2e771d6..b1cd03c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -116,6 +116,9 @@
: icon.getInvisibleDrawable(mContext) : null;
int padding = icon != null ? icon.getPadding() : 0;
if (d != null) {
+ if (d.getConstantState() != null) {
+ d = d.getConstantState().newDrawable();
+ }
d.setAutoMirrored(false);
d.setLayoutDirection(getLayoutDirection());
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 4616be8..70e3a2b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -105,7 +105,7 @@
protected final ActivityStarter mActivityStarter;
private final UiEventLogger mUiEventLogger;
private final FalsingManager mFalsingManager;
- private final QSLogger mQSLogger;
+ protected final QSLogger mQSLogger;
private volatile int mReadyState;
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index 04437ea..ccc08e0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -23,6 +23,7 @@
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
+import android.os.Trace;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@@ -150,6 +151,7 @@
return;
}
+ Trace.beginSection("UserDetailView.Adapter#onClick");
UserSwitcherController.UserRecord tag =
(UserSwitcherController.UserRecord) view.getTag();
if (tag.isDisabledByAdmin) {
@@ -167,6 +169,7 @@
}
onUserListItemClicked(tag);
}
+ Trace.endSection();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 4d0cbd1..52bf2df 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -486,8 +486,7 @@
}
private void showTurnOffMobileDialog() {
- CharSequence carrierName =
- mSubscriptionManager.getDefaultDataSubscriptionInfo().getCarrierName();
+ CharSequence carrierName = getMobileNetworkTitle();
boolean isInService = mInternetDialogController.isVoiceStateInService();
if (TextUtils.isEmpty(carrierName) || !isInService) {
carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index dfe78de8..62fa3d4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -391,7 +391,8 @@
level += 1;
numLevels += 1;
}
- return getSignalStrengthIcon(mContext, level, numLevels, NO_CELL_DATA_TYPE_ICON, false);
+ return getSignalStrengthIcon(mContext, level, numLevels, NO_CELL_DATA_TYPE_ICON,
+ !isMobileDataEnabled());
}
Drawable getSignalStrengthIcon(Context context, int level, int numLevels,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index a5fc5ab..50b1186 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -94,6 +94,7 @@
import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.statusbar.policy.CallbackController;
@@ -626,18 +627,22 @@
mNavBarControllerLazy.get().getDefaultNavigationBar();
final NavigationBarView navBarView =
mNavBarControllerLazy.get().getNavigationBarView(mContext.getDisplayId());
+ final NotificationPanelViewController panelController =
+ mStatusBarOptionalLazy.get().get().getPanelController();
if (SysUiState.DEBUG) {
Log.d(TAG_OPS, "Updating sysui state flags: navBarFragment=" + navBarFragment
- + " navBarView=" + navBarView);
+ + " navBarView=" + navBarView + " panelController=" + panelController);
}
if (navBarFragment != null) {
navBarFragment.updateSystemUiStateFlags(-1);
}
if (navBarView != null) {
- navBarView.updatePanelSystemUiStateFlags();
navBarView.updateDisabledSystemUiStateFlags();
}
+ if (panelController != null) {
+ panelController.updateSystemUiStateFlags();
+ }
if (mStatusBarWinController != null) {
mStatusBarWinController.notifyStateChangedCallbacks();
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index cab2168..8def475 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -33,6 +33,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.annotation.MainThread;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
@@ -261,6 +262,7 @@
private Bitmap mScreenBitmap;
private SaveImageInBackgroundTask mSaveInBgTask;
private boolean mScreenshotTakenInPortrait;
+ private boolean mBlockAttach;
private Animator mScreenshotAnimation;
private RequestCallback mCurrentRequestCallback;
@@ -559,8 +561,8 @@
mScreenshotView.reset();
}
- mScreenshotView.updateOrientation(mWindowManager.getCurrentWindowMetrics()
- .getWindowInsets().getDisplayCutout());
+ mScreenshotView.updateOrientation(
+ mWindowManager.getCurrentWindowMetrics().getWindowInsets());
mScreenBitmap = screenshot;
@@ -594,9 +596,8 @@
// Delay scroll capture eval a bit to allow the underlying activity
// to set up in the new orientation.
mScreenshotHandler.postDelayed(this::requestScrollCapture, 150);
- mScreenshotView.updateDisplayCutoutMargins(
- mWindowManager.getCurrentWindowMetrics().getWindowInsets()
- .getDisplayCutout());
+ mScreenshotView.updateInsets(
+ mWindowManager.getCurrentWindowMetrics().getWindowInsets());
// screenshot animation calculations won't be valid anymore, so just end
if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
mScreenshotAnimation.end();
@@ -732,6 +733,7 @@
new ViewTreeObserver.OnWindowAttachListener() {
@Override
public void onWindowAttached() {
+ mBlockAttach = false;
decorView.getViewTreeObserver().removeOnWindowAttachListener(this);
action.run();
}
@@ -748,14 +750,16 @@
mWindow.setContentView(contentView);
}
+ @MainThread
private void attachWindow() {
View decorView = mWindow.getDecorView();
- if (decorView.isAttachedToWindow()) {
+ if (decorView.isAttachedToWindow() || mBlockAttach) {
return;
}
if (DEBUG_WINDOW) {
Log.d(TAG, "attachWindow");
}
+ mBlockAttach = true;
mWindowManager.addView(decorView, mWindowLayoutParams);
decorView.requestApplyInsets();
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index e5e690bcb..dfb39e3 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -118,8 +118,8 @@
private static final long SCREENSHOT_TO_CORNER_SCALE_DURATION_MS = 234;
private static final long SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS = 400;
private static final long SCREENSHOT_ACTIONS_ALPHA_DURATION_MS = 100;
- private static final long SCREENSHOT_DISMISS_Y_DURATION_MS = 350;
- private static final long SCREENSHOT_DISMISS_ALPHA_DURATION_MS = 183;
+ private static final long SCREENSHOT_DISMISS_X_DURATION_MS = 350;
+ private static final long SCREENSHOT_DISMISS_ALPHA_DURATION_MS = 350;
private static final long SCREENSHOT_DISMISS_ALPHA_OFFSET_MS = 50; // delay before starting fade
private static final float SCREENSHOT_ACTIONS_START_SCALE_X = .7f;
private static final float ROUNDED_CORNER_RADIUS = .25f;
@@ -416,21 +416,30 @@
mScreenshotPreview.setImageDrawable(createScreenDrawable(mResources, bitmap, screenInsets));
}
- void updateDisplayCutoutMargins(DisplayCutout cutout) {
+ void updateInsets(WindowInsets insets) {
int orientation = mContext.getResources().getConfiguration().orientation;
mOrientationPortrait = (orientation == ORIENTATION_PORTRAIT);
FrameLayout.LayoutParams p =
(FrameLayout.LayoutParams) mScreenshotStatic.getLayoutParams();
+ DisplayCutout cutout = insets.getDisplayCutout();
+ Insets navBarInsets = insets.getInsets(WindowInsets.Type.navigationBars());
if (cutout == null) {
- p.setMargins(0, 0, 0, 0);
+ p.setMargins(0, 0, 0, navBarInsets.bottom);
} else {
Insets waterfall = cutout.getWaterfallInsets();
if (mOrientationPortrait) {
- p.setMargins(waterfall.left, Math.max(cutout.getSafeInsetTop(), waterfall.top),
- waterfall.right, Math.max(cutout.getSafeInsetBottom(), waterfall.bottom));
+ p.setMargins(
+ waterfall.left,
+ Math.max(cutout.getSafeInsetTop(), waterfall.top),
+ waterfall.right,
+ Math.max(cutout.getSafeInsetBottom(),
+ Math.max(navBarInsets.bottom, waterfall.bottom)));
} else {
- p.setMargins(Math.max(cutout.getSafeInsetLeft(), waterfall.left), waterfall.top,
- Math.max(cutout.getSafeInsetRight(), waterfall.right), waterfall.bottom);
+ p.setMargins(
+ Math.max(cutout.getSafeInsetLeft(), waterfall.left),
+ waterfall.top,
+ Math.max(cutout.getSafeInsetRight(), waterfall.right),
+ Math.max(navBarInsets.bottom, waterfall.bottom));
}
}
mStaticLeftMargin = p.leftMargin;
@@ -438,10 +447,10 @@
mScreenshotStatic.requestLayout();
}
- void updateOrientation(DisplayCutout cutout) {
+ void updateOrientation(WindowInsets insets) {
int orientation = mContext.getResources().getConfiguration().orientation;
mOrientationPortrait = (orientation == ORIENTATION_PORTRAIT);
- updateDisplayCutoutMargins(cutout);
+ updateInsets(insets);
int screenshotFixedSize =
mContext.getResources().getDimensionPixelSize(R.dimen.global_screenshot_x_scale);
ViewGroup.LayoutParams params = mScreenshotPreview.getLayoutParams();
@@ -980,7 +989,6 @@
mScrollingScrim.setVisibility(View.GONE);
mScrollablePreview.setVisibility(View.GONE);
mScreenshotStatic.setTranslationX(0);
- mScreenshotPreview.setTranslationY(0);
mScreenshotPreview.setContentDescription(
mContext.getResources().getString(R.string.screenshot_preview_description));
mScreenshotPreview.setOnClickListener(null);
@@ -996,9 +1004,6 @@
mSmartChips.clear();
mQuickShareChip = null;
setAlpha(1);
- mDismissButton.setTranslationY(0);
- mActionsContainer.setTranslationY(0);
- mActionsContainerBackground.setTranslationY(0);
mScreenshotSelectorView.stop();
}
@@ -1026,22 +1031,19 @@
setAlpha(1 - animation.getAnimatedFraction());
});
- ValueAnimator yAnim = ValueAnimator.ofFloat(0, 1);
- yAnim.setInterpolator(mAccelerateInterpolator);
- yAnim.setDuration(SCREENSHOT_DISMISS_Y_DURATION_MS);
- float screenshotStartY = mScreenshotPreview.getTranslationY();
- float dismissStartY = mDismissButton.getTranslationY();
- yAnim.addUpdateListener(animation -> {
- float yDelta = MathUtils.lerp(0, mDismissDeltaY, animation.getAnimatedFraction());
- mScreenshotPreview.setTranslationY(screenshotStartY + yDelta);
- mScreenshotPreviewBorder.setTranslationY(screenshotStartY + yDelta);
- mDismissButton.setTranslationY(dismissStartY + yDelta);
- mActionsContainer.setTranslationY(yDelta);
- mActionsContainerBackground.setTranslationY(yDelta);
+ ValueAnimator xAnim = ValueAnimator.ofFloat(0, 1);
+ xAnim.setInterpolator(mAccelerateInterpolator);
+ xAnim.setDuration(SCREENSHOT_DISMISS_X_DURATION_MS);
+ float deltaX = mDirectionLTR
+ ? -1 * (mScreenshotPreviewBorder.getX() + mScreenshotPreviewBorder.getWidth())
+ : (mDisplayMetrics.widthPixels - mScreenshotPreviewBorder.getX());
+ xAnim.addUpdateListener(animation -> {
+ float currXDelta = MathUtils.lerp(0, deltaX, animation.getAnimatedFraction());
+ mScreenshotStatic.setTranslationX(currXDelta);
});
AnimatorSet animSet = new AnimatorSet();
- animSet.play(yAnim).with(alphaAnim);
+ animSet.play(xAnim).with(alphaAnim);
return animSet;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 90158c32..aba1a24 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -291,8 +291,8 @@
default void showAuthenticationDialog(PromptInfo promptInfo,
IBiometricSysuiReceiver receiver,
int[] sensorIds, boolean credentialAllowed,
- boolean requireConfirmation, int userId, String opPackageName,
- long operationId, @BiometricMultiSensorMode int multiSensorConfig) {
+ boolean requireConfirmation, int userId, long operationId, String opPackageName,
+ long requestId, @BiometricMultiSensorMode int multiSensorConfig) {
}
/** @see IStatusBar#onBiometricAuthenticated() */
@@ -845,7 +845,7 @@
@Override
public void showAuthenticationDialog(PromptInfo promptInfo, IBiometricSysuiReceiver receiver,
int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation,
- int userId, String opPackageName, long operationId,
+ int userId, long operationId, String opPackageName, long requestId,
@BiometricMultiSensorMode int multiSensorConfig) {
synchronized (mLock) {
SomeArgs args = SomeArgs.obtain();
@@ -857,6 +857,7 @@
args.argi1 = userId;
args.arg6 = opPackageName;
args.arg7 = operationId;
+ args.arg8 = requestId;
args.argi2 = multiSensorConfig;
mHandler.obtainMessage(MSG_BIOMETRIC_SHOW, args)
.sendToTarget();
@@ -1315,8 +1316,9 @@
(boolean) someArgs.arg4 /* credentialAllowed */,
(boolean) someArgs.arg5 /* requireConfirmation */,
someArgs.argi1 /* userId */,
- (String) someArgs.arg6 /* opPackageName */,
(long) someArgs.arg7 /* operationId */,
+ (String) someArgs.arg6 /* opPackageName */,
+ (long) someArgs.arg8 /* requestId */,
someArgs.argi2 /* multiSensorConfig */);
}
someArgs.recycle();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 1431c9e..eb5f82c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -811,13 +811,8 @@
mStatusBarKeyguardViewManager.showBouncerMessage(message, mInitialTextColorState);
}
} else if (mKeyguardUpdateMonitor.isScreenOn()) {
- if (mKeyguardUpdateMonitor.isUdfpsAvailable()) {
- showTransientIndication(mContext.getString(R.string.keyguard_unlock_press),
- false /* isError */, true /* hideOnScreenOff */);
- } else {
- showTransientIndication(mContext.getString(R.string.keyguard_unlock),
- false /* isError */, true /* hideOnScreenOff */);
- }
+ showTransientIndication(mContext.getString(R.string.keyguard_unlock),
+ false /* isError */, true /* hideOnScreenOff */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index b833427..e845804 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -181,7 +181,8 @@
if (shouldApplyShadeBlur()) shadeExpansion else 0f, false))
var combinedBlur = (expansionRadius * INTERACTION_BLUR_FRACTION +
animationRadius * ANIMATION_BLUR_FRACTION)
- val qsExpandedRatio = qsPanelExpansion * shadeExpansion
+ val qsExpandedRatio = Interpolators.getNotificationScrimAlpha(qsPanelExpansion,
+ false /* notification */) * shadeExpansion
combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(qsExpandedRatio))
combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(transitionToFullShadeProgress))
var shadeRadius = max(combinedBlur, wakeAndUnlockBlurRadius)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
index 6ea79af..4adf2bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar;
+import android.graphics.Region;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
@@ -158,6 +159,9 @@
/** Sets the state of whether the notification shade is touchable or not. */
default void setNotTouchable(boolean notTouchable) {}
+ /** Sets the region where touch is excluded from the parent window. */
+ default void setTouchExclusionRegion(Region region) {}
+
/** Sets a {@link OtherwisedCollapsedListener}. */
default void setStateListener(OtherwisedCollapsedListener listener) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java b/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java
deleted file mode 100644
index e4ae560..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-
-import com.android.systemui.R;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
-import com.android.systemui.statusbar.phone.StatusBarWindowView;
-import com.android.systemui.util.InjectionInflationController;
-
-import javax.inject.Inject;
-
-/**
- * Creates a single instance of super_status_bar and super_notification_shade that can be shared
- * across various system ui objects.
- */
-@SysUISingleton
-public class SuperStatusBarViewFactory {
-
- private final Context mContext;
- private final InjectionInflationController mInjectionInflationController;
- private final NotificationShelfComponent.Builder mNotificationShelfComponentBuilder;
-
- private NotificationShadeWindowView mNotificationShadeWindowView;
- private StatusBarWindowView mStatusBarWindowView;
- private NotificationShelfController mNotificationShelfController;
-
- @Inject
- public SuperStatusBarViewFactory(Context context,
- InjectionInflationController injectionInflationController,
- NotificationShelfComponent.Builder notificationShelfComponentBuilder) {
- mContext = context;
- mInjectionInflationController = injectionInflationController;
- mNotificationShelfComponentBuilder = notificationShelfComponentBuilder;
- }
-
- /**
- * Gets the inflated {@link NotificationShadeWindowView} from
- * {@link R.layout#super_notification_shade}.
- * Returns a cached instance, if it has already been inflated.
- */
- public NotificationShadeWindowView getNotificationShadeWindowView() {
- if (mNotificationShadeWindowView != null) {
- return mNotificationShadeWindowView;
- }
-
- mNotificationShadeWindowView = (NotificationShadeWindowView)
- mInjectionInflationController.injectable(
- LayoutInflater.from(mContext)).inflate(R.layout.super_notification_shade,
- /* root= */ null);
- if (mNotificationShadeWindowView == null) {
- throw new IllegalStateException(
- "R.layout.super_notification_shade could not be properly inflated");
- }
-
- return mNotificationShadeWindowView;
- }
-
- /**
- * Gets the inflated {@link StatusBarWindowView} from {@link R.layout#super_status_bar}.
- * Returns a cached instance, if it has already been inflated.
- */
- public StatusBarWindowView getStatusBarWindowView() {
- if (mStatusBarWindowView != null) {
- return mStatusBarWindowView;
- }
-
- mStatusBarWindowView =
- (StatusBarWindowView) mInjectionInflationController.injectable(
- LayoutInflater.from(mContext)).inflate(R.layout.super_status_bar,
- /* root= */ null);
- if (mStatusBarWindowView == null) {
- throw new IllegalStateException(
- "R.layout.super_status_bar could not be properly inflated");
- }
- return mStatusBarWindowView;
- }
-
- /**
- * Gets the inflated {@link NotificationShelf} from
- * {@link R.layout#status_bar_notification_shelf}.
- * Returns a cached instance, if it has already been inflated.
- *
- * @param container the expected container to hold the {@link NotificationShelf}. The view
- * isn't immediately attached, but the layout params of this view is used
- * during inflation.
- */
- public NotificationShelfController getNotificationShelfController(ViewGroup container) {
- if (mNotificationShelfController != null) {
- return mNotificationShelfController;
- }
-
- NotificationShelf view = (NotificationShelf) LayoutInflater.from(mContext)
- .inflate(R.layout.status_bar_notification_shelf, container, /* attachToRoot= */
- false);
-
- if (view == null) {
- throw new IllegalStateException(
- "R.layout.status_bar_notification_shelf could not be properly inflated");
- }
-
- NotificationShelfComponent component = mNotificationShelfComponentBuilder
- .notificationShelf(view)
- .build();
- mNotificationShelfController = component.getNotificationShelfController();
- mNotificationShelfController.init();
-
- return mNotificationShelfController;
- }
-
- public NotificationPanelView getNotificationPanelView() {
- NotificationShadeWindowView notificationShadeWindowView = getNotificationShadeWindowView();
- if (notificationShadeWindowView == null) {
- return null;
- }
-
- return mNotificationShadeWindowView.findViewById(R.id.notification_panel);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
index 7291b5a..589446f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
@@ -24,13 +24,10 @@
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.FrameLayout
-
import com.android.systemui.R
-import com.android.systemui.statusbar.SuperStatusBarViewFactory
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher
import com.android.systemui.statusbar.phone.StatusBarWindowController
import com.android.systemui.statusbar.phone.StatusBarWindowView
-
import javax.inject.Inject
/**
@@ -38,7 +35,7 @@
*/
class SystemEventChipAnimationController @Inject constructor(
private val context: Context,
- private val statusBarViewFactory: SuperStatusBarViewFactory,
+ private val statusBarWindowView: StatusBarWindowView,
private val statusBarWindowController: StatusBarWindowController,
private val locationPublisher: StatusBarLocationPublisher
) : SystemStatusChipAnimationCallback {
@@ -51,7 +48,6 @@
private lateinit var animationWindowView: FrameLayout
private lateinit var animationDotView: View
- private lateinit var statusBarWindowView: StatusBarWindowView
private var currentAnimatedView: View? = null
// TODO: move to dagger
@@ -125,7 +121,6 @@
private fun init() {
initialized = true
- statusBarWindowView = statusBarViewFactory.statusBarWindowView
animationWindowView = LayoutInflater.from(context)
.inflate(R.layout.system_event_animation_window, null) as FrameLayout
animationDotView = animationWindowView.findViewById(R.id.dot_view)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt
index 727ce20..289dacb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt
@@ -78,7 +78,7 @@
}
private fun treeSpecToStrHelper(tree: NodeSpec, sb: StringBuilder, indent: String) {
- sb.append("${indent}ns{${tree.controller.nodeLabel}")
+ sb.append("${indent}{${tree.controller.nodeLabel}}\n")
if (tree.children.isNotEmpty()) {
val childIndent = "$indent "
for (child in tree.children) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
new file mode 100644
index 0000000..9b8ac72
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.render
+
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
+
+/**
+ * Converts a notif list (the output of the ShadeListBuilder) into a NodeSpec, an abstract
+ * representation of which views should be present in the shade. This spec will later be consumed
+ * by the ViewDiffer, which will add and remove views until the shade matches the spec. Up until
+ * this point, the pipeline has dealt with pure data representations of notifications (in the
+ * form of NotificationEntries). In this step, NotificationEntries finally become associated with
+ * the views that will represent them. In addition, we add in any non-notification views that also
+ * need to present in the shade, notably the section headers.
+ */
+class NodeSpecBuilder(
+ private val viewBarn: NotifViewBarn
+) {
+ fun buildNodeSpec(
+ rootController: NodeController,
+ notifList: List<ListEntry>
+ ): NodeSpec {
+ val root = NodeSpecImpl(null, rootController)
+ var currentSection: NotifSection? = null
+ val prevSections = mutableSetOf<NotifSection?>()
+
+ for (entry in notifList) {
+ val section = entry.section!!
+
+ if (prevSections.contains(section)) {
+ throw java.lang.RuntimeException("Section ${section.label} has been duplicated")
+ }
+
+ // If this notif begins a new section, first add the section's header view
+ if (section != currentSection) {
+ section.headerController?.let { headerController ->
+ root.children.add(NodeSpecImpl(root, headerController))
+ }
+ prevSections.add(currentSection)
+ currentSection = section
+ }
+
+ // Finally, add the actual notif node!
+ root.children.add(buildNotifNode(root, entry))
+ }
+
+ return root
+ }
+
+ private fun buildNotifNode(parent: NodeSpec, entry: ListEntry): NodeSpec = when (entry) {
+ is NotificationEntry -> NodeSpecImpl(parent, viewBarn.requireView(entry))
+ is GroupEntry -> NodeSpecImpl(parent, viewBarn.requireView(checkNotNull(entry.summary)))
+ .apply { entry.children.forEach { children.add(buildNotifNode(this, it)) } }
+ else -> throw RuntimeException("Unexpected entry: $entry")
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt
index 79bc3d7..c79f59b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt
@@ -19,18 +19,16 @@
import android.view.textclassifier.Log
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.collection.ListEntry
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController
import javax.inject.Inject
/**
- * The ViewBarn is just a map from [ListEntry] to an instance of an
- * [ExpandableNotificationRowController].
+ * The ViewBarn is just a map from [ListEntry] to an instance of a [NodeController].
*/
@SysUISingleton
class NotifViewBarn @Inject constructor() {
- private val rowMap = mutableMapOf<String, ExpandableNotificationRowController>()
+ private val rowMap = mutableMapOf<String, NodeController>()
- fun requireView(forEntry: ListEntry): ExpandableNotificationRowController {
+ fun requireView(forEntry: ListEntry): NodeController {
if (DEBUG) {
Log.d(TAG, "requireView: $forEntry.key")
}
@@ -42,7 +40,7 @@
return li
}
- fun registerViewForEntry(entry: ListEntry, controller: ExpandableNotificationRowController) {
+ fun registerViewForEntry(entry: ListEntry, controller: NodeController) {
if (DEBUG) {
Log.d(TAG, "registerViewForEntry: $entry.key")
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index ef1d75e9..a2c7aa5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -18,9 +18,7 @@
import android.content.Context
import android.view.View
-import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.NotificationIconAreaController
@@ -34,45 +32,21 @@
context: Context,
listContainer: NotificationListContainer,
logger: ShadeViewDifferLogger,
- private val viewBarn: NotifViewBarn,
+ viewBarn: NotifViewBarn,
private val notificationIconAreaController: NotificationIconAreaController
) {
// We pass a shim view here because the listContainer may not actually have a view associated
// with it and the differ never actually cares about the root node's view.
private val rootController = RootNodeController(listContainer, View(context))
+ private val specBuilder = NodeSpecBuilder(viewBarn)
private val viewDiffer = ShadeViewDiffer(rootController, logger)
fun attach(listBuilder: ShadeListBuilder) =
listBuilder.setOnRenderListListener(::onNewNotifTree)
- private fun onNewNotifTree(tree: List<ListEntry>) = viewDiffer.applySpec(buildTree(tree))
-
- private fun buildTree(notifList: List<ListEntry>): NodeSpec {
- val root = NodeSpecImpl(null, rootController).apply {
- // Insert first section header, if present
- notifList.firstOrNull()?.section?.headerController?.let {
- children.add(NodeSpecImpl(this, it))
- }
- notifList.firstOrNull()?.let {
- children.add(buildNotifNode(it, this))
- }
- notifList.asSequence().zipWithNext().forEach { (prev, entry) ->
- // Insert new header if the section has changed between two entries
- entry.section.takeIf { it != prev.section }?.headerController?.let {
- children.add(NodeSpecImpl(this, it))
- }
- children.add(buildNotifNode(entry, this))
- }
- }
+ private fun onNewNotifTree(notifList: List<ListEntry>) {
+ viewDiffer.applySpec(specBuilder.buildNodeSpec(rootController, notifList))
notificationIconAreaController.updateNotificationIcons(notifList)
- return root
- }
-
- private fun buildNotifNode(entry: ListEntry, parent: NodeSpec): NodeSpec = when (entry) {
- is NotificationEntry -> NodeSpecImpl(parent, viewBarn.requireView(entry))
- is GroupEntry -> NodeSpecImpl(parent, viewBarn.requireView(checkNotNull(entry.summary)))
- .apply { entry.children.forEach { children.add(buildNotifNode(it, this)) } }
- else -> throw RuntimeException("Unexpected entry: $entry")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
index 07618da..12ae3f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
@@ -17,6 +17,7 @@
import android.content.Context
import android.content.pm.ActivityInfo
import android.content.res.Configuration
+import android.graphics.Rect
import android.os.LocaleList
import android.view.View.LAYOUT_DIRECTION_RTL
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -29,6 +30,7 @@
private val lastConfig = Configuration()
private var density: Int = 0
private var smallestScreenWidth: Int = 0
+ private var maxBounds: Rect? = null
private var fontScale: Float = 0.toFloat()
private val inCarMode: Boolean
private var uiMode: Int = 0
@@ -85,6 +87,14 @@
}
}
+ val maxBounds = newConfig.windowConfiguration.maxBounds
+ if (maxBounds != this.maxBounds) {
+ this.maxBounds = maxBounds
+ listeners.filterForEach({ this.listeners.contains(it) }) {
+ it.onMaxBoundsChanged()
+ }
+ }
+
val localeList = newConfig.locales
if (localeList != this.localeList) {
this.localeList = localeList
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index f77c052..d348954 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -32,12 +32,6 @@
* Utility class to calculate the clock position and top padding of notifications on Keyguard.
*/
public class KeyguardClockPositionAlgorithm {
- /**
- * How much the clock height influences the shade position.
- * 0 means nothing, 1 means move the shade up by the height of the clock
- * 0.5f means move the shade up by half of the size of the clock.
- */
- private static float CLOCK_HEIGHT_WEIGHT = 0.7f;
/**
* Margin between the bottom of the status view and the notification shade.
@@ -45,11 +39,6 @@
private int mStatusViewBottomMargin;
/**
- * Height of the parent view - display size in px.
- */
- private int mHeight;
-
- /**
* Height of {@link KeyguardStatusView}.
*/
private int mKeyguardStatusHeight;
@@ -68,21 +57,6 @@
private int mUserSwitchPreferredY;
/**
- * Whether or not there is a custom clock face on keyguard.
- */
- private boolean mHasCustomClock;
-
- /**
- * Whether or not the NSSL contains any visible notifications.
- */
- private boolean mHasVisibleNotifs;
-
- /**
- * Height of notification stack: Sum of height of each notification.
- */
- private int mNotificationStackHeight;
-
- /**
* Minimum top margin to avoid overlap with status bar, lock icon, or multi-user switcher
* avatar.
*/
@@ -94,12 +68,6 @@
private int mCutoutTopInset = 0;
/**
- * Maximum bottom padding to avoid overlap with {@link KeyguardBottomAreaView} or
- * the ambient indication.
- */
- private int mMaxShadeBottom;
-
- /**
* Recommended distance from the status bar.
*/
private int mContainerTopPadding;
@@ -115,14 +83,9 @@
private int mBurnInPreventionOffsetX;
/**
- * Burn-in prevention y translation.
+ * Burn-in prevention y translation for clock layouts.
*/
- private int mBurnInPreventionOffsetY;
-
- /**
- * Burn-in prevention y translation for large clock layouts.
- */
- private int mBurnInPreventionOffsetYLargeClock;
+ private int mBurnInPreventionOffsetYClock;
/**
* Doze/AOD transition amount.
@@ -160,34 +123,26 @@
res.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) / 2;
mBurnInPreventionOffsetX = res.getDimensionPixelSize(
R.dimen.burn_in_prevention_offset_x);
- mBurnInPreventionOffsetY = res.getDimensionPixelSize(
- R.dimen.burn_in_prevention_offset_y);
- mBurnInPreventionOffsetYLargeClock = res.getDimensionPixelSize(
- R.dimen.burn_in_prevention_offset_y_large_clock);
+ mBurnInPreventionOffsetYClock = res.getDimensionPixelSize(
+ R.dimen.burn_in_prevention_offset_y_clock);
}
/**
* Sets up algorithm values.
*/
- public void setup(int keyguardStatusBarHeaderHeight, int maxShadeBottom,
- int notificationStackHeight, float panelExpansion, int parentHeight,
+ public void setup(int keyguardStatusBarHeaderHeight, float panelExpansion,
int keyguardStatusHeight, int userSwitchHeight, int userSwitchPreferredY,
- boolean hasCustomClock, boolean hasVisibleNotifs, float dark,
- float overStrechAmount, boolean bypassEnabled, int unlockedStackScrollerPadding,
- float qsExpansion, int cutoutTopInset, boolean isSplitShade) {
+ float dark, float overStretchAmount, boolean bypassEnabled,
+ int unlockedStackScrollerPadding, float qsExpansion, int cutoutTopInset,
+ boolean isSplitShade) {
mMinTopMargin = keyguardStatusBarHeaderHeight + Math.max(mContainerTopPadding,
userSwitchHeight);
- mMaxShadeBottom = maxShadeBottom;
- mNotificationStackHeight = notificationStackHeight;
mPanelExpansion = panelExpansion;
- mHeight = parentHeight;
mKeyguardStatusHeight = keyguardStatusHeight + mStatusViewBottomMargin;
mUserSwitchHeight = userSwitchHeight;
mUserSwitchPreferredY = userSwitchPreferredY;
- mHasCustomClock = hasCustomClock;
- mHasVisibleNotifs = hasVisibleNotifs;
mDarkAmount = dark;
- mOverStretchAmount = overStrechAmount;
+ mOverStretchAmount = overStretchAmount;
mBypassEnabled = bypassEnabled;
mUnlockedStackScrollerPadding = unlockedStackScrollerPadding;
mQsExpansion = qsExpansion;
@@ -244,8 +199,8 @@
// This will keep the clock at the top but out of the cutout area
float shift = 0;
- if (clockY - mBurnInPreventionOffsetYLargeClock < mCutoutTopInset) {
- shift = mCutoutTopInset - (clockY - mBurnInPreventionOffsetYLargeClock);
+ if (clockY - mBurnInPreventionOffsetYClock < mCutoutTopInset) {
+ shift = mCutoutTopInset - (clockY - mBurnInPreventionOffsetYClock);
}
float clockYDark = clockY + burnInPreventionOffsetY() + shift;
@@ -281,7 +236,7 @@
}
private float burnInPreventionOffsetY() {
- int offset = mBurnInPreventionOffsetYLargeClock;
+ int offset = mBurnInPreventionOffsetYClock;
return getBurnInOffset(offset * 2, false /* xAxis */) - offset;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 3e604ec..893aa6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -16,21 +16,38 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN;
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.res.Resources;
+import android.hardware.biometrics.BiometricSourceType;
+import android.util.MathUtils;
+import android.view.View;
import androidx.annotation.NonNull;
import com.android.keyguard.CarrierTextController;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.R;
+import com.android.systemui.animation.Interpolators;
import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.PropertyAnimator;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.util.ViewController;
@@ -44,6 +61,21 @@
/** View Controller for {@link com.android.systemui.statusbar.phone.KeyguardStatusBarView}. */
public class KeyguardStatusBarViewController extends ViewController<KeyguardStatusBarView> {
+ private static final AnimationProperties KEYGUARD_HUN_PROPERTIES =
+ new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+
+ private float mKeyguardHeadsUpShowingAmount = 0.0f;
+ private final AnimatableProperty mHeadsUpShowingAmountAnimation = AnimatableProperty.from(
+ "KEYGUARD_HEADS_UP_SHOWING_AMOUNT",
+ (view, aFloat) -> {
+ mKeyguardHeadsUpShowingAmount = aFloat;
+ updateViewState();
+ },
+ view -> mKeyguardHeadsUpShowingAmount,
+ R.id.keyguard_hun_animator_tag,
+ R.id.keyguard_hun_animator_end_tag,
+ R.id.keyguard_hun_animator_start_tag);
+
private final CarrierTextController mCarrierTextController;
private final ConfigurationController mConfigurationController;
private final SystemStatusAnimationScheduler mAnimationScheduler;
@@ -52,6 +84,13 @@
private final StatusBarIconController mStatusBarIconController;
private final StatusBarIconController.TintedIconManager.Factory mTintedIconManagerFactory;
private final BatteryMeterViewController mBatteryMeterViewController;
+ private final NotificationPanelViewController.NotificationPanelViewStateProvider
+ mNotificationPanelViewStateProvider;
+ private final KeyguardStateController mKeyguardStateController;
+ private final KeyguardBypassController mKeyguardBypassController;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final BiometricUnlockController mBiometricUnlockController;
+ private final SysuiStatusBarStateController mStatusBarStateController;
private final ConfigurationController.ConfigurationListener mConfigurationListener =
new ConfigurationController.ConfigurationListener() {
@@ -103,11 +142,80 @@
private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener =
(name, picture, userAccount) -> mView.onUserInfoChanged(picture);
+ private final ValueAnimator.AnimatorUpdateListener mAnimatorUpdateListener =
+ animation -> {
+ mKeyguardStatusBarAnimateAlpha = (float) animation.getAnimatedValue();
+ updateViewState();
+ };
+
+ private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onBiometricAuthenticated(
+ int userId,
+ BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
+ if (mFirstBypassAttempt
+ && mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ isStrongBiometric)) {
+ mDelayShowingKeyguardStatusBar = true;
+ }
+ }
+
+ @Override
+ public void onBiometricRunningStateChanged(
+ boolean running,
+ BiometricSourceType biometricSourceType) {
+ boolean keyguardOrShadeLocked =
+ mStatusBarState == KEYGUARD
+ || mStatusBarState == StatusBarState.SHADE_LOCKED;
+ if (!running
+ && mFirstBypassAttempt
+ && keyguardOrShadeLocked
+ && !mDozing
+ && !mDelayShowingKeyguardStatusBar
+ && !mBiometricUnlockController.isBiometricUnlock()) {
+ mFirstBypassAttempt = false;
+ animateKeyguardStatusBarIn();
+ }
+ }
+
+ @Override
+ public void onFinishedGoingToSleep(int why) {
+ mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
+ mDelayShowingKeyguardStatusBar = false;
+ }
+ };
+
+ private final StatusBarStateController.StateListener mStatusBarStateListener =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onStateChanged(int newState) {
+ mStatusBarState = newState;
+ }
+ };
+
private final List<String> mBlockedIcons;
+ private final int mNotificationsHeaderCollideDistance;
private boolean mBatteryListening;
private StatusBarIconController.TintedIconManager mTintedIconManager;
+ private float mKeyguardStatusBarAnimateAlpha = 1f;
+ /**
+ * If face auth with bypass is running for the first time after you turn on the screen.
+ * (From aod or screen off)
+ */
+ private boolean mFirstBypassAttempt;
+ /**
+ * If auth happens successfully during {@code mFirstBypassAttempt}, and we should wait until
+ * the keyguard is dismissed to show the status bar.
+ */
+ private boolean mDelayShowingKeyguardStatusBar;
+ private int mStatusBarState;
+ private boolean mDozing;
+ private boolean mShowingKeyguardHeadsUp;
+
@Inject
public KeyguardStatusBarViewController(
KeyguardStatusBarView view,
@@ -118,7 +226,14 @@
UserInfoController userInfoController,
StatusBarIconController statusBarIconController,
StatusBarIconController.TintedIconManager.Factory tintedIconManagerFactory,
- BatteryMeterViewController batteryMeterViewController) {
+ BatteryMeterViewController batteryMeterViewController,
+ NotificationPanelViewController.NotificationPanelViewStateProvider
+ notificationPanelViewStateProvider,
+ KeyguardStateController keyguardStateController,
+ KeyguardBypassController bypassController,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ BiometricUnlockController biometricUnlockController,
+ SysuiStatusBarStateController statusBarStateController) {
super(view);
mCarrierTextController = carrierTextController;
mConfigurationController = configurationController;
@@ -128,12 +243,33 @@
mStatusBarIconController = statusBarIconController;
mTintedIconManagerFactory = tintedIconManagerFactory;
mBatteryMeterViewController = batteryMeterViewController;
+ mNotificationPanelViewStateProvider = notificationPanelViewStateProvider;
+ mKeyguardStateController = keyguardStateController;
+ mKeyguardBypassController = bypassController;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mBiometricUnlockController = biometricUnlockController;
+ mStatusBarStateController = statusBarStateController;
+
+ mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
+ mKeyguardStateController.addCallback(
+ new KeyguardStateController.Callback() {
+ @Override
+ public void onKeyguardFadingAwayChanged() {
+ if (!mKeyguardStateController.isKeyguardFadingAway()) {
+ mFirstBypassAttempt = false;
+ mDelayShowingKeyguardStatusBar = false;
+ }
+ }
+ }
+ );
Resources r = getResources();
mBlockedIcons = Collections.unmodifiableList(Arrays.asList(
r.getString(com.android.internal.R.string.status_bar_volume),
r.getString(com.android.internal.R.string.status_bar_alarm_clock),
r.getString(com.android.internal.R.string.status_bar_call_strength)));
+ mNotificationsHeaderCollideDistance = r.getDimensionPixelSize(
+ R.dimen.header_notifications_collide_distance);
}
@Override
@@ -148,6 +284,8 @@
mConfigurationController.addCallback(mConfigurationListener);
mAnimationScheduler.addCallback(mAnimationCallback);
mUserInfoController.addCallback(mOnUserInfoChangedListener);
+ mStatusBarStateController.addCallback(mStatusBarStateListener);
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
if (mTintedIconManager == null) {
mTintedIconManager =
mTintedIconManagerFactory.create(mView.findViewById(R.id.statusIcons));
@@ -162,6 +300,8 @@
mConfigurationController.removeCallback(mConfigurationListener);
mAnimationScheduler.removeCallback(mAnimationCallback);
mUserInfoController.removeCallback(mOnUserInfoChangedListener);
+ mStatusBarStateController.removeCallback(mStatusBarStateListener);
+ mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
if (mTintedIconManager != null) {
mStatusBarIconController.removeIconGroup(mTintedIconManager);
}
@@ -205,6 +345,65 @@
mView.setTopClipping(notificationPanelTop - mView.getTop());
}
+ /** Sets the dozing state. */
+ public void setDozing(boolean dozing) {
+ mDozing = dozing;
+ }
+
+ /** Animate the keyguard status bar in. */
+ public void animateKeyguardStatusBarIn() {
+ mView.setVisibility(View.VISIBLE);
+ mView.setAlpha(0f);
+ ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
+ anim.addUpdateListener(mAnimatorUpdateListener);
+ anim.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ anim.start();
+ }
+
+ /** Animate the keyguard status bar out. */
+ public void animateKeyguardStatusBarOut(long startDelay, long duration) {
+ ValueAnimator anim = ValueAnimator.ofFloat(mView.getAlpha(), 0f);
+ anim.addUpdateListener(mAnimatorUpdateListener);
+ anim.setStartDelay(startDelay);
+ anim.setDuration(duration);
+ anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mView.setVisibility(View.INVISIBLE);
+ mView.setAlpha(1f);
+ mKeyguardStatusBarAnimateAlpha = 1f;
+ }
+ });
+ anim.start();
+ }
+
+ /**
+ * Updates the {@link KeyguardStatusBarView} state based on what the
+ * {@link NotificationPanelViewController.NotificationPanelViewStateProvider} and other
+ * controllers provide.
+ */
+ public void updateViewState() {
+ if (!isKeyguardShowing()) {
+ return;
+ }
+
+ float alphaQsExpansion = 1 - Math.min(
+ 1, mNotificationPanelViewStateProvider.getQsExpansionFraction() * 2);
+ float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
+ * mKeyguardStatusBarAnimateAlpha
+ * (1.0f - mKeyguardHeadsUpShowingAmount);
+
+ boolean hideForBypass =
+ mFirstBypassAttempt && mKeyguardUpdateMonitor.shouldListenForFace()
+ || mDelayShowingKeyguardStatusBar;
+ int newVisibility = newAlpha != 0f && !mDozing && !hideForBypass
+ ? View.VISIBLE : View.INVISIBLE;
+
+ updateViewState(newAlpha, newVisibility);
+ }
+
/**
* Updates the {@link KeyguardStatusBarView} state based on the provided values.
*/
@@ -213,10 +412,63 @@
mView.setVisibility(visibility);
}
+ /**
+ * @return the alpha to be used to fade out the contents on Keyguard (status bar, bottom area)
+ * during swiping up.
+ */
+ private float getKeyguardContentsAlpha() {
+ float alpha;
+ if (isKeyguardShowing()) {
+ // When on Keyguard, we hide the header as soon as we expanded close enough to the
+ // header
+ alpha = mNotificationPanelViewStateProvider.getPanelViewExpandedHeight()
+ / (mView.getHeight() + mNotificationsHeaderCollideDistance);
+ } else {
+ // In SHADE_LOCKED, the top card is already really close to the header. Hide it as
+ // soon as we start translating the stack.
+ alpha = mNotificationPanelViewStateProvider.getPanelViewExpandedHeight()
+ / mView.getHeight();
+ }
+ alpha = MathUtils.saturate(alpha);
+ alpha = (float) Math.pow(alpha, 0.75);
+ return alpha;
+ }
+
+ /**
+ * Update {@link KeyguardStatusBarView}'s visibility based on whether keyguard is showing and
+ * whether heads up is visible.
+ */
+ public void updateForHeadsUp() {
+ updateForHeadsUp(true);
+ }
+
+ void updateForHeadsUp(boolean animate) {
+ boolean showingKeyguardHeadsUp =
+ isKeyguardShowing() && mNotificationPanelViewStateProvider.shouldHeadsUpBeVisible();
+ if (mShowingKeyguardHeadsUp != showingKeyguardHeadsUp) {
+ mShowingKeyguardHeadsUp = showingKeyguardHeadsUp;
+ if (isKeyguardShowing()) {
+ PropertyAnimator.setProperty(
+ mView,
+ mHeadsUpShowingAmountAnimation,
+ showingKeyguardHeadsUp ? 1.0f : 0.0f,
+ KEYGUARD_HUN_PROPERTIES,
+ animate);
+ } else {
+ PropertyAnimator.applyImmediately(mView, mHeadsUpShowingAmountAnimation, 0.0f);
+ }
+ }
+ }
+
+ private boolean isKeyguardShowing() {
+ return mStatusBarState == KEYGUARD;
+ }
+
/** */
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("KeyguardStatusBarView:");
pw.println(" mBatteryListening: " + mBatteryListening);
mView.dump(fd, pw, args);
}
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index b5eb90e..d241d4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -28,6 +28,8 @@
import static com.android.keyguard.KeyguardClockSwitch.SMALL;
import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
@@ -55,7 +57,6 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
-import android.hardware.biometrics.BiometricSourceType;
import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
@@ -92,13 +93,13 @@
import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardStatusViewController;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.LockIconViewController;
import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent;
import com.android.keyguard.dagger.KeyguardStatusBarViewComponent;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
import com.android.systemui.DejankUtils;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.Interpolators;
@@ -107,6 +108,7 @@
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.communal.CommunalHostView;
import com.android.systemui.communal.CommunalHostViewController;
+import com.android.systemui.communal.CommunalHostViewPositionAlgorithm;
import com.android.systemui.communal.CommunalSource;
import com.android.systemui.communal.CommunalSourceMonitor;
import com.android.systemui.communal.CommunalStateController;
@@ -115,13 +117,16 @@
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeLog;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
import com.android.systemui.fragments.FragmentService;
+import com.android.systemui.idle.IdleHostView;
import com.android.systemui.idle.IdleHostViewController;
import com.android.systemui.idle.dagger.IdleViewComponent;
import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
+import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.FalsingManager.FalsingTapListener;
@@ -188,7 +193,6 @@
import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
-import java.util.function.Function;
import javax.inject.Inject;
import javax.inject.Provider;
@@ -242,7 +246,6 @@
@VisibleForTesting final StatusBarStateListener mStatusBarStateListener =
new StatusBarStateListener();
- private final BiometricUnlockController mBiometricUnlockController;
private final NotificationPanelView mView;
private final VibratorHelper mVibratorHelper;
private final MetricsLogger mMetricsLogger;
@@ -270,54 +273,8 @@
private static final Rect M_DUMMY_DIRTY_RECT = new Rect(0, 0, 1, 1);
private static final Rect EMPTY_RECT = new Rect();
- private final AnimatableProperty KEYGUARD_HEADS_UP_SHOWING_AMOUNT = AnimatableProperty.from(
- "KEYGUARD_HEADS_UP_SHOWING_AMOUNT",
- (notificationPanelView, aFloat) -> setKeyguardHeadsUpShowingAmount(aFloat),
- (Function<NotificationPanelView, Float>) notificationPanelView ->
- getKeyguardHeadsUpShowingAmount(),
- R.id.keyguard_hun_animator_tag, R.id.keyguard_hun_animator_end_tag,
- R.id.keyguard_hun_animator_start_tag);
- private static final AnimationProperties
- KEYGUARD_HUN_PROPERTIES =
- new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- @VisibleForTesting
- final KeyguardUpdateMonitorCallback
- mKeyguardUpdateCallback =
- new KeyguardUpdateMonitorCallback() {
-
- @Override
- public void onBiometricAuthenticated(int userId,
- BiometricSourceType biometricSourceType,
- boolean isStrongBiometric) {
- if (mFirstBypassAttempt
- && mUpdateMonitor.isUnlockingWithBiometricAllowed(isStrongBiometric)) {
- mDelayShowingKeyguardStatusBar = true;
- }
- }
-
- @Override
- public void onBiometricRunningStateChanged(boolean running,
- BiometricSourceType biometricSourceType) {
- boolean
- keyguardOrShadeLocked =
- mBarState == KEYGUARD
- || mBarState == StatusBarState.SHADE_LOCKED;
- if (!running && mFirstBypassAttempt && keyguardOrShadeLocked && !mDozing
- && !mDelayShowingKeyguardStatusBar
- && !mBiometricUnlockController.isBiometricUnlock()) {
- mFirstBypassAttempt = false;
- animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- }
- }
-
- @Override
- public void onFinishedGoingToSleep(int why) {
- mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
- mDelayShowingKeyguardStatusBar = false;
- }
- };
-
private final LayoutInflater mLayoutInflater;
+ private final FeatureFlags mFeatureFlags;
private final PowerManager mPowerManager;
private final AccessibilityManager mAccessibilityManager;
private final NotificationWakeUpCoordinator mWakeUpCoordinator;
@@ -415,7 +372,6 @@
private FlingAnimationUtils mFlingAnimationUtils;
private int mStatusBarMinHeight;
private int mStatusBarHeaderHeightKeyguard;
- private int mNotificationsHeaderCollideDistance;
private float mOverStretchAmount;
private float mDownX;
private float mDownY;
@@ -429,6 +385,12 @@
private final KeyguardClockPositionAlgorithm.Result
mClockPositionResult =
new KeyguardClockPositionAlgorithm.Result();
+ private final CommunalHostViewPositionAlgorithm
+ mCommunalPositionAlgorithm =
+ new CommunalHostViewPositionAlgorithm();
+ private final CommunalHostViewPositionAlgorithm.Result
+ mCommunalPositionResult =
+ new CommunalHostViewPositionAlgorithm.Result();
private boolean mIsExpanding;
private boolean mBlockTouches;
@@ -452,7 +414,6 @@
private boolean mQsTouchAboveFalsingThreshold;
private int mQsFalsingThreshold;
- private float mKeyguardStatusBarAnimateAlpha = 1f;
private HeadsUpTouchHelper mHeadsUpTouchHelper;
private boolean mListenForHeadsUp;
private int mNavigationBarBottomHeight;
@@ -551,6 +512,8 @@
private final NotificationLockscreenUserManager mLockscreenUserManager;
private final UserManager mUserManager;
private final MediaDataManager mMediaDataManager;
+ private final SysUiState mSysUiState;
+
private NotificationShadeDepthController mDepthController;
private int mDisplayId;
@@ -566,8 +529,6 @@
private int mDarkIconSize;
private int mHeadsUpInset;
private boolean mHeadsUpPinnedMode;
- private float mKeyguardHeadsUpShowingAmount = 0.0f;
- private boolean mShowingKeyguardHeadsUp;
private boolean mAllowExpandForSmallExpansion;
private Runnable mExpandAfterLayoutRunnable;
@@ -626,17 +587,6 @@
*/
private boolean mIsPanelCollapseOnQQS;
- /**
- * If face auth with bypass is running for the first time after you turn on the screen.
- * (From aod or screen off)
- */
- private boolean mFirstBypassAttempt;
- /**
- * If auth happens successfully during {@code mFirstBypassAttempt}, and we should wait until
- * the keyguard is dismissed to show the status bar.
- */
- private boolean mDelayShowingKeyguardStatusBar;
-
private boolean mAnimatingQS;
/**
@@ -752,6 +702,7 @@
@Main Resources resources,
@Main Handler handler,
LayoutInflater layoutInflater,
+ FeatureFlags featureFlags,
NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler,
DynamicPrivacyController dynamicPrivacyController,
KeyguardBypassController bypassController, FalsingManager falsingManager,
@@ -772,7 +723,6 @@
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
ConversationNotificationManager conversationNotificationManager,
MediaHierarchyManager mediaHierarchyManager,
- BiometricUnlockController biometricUnlockController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
NotificationStackScrollLayoutController notificationStackScrollLayoutController,
KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
@@ -851,6 +801,7 @@
mView.setWillNotDraw(!DEBUG);
mSplitShadeHeaderController = splitShadeHeaderController;
mLayoutInflater = layoutInflater;
+ mFeatureFlags = featureFlags;
mFalsingManager = falsingManager;
mFalsingCollector = falsingCollector;
mPowerManager = powerManager;
@@ -863,7 +814,6 @@
mDisplayId = displayId;
mPulseExpansionHandler = pulseExpansionHandler;
mDozeParameters = dozeParameters;
- mBiometricUnlockController = biometricUnlockController;
mScrimController = scrimController;
mScrimController.setClipsQsScrim(!mShouldUseSplitNotificationShade);
mUserManager = userManager;
@@ -871,6 +821,8 @@
mTapAgainViewController = tapAgainViewController;
mUiExecutor = uiExecutor;
mSecureSettings = secureSettings;
+ // TODO: inject via dagger instead of Dependency
+ mSysUiState = Dependency.get(SysUiState.class);
pulseExpansionHandler.setPulseExpandAbortListener(() -> {
if (mQs != null) {
mQs.animateHeaderSlidingOut();
@@ -880,21 +832,8 @@
mKeyguardBypassController = bypassController;
mUpdateMonitor = keyguardUpdateMonitor;
mCommunalSourceMonitor = communalSourceMonitor;
- mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
- KeyguardStateController.Callback
- keyguardMonitorCallback =
- new KeyguardStateController.Callback() {
- @Override
- public void onKeyguardFadingAwayChanged() {
- if (!mKeyguardStateController.isKeyguardFadingAway()) {
- mFirstBypassAttempt = false;
- mDelayShowingKeyguardStatusBar = false;
- }
- }
- };
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
lockscreenShadeTransitionController.setNotificationPanelController(this);
- mKeyguardStateController.addCallback(keyguardMonitorCallback);
DynamicPrivacyControlListener
dynamicPrivacyControlListener =
new DynamicPrivacyControlListener();
@@ -957,7 +896,9 @@
}
mKeyguardStatusBarViewController =
- mKeyguardStatusBarViewComponentFactory.build(mKeyguardStatusBar)
+ mKeyguardStatusBarViewComponentFactory.build(
+ mKeyguardStatusBar,
+ mNotificationPanelViewStateProvider)
.getKeyguardStatusBarViewController();
mKeyguardStatusBarViewController.init();
@@ -966,10 +907,20 @@
mIdleHostViewController = idleViewComponent.getIdleHostViewController();
mIdleHostViewController.init();
+ if (mCommunalView != null) {
+ CommunalViewComponent communalViewComponent =
+ mCommunalViewComponentFactory.build(mCommunalView);
+ mCommunalViewController =
+ communalViewComponent.getCommunalHostViewController();
+ mCommunalViewController.init();
+ }
+
+
updateViewControllers(
mView.findViewById(R.id.keyguard_status_view),
userAvatarView,
keyguardUserSwitcherView,
+ mView.findViewById(R.id.idle_host_view),
mCommunalView);
mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
NotificationStackScrollLayout stackScrollLayout = mView.findViewById(
@@ -997,7 +948,7 @@
mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() {
@Override
public void onFullyHiddenChanged(boolean isFullyHidden) {
- updateKeyguardStatusBarForHeadsUp();
+ mKeyguardStatusBarViewController.updateForHeadsUp();
}
@Override
@@ -1034,8 +985,6 @@
mStatusBarHeaderHeightKeyguard = mResources.getDimensionPixelSize(
R.dimen.status_bar_header_height_keyguard);
mQsPeekHeight = mResources.getDimensionPixelSize(R.dimen.qs_peek_height);
- mNotificationsHeaderCollideDistance = mResources.getDimensionPixelSize(
- R.dimen.header_notifications_collide_distance);
mClockPositionAlgorithm.loadDimens(mResources);
mQsFalsingThreshold = mResources.getDimensionPixelSize(R.dimen.qs_falsing_threshold);
mPositionMinSideMargin = mResources.getDimensionPixelSize(
@@ -1062,6 +1011,7 @@
private void updateViewControllers(KeyguardStatusView keyguardStatusView,
UserAvatarView userAvatarView,
KeyguardUserSwitcherView keyguardUserSwitcherView,
+ IdleHostView idleHostView,
CommunalHostView communalView) {
// Re-associate the KeyguardStatusViewController
KeyguardStatusViewComponent statusViewComponent =
@@ -1069,13 +1019,9 @@
mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
mKeyguardStatusViewController.init();
- if (communalView != null) {
- CommunalViewComponent communalViewComponent =
- mCommunalViewComponentFactory.build(communalView);
- mCommunalViewController =
- communalViewComponent.getCommunalHostViewController();
- mCommunalViewController.init();
- }
+ IdleViewComponent idleViewComponent = mIdleViewComponentFactory.build(idleHostView);
+ mIdleHostViewController = idleViewComponent.getIdleHostViewController();
+ mIdleHostViewController.init();
if (mKeyguardUserSwitcherController != null) {
// Try to close the switcher so that callbacks are triggered if necessary.
@@ -1240,7 +1186,7 @@
mBigClockContainer.removeAllViews();
updateViewControllers(mView.findViewById(R.id.keyguard_status_view), userAvatarView,
- keyguardUserSwitcherView, mCommunalView);
+ keyguardUserSwitcherView, mView.findViewById(R.id.idle_host_view), mCommunalView);
// Update keyguard bottom area
int index = mView.indexOfChild(mKeyguardBottomArea);
@@ -1380,6 +1326,11 @@
boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
int stackScrollerPadding;
boolean onKeyguard = isOnKeyguard();
+
+ if (onKeyguard) {
+ updateCommunalViewAppearance();
+ }
+
if (onKeyguard || forceClockUpdate) {
updateClockAppearance();
}
@@ -1405,9 +1356,23 @@
mAnimateNextPositionUpdate = false;
}
+ private void updateCommunalViewAppearance() {
+ if (mCommunalViewController == null) {
+ return;
+ }
+
+ float expandedFraction =
+ mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()
+ ? 1.0f : getExpandedFraction();
+ mCommunalPositionAlgorithm.setup(expandedFraction, mCommunalView.getHeight());
+ mCommunalPositionAlgorithm.run(mCommunalPositionResult);
+ boolean animate =
+ mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending()
+ || mAnimateNextPositionUpdate;
+ mCommunalViewController.updatePosition(mCommunalPositionResult.communalY, animate);
+ }
+
private void updateClockAppearance() {
- int totalHeight = mView.getHeight();
- int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard;
boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
@@ -1428,14 +1393,11 @@
mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()
? 1.0f : mInterpolatedDarkAmount;
mClockPositionAlgorithm.setup(mStatusBarHeaderHeightKeyguard,
- totalHeight - bottomPadding,
- mNotificationStackScrollLayoutController.getIntrinsicContentHeight(),
expandedFraction,
- totalHeight,
mKeyguardStatusViewController.getLockscreenHeight(),
userIconHeight,
- userSwitcherPreferredY, hasCustomClock(),
- hasVisibleNotifications, darkamount, mOverStretchAmount,
+ userSwitcherPreferredY,
+ darkamount, mOverStretchAmount,
bypassEnabled, getUnlockedStackScrollerPadding(),
computeQsExpansionFraction(),
mDisplayTopInset,
@@ -1624,6 +1586,12 @@
return true;
}
+ private void updateCommunal() {
+ if (mCommunalViewController != null) {
+ mCommunalViewController.setAlpha(mKeyguardOnlyContentAlpha);
+ }
+ }
+
private void updateClock() {
float alpha = mClockPositionResult.clockAlpha * mKeyguardOnlyContentAlpha;
mKeyguardStatusViewController.setAlpha(alpha);
@@ -1983,6 +1951,9 @@
private boolean handleQsTouch(MotionEvent event) {
+ if (mShouldUseSplitNotificationShade && touchXOutsideOfQs(event.getX())) {
+ return false;
+ }
final int action = event.getActionMasked();
if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
&& mBarState != KEYGUARD && !mQsExpanded && isQsExpansionEnabled()) {
@@ -2024,8 +1995,12 @@
return false;
}
+ private boolean touchXOutsideOfQs(float touchX) {
+ return touchX < mQsFrame.getX() || touchX > mQsFrame.getX() + mQsFrame.getWidth();
+ }
+
private boolean isInQsArea(float x, float y) {
- if (x < mQsFrame.getX() || x > mQsFrame.getX() + mQsFrame.getWidth()) {
+ if (touchXOutsideOfQs(x)) {
return false;
}
// Let's reject anything at the very bottom around the home handle in gesture nav
@@ -2262,59 +2237,6 @@
}
}
- private final Runnable mAnimateKeyguardStatusBarInvisibleEndRunnable = new Runnable() {
- @Override
- public void run() {
- mKeyguardStatusBar.setVisibility(View.INVISIBLE);
- mKeyguardStatusBar.setAlpha(1f);
- mKeyguardStatusBarAnimateAlpha = 1f;
- }
- };
-
- private void animateKeyguardStatusBarOut() {
- ValueAnimator anim = ValueAnimator.ofFloat(mKeyguardStatusBar.getAlpha(), 0f);
- anim.addUpdateListener(mStatusBarAnimateAlphaListener);
- anim.setStartDelay(mKeyguardStateController.isKeyguardFadingAway()
- ? mKeyguardStateController.getKeyguardFadingAwayDelay() : 0);
-
- long duration;
- if (mKeyguardStateController.isKeyguardFadingAway()) {
- duration = mKeyguardStateController.getShortenedFadingAwayDuration();
- } else {
- duration = StackStateAnimator.ANIMATION_DURATION_STANDARD;
- }
- anim.setDuration(duration);
-
- anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mAnimateKeyguardStatusBarInvisibleEndRunnable.run();
- }
- });
- anim.start();
- }
-
- private final ValueAnimator.AnimatorUpdateListener
- mStatusBarAnimateAlphaListener =
- new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mKeyguardStatusBarAnimateAlpha = (float) animation.getAnimatedValue();
- updateHeaderKeyguardAlpha();
- }
- };
-
- private void animateKeyguardStatusBarIn(long duration) {
- mKeyguardStatusBar.setVisibility(View.VISIBLE);
- mKeyguardStatusBar.setAlpha(0f);
- ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
- anim.addUpdateListener(mStatusBarAnimateAlphaListener);
- anim.setDuration(duration);
- anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- anim.start();
- }
-
private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable = new Runnable() {
@Override
public void run() {
@@ -2367,7 +2289,7 @@
mQsExpansionHeight = height;
updateQsExpansion();
requestScrollerTopPaddingUpdate(false /* animate */);
- updateHeaderKeyguardAlpha();
+ mKeyguardStatusBarViewController.updateViewState();
if (mBarState == StatusBarState.SHADE_LOCKED || mBarState == KEYGUARD) {
updateKeyguardBottomAreaAlpha();
positionClockAndNotifications();
@@ -2771,6 +2693,7 @@
updateKeyguardBottomAreaAlpha();
}
updateClock();
+ updateCommunal();
}
private void trackMovement(MotionEvent event) {
@@ -3099,7 +3022,7 @@
*/
private void updateHeader() {
if (mBarState == KEYGUARD) {
- updateHeaderKeyguardAlpha();
+ mKeyguardStatusBarViewController.updateViewState();
}
updateQsExpansion();
}
@@ -3123,47 +3046,6 @@
return Math.min(0, translation);
}
- /**
- * @return the alpha to be used to fade out the contents on Keyguard (status bar, bottom area)
- * during swiping up
- */
- private float getKeyguardContentsAlpha() {
- float alpha;
- if (mBarState == KEYGUARD) {
-
- // When on Keyguard, we hide the header as soon as we expanded close enough to the
- // header
- alpha =
- getExpandedHeight() / (mKeyguardStatusBar.getHeight()
- + mNotificationsHeaderCollideDistance);
- } else {
-
- // In SHADE_LOCKED, the top card is already really close to the header. Hide it as
- // soon as we start translating the stack.
- alpha = getExpandedHeight() / mKeyguardStatusBar.getHeight();
- }
- alpha = MathUtils.saturate(alpha);
- alpha = (float) Math.pow(alpha, 0.75);
- return alpha;
- }
-
- private void updateHeaderKeyguardAlpha() {
- if (!mKeyguardShowing) {
- return;
- }
- float alphaQsExpansion = 1 - Math.min(1, computeQsExpansionFraction() * 2);
- float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
- * mKeyguardStatusBarAnimateAlpha;
- newAlpha *= 1.0f - mKeyguardHeadsUpShowingAmount;
- mKeyguardStatusBar.setAlpha(newAlpha);
- boolean
- hideForBypass =
- mFirstBypassAttempt && mUpdateMonitor.shouldListenForFace()
- || mDelayShowingKeyguardStatusBar;
- mKeyguardStatusBar.setVisibility(
- newAlpha != 0f && !mDozing && !hideForBypass ? View.VISIBLE : View.INVISIBLE);
- }
-
private void updateKeyguardBottomAreaAlpha() {
// There are two possible panel expansion behaviors:
// • User dragging up to unlock: we want to fade out as quick as possible
@@ -3390,7 +3272,7 @@
private void updateDozingVisibilities(boolean animate) {
mKeyguardBottomArea.setDozing(mDozing, animate);
if (!mDozing && animate) {
- animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ mKeyguardStatusBarViewController.animateKeyguardStatusBarIn();
}
}
@@ -3459,31 +3341,6 @@
mPanelAlphaEndAction = r;
}
- private void updateKeyguardStatusBarForHeadsUp() {
- boolean
- showingKeyguardHeadsUp =
- mKeyguardShowing && mHeadsUpAppearanceController.shouldBeVisible();
- if (mShowingKeyguardHeadsUp != showingKeyguardHeadsUp) {
- mShowingKeyguardHeadsUp = showingKeyguardHeadsUp;
- if (mKeyguardShowing) {
- PropertyAnimator.setProperty(mView, KEYGUARD_HEADS_UP_SHOWING_AMOUNT,
- showingKeyguardHeadsUp ? 1.0f : 0.0f, KEYGUARD_HUN_PROPERTIES,
- true /* animate */);
- } else {
- PropertyAnimator.applyImmediately(mView, KEYGUARD_HEADS_UP_SHOWING_AMOUNT, 0.0f);
- }
- }
- }
-
- private void setKeyguardHeadsUpShowingAmount(float amount) {
- mKeyguardHeadsUpShowingAmount = amount;
- updateHeaderKeyguardAlpha();
- }
-
- private float getKeyguardHeadsUpShowingAmount() {
- return mKeyguardHeadsUpShowingAmount;
- }
-
public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
mHeadsUpAnimatingAway = headsUpAnimatingAway;
mNotificationStackScrollLayoutController.setHeadsUpAnimatingAway(headsUpAnimatingAway);
@@ -3786,6 +3643,7 @@
mDozing = dozing;
mNotificationStackScrollLayoutController.setDozing(mDozing, animate, wakeUpTouchLocation);
mKeyguardBottomArea.setDozing(mDozing, animate);
+ mKeyguardStatusBarViewController.setDozing(mDozing);
if (dozing) {
mBottomAreaShadeAlphaAnimator.cancel();
@@ -4169,8 +4027,8 @@
mKeyguardUserSwitcherEnabled = mResources.getBoolean(
com.android.internal.R.bool.config_keyguardUserSwitcher);
mKeyguardQsUserSwitchEnabled =
- mKeyguardUserSwitcherEnabled && mResources.getBoolean(
- R.bool.config_keyguard_user_switch_opens_qs_details);
+ mKeyguardUserSwitcherEnabled
+ && mFeatureFlags.isKeyguardQsUserDetailsShortcutEnabled();
}
private void registerSettingsChangeListener() {
@@ -4185,6 +4043,20 @@
mContentResolver.unregisterContentObserver(mSettingsChangeObserver);
}
+ /**
+ * Updates notification panel-specific flags on {@link SysUiState}.
+ */
+ public void updateSystemUiStateFlags() {
+ if (SysUiState.DEBUG) {
+ Log.d(TAG, "Updating panel sysui state flags: fullyExpanded="
+ + isFullyExpanded() + " inQs=" + isInSettings());
+ }
+ mSysUiState.setFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
+ isFullyExpanded() && !isInSettings())
+ .setFlag(SYSUI_STATE_QUICK_SETTINGS_EXPANDED, isInSettings())
+ .commitUpdate(mDisplayId);
+ }
+
private class OnHeightChangedListener implements ExpandableView.OnHeightChangedListener {
@Override
public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
@@ -4253,9 +4125,7 @@
@Override
public void flingTopOverscroll(float velocity, boolean open) {
// in split shade mode we want to expand/collapse QS only when touch happens within QS
- if (mShouldUseSplitNotificationShade
- && (mInitialTouchX < mQsFrame.getX()
- || mInitialTouchX > mQsFrame.getX() + mQsFrame.getWidth())) {
+ if (mShouldUseSplitNotificationShade && touchXOutsideOfQs(mInitialTouchX)) {
return;
}
mLastOverscroll = 0f;
@@ -4451,7 +4321,7 @@
updateGestureExclusionRect();
mHeadsUpPinnedMode = inPinnedMode;
updateHeadsUpVisibility();
- updateKeyguardStatusBarForHeadsUp();
+ mKeyguardStatusBarViewController.updateForHeadsUp();
}
@Override
@@ -4583,11 +4453,22 @@
if (oldState == KEYGUARD && (goingToFullShade
|| statusBarState == StatusBarState.SHADE_LOCKED)) {
- animateKeyguardStatusBarOut();
+
+ long startDelay;
+ long duration;
+ if (mKeyguardStateController.isKeyguardFadingAway()) {
+ startDelay = mKeyguardStateController.getKeyguardFadingAwayDelay();
+ duration = mKeyguardStateController.getShortenedFadingAwayDuration();
+ } else {
+ startDelay = 0;
+ duration = StackStateAnimator.ANIMATION_DURATION_STANDARD;
+ }
+ mKeyguardStatusBarViewController.animateKeyguardStatusBarOut(startDelay, duration);
updateQSMinHeight();
} else if (oldState == StatusBarState.SHADE_LOCKED
&& statusBarState == KEYGUARD) {
- animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ mKeyguardStatusBarViewController.animateKeyguardStatusBarIn();
+
mNotificationStackScrollLayoutController.resetScrollPosition();
// Only animate header if the header is visible. If not, it will partially
// animate out
@@ -4608,7 +4489,7 @@
}
}
}
- updateKeyguardStatusBarForHeadsUp();
+ mKeyguardStatusBarViewController.updateForHeadsUp();
if (keyguardShowing) {
updateDozingVisibilities(false /* animate */);
}
@@ -4634,6 +4515,43 @@
}
/**
+ * An interface that provides the current state of the notification panel and related views,
+ * which is needed to calculate {@link KeyguardStatusBarView}'s state in
+ * {@link KeyguardStatusBarViewController}.
+ */
+ public interface NotificationPanelViewStateProvider {
+ /** Returns the expanded height of the panel view. */
+ float getPanelViewExpandedHeight();
+ /** Returns the fraction of QS that's expanded. */
+ float getQsExpansionFraction();
+ /**
+ * Returns true if heads up should be visible.
+ *
+ * TODO(b/138786270): If HeadsUpAppearanceController was injectable, we could inject it into
+ * {@link KeyguardStatusBarViewController} and remove this method.
+ */
+ boolean shouldHeadsUpBeVisible();
+ }
+
+ private final NotificationPanelViewStateProvider mNotificationPanelViewStateProvider =
+ new NotificationPanelViewStateProvider() {
+ @Override
+ public float getPanelViewExpandedHeight() {
+ return getExpandedHeight();
+ }
+
+ @Override
+ public float getQsExpansionFraction() {
+ return computeQsExpansionFraction();
+ }
+
+ @Override
+ public boolean shouldHeadsUpBeVisible() {
+ return mHeadsUpAppearanceController.shouldBeVisible();
+ }
+ };
+
+ /**
* Reconfigures the shade to show the AOD UI (clock, smartspace, etc). This is called by the
* screen off animation controller in order to animate in AOD without "actually" fully switching
* to the KEYGUARD state, which is a heavy transition that causes jank as 10+ files react to the
@@ -4686,7 +4604,6 @@
.addTagListener(QS.TAG, mFragmentListener);
mStatusBarStateController.addCallback(mStatusBarStateListener);
mConfigurationController.addCallback(mConfigurationListener);
- mUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
mCommunalSourceMonitor.addCallback(mCommunalSourceMonitorCallback);
// Theme might have changed between inflating this view and attaching it to the
// window, so
@@ -4705,10 +4622,7 @@
.removeTagListener(QS.TAG, mFragmentListener);
mStatusBarStateController.removeCallback(mStatusBarStateListener);
mConfigurationController.removeCallback(mConfigurationListener);
- mUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
mCommunalSourceMonitor.removeCallback(mCommunalSourceMonitorCallback);
- // Clear source when detached.
- setCommunalSource(null /*source*/);
mFalsingManager.removeTapListener(mFalsingTapListener);
mCommunalStateController.removeCallback(mCommunalStateCallback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index 030a895..e1cb9f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -27,16 +27,20 @@
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.PixelFormat;
+import android.graphics.Region;
import android.os.Binder;
import android.os.RemoteException;
import android.os.Trace;
import android.util.Log;
import android.view.Display;
import android.view.Gravity;
+import android.view.IWindow;
+import android.view.IWindowSession;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
+import android.view.WindowManagerGlobal;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dumpable;
@@ -507,6 +511,19 @@
}
@Override
+ public void setTouchExclusionRegion(Region region) {
+ try {
+ final IWindowSession session = WindowManagerGlobal.getWindowSession();
+ session.updateTapExcludeRegion(
+ IWindow.Stub.asInterface(getNotificationShadeView().getWindowToken()),
+ region);
+ } catch (RemoteException e) {
+ Log.e(TAG, "could not update the tap exclusion region:" + e);
+ }
+ }
+
+
+ @Override
public void setKeyguardShowing(boolean showing) {
mCurrentState.mKeyguardShowing = showing;
apply(mCurrentState);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index e57e200..147ebfb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -47,7 +47,6 @@
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -106,7 +105,7 @@
private boolean mExpandingBelowNotch;
private final DockManager mDockManager;
private final NotificationPanelViewController mNotificationPanelViewController;
- private final SuperStatusBarViewFactory mStatusBarViewFactory;
+ private final StatusBarWindowView mStatusBarWindowView;
// Used for determining view / touch intersection
private int[] mTempLocation = new int[2];
@@ -136,7 +135,7 @@
NotificationShadeDepthController depthController,
NotificationShadeWindowView notificationShadeWindowView,
NotificationPanelViewController notificationPanelViewController,
- SuperStatusBarViewFactory statusBarViewFactory,
+ StatusBarWindowView statusBarWindowView,
NotificationStackScrollLayoutController notificationStackScrollLayoutController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
mInjectionInflationController = injectionInflationController;
@@ -160,7 +159,7 @@
mDockManager = dockManager;
mNotificationPanelViewController = notificationPanelViewController;
mDepthController = depthController;
- mStatusBarViewFactory = statusBarViewFactory;
+ mStatusBarWindowView = statusBarWindowView;
mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
@@ -477,11 +476,10 @@
public void setStatusBarView(PhoneStatusBarView statusBarView) {
mStatusBarView = statusBarView;
- if (statusBarView != null && mStatusBarViewFactory != null) {
+ if (statusBarView != null) {
mBarTransitions = new PhoneStatusBarTransitions(
statusBarView,
- mStatusBarViewFactory.getStatusBarWindowView()
- .findViewById(R.id.status_bar_container));
+ mStatusBarWindowView.findViewById(R.id.status_bar_container));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 70a46b2..88a823c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -287,8 +287,13 @@
public void panelExpansionChanged(float frac, boolean expanded) {
super.panelExpansionChanged(frac, expanded);
updateScrimFraction();
- if ((frac == 0 || frac == 1) && mBar.getNavigationBarView() != null) {
- mBar.getNavigationBarView().onStatusBarPanelStateChanged();
+ if ((frac == 0 || frac == 1)) {
+ if (mBar.getNavigationBarView() != null) {
+ mBar.getNavigationBarView().onStatusBarPanelStateChanged();
+ }
+ if (mBar.getNotificationPanelViewController() != null) {
+ mBar.getNotificationPanelViewController().updateSystemUiStateFlags();
+ }
}
if (mExpansionChangedListeners != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 87d4543..4213902 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -556,6 +556,8 @@
if (isNaN(expansionFraction)) {
return;
}
+ expansionFraction = Interpolators
+ .getNotificationScrimAlpha(expansionFraction, false /* notification */);
boolean qsBottomVisible = qsPanelBottomY > 0;
if (mQsExpansion != expansionFraction || mQsBottomVisible != qsBottomVisible) {
mQsExpansion = expansionFraction;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index f07f619..0b688d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -194,7 +194,6 @@
import com.android.systemui.statusbar.PowerButtonReveal;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
@@ -228,11 +227,11 @@
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
+import com.android.systemui.unfold.config.UnfoldTransitionConfig;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.MessageRouter;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.wmshell.BubblesManager;
-import com.android.systemui.unfold.config.UnfoldTransitionConfig;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.startingsurface.SplashscreenContentDrawer;
@@ -248,7 +247,6 @@
import java.util.concurrent.Executor;
import javax.inject.Named;
-import javax.inject.Provider;
import dagger.Lazy;
@@ -459,7 +457,6 @@
private final Point mCurrentDisplaySize = new Point();
protected NotificationShadeWindowView mNotificationShadeWindowView;
- protected StatusBarWindowView mPhoneStatusBarWindow;
protected PhoneStatusBarView mStatusBarView;
private PhoneStatusBarViewController mPhoneStatusBarViewController;
private AuthRippleController mAuthRippleController;
@@ -491,13 +488,13 @@
protected NotificationShadeWindowViewController mNotificationShadeWindowViewController;
private final DozeParameters mDozeParameters;
private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
- private final Provider<StatusBarComponent.Builder> mStatusBarComponentBuilder;
+ private final StatusBarComponent.Factory mStatusBarComponentFactory;
private final PluginManager mPluginManager;
private final Optional<LegacySplitScreen> mSplitScreenOptional;
private final StatusBarNotificationActivityStarter.Builder
mStatusBarNotificationActivityStarterBuilder;
private final ShadeController mShadeController;
- private final SuperStatusBarViewFactory mSuperStatusBarViewFactory;
+ private final StatusBarWindowView mStatusBarWindowView;
private final LightsOutNotifController mLightsOutNotifController;
private final InitController mInitController;
@@ -744,14 +741,14 @@
DozeScrimController dozeScrimController,
VolumeComponent volumeComponent,
CommandQueue commandQueue,
- Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
+ StatusBarComponent.Factory statusBarComponentFactory,
PluginManager pluginManager,
Optional<LegacySplitScreen> splitScreenOptional,
LightsOutNotifController lightsOutNotifController,
StatusBarNotificationActivityStarter.Builder
statusBarNotificationActivityStarterBuilder,
ShadeController shadeController,
- SuperStatusBarViewFactory superStatusBarViewFactory,
+ StatusBarWindowView statusBarWindowView,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
ViewMediatorCallback viewMediatorCallback,
InitController initController,
@@ -841,12 +838,12 @@
mNotificationShadeDepthControllerLazy = notificationShadeDepthControllerLazy;
mVolumeComponent = volumeComponent;
mCommandQueue = commandQueue;
- mStatusBarComponentBuilder = statusBarComponentBuilder;
+ mStatusBarComponentFactory = statusBarComponentFactory;
mPluginManager = pluginManager;
mSplitScreenOptional = splitScreenOptional;
mStatusBarNotificationActivityStarterBuilder = statusBarNotificationActivityStarterBuilder;
mShadeController = shadeController;
- mSuperStatusBarViewFactory = superStatusBarViewFactory;
+ mStatusBarWindowView = statusBarWindowView;
mLightsOutNotifController = lightsOutNotifController;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardViewMediatorCallback = viewMediatorCallback;
@@ -1092,6 +1089,11 @@
}
}
}, OverlayPlugin.class, true /* Allow multiple plugins */);
+
+ mStartingSurfaceOptional.ifPresent(startingSurface -> startingSurface.setSysuiProxy(
+ (requestTopUi, componentTag) -> mMainExecutor.execute(() ->
+ mNotificationShadeWindowController.setRequestTopUi(
+ requestTopUi, componentTag))));
}
// ================================================================================
@@ -1108,14 +1110,10 @@
// TODO: Deal with the ugliness that comes from having some of the statusbar broken out
// into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
- mStackScrollerController =
- mNotificationPanelViewController.getNotificationStackScrollLayoutController();
- mStackScroller = mStackScrollerController.getView();
NotificationListContainer notifListContainer =
mStackScrollerController.getNotificationListContainer();
mNotificationLogger.setUpWithContainer(notifListContainer);
- inflateShelf();
mNotificationIconAreaController.setupShelf(mNotificationShelfController);
mNotificationPanelViewController.addExpansionListener(mWakeUpCoordinator);
mNotificationPanelViewController.addExpansionListener(
@@ -1124,7 +1122,7 @@
// Allow plugins to reference DarkIconDispatcher and StatusBarStateController
mPluginDependencyProvider.allowPluginDependency(DarkIconDispatcher.class);
mPluginDependencyProvider.allowPluginDependency(StatusBarStateController.class);
- FragmentHostManager.get(mPhoneStatusBarWindow)
+ FragmentHostManager.get(mStatusBarWindowView)
.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
CollapsedStatusBarFragment statusBarFragment =
(CollapsedStatusBarFragment) fragment;
@@ -1542,24 +1540,20 @@
};
}
- private void inflateShelf() {
- mNotificationShelfController = mSuperStatusBarViewFactory
- .getNotificationShelfController(mStackScroller);
- }
-
private void inflateStatusBarWindow() {
- mNotificationShadeWindowView = mSuperStatusBarViewFactory.getNotificationShadeWindowView();
- StatusBarComponent statusBarComponent = mStatusBarComponentBuilder.get()
- .statusBarWindowView(mNotificationShadeWindowView).build();
+ StatusBarComponent statusBarComponent = mStatusBarComponentFactory.create();
+ mNotificationShadeWindowView = statusBarComponent.getNotificationShadeWindowView();
mNotificationShadeWindowViewController = statusBarComponent
.getNotificationShadeWindowViewController();
mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
mNotificationShadeWindowViewController.setupExpandedStatusBar();
mStatusBarWindowController = statusBarComponent.getStatusBarWindowController();
- mPhoneStatusBarWindow = mSuperStatusBarViewFactory.getStatusBarWindowView();
mNotificationPanelViewController = statusBarComponent.getNotificationPanelViewController();
statusBarComponent.getLockIconViewController().init();
+ mStackScrollerController = statusBarComponent.getNotificationStackScrollLayoutController();
+ mStackScroller = mStackScrollerController.getView();
+ mNotificationShelfController = statusBarComponent.getNotificationShelfController();
mAuthRippleController = statusBarComponent.getAuthRippleController();
mAuthRippleController.init();
@@ -1634,7 +1628,7 @@
}
public StatusBarWindowView getStatusBarWindow() {
- return mPhoneStatusBarWindow;
+ return mStatusBarWindowView;
}
public NotificationShadeWindowViewController getNotificationShadeWindowViewController() {
@@ -1835,6 +1829,7 @@
mNotificationPanelViewController.setStatusAccessibilityImportance(expanded
? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
: View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+ mNotificationPanelViewController.updateSystemUiStateFlags();
if (getNavigationBarView() != null) {
getNavigationBarView().onStatusBarPanelStateChanged();
}
@@ -2587,7 +2582,7 @@
private ActivityLaunchAnimator.Controller wrapAnimationController(
ActivityLaunchAnimator.Controller animationController, boolean dismissShade) {
View rootView = animationController.getLaunchContainer().getRootView();
- if (rootView == mSuperStatusBarViewFactory.getStatusBarWindowView()) {
+ if (rootView == mStatusBarWindowView) {
// We are animating a view in the status bar. We have to make sure that the status bar
// window matches the full screen during the animation and that we are expanding the
// view below the other status bar text.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index 1dd22b4..515094b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -19,10 +19,10 @@
import android.content.Context
import android.content.res.Resources
import android.graphics.Rect
+import android.util.LruCache
import android.util.Pair
import android.view.DisplayCutout
import android.view.View.LAYOUT_DIRECTION_RTL
-import android.view.WindowManager
import android.view.WindowMetrics
import androidx.annotation.VisibleForTesting
import com.android.systemui.Dumpable
@@ -37,6 +37,7 @@
import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE
import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN
import com.android.systemui.util.leak.RotationUtils.Rotation
+import com.android.systemui.util.leak.RotationUtils.getResourcesForRotation
import java.io.FileDescriptor
import java.io.PrintWriter
import java.lang.Math.max
@@ -60,13 +61,14 @@
class StatusBarContentInsetsProvider @Inject constructor(
val context: Context,
val configurationController: ConfigurationController,
- val windowManager: WindowManager,
val dumpManager: DumpManager
) : CallbackController<StatusBarContentInsetsChangedListener>,
ConfigurationController.ConfigurationListener,
Dumpable {
- // Indexed by @Rotation
- private val insetsByCorner = arrayOfNulls<Rect>(4)
+
+ // Limit cache size as potentially we may connect large number of displays
+ // (e.g. network displays)
+ private val insetsCache = LruCache<CacheKey, Rect>(MAX_CACHE_SIZE)
private val listeners = mutableSetOf<StatusBarContentInsetsChangedListener>()
init {
@@ -90,12 +92,12 @@
clearCachedInsets()
}
- private fun clearCachedInsets() {
- insetsByCorner[0] = null
- insetsByCorner[1] = null
- insetsByCorner[2] = null
- insetsByCorner[3] = null
+ override fun onMaxBoundsChanged() {
+ notifyInsetsChanged()
+ }
+ private fun clearCachedInsets() {
+ insetsCache.evictAll()
notifyInsetsChanged()
}
@@ -110,10 +112,10 @@
* dot in the coordinates relative to the given rotation.
*/
fun getBoundingRectForPrivacyChipForRotation(@Rotation rotation: Int): Rect {
- var insets = insetsByCorner[rotation]
- val rotatedResources = RotationUtils.getResourcesForRotation(rotation, context)
+ var insets = insetsCache[getCacheKey(rotation = rotation)]
+ val rotatedResources = getResourcesForRotation(rotation, context)
if (insets == null) {
- insets = getAndSetInsetsForRotation(rotation, rotatedResources)
+ insets = getStatusBarContentInsetsForRotation(rotation, rotatedResources)
}
val dotWidth = rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_dot_diameter)
@@ -128,24 +130,16 @@
* Calculates the necessary left and right locations for the status bar contents invariant of
* the current device rotation, in the target rotation's coordinates
*/
- fun getStatusBarContentInsetsForRotation(@Rotation rotation: Int): Rect {
- var insets = insetsByCorner[rotation]
- if (insets == null) {
- val rotatedResources = RotationUtils.getResourcesForRotation(rotation, context)
- insets = getAndSetInsetsForRotation(rotation, rotatedResources)
- }
-
- return insets
- }
-
- private fun getAndSetInsetsForRotation(
- @Rotation rot: Int,
- rotatedResources: Resources
+ @JvmOverloads
+ fun getStatusBarContentInsetsForRotation(
+ @Rotation rotation: Int,
+ rotatedResources: Resources = getResourcesForRotation(rotation, context)
): Rect {
- val insets = getCalculatedInsetsForRotation(rot, rotatedResources)
- insetsByCorner[rot] = insets
-
- return insets
+ val key = getCacheKey(rotation = rotation)
+ return insetsCache[key] ?: getCalculatedInsetsForRotation(rotation, rotatedResources)
+ .also {
+ insetsCache.put(key, it)
+ }
}
private fun getCalculatedInsetsForRotation(
@@ -175,17 +169,29 @@
currentRotation,
targetRotation,
dc,
- windowManager.maximumWindowMetrics,
+ context.resources.configuration.windowConfiguration.maxBounds,
rotatedResources.getDimensionPixelSize(R.dimen.status_bar_height),
minLeft,
minRight)
}
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
- insetsByCorner.forEachIndexed { index, rect ->
- pw.println("${RotationUtils.toString(index)} -> $rect")
+ insetsCache.snapshot().forEach { (key, rect) ->
+ pw.println("$key -> $rect")
}
+ pw.println(insetsCache)
}
+
+ private fun getCacheKey(@Rotation rotation: Int): CacheKey =
+ CacheKey(
+ uniqueDisplayId = context.display.uniqueId,
+ rotation = rotation
+ )
+
+ private data class CacheKey(
+ val uniqueDisplayId: String,
+ @Rotation val rotation: Int
+ )
}
interface StatusBarContentInsetsChangedListener {
@@ -193,10 +199,9 @@
}
private const val TAG = "StatusBarInsetsProvider"
+private const val MAX_CACHE_SIZE = 16
-private fun getRotationZeroDisplayBounds(wm: WindowMetrics, @Rotation exactRotation: Int): Rect {
- val bounds = wm.bounds
-
+private fun getRotationZeroDisplayBounds(bounds: Rect, @Rotation exactRotation: Int): Rect {
if (exactRotation == ROTATION_NONE || exactRotation == ROTATION_UPSIDE_DOWN) {
return bounds
}
@@ -242,7 +247,7 @@
@Rotation currentRotation: Int,
@Rotation targetRotation: Int,
displayCutout: DisplayCutout?,
- windowMetrics: WindowMetrics,
+ maxBounds: Rect,
statusBarHeight: Int,
minLeft: Int,
minRight: Int
@@ -253,16 +258,15 @@
val right = if (isRtl) paddingStart else paddingEnd
*/
- val rotZeroBounds = getRotationZeroDisplayBounds(windowMetrics, currentRotation)
- val currentBounds = windowMetrics.bounds
+ val rotZeroBounds = getRotationZeroDisplayBounds(maxBounds, currentRotation)
val sbLeftRight = getStatusBarLeftRight(
displayCutout,
statusBarHeight,
rotZeroBounds.right,
rotZeroBounds.bottom,
- currentBounds.width(),
- currentBounds.height(),
+ maxBounds.width(),
+ maxBounds.height(),
minLeft,
minRight,
targetRotation,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index 3d3b58a..aec27d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -43,7 +43,6 @@
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import javax.inject.Inject;
@@ -58,14 +57,13 @@
private final Context mContext;
private final WindowManager mWindowManager;
private final IWindowManager mIWindowManager;
- private final SuperStatusBarViewFactory mSuperStatusBarViewFactory;
private final StatusBarContentInsetsProvider mContentInsetsProvider;
private final Resources mResources;
private int mBarHeight = -1;
private final State mCurrentState = new State();
- private ViewGroup mStatusBarView;
- private ViewGroup mLaunchAnimationContainer;
+ private final ViewGroup mStatusBarView;
+ private final ViewGroup mLaunchAnimationContainer;
private WindowManager.LayoutParams mLp;
private final WindowManager.LayoutParams mLpChanged;
@@ -74,15 +72,14 @@
Context context,
WindowManager windowManager,
IWindowManager iWindowManager,
- SuperStatusBarViewFactory superStatusBarViewFactory,
+ StatusBarWindowView statusBarWindowView,
StatusBarContentInsetsProvider contentInsetsProvider,
@Main Resources resources) {
mContext = context;
mWindowManager = windowManager;
mIWindowManager = iWindowManager;
mContentInsetsProvider = contentInsetsProvider;
- mSuperStatusBarViewFactory = superStatusBarViewFactory;
- mStatusBarView = mSuperStatusBarViewFactory.getStatusBarWindowView();
+ mStatusBarView = statusBarWindowView;
mLaunchAnimationContainer = mStatusBarView.findViewById(
R.id.status_bar_launch_animation_container);
mLpChanged = new WindowManager.LayoutParams();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 6b52dca..f8120a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -5,20 +5,23 @@
import android.animation.ValueAnimator
import android.content.Context
import android.content.res.Configuration
+import android.database.ContentObserver
import android.os.Handler
+import android.provider.Settings
+import com.android.systemui.statusbar.StatusBarState
import android.view.View
import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.LightRevealScrim
-import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.StatusBarStateControllerImpl
import com.android.systemui.statusbar.notification.AnimatableProperty
import com.android.systemui.statusbar.notification.PropertyAnimator
import com.android.systemui.statusbar.notification.stack.AnimationProperties
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.settings.GlobalSettings
import javax.inject.Inject
/**
@@ -46,13 +49,16 @@
private val statusBarStateControllerImpl: StatusBarStateControllerImpl,
private val keyguardViewMediatorLazy: dagger.Lazy<KeyguardViewMediator>,
private val keyguardStateController: KeyguardStateController,
- private val dozeParameters: dagger.Lazy<DozeParameters>
+ private val dozeParameters: dagger.Lazy<DozeParameters>,
+ private val globalSettings: GlobalSettings
) : WakefulnessLifecycle.Observer {
private val handler = Handler()
private lateinit var statusBar: StatusBar
private lateinit var lightRevealScrim: LightRevealScrim
+ private var animatorDurationScale = 1f
+ private var shouldAnimateInKeyguard = false
private var lightRevealAnimationPlaying = false
private var aodUiAnimationPlaying = false
@@ -79,6 +85,12 @@
})
}
+ val animatorDurationScaleObserver = object : ContentObserver(null) {
+ override fun onChange(selfChange: Boolean) {
+ updateAnimatorDurationScale()
+ }
+ }
+
fun initialize(
statusBar: StatusBar,
lightRevealScrim: LightRevealScrim
@@ -86,14 +98,25 @@
this.lightRevealScrim = lightRevealScrim
this.statusBar = statusBar
+ updateAnimatorDurationScale()
+ globalSettings.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
+ /* notify for descendants */ false,
+ animatorDurationScaleObserver)
wakefulnessLifecycle.addObserver(this)
}
+ fun updateAnimatorDurationScale() {
+ animatorDurationScale =
+ globalSettings.getFloat(Settings.Global.ANIMATOR_DURATION_SCALE, 1f)
+ }
+
/**
* Animates in the provided keyguard view, ending in the same position that it will be in on
* AOD.
*/
fun animateInKeyguard(keyguardView: View, after: Runnable) {
+ shouldAnimateInKeyguard = false
keyguardView.alpha = 0f
keyguardView.visibility = View.VISIBLE
@@ -138,6 +161,7 @@
// Waking up, so reset this flag.
decidedToAnimateGoingToSleep = null
+ shouldAnimateInKeyguard = false
lightRevealAnimator.cancel()
handler.removeCallbacksAndMessages(null)
}
@@ -146,7 +170,6 @@
// Set this to false in onFinishedWakingUp rather than onStartedWakingUp so that other
// observers (such as StatusBar) can ask us whether we were playing the screen off animation
// and reset accordingly.
- lightRevealAnimationPlaying = false
aodUiAnimationPlaying = false
// If we can't control the screen off animation, we shouldn't mess with the StatusBar's
@@ -167,15 +190,15 @@
if (dozeParameters.get().shouldControlUnlockedScreenOff()) {
decidedToAnimateGoingToSleep = true
+ shouldAnimateInKeyguard = true
lightRevealAnimationPlaying = true
lightRevealAnimator.start()
-
handler.postDelayed({
aodUiAnimationPlaying = true
// Show AOD. That'll cause the KeyguardVisibilityHelper to call #animateInKeyguard.
statusBar.notificationPanelViewController.showAodUi()
- }, ANIMATE_IN_KEYGUARD_DELAY)
+ }, (ANIMATE_IN_KEYGUARD_DELAY * animatorDurationScale).toLong())
} else {
decidedToAnimateGoingToSleep = false
}
@@ -228,6 +251,10 @@
return lightRevealAnimationPlaying || aodUiAnimationPlaying
}
+ fun shouldAnimateInKeyguard(): Boolean {
+ return shouldAnimateInKeyguard
+ }
+
/**
* Whether the light reveal animation is playing. The second part of the screen off animation,
* where AOD animates in, might still be playing if this returns false.
@@ -235,4 +262,4 @@
fun isScreenOffLightRevealAnimationPlaying(): Boolean {
return lightRevealAnimationPlaying
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
index d408c0c..418f588 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
@@ -20,6 +20,8 @@
import com.android.keyguard.LockIconViewController;
import com.android.systemui.biometrics.AuthRippleController;
+import com.android.systemui.statusbar.NotificationShelfController;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController;
@@ -34,7 +36,6 @@
import javax.inject.Scope;
-import dagger.BindsInstance;
import dagger.Subcomponent;
/**
@@ -46,11 +47,9 @@
/**
* Builder for {@link StatusBarComponent}.
*/
- @Subcomponent.Builder
- interface Builder {
- @BindsInstance Builder statusBarWindowView(
- NotificationShadeWindowView notificationShadeWindowView);
- StatusBarComponent build();
+ @Subcomponent.Factory
+ interface Factory {
+ StatusBarComponent create();
}
/**
@@ -62,6 +61,21 @@
@interface StatusBarScope {}
/**
+ * Creates a {@link NotificationShadeWindowView}/
+ * @return
+ */
+ @StatusBarScope
+ NotificationShadeWindowView getNotificationShadeWindowView();
+
+ /** */
+ @StatusBarScope
+ NotificationShelfController getNotificationShelfController();
+
+ /** */
+ @StatusBarScope
+ NotificationStackScrollLayoutController getNotificationStackScrollLayoutController();
+
+ /**
* Creates a NotificationShadeWindowViewController.
*/
@StatusBarScope
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index b36c45e..a7b57b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -58,7 +58,6 @@
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
@@ -92,6 +91,7 @@
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
+import com.android.systemui.statusbar.phone.StatusBarWindowView;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -104,11 +104,11 @@
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
+import com.android.systemui.unfold.config.UnfoldTransitionConfig;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.MessageRouter;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.wmshell.BubblesManager;
-import com.android.systemui.unfold.config.UnfoldTransitionConfig;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.startingsurface.StartingSurface;
@@ -117,7 +117,6 @@
import java.util.concurrent.Executor;
import javax.inject.Named;
-import javax.inject.Provider;
import dagger.Lazy;
import dagger.Module;
@@ -187,14 +186,14 @@
DozeScrimController dozeScrimController,
VolumeComponent volumeComponent,
CommandQueue commandQueue,
- Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
+ StatusBarComponent.Factory statusBarComponentFactory,
PluginManager pluginManager,
Optional<LegacySplitScreen> splitScreenOptional,
LightsOutNotifController lightsOutNotifController,
StatusBarNotificationActivityStarter.Builder
statusBarNotificationActivityStarterBuilder,
ShadeController shadeController,
- SuperStatusBarViewFactory superStatusBarViewFactory,
+ StatusBarWindowView statusBarWindowView,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
ViewMediatorCallback viewMediatorCallback,
InitController initController,
@@ -281,13 +280,13 @@
dozeScrimController,
volumeComponent,
commandQueue,
- statusBarComponentBuilder,
+ statusBarComponentFactory,
pluginManager,
splitScreenOptional,
lightsOutNotifController,
statusBarNotificationActivityStarterBuilder,
shadeController,
- superStatusBarViewFactory,
+ statusBarWindowView,
statusBarKeyguardViewManager,
viewMediatorCallback,
initController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 0e83eda..ecf3b86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -17,15 +17,23 @@
package com.android.systemui.statusbar.phone.dagger;
import android.annotation.Nullable;
+import android.content.Context;
+import android.view.LayoutInflater;
import android.view.View;
import com.android.keyguard.LockIconView;
import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.biometrics.AuthRippleView;
+import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.NotificationShelfController;
+import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.TapAgainView;
+import com.android.systemui.util.InjectionInflationController;
import javax.inject.Named;
@@ -40,6 +48,63 @@
/** */
@Provides
@StatusBarComponent.StatusBarScope
+ public static NotificationShadeWindowView providesNotificationShadeWindowView(
+ InjectionInflationController injectionInflationController,
+ Context context) {
+ NotificationShadeWindowView notificationShadeWindowView = (NotificationShadeWindowView)
+ injectionInflationController.injectable(
+ LayoutInflater.from(context)).inflate(R.layout.super_notification_shade,
+ /* root= */ null);
+ if (notificationShadeWindowView == null) {
+ throw new IllegalStateException(
+ "R.layout.super_notification_shade could not be properly inflated");
+ }
+
+ return notificationShadeWindowView;
+ }
+
+ /** */
+ @Provides
+ @StatusBarComponent.StatusBarScope
+ public static NotificationStackScrollLayout providesNotificationStackScrollLayout(
+ NotificationStackScrollLayoutController notificationStackScrollLayoutController) {
+ return notificationStackScrollLayoutController.getView();
+ }
+
+ /** */
+ @Provides
+ @StatusBarComponent.StatusBarScope
+ public static NotificationShelf providesNotificationShelf(LayoutInflater layoutInflater,
+ NotificationStackScrollLayout notificationStackScrollLayout) {
+ NotificationShelf view = (NotificationShelf) layoutInflater.inflate(
+ R.layout.status_bar_notification_shelf, notificationStackScrollLayout, false);
+
+ if (view == null) {
+ throw new IllegalStateException(
+ "R.layout.status_bar_notification_shelf could not be properly inflated");
+ }
+ return view;
+ }
+
+ /** */
+ @Provides
+ @StatusBarComponent.StatusBarScope
+ public static NotificationShelfController providesStatusBarWindowView(
+ NotificationShelfComponent.Builder notificationShelfComponentBuilder,
+ NotificationShelf notificationShelf) {
+ NotificationShelfComponent component = notificationShelfComponentBuilder
+ .notificationShelf(notificationShelf)
+ .build();
+ NotificationShelfController notificationShelfController =
+ component.getNotificationShelfController();
+ notificationShelfController.init();
+
+ return notificationShelfController;
+ }
+
+ /** */
+ @Provides
+ @StatusBarComponent.StatusBarScope
public static NotificationPanelView getNotificationPanelView(
NotificationShadeWindowView notificationShadeWindowView) {
return notificationShadeWindowView.getNotificationPanelView();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
index 3a05ec7..e679c4c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
@@ -37,6 +37,7 @@
default void onConfigChanged(Configuration newConfig) {}
default void onDensityOrFontScaleChanged() {}
default void onSmallestScreenWidthChanged() {}
+ default void onMaxBoundsChanged() {}
default void onOverlayChanged() {}
default void onUiModeChanged() {}
default void onThemeChanged() {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 65e691f..3bfc2a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -71,6 +71,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
import com.android.systemui.qs.tiles.dialog.InternetDialogUtil;
@@ -129,6 +130,7 @@
private Config mConfig;
private final CarrierConfigTracker mCarrierConfigTracker;
private final FeatureFlags mFeatureFlags;
+ private final DumpManager mDumpManager;
private TelephonyCallback.ActiveDataSubscriptionIdListener mPhoneStateListener;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -225,7 +227,8 @@
CarrierConfigTracker carrierConfigTracker,
@Main Handler handler,
InternetDialogFactory internetDialogFactory,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ DumpManager dumpManager) {
this(context, connectivityManager,
telephonyManager,
telephonyListenerManager,
@@ -243,7 +246,8 @@
broadcastDispatcher,
demoModeController,
carrierConfigTracker,
- featureFlags);
+ featureFlags,
+ dumpManager);
mReceiverHandler.post(mRegisterListeners);
mMainHandler = handler;
mInternetDialogFactory = internetDialogFactory;
@@ -265,7 +269,8 @@
BroadcastDispatcher broadcastDispatcher,
DemoModeController demoModeController,
CarrierConfigTracker carrierConfigTracker,
- FeatureFlags featureFlags
+ FeatureFlags featureFlags,
+ DumpManager dumpManager
) {
mContext = context;
mTelephonyListenerManager = telephonyListenerManager;
@@ -284,6 +289,7 @@
mDemoModeController = demoModeController;
mCarrierConfigTracker = carrierConfigTracker;
mFeatureFlags = featureFlags;
+ mDumpManager = dumpManager;
// telephony
mPhone = telephonyManager;
@@ -434,6 +440,8 @@
mDemoModeController.addCallback(this);
mProviderModelBehavior = mFeatureFlags.isCombinedStatusBarSignalIconsEnabled();
mProviderModelSetting = mFeatureFlags.isProviderModelSettingEnabled();
+
+ mDumpManager.registerDumpable(TAG, this);
}
private final Runnable mClearForceValidated = () -> {
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
index c3b4fbe..fe0b970 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
@@ -132,14 +132,18 @@
/* Target package for each overlay category. */
private final Map<String, String> mCategoryToTargetPackage = new ArrayMap<>();
private final OverlayManager mOverlayManager;
- private final Executor mExecutor;
+ private final Executor mBgExecutor;
+ private final Executor mMainExecutor;
private final String mLauncherPackage;
private final String mThemePickerPackage;
- public ThemeOverlayApplier(OverlayManager overlayManager, Executor executor,
+ public ThemeOverlayApplier(OverlayManager overlayManager,
+ Executor bgExecutor,
+ Executor mainExecutor,
String launcherPackage, String themePickerPackage, DumpManager dumpManager) {
mOverlayManager = overlayManager;
- mExecutor = executor;
+ mBgExecutor = bgExecutor;
+ mMainExecutor = mainExecutor;
mLauncherPackage = launcherPackage;
mThemePickerPackage = themePickerPackage;
mTargetPackageToCategories.put(ANDROID_PACKAGE, Sets.newHashSet(
@@ -170,12 +174,13 @@
* Apply the set of overlay packages to the set of {@code UserHandle}s provided. Overlays that
* affect sysui will also be applied to the system user.
*/
- void applyCurrentUserOverlays(
+ public void applyCurrentUserOverlays(
Map<String, OverlayIdentifier> categoryToPackage,
FabricatedOverlay[] pendingCreation,
int currentUser,
- Set<UserHandle> managedProfiles) {
- mExecutor.execute(() -> {
+ Set<UserHandle> managedProfiles,
+ Runnable onOverlaysApplied) {
+ mBgExecutor.execute(() -> {
// Disable all overlays that have not been specified in the user setting.
final Set<String> overlayCategoriesToDisable = new HashSet<>(THEME_CATEGORIES);
@@ -221,6 +226,7 @@
try {
mOverlayManager.commit(transaction.build());
+ mMainExecutor.execute(onOverlaysApplied);
} catch (SecurityException | IllegalStateException e) {
Log.e(TAG, "setEnabled failed", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 377a7e6..a5871f0 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -526,16 +526,16 @@
.map(key -> key + " -> " + categoryToPackage.get(key)).collect(
Collectors.joining(", ")));
}
+ Runnable overlaysAppliedRunnable = () -> onOverlaysApplied();
if (mNeedsOverlayCreation) {
mNeedsOverlayCreation = false;
mThemeManager.applyCurrentUserOverlays(categoryToPackage, new FabricatedOverlay[] {
mSecondaryOverlay, mNeutralOverlay
- }, currentUser, managedProfiles);
+ }, currentUser, managedProfiles, overlaysAppliedRunnable);
} else {
mThemeManager.applyCurrentUserOverlays(categoryToPackage, null, currentUser,
- managedProfiles);
+ managedProfiles, overlaysAppliedRunnable);
}
- onOverlaysApplied();
}
protected void onOverlaysApplied() {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index 9283403..3c3cc64 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -58,6 +58,10 @@
}
override fun onTransitionStarted() {
+ // When unfolding the view is added earlier, add view for folding case
+ if (scrimView == null) {
+ addOverlayView()
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index c178b29..f420a85 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -377,11 +377,13 @@
SyncTransactionQueue syncQueue, Context context,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
@ShellMainThread ShellExecutor mainExecutor,
- DisplayImeController displayImeController, Transitions transitions,
+ DisplayImeController displayImeController,
+ DisplayInsetsController displayInsetsController, Transitions transitions,
TransactionPool transactionPool) {
if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
return Optional.of(new SplitScreenController(shellTaskOrganizer, syncQueue, context,
- rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController, transitions,
+ rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController,
+ displayInsetsController, transitions,
transactionPool));
} else {
return Optional.empty();
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index 2c9c980..763a5cb 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -101,9 +101,9 @@
android:excludeFromRecents="true" />
<provider
- android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer"
+ android:name="androidx.startup.InitializationProvider"
tools:replace="android:authorities"
- android:authorities="${applicationId}.lifecycle-tests"
+ android:authorities="${applicationId}.startup-tests"
android:exported="false"
android:enabled="false"
android:multiprocess="true" />
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 0276323..2efd369 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -165,7 +165,8 @@
mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
/* leftAligned= */false, /* animate= */false);
- verify(mSecurityViewFlipper).setTranslationX(SCREEN_WIDTH / 2.0f);
+ verify(mSecurityViewFlipper).setTranslationX(
+ mKeyguardSecurityContainer.getWidth() - mSecurityViewFlipper.getWidth());
mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
/* leftAligned= */true, /* animate= */false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 934aa35..b27f5b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -169,6 +169,29 @@
}
@Test
+ public void enableWindowMagnification_LargeScreen_windowSizeIsConstrained() {
+ final int screenSize = mContext.getResources().getDimensionPixelSize(
+ R.dimen.magnification_max_frame_size) * 10;
+ mWindowManager.setWindowBounds(new Rect(0, 0, screenSize, screenSize));
+ //We need to initialize new one because the window size is determined when initialization.
+ final WindowMagnificationController controller = new WindowMagnificationController(mContext,
+ mHandler, mSfVsyncFrameProvider,
+ mMirrorWindowControl, mTransaction, mWindowMagnifierCallback, mSysUiState);
+
+ mInstrumentation.runOnMainSync(() -> {
+ controller.enableWindowMagnification(Float.NaN, Float.NaN,
+ Float.NaN);
+ });
+
+ final int halfScreenSize = screenSize / 2;
+ WindowManager.LayoutParams params = mWindowManager.getLayoutParamsFromAttachedView();
+ // The frame size should be the half of smaller value of window height/width unless it
+ //exceed the max frame size.
+ assertTrue(params.width < halfScreenSize);
+ assertTrue(params.height < halfScreenSize);
+ }
+
+ @Test
public void deleteWindowMagnification_destroyControl() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
@@ -250,7 +273,9 @@
@Test
public void onOrientationChanged_enabled_updateDisplayRotationAndCenterStayAtSamePosition() {
final Display display = Mockito.spy(mContext.getDisplay());
- when(display.getRotation()).thenReturn(Surface.ROTATION_90);
+ final int currentRotation = display.getRotation();
+ final int newRotation = (currentRotation + 1) % 4;
+ when(display.getRotation()).thenReturn(newRotation);
when(mContext.getDisplay()).thenReturn(display);
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
@@ -259,7 +284,7 @@
final PointF expectedCenter = new PointF(mWindowMagnificationController.getCenterY(),
mWindowMagnificationController.getCenterX());
final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
- // Rotate the window 90 degrees.
+ // Rotate the window clockwise 90 degree.
windowBounds.set(windowBounds.top, windowBounds.left, windowBounds.bottom,
windowBounds.right);
mWindowManager.setWindowBounds(windowBounds);
@@ -268,7 +293,7 @@
mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION);
});
- assertEquals(Surface.ROTATION_90, mWindowMagnificationController.mRotation);
+ assertEquals(newRotation, mWindowMagnificationController.mRotation);
final PointF actualCenter = new PointF(mWindowMagnificationController.getCenterX(),
mWindowMagnificationController.getCenterY());
assertEquals(expectedCenter, actualCenter);
@@ -314,6 +339,27 @@
mWindowMagnificationController.getCenterY() / testWindowBounds.height(),
0);
}
+ @Test
+ public void screenSizeIsChangedToLarge_enabled_windowSizeIsConstrained() {
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ Float.NaN);
+ });
+ final int screenSize = mContext.getResources().getDimensionPixelSize(
+ R.dimen.magnification_max_frame_size) * 10;
+ mWindowManager.setWindowBounds(new Rect(0, 0, screenSize, screenSize));
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
+ });
+
+ final int halfScreenSize = screenSize / 2;
+ WindowManager.LayoutParams params = mWindowManager.getLayoutParamsFromAttachedView();
+ // The frame size should be the half of smaller value of window height/width unless it
+ //exceed the max frame size.
+ assertTrue(params.width < halfScreenSize);
+ assertTrue(params.height < halfScreenSize);
+ }
@Test
public void onDensityChanged_enabled_updateDimensionsAndResetWindowMagnification() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
index f91c029..b6d1e42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
@@ -47,6 +47,7 @@
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -165,6 +166,7 @@
}
@Test
+ @Ignore("flaky, b/189031816")
public void testModeUpdated_onSoftError_whenSwitchToFingerprint() {
mFaceToFpView.onDialogAnimatedIn();
mFaceToFpView.onAuthenticationFailed(TYPE_FACE, "no face");
@@ -181,6 +183,7 @@
}
@Test
+ @Ignore("flaky, b/189031816")
public void testModeUpdated_onHardError_whenSwitchToFingerprint() {
mFaceToFpView.onDialogAnimatedIn();
mFaceToFpView.onError(TYPE_FACE, "oh no!");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
index bd518ff..f8e38e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
@@ -45,6 +45,7 @@
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -199,6 +200,7 @@
}
@Test
+ @Ignore("flaky, b/189031816")
public void testError_sendsActionError() {
initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
final String testError = "testError";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 39d5314..8dd5d6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -565,8 +565,9 @@
credentialAllowed,
true /* requireConfirmation */,
0 /* userId */,
- "testPackage",
0 /* operationId */,
+ "testPackage",
+ 1 /* requestId */,
BIOMETRIC_MULTI_SENSOR_FACE_THEN_FINGERPRINT);
}
@@ -612,7 +613,7 @@
@Override
protected AuthDialog buildDialog(PromptInfo promptInfo,
boolean requireConfirmation, int userId, int[] sensorIds, boolean credentialAllowed,
- String opPackageName, boolean skipIntro, long operationId,
+ String opPackageName, boolean skipIntro, long operationId, long requestId,
@BiometricManager.BiometricMultiSensorMode int multiSensorConfig) {
mLastBiometricPromptInfo = promptInfo;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index ae009bc..f5c6f98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.biometrics;
+import static android.media.AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY;
+
import static junit.framework.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
@@ -563,5 +565,10 @@
eq(mUdfpsController.EFFECT_CLICK),
eq("udfps-onStart"),
eq(UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES));
+
+ // THEN make sure vibration attributes has so that it always will play the haptic,
+ // even in battery saver mode
+ assertEquals(USAGE_ASSISTANCE_ACCESSIBILITY,
+ UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES.getUsage());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index decec63..2c08fe6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -369,6 +369,25 @@
verify(mView).setUnpausedAlpha(0);
}
+ @Test
+ public void testShowUdfpsBouncer() {
+ // GIVEN view is attached and status bar expansion is 0
+ mController.onViewAttached();
+ captureExpansionListeners();
+ captureKeyguardStateControllerCallback();
+ captureAltAuthInterceptor();
+ updateStatusBarExpansion(0, true);
+ reset(mView);
+ when(mView.getContext()).thenReturn(mResourceContext);
+ when(mResourceContext.getString(anyInt())).thenReturn("test string");
+
+ // WHEN status bar expansion is 0 but udfps bouncer is requested
+ mAltAuthInterceptor.showAlternateAuthBouncer();
+
+ // THEN alpha is 0
+ verify(mView).setUnpausedAlpha(255);
+ }
+
private void sendStatusBarStateChanged(int statusBarState) {
mStatusBarStateListener.onStateChanged(statusBarState);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java
index 9c6c044..6cfa40a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java
@@ -16,7 +16,10 @@
package com.android.systemui.communal;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -29,6 +32,7 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -76,18 +80,23 @@
@Mock
private CommunalSource mCommunalSource;
+ @Mock
+ private View mChildView;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mCommunalView.isAttachedToWindow()).thenReturn(true);
+ when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
mController = new CommunalHostViewController(mFakeExecutor, mCommunalStateController,
mKeyguardUpdateMonitor, mKeyguardStateController, mDozeParameters,
mUnlockedScreenOffAnimationController, mStatusBarStateController, mCommunalView);
mController.init();
mFakeExecutor.runAllReady();
+
Mockito.clearInvocations(mCommunalView);
}
@@ -113,33 +122,6 @@
}
@Test
- public void testHideOnBouncer() {
- ArgumentCaptor<KeyguardUpdateMonitorCallback> callbackCapture =
- ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
-
- // Capture callback value for later use.
- verify(mKeyguardUpdateMonitor).registerCallback(callbackCapture.capture());
-
- // Establish a visible communal view.
- mController.show(new WeakReference<>(mCommunalSource));
- mFakeExecutor.runAllReady();
- verify(mCommunalView).setVisibility(View.VISIBLE);
- Mockito.clearInvocations(mCommunalView);
-
- // Trigger bouncer.
- Mockito.clearInvocations(mCommunalView);
- callbackCapture.getValue().onKeyguardBouncerChanged(true);
- mFakeExecutor.runAllReady();
- verify(mCommunalView).setVisibility(View.INVISIBLE);
-
- // Hide bouncer
- Mockito.clearInvocations(mCommunalView);
- callbackCapture.getValue().onKeyguardBouncerChanged(false);
- mFakeExecutor.runAllReady();
- verify(mCommunalView).setVisibility(View.VISIBLE);
- }
-
- @Test
public void testHideOnOcclude() {
ArgumentCaptor<KeyguardUpdateMonitorCallback> callbackCapture =
ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
@@ -170,6 +152,7 @@
public void testReportOcclusion() {
// Ensure CommunalHostViewController reports view occluded when either the QS or Shade is
// expanded.
+ clearInvocations(mCommunalStateController);
mController.updateShadeExpansion(0);
verify(mCommunalStateController).setCommunalViewOccluded(false);
clearInvocations(mCommunalStateController);
@@ -207,4 +190,53 @@
// Verify state controller is notified communal view is hidden.
verify(mCommunalStateController).setCommunalViewShowing(false);
}
+
+ @Test
+ public void testAlphaPropagation() {
+ final float alpha = 0.8f;
+
+ // Ensure alpha setting is propagated to children.
+ when(mCommunalView.getChildCount()).thenReturn(1);
+ when(mCommunalView.getChildAt(0)).thenReturn(mChildView);
+ mController.setAlpha(alpha);
+ verify(mChildView).setAlpha(alpha);
+ verify(mCommunalView).setAlpha(alpha);
+ }
+
+ @Test
+ public void testMultipleShowRequestSuppression() {
+ // Ensure first request invokes source.
+ mController.show(new WeakReference<>(mCommunalSource));
+ mFakeExecutor.runAllReady();
+ verify(mCommunalSource).requestCommunalView(any());
+ clearInvocations(mCommunalSource);
+
+ // Ensure subsequent identical request is suppressed
+ mController.show(new WeakReference<>(mCommunalSource));
+ mFakeExecutor.runAllReady();
+ verify(mCommunalSource, never()).requestCommunalView(any());
+ }
+
+ @Test
+ public void testNoShowInvocationOnBouncer() {
+ ArgumentCaptor<KeyguardUpdateMonitorCallback> callbackCapture =
+ ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+
+ // Capture callback value for later use.
+ verify(mKeyguardUpdateMonitor).registerCallback(callbackCapture.capture());
+
+ // Set source so it will be cleared if in invalid state.
+ mController.show(new WeakReference<>(mCommunalSource));
+ mFakeExecutor.runAllReady();
+ clearInvocations(mCommunalStateController, mCommunalView);
+
+ // Change bouncer to showing.
+ callbackCapture.getValue().onKeyguardBouncerChanged(true);
+ mFakeExecutor.runAllReady();
+
+ // Verify that there were no requests to remove all child views or set the communal
+ // state to not showing.
+ verify(mCommunalStateController, never()).setCommunalViewShowing(eq(false));
+ verify(mCommunalView, never()).removeAllViews();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewPositionAlgorithmTest.java
new file mode 100644
index 0000000..0a0266b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewPositionAlgorithmTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.communal.CommunalHostViewPositionAlgorithm.Result;
+
+import org.junit.Test;
+
+
+@SmallTest
+public class CommunalHostViewPositionAlgorithmTest extends SysuiTestCase {
+ @Test
+ public void testOutput() {
+ final float expansion = 0.25f;
+ final int height = 120;
+
+ final CommunalHostViewPositionAlgorithm algorithm = new CommunalHostViewPositionAlgorithm();
+ algorithm.setup(expansion, height);
+ final Result result = new Result();
+ algorithm.run(result);
+
+ // Verify the communal view is shifted offscreen vertically by the correct amount.
+ assertThat((1 - expansion) * -height).isEqualTo(result.communalY);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/service/CommunalSurfaceViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/service/CommunalSurfaceViewControllerTest.java
index a31e543..cf2e029 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/service/CommunalSurfaceViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/service/CommunalSurfaceViewControllerTest.java
@@ -18,22 +18,29 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.graphics.Region;
import android.os.IBinder;
import android.view.Display;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
+import android.view.View;
import androidx.test.filters.SmallTest;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.communal.CommunalStateController;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -50,6 +57,9 @@
private static final int MEASURED_HEIGHT = 200;
private static final int MEASURED_WIDTH = 500;
private static final int DISPLAY_ID = 3;
+ private static final int SPLIT_NOTIFICATION_STATUS_BAR_HEIGHT = 23;
+ private static final int NOTIFICATION_PANEL_MARGIN_TOP = 20;
+ private static final int KEYGUARD_INDICATION_BOTTOM_PADDING = 15;
@Mock
private Display mDisplay;
@@ -72,6 +82,15 @@
@Mock
private CommunalStateController mCommunalStateController;
+ @Mock
+ private Resources mResources;
+
+ @Mock
+ private NotificationShadeWindowController mNotificationShadeWindowController;
+
+ @Mock
+ private IBinder mWindowToken;
+
private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
private SurfaceHolder.Callback mCallback;
@@ -80,27 +99,44 @@
private SettableFuture<SurfaceControlViewHost.SurfacePackage> mPackageFuture;
+ private View.OnLayoutChangeListener mLayoutChangeListener;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- final ArgumentCaptor<SurfaceHolder.Callback> callbackCapture =
- ArgumentCaptor.forClass(SurfaceHolder.Callback.class);
when(mSurfaceView.getHolder()).thenReturn(mSurfaceHolder);
when(mSurfaceView.getDisplay()).thenReturn(mDisplay);
when(mDisplay.getDisplayId()).thenReturn(DISPLAY_ID);
when(mSurfaceView.getHostToken()).thenReturn(mHostToken);
+ when(mSurfaceView.getWindowToken()).thenReturn(mWindowToken);
when(mSurfaceView.getMeasuredWidth()).thenReturn(MEASURED_WIDTH);
when(mSurfaceView.getMeasuredHeight()).thenReturn(MEASURED_HEIGHT);
when(mSurfaceView.isAttachedToWindow()).thenReturn(false);
- mController = new CommunalSurfaceViewController(mSurfaceView, mFakeExecutor,
- mCommunalStateController, mCommunalSource);
+ when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
+ when(mResources.getDimensionPixelSize(R.dimen.split_shade_header_height))
+ .thenReturn(SPLIT_NOTIFICATION_STATUS_BAR_HEIGHT);
+ when(mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_top))
+ .thenReturn(NOTIFICATION_PANEL_MARGIN_TOP);
+ when(mResources.getDimensionPixelSize(R.dimen.keyguard_indication_bottom_padding))
+ .thenReturn(KEYGUARD_INDICATION_BOTTOM_PADDING);
+ mController = new CommunalSurfaceViewController(mSurfaceView, mResources, mFakeExecutor,
+ mCommunalStateController, mNotificationShadeWindowController, mCommunalSource);
mController.init();
+
+ final ArgumentCaptor<SurfaceHolder.Callback> callbackCapture =
+ ArgumentCaptor.forClass(SurfaceHolder.Callback.class);
verify(mSurfaceHolder).addCallback(callbackCapture.capture());
+ verify(mSurfaceHolder).setFormat(PixelFormat.TRANSPARENT);
mCallback = callbackCapture.getValue();
+ final ArgumentCaptor<View.OnLayoutChangeListener> listenerCapture =
+ ArgumentCaptor.forClass(View.OnLayoutChangeListener.class);
+ verify(mSurfaceView).addOnLayoutChangeListener(listenerCapture.capture());
+ mLayoutChangeListener = listenerCapture.getValue();
+
mPackageFuture = SettableFuture.create();
- when(mCommunalSource.requestCommunalSurface(any(), anyInt(), anyInt(), anyInt()))
+ when(mCommunalSource.requestCommunalSurface(any()))
.thenReturn(mPackageFuture);
}
@@ -108,19 +144,20 @@
public void testSetSurfacePackage() {
// There should be no requests without the proper state.
verify(mCommunalSource, times(0))
- .requestCommunalSurface(any(), anyInt(), anyInt(), anyInt());
+ .requestCommunalSurface(any());
// The full state must be present to make a request.
mController.onViewAttached();
verify(mCommunalSource, times(0))
- .requestCommunalSurface(any(), anyInt(), anyInt(), anyInt());
+ .requestCommunalSurface(any());
clearInvocations(mSurfaceView);
// Request surface view once all conditions are met.
mCallback.surfaceCreated(mSurfaceHolder);
- verify(mCommunalSource)
- .requestCommunalSurface(mHostToken, DISPLAY_ID, MEASURED_WIDTH, MEASURED_HEIGHT);
+ final CommunalSourceImpl.Request expectedRequest = new CommunalSourceImpl.Request(
+ MEASURED_WIDTH, MEASURED_HEIGHT, DISPLAY_ID, mHostToken);
+ verify(mCommunalSource).requestCommunalSurface(eq(expectedRequest));
when(mSurfaceView.isAttachedToWindow()).thenReturn(true);
@@ -131,7 +168,6 @@
// Make sure SurfaceView is set.
verify(mSurfaceView).setChildSurfacePackage(mSurfacePackage);
- verify(mSurfaceView).setZOrderOnTop(true);
verify(mSurfaceView).setWillNotDraw(false);
}
@@ -181,11 +217,86 @@
mFakeExecutor.runAllReady();
clearInvocations(mSurfaceView);
- verify(mCommunalSource, times(1))
- .requestCommunalSurface(mHostToken, DISPLAY_ID, MEASURED_WIDTH, MEASURED_HEIGHT);
+ final CommunalSourceImpl.Request expectedRequest = new CommunalSourceImpl.Request(
+ MEASURED_WIDTH, MEASURED_HEIGHT, DISPLAY_ID, mHostToken);
+ verify(mCommunalSource, times(1)).requestCommunalSurface(eq(expectedRequest));
mController.onViewDetached();
assertTrue(mPackageFuture.isCancelled());
verify(mSurfaceView).setWillNotDraw(true);
}
+
+ @Test
+ public void testTapExclusion() {
+ final int left = 0;
+ final int top = 0;
+ final int right = 200;
+ final int bottom = 100;
+ final Region splitNotificationExclusionRegion = new Region(
+ left,
+ top + SPLIT_NOTIFICATION_STATUS_BAR_HEIGHT,
+ right,
+ bottom - KEYGUARD_INDICATION_BOTTOM_PADDING);
+
+ final Region notificationExclusionRegion = new Region(
+ left,
+ top + NOTIFICATION_PANEL_MARGIN_TOP,
+ right,
+ bottom - KEYGUARD_INDICATION_BOTTOM_PADDING);
+
+ // There should be no exclusion when communal isn't present.
+ mLayoutChangeListener.onLayoutChange(mSurfaceView, left, top, right, bottom, 0, 0, 0, 0);
+ verify(mNotificationShadeWindowController)
+ .setTouchExclusionRegion(eq(new Region()));
+
+
+ // Attach view
+ mController.onViewAttached();
+ clearInvocations(mNotificationShadeWindowController);
+ // Verify tap exclusion area matches proper dimensions.
+ mLayoutChangeListener.onLayoutChange(mSurfaceView, left, top, right, bottom, 0, 0, 0, 0);
+ verify(mNotificationShadeWindowController)
+ .setTouchExclusionRegion(eq(splitNotificationExclusionRegion));
+
+ // Switch to normal notification margin, verify padding changes.
+ clearInvocations(mNotificationShadeWindowController);
+ when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(false);
+ mLayoutChangeListener.onLayoutChange(mSurfaceView, left, top, right, bottom, 0, 0, 0, 0);
+ verify(mNotificationShadeWindowController)
+ .setTouchExclusionRegion(eq(notificationExclusionRegion));
+
+ // Occlude, verify no exclude region.
+ clearInvocations(mNotificationShadeWindowController);
+ when(mCommunalStateController.getCommunalViewOccluded()).thenReturn(true);
+ mLayoutChangeListener.onLayoutChange(mSurfaceView, left, top, right, bottom, 0, 0, 0, 0);
+ verify(mNotificationShadeWindowController)
+ .setTouchExclusionRegion(eq(new Region()));
+ }
+
+ @Test
+ public void testLayoutChange() {
+ final int left = 0;
+ final int top = 0;
+ final int right = 200;
+ final int bottom = 100;
+
+ givenSurfacePresent();
+
+ // Layout change should trigger a request to get new communal surface.
+ mLayoutChangeListener.onLayoutChange(mSurfaceView, left, top, right, bottom, 0, 0, 0,
+ 0);
+ // Note that the measured are preset and different than the layout input.
+ final CommunalSourceImpl.Request expectedRequest =
+ new CommunalSourceImpl.Request(MEASURED_WIDTH, MEASURED_HEIGHT, DISPLAY_ID,
+ mHostToken);
+ verify(mCommunalSource)
+ .requestCommunalSurface(eq(expectedRequest));
+
+ clearInvocations(mCommunalSource);
+
+ // Subsequent matching layout change should not trigger any request.
+ mLayoutChangeListener.onLayoutChange(mSurfaceView, left, top, right, bottom, 0, 0, 0,
+ 0);
+ verify(mCommunalSource, never()).requestCommunalSurface(any());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java
index 664072f..a685e20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java
@@ -42,6 +42,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -76,6 +77,7 @@
@Mock private Sensor mSensor;
@Mock private DreamHelper mDreamHelper;
@Mock private InputMonitorCompat mInputMonitor;
+ @Mock private InputChannelCompat.InputEventReceiver mInputEventReceiver;
private final long mTimestamp = Instant.now().toEpochMilli();
private KeyguardStateController.Callback mKeyguardStateCallback;
@@ -91,6 +93,7 @@
when(mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(mSensor);
when(mInputMonitorFactory.getInputMonitor("IdleHostViewController"))
.thenReturn(mInputMonitor);
+ when(mInputMonitor.getInputReceiver(any(), any(), any())).thenReturn(mInputEventReceiver);
mController = new IdleHostViewController(mContext,
mBroadcastDispatcher, mPowerManager, mSensorManager, mIdleHostView,
@@ -231,4 +234,21 @@
// Verifies it goes to sleep.
verify(mPowerManager).goToSleep(anyLong(), anyInt(), anyInt());
}
+
+ @Test
+ public void testInputEventReceiverLifecycle() {
+ // Keyguard showing.
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ mKeyguardStateCallback.onKeyguardShowingChanged();
+
+ // Should register input event receiver.
+ verify(mInputMonitor).getInputReceiver(any(), any(), any());
+
+ // Keyguard dismissed.
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
+ mKeyguardStateCallback.onKeyguardShowingChanged();
+
+ // Should dispose input event receiver.
+ verify(mInputEventReceiver).dispose();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index fbb0a95..c746bca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -20,8 +20,8 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.mock;
@@ -424,7 +424,7 @@
mock(MetricsLogger.class),
mock(StatusBarStateController.class),
mock(ActivityStarter.class),
- mQSLogger
+ QSTileHostTest.this.mQSLogger
);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 912bea2f..59948d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs
+import android.content.res.Configuration
import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import com.android.internal.logging.MetricsLogger
@@ -27,12 +28,13 @@
import com.android.systemui.plugins.qs.QSTileView
import com.android.systemui.qs.customize.QSCustomizerController
import com.android.systemui.qs.logging.QSLogger
-import com.android.systemui.flags.FeatureFlags
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
@@ -65,11 +67,11 @@
@Mock
private lateinit var tileView: QSTileView
@Mock
- private lateinit var featureFlags: FeatureFlags
- @Mock
private lateinit var quickQsBrightnessController: QuickQSBrightnessController
@Mock
private lateinit var footerActionsController: FooterActionsController
+ @Captor
+ private lateinit var captor: ArgumentCaptor<QSPanel.OnConfigurationChangedListener>
private lateinit var controller: QuickQSPanelController
@@ -78,6 +80,7 @@
MockitoAnnotations.initMocks(this)
`when`(quickQSPanel.tileLayout).thenReturn(tileLayout)
+ `when`(quickQSPanel.isAttachedToWindow).thenReturn(true)
`when`(quickQSPanel.dumpableTag).thenReturn("")
`when`(quickQSPanel.resources).thenReturn(mContext.resources)
`when`(qsTileHost.createTileView(any(), any(), anyBoolean())).thenReturn(tileView)
@@ -123,4 +126,16 @@
verify(quickQSPanel, times(limit)).addTile(any())
}
+
+ @Test
+ fun testBrightnessAndFooterVisibilityRefreshedWhenConfigurationChanged() {
+ // times(2) because both controller and base controller are registering their listeners
+ verify(quickQSPanel, times(2)).addOnConfigurationChangedListener(captor.capture())
+
+ captor.allValues.forEach { it.onConfigurationChange(Configuration.EMPTY) }
+
+ verify(quickQsBrightnessController).refreshVisibility(anyBoolean())
+ // times(2) because footer visibility is also refreshed on controller init
+ verify(footerActionsController, times(2)).refreshVisibility(anyBoolean())
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index 9b5c161..9755d91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -270,4 +270,20 @@
verify(customTileStatePersister)
.persistState(TileServiceKey(componentName, customTile.user), t)
}
+
+ @Test
+ fun testAvailableBeforeInitialization() {
+ `when`(packageManager.getApplicationInfo(anyString(), anyInt()))
+ .thenThrow(PackageManager.NameNotFoundException())
+ val tile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ assertTrue(tile.isAvailable)
+ }
+
+ @Test
+ fun testNotAvailableAfterInitializationWithoutIcon() {
+ val tile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ tile.initialize()
+ testableLooper.processAllMessages()
+ assertFalse(tile.isAvailable)
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
index 63ebe92..23e5168 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -74,6 +75,24 @@
}
@Test
+ public void testMutateIconDrawable() {
+ SlashImageView iv = mock(SlashImageView.class);
+ Drawable originalDrawable = mock(Drawable.class);
+ Drawable otherDrawable = mock(Drawable.class);
+ State s = new State();
+ s.icon = mock(Icon.class);
+ when(s.icon.getInvisibleDrawable(eq(mContext))).thenReturn(originalDrawable);
+ when(s.icon.getDrawable(eq(mContext))).thenReturn(originalDrawable);
+ when(iv.isShown()).thenReturn(true);
+ when(originalDrawable.getConstantState()).thenReturn(fakeConstantState(otherDrawable));
+
+
+ mIconView.updateIcon(iv, s, /* allowAnimations= */true);
+
+ verify(iv).setState(any(), eq(otherDrawable));
+ }
+
+ @Test
public void testNoFirstFade() {
ImageView iv = mock(ImageView.class);
State s = new State();
@@ -104,4 +123,18 @@
public void testIconNotSet_toString() {
assertFalse(mIconView.toString().contains("lastIcon"));
}
+
+ private static Drawable.ConstantState fakeConstantState(Drawable otherDrawable) {
+ return new Drawable.ConstantState() {
+ @Override
+ public Drawable newDrawable() {
+ return otherDrawable;
+ }
+
+ @Override
+ public int getChangingConfigurations() {
+ return 1;
+ }
+ };
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index be7917a..2416132 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -425,17 +425,18 @@
final boolean credentialAllowed = true;
final boolean requireConfirmation = true;
final int userId = 10;
- final String packageName = "test";
final long operationId = 1;
+ final String packageName = "test";
+ final long requestId = 10;
final int multiSensorConfig = BiometricManager.BIOMETRIC_MULTI_SENSOR_DEFAULT;
mCommandQueue.showAuthenticationDialog(promptInfo, receiver, sensorIds,
- credentialAllowed, requireConfirmation , userId, packageName, operationId,
+ credentialAllowed, requireConfirmation, userId, operationId, packageName, requestId,
multiSensorConfig);
waitForIdleSync();
verify(mCallbacks).showAuthenticationDialog(eq(promptInfo), eq(receiver), eq(sensorIds),
- eq(credentialAllowed), eq(requireConfirmation), eq(userId), eq(packageName),
- eq(operationId), eq(multiSensorConfig));
+ eq(credentialAllowed), eq(requireConfirmation), eq(userId), eq(operationId),
+ eq(packageName), eq(requestId), eq(multiSensorConfig));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index a7b1446..7f72f19 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -25,6 +25,7 @@
import android.view.ViewRootImpl
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.Interpolators
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.BiometricUnlockController
@@ -178,10 +179,21 @@
@Test
fun setQsPanelExpansion_appliesBlur() {
+ statusBarState = StatusBarState.KEYGUARD
notificationShadeDepthController.qsPanelExpansion = 1f
- notificationShadeDepthController.onPanelExpansionChanged(0.5f, tracking = false)
+ notificationShadeDepthController.onPanelExpansionChanged(1f, tracking = false)
notificationShadeDepthController.updateBlurCallback.doFrame(0)
- verify(blurUtils).applyBlur(any(), anyInt(), eq(false))
+ verify(blurUtils).applyBlur(any(), eq(maxBlur), eq(false))
+ }
+
+ @Test
+ fun setQsPanelExpansion_easing() {
+ statusBarState = StatusBarState.KEYGUARD
+ notificationShadeDepthController.qsPanelExpansion = 0.25f
+ notificationShadeDepthController.onPanelExpansionChanged(1f, tracking = false)
+ notificationShadeDepthController.updateBlurCallback.doFrame(0)
+ verify(wallpaperManager).setWallpaperZoomOut(any(),
+ eq(Interpolators.getNotificationScrimAlpha(0.25f, false /* notifications */)))
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/EntryUtil.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/EntryUtil.kt
index 62667bc..da956ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/EntryUtil.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/EntryUtil.kt
@@ -30,3 +30,7 @@
modifier(builder)
builder.apply(entry)
}
+
+fun getAttachState(entry: ListEntry): ListAttachState {
+ return entry.attachState
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
new file mode 100644
index 0000000..2e676bb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.render
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.getAttachState
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import com.android.systemui.util.mockito.any
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+class NodeSpecBuilderTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var viewBarn: NotifViewBarn
+
+ private var rootController: NodeController = buildFakeController("rootController")
+ private var headerController0: NodeController = buildFakeController("header0")
+ private var headerController1: NodeController = buildFakeController("header1")
+ private var headerController2: NodeController = buildFakeController("header2")
+
+ private val section0 = buildSection(0, headerController0)
+ private val section0NoHeader = buildSection(0, null)
+ private val section1 = buildSection(1, headerController1)
+ private val section1NoHeader = buildSection(1, null)
+ private val section2 = buildSection(2, headerController2)
+
+ private val fakeViewBarn = FakeViewBarn()
+
+ private lateinit var specBuilder: NodeSpecBuilder
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ `when`(viewBarn.requireView(any())).thenAnswer {
+ fakeViewBarn.getViewByEntry(it.getArgument(0))
+ }
+
+ specBuilder = NodeSpecBuilder(viewBarn)
+ }
+
+ @Test
+ fun testSimpleMapping() {
+ checkOutput(
+ // GIVEN a simple flat list of notifications all in the same headerless section
+ listOf(
+ notif(0, section0NoHeader),
+ notif(1, section0NoHeader),
+ notif(2, section0NoHeader),
+ notif(3, section0NoHeader)
+ ),
+
+ // THEN we output a similarly simple flag list of nodes
+ tree(
+ notifNode(0),
+ notifNode(1),
+ notifNode(2),
+ notifNode(3)
+ )
+ )
+ }
+
+ @Test
+ fun testHeaderInjection() {
+ checkOutput(
+ // GIVEN a flat list of notifications, spread across three sections
+ listOf(
+ notif(0, section0),
+ notif(1, section0),
+ notif(2, section1),
+ notif(3, section2)
+ ),
+
+ // THEN each section has its header injected
+ tree(
+ node(headerController0),
+ notifNode(0),
+ notifNode(1),
+ node(headerController1),
+ notifNode(2),
+ node(headerController2),
+ notifNode(3)
+ )
+ )
+ }
+
+ @Test
+ fun testGroups() {
+ checkOutput(
+ // GIVEN a mixed list of top-level notifications and groups
+ listOf(
+ notif(0, section0),
+ group(1, section1,
+ notif(2),
+ notif(3),
+ notif(4)
+ ),
+ notif(5, section2),
+ group(6, section2,
+ notif(7),
+ notif(8),
+ notif(9)
+ )
+ ),
+
+ // THEN we properly construct all the nodes
+ tree(
+ node(headerController0),
+ notifNode(0),
+ node(headerController1),
+ notifNode(1,
+ notifNode(2),
+ notifNode(3),
+ notifNode(4)
+ ),
+ node(headerController2),
+ notifNode(5),
+ notifNode(6,
+ notifNode(7),
+ notifNode(8),
+ notifNode(9)
+ )
+ )
+ )
+ }
+
+ @Test
+ fun testSecondSectionWithNoHeader() {
+ checkOutput(
+ // GIVEN a middle section with no associated header view
+ listOf(
+ notif(0, section0),
+ notif(1, section1NoHeader),
+ group(2, section1NoHeader,
+ notif(3),
+ notif(4)
+ ),
+ notif(5, section2)
+ ),
+
+ // THEN the header view is left out of the tree (but the notifs are still present)
+ tree(
+ node(headerController0),
+ notifNode(0),
+ notifNode(1),
+ notifNode(2,
+ notifNode(3),
+ notifNode(4)
+ ),
+ node(headerController2),
+ notifNode(5)
+ )
+ )
+ }
+
+ @Test(expected = RuntimeException::class)
+ fun testRepeatedSectionsThrow() {
+ checkOutput(
+ // GIVEN a malformed list where sections are not contiguous
+ listOf(
+ notif(0, section0),
+ notif(1, section1),
+ notif(2, section0)
+ ),
+
+ // THEN an exception is thrown
+ tree()
+ )
+ }
+
+ private fun checkOutput(list: List<ListEntry>, desiredTree: NodeSpecImpl) {
+ checkTree(desiredTree, specBuilder.buildNodeSpec(rootController, list))
+ }
+
+ private fun checkTree(desiredTree: NodeSpec, actualTree: NodeSpec) {
+ try {
+ checkNode(desiredTree, actualTree)
+ } catch (e: AssertionError) {
+ throw AssertionError("Trees don't match: ${e.message}\nActual tree:\n" +
+ treeSpecToStr(actualTree))
+ }
+ }
+
+ private fun checkNode(desiredTree: NodeSpec, actualTree: NodeSpec) {
+ if (actualTree.controller != desiredTree.controller) {
+ throw AssertionError("Node {${actualTree.controller.nodeLabel}} should " +
+ "be ${desiredTree.controller.nodeLabel}")
+ }
+ for (i in 0 until desiredTree.children.size) {
+ if (i >= actualTree.children.size) {
+ throw AssertionError("Node {${actualTree.controller.nodeLabel}}" +
+ " is missing child ${desiredTree.children[i].controller.nodeLabel}")
+ }
+ checkNode(desiredTree.children[i], actualTree.children[i])
+ }
+ }
+
+ private fun notif(id: Int, section: NotifSection? = null): NotificationEntry {
+ val entry = NotificationEntryBuilder()
+ .setId(id)
+ .build()
+ if (section != null) {
+ getAttachState(entry).section = section
+ }
+ fakeViewBarn.buildNotifView(id, entry)
+ return entry
+ }
+
+ private fun group(
+ id: Int,
+ section: NotifSection,
+ vararg children: NotificationEntry
+ ): GroupEntry {
+ val group = GroupEntryBuilder()
+ .setKey("group_$id")
+ .setSummary(
+ NotificationEntryBuilder()
+ .setId(id)
+ .build())
+ .setChildren(children.asList())
+ .build()
+ getAttachState(group).section = section
+ fakeViewBarn.buildNotifView(id, group.summary!!)
+
+ for (child in children) {
+ getAttachState(child).section = section
+ }
+ return group
+ }
+
+ private fun tree(vararg children: NodeSpecImpl): NodeSpecImpl {
+ return node(rootController, *children)
+ }
+
+ private fun node(view: NodeController, vararg children: NodeSpecImpl): NodeSpecImpl {
+ val node = NodeSpecImpl(null, view)
+ node.children.addAll(children)
+ return node
+ }
+
+ private fun notifNode(id: Int, vararg children: NodeSpecImpl): NodeSpecImpl {
+ return node(fakeViewBarn.getViewById(id), *children)
+ }
+}
+
+private class FakeViewBarn {
+ private val entries = mutableMapOf<Int, NotificationEntry>()
+ private val views = mutableMapOf<NotificationEntry, NodeController>()
+
+ fun buildNotifView(id: Int, entry: NotificationEntry) {
+ if (entries.contains(id)) {
+ throw RuntimeException("ID $id is already in use")
+ }
+ entries[id] = entry
+ views[entry] = buildFakeController("Entry $id")
+ }
+
+ fun getViewById(id: Int): NodeController {
+ return views[entries[id] ?: throw RuntimeException("No view with ID $id")]!!
+ }
+
+ fun getViewByEntry(entry: NotificationEntry): NodeController {
+ return views[entry] ?: throw RuntimeException("No view defined for key ${entry.key}")
+ }
+}
+
+private fun buildFakeController(name: String): NodeController {
+ val controller = Mockito.mock(NodeController::class.java)
+ `when`(controller.nodeLabel).thenReturn(name)
+ return controller
+}
+
+private fun buildSection(index: Int, nodeController: NodeController?): NotifSection {
+ return NotifSection(object : NotifSectioner("Section $index") {
+
+ override fun isInSection(entry: ListEntry?): Boolean {
+ throw NotImplementedError("This should never be called")
+ }
+
+ override fun getHeaderNodeController(): NodeController? {
+ return nodeController
+ }
+ }, index)
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 690b841..f34f21b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -31,42 +31,31 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
-
private static final int SCREEN_HEIGHT = 2000;
private static final int EMPTY_MARGIN = 0;
private static final int EMPTY_HEIGHT = 0;
private static final float ZERO_DRAG = 0.f;
private static final float OPAQUE = 1.f;
private static final float TRANSPARENT = 0.f;
- private static final boolean HAS_CUSTOM_CLOCK = false;
- private static final boolean HAS_VISIBLE_NOTIFS = false;
-
private KeyguardClockPositionAlgorithm mClockPositionAlgorithm;
private KeyguardClockPositionAlgorithm.Result mClockPosition;
- private int mNotificationStackHeight;
private float mPanelExpansion;
private int mKeyguardStatusHeight;
private float mDark;
- private boolean mHasCustomClock;
- private boolean mHasVisibleNotifs;
private float mQsExpansion;
- private int mCutoutTopInset = 0; // in pixels
+ private int mCutoutTopInsetPx = 0;
private boolean mIsSplitShade = false;
@Before
public void setUp() {
mClockPositionAlgorithm = new KeyguardClockPositionAlgorithm();
mClockPosition = new KeyguardClockPositionAlgorithm.Result();
-
- mHasCustomClock = HAS_CUSTOM_CLOCK;
- mHasVisibleNotifs = HAS_VISIBLE_NOTIFS;
}
@Test
public void clockPositionTopOfScreenOnAOD() {
- // GIVEN on AOD and both stack scroll and clock have 0 height
+ // GIVEN on AOD and clock has 0 height
givenAOD();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = EMPTY_HEIGHT;
// WHEN the clock position algorithm is run
positionClock();
@@ -79,11 +68,10 @@
@Test
public void clockPositionBelowCutout() {
- // GIVEN on AOD and both stack scroll and clock have 0 height
+ // GIVEN on AOD and clock has 0 height
givenAOD();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = EMPTY_HEIGHT;
- mCutoutTopInset = 300;
+ mCutoutTopInsetPx = 300;
// WHEN the clock position algorithm is run
positionClock();
// THEN the clock Y position is below the cutout
@@ -97,7 +85,6 @@
public void clockPositionAdjustsForKeyguardStatusOnAOD() {
// GIVEN on AOD with a clock of height 100
givenAOD();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = 100;
// WHEN the clock position algorithm is run
positionClock();
@@ -112,7 +99,6 @@
public void clockPositionLargeClockOnAOD() {
// GIVEN on AOD with a full screen clock
givenAOD();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = SCREEN_HEIGHT;
// WHEN the clock position algorithm is run
positionClock();
@@ -125,9 +111,8 @@
@Test
public void clockPositionTopOfScreenOnLockScreen() {
- // GIVEN on lock screen with stack scroll and clock of 0 height
+ // GIVEN on lock screen with clock of 0 height
givenLockScreen();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = EMPTY_HEIGHT;
// WHEN the clock position algorithm is run
positionClock();
@@ -138,24 +123,9 @@
}
@Test
- public void clockPositionWithStackScrollExpandOnLockScreen() {
- // GIVEN on lock screen with stack scroll of height 500
- givenLockScreen();
- mNotificationStackHeight = 500;
- mKeyguardStatusHeight = EMPTY_HEIGHT;
- // WHEN the clock position algorithm is run
- positionClock();
- // THEN the clock Y position stays to the top
- assertThat(mClockPosition.clockY).isEqualTo(0);
- // AND the clock is positioned on the left.
- assertThat(mClockPosition.clockX).isEqualTo(0);
- }
-
- @Test
public void clockPositionWithPartialDragOnLockScreen() {
// GIVEN dragging up on lock screen
givenLockScreen();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = EMPTY_HEIGHT;
mPanelExpansion = 0.5f;
// WHEN the clock position algorithm is run
@@ -171,7 +141,6 @@
public void clockPositionWithFullDragOnLockScreen() {
// GIVEN the lock screen is dragged up
givenLockScreen();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = EMPTY_HEIGHT;
mPanelExpansion = 0.f;
// WHEN the clock position algorithm is run
@@ -184,7 +153,6 @@
public void largeClockOnLockScreenIsTransparent() {
// GIVEN on lock screen with a full screen clock
givenLockScreen();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = SCREEN_HEIGHT;
// WHEN the clock position algorithm is run
positionClock();
@@ -194,9 +162,8 @@
@Test
public void notifPositionTopOfScreenOnAOD() {
- // GIVEN on AOD and both stack scroll and clock have 0 height
+ // GIVEN on AOD and clock has 0 height
givenAOD();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = EMPTY_HEIGHT;
// WHEN the position algorithm is run
positionClock();
@@ -208,7 +175,6 @@
public void notifPositionIndependentOfKeyguardStatusHeightOnAOD() {
// GIVEN on AOD and clock has a nonzero height
givenAOD();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = 100;
// WHEN the position algorithm is run
positionClock();
@@ -220,7 +186,6 @@
public void notifPositionWithLargeClockOnAOD() {
// GIVEN on AOD and clock has a nonzero height
givenAOD();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = SCREEN_HEIGHT;
// WHEN the position algorithm is run
positionClock();
@@ -230,9 +195,8 @@
@Test
public void notifPositionMiddleOfScreenOnLockScreen() {
- // GIVEN on lock screen and both stack scroll and clock have 0 height
+ // GIVEN on lock screen and clock has 0 height
givenLockScreen();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = EMPTY_HEIGHT;
// WHEN the position algorithm is run
positionClock();
@@ -241,47 +205,20 @@
}
@Test
- public void notifPositionAdjustsForStackHeightOnLockScreen() {
- // GIVEN on lock screen and stack scroller has a nonzero height
- givenLockScreen();
- mNotificationStackHeight = 500;
- mKeyguardStatusHeight = EMPTY_HEIGHT;
- // WHEN the position algorithm is run
- positionClock();
- // THEN the notif padding adjusts for keyguard status height
- assertThat(mClockPosition.stackScrollerPadding).isEqualTo(0);
- }
-
- @Test
public void notifPositionAdjustsForClockHeightOnLockScreen() {
// GIVEN on lock screen and stack scroller has a nonzero height
givenLockScreen();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = 200;
// WHEN the position algorithm is run
positionClock();
- // THEN the notif padding adjusts for both clock and notif stack.
- assertThat(mClockPosition.stackScrollerPadding).isEqualTo(200);
- }
-
- @Test
- public void notifPositionAdjustsForStackHeightAndClockHeightOnLockScreen() {
- // GIVEN on lock screen and stack scroller has a nonzero height
- givenLockScreen();
- mNotificationStackHeight = 500;
- mKeyguardStatusHeight = 200;
- // WHEN the position algorithm is run
- positionClock();
- // THEN the notifs are placed below the statusview
assertThat(mClockPosition.stackScrollerPadding).isEqualTo(200);
}
@Test
public void notifPositionAlignedWithClockInSplitShadeMode() {
- // GIVEN on lock screen and split shade mode
givenLockScreen();
mIsSplitShade = true;
- mHasCustomClock = true;
+ mKeyguardStatusHeight = 200;
// WHEN the position algorithm is run
positionClock();
// THEN the notif padding DOESN'T adjust for keyguard status height.
@@ -292,7 +229,6 @@
public void notifPositionWithLargeClockOnLockScreen() {
// GIVEN on lock screen and clock has a nonzero height
givenLockScreen();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = SCREEN_HEIGHT;
// WHEN the position algorithm is run
positionClock();
@@ -304,7 +240,6 @@
public void notifPositionWithFullDragOnLockScreen() {
// GIVEN the lock screen is dragged up
givenLockScreen();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = EMPTY_HEIGHT;
mPanelExpansion = 0.f;
// WHEN the clock position algorithm is run
@@ -317,19 +252,17 @@
public void notifPositionWithLargeClockFullDragOnLockScreen() {
// GIVEN the lock screen is dragged up and a full screen clock
givenLockScreen();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = SCREEN_HEIGHT;
mPanelExpansion = 0.f;
// WHEN the clock position algorithm is run
positionClock();
- // THEN the notif padding is zero.
assertThat(mClockPosition.stackScrollerPadding).isEqualTo(
(int) (mKeyguardStatusHeight * .667f));
}
@Test
public void clockHiddenWhenQsIsExpanded() {
- // GIVEN on the lock screen with a custom clock and visible notifications
+ // GIVEN on the lock screen with visible notifications
givenLockScreen();
mQsExpansion = 1;
// WHEN the clock position algorithm is run
@@ -349,12 +282,11 @@
}
private void positionClock() {
- mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT, mNotificationStackHeight,
- mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight,
+ mClockPositionAlgorithm.setup(EMPTY_MARGIN, mPanelExpansion, mKeyguardStatusHeight,
0 /* userSwitchHeight */, 0 /* userSwitchPreferredY */,
- mHasCustomClock, mHasVisibleNotifs, mDark, ZERO_DRAG, false /* bypassEnabled */,
+ mDark, ZERO_DRAG, false /* bypassEnabled */,
0 /* unlockedStackScrollerPadding */, mQsExpansion,
- mCutoutTopInset, mIsSplitShade);
+ mCutoutTopInsetPx, mIsSplitShade);
mClockPositionAlgorithm.run(mClockPosition);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index d92816e..faf968b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -17,10 +17,14 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import static com.android.systemui.statusbar.StatusBarState.SHADE;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -30,18 +34,24 @@
import androidx.test.filters.SmallTest;
import com.android.keyguard.CarrierTextController;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -65,12 +75,25 @@
private FeatureFlags mFeatureFlags;
@Mock
private BatteryMeterViewController mBatteryMeterViewController;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+ @Mock
+ private KeyguardBypassController mKeyguardBypassController;
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ private BiometricUnlockController mBiometricUnlockController;
+ @Mock
+ private SysuiStatusBarStateController mStatusBarStateController;
+ private TestNotificationPanelViewStateProvider mNotificationPanelViewStateProvider;
private KeyguardStatusBarView mKeyguardStatusBarView;
private KeyguardStatusBarViewController mController;
@Before
public void setup() throws Exception {
+ mNotificationPanelViewStateProvider = new TestNotificationPanelViewStateProvider();
+
MockitoAnnotations.initMocks(this);
allowTestableLooperAsMainThread();
@@ -89,7 +112,13 @@
mUserInfoController,
mStatusBarIconController,
new StatusBarIconController.TintedIconManager.Factory(mFeatureFlags),
- mBatteryMeterViewController
+ mBatteryMeterViewController,
+ mNotificationPanelViewStateProvider,
+ mKeyguardStateController,
+ mKeyguardBypassController,
+ mKeyguardUpdateMonitor,
+ mBiometricUnlockController,
+ mStatusBarStateController
);
}
@@ -177,4 +206,179 @@
assertThat(mKeyguardStatusBarView.getAlpha()).isEqualTo(newAlpha);
assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(newVisibility);
}
+
+ @Test
+ public void updateViewState_notKeyguardState_nothingUpdated() {
+ mController.onViewAttached();
+ updateStateToNotKeyguard();
+
+ float oldAlpha = mKeyguardStatusBarView.getAlpha();
+
+ mController.updateViewState();
+
+ assertThat(mKeyguardStatusBarView.getAlpha()).isEqualTo(oldAlpha);
+ }
+
+ @Test
+ public void updateViewState_bypassEnabledAndShouldListenForFace_viewHidden() {
+ mController.onViewAttached();
+ updateStateToKeyguard();
+ assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
+
+ when(mKeyguardUpdateMonitor.shouldListenForFace()).thenReturn(true);
+ when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
+ onFinishedGoingToSleep();
+
+ mController.updateViewState();
+
+ assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
+ }
+
+ @Test
+ public void updateViewState_bypassNotEnabled_viewShown() {
+ mController.onViewAttached();
+ updateStateToKeyguard();
+
+ when(mKeyguardUpdateMonitor.shouldListenForFace()).thenReturn(true);
+ when(mKeyguardBypassController.getBypassEnabled()).thenReturn(false);
+ onFinishedGoingToSleep();
+
+ mController.updateViewState();
+
+ assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void updateViewState_shouldNotListenForFace_viewShown() {
+ mController.onViewAttached();
+ updateStateToKeyguard();
+
+ when(mKeyguardUpdateMonitor.shouldListenForFace()).thenReturn(false);
+ when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
+ onFinishedGoingToSleep();
+
+ mController.updateViewState();
+
+ assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void updateViewState_panelExpandedHeightZero_viewHidden() {
+ mController.onViewAttached();
+ updateStateToKeyguard();
+
+ mNotificationPanelViewStateProvider.setPanelViewExpandedHeight(0);
+
+ mController.updateViewState();
+
+ assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
+ }
+
+ @Test
+ public void updateViewState_qsExpansionOne_viewHidden() {
+ mController.onViewAttached();
+ updateStateToKeyguard();
+
+ mNotificationPanelViewStateProvider.setQsExpansionFraction(1f);
+
+ mController.updateViewState();
+
+ assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
+ }
+
+ // TODO(b/195442899): Add more tests for #updateViewState once CLs are finalized.
+
+ @Test
+ public void updateForHeadsUp_headsUpShouldBeVisible_viewHidden() {
+ mController.onViewAttached();
+ updateStateToKeyguard();
+ mKeyguardStatusBarView.setVisibility(View.VISIBLE);
+
+ mNotificationPanelViewStateProvider.setShouldHeadsUpBeVisible(true);
+ mController.updateForHeadsUp(/* animate= */ false);
+
+ assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
+ }
+
+ @Test
+ public void updateForHeadsUp_headsUpShouldNotBeVisible_viewShown() {
+ mController.onViewAttached();
+ updateStateToKeyguard();
+
+ // Start with the opposite state.
+ mNotificationPanelViewStateProvider.setShouldHeadsUpBeVisible(true);
+ mController.updateForHeadsUp(/* animate= */ false);
+
+ mNotificationPanelViewStateProvider.setShouldHeadsUpBeVisible(false);
+ mController.updateForHeadsUp(/* animate= */ false);
+
+ assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ private void updateStateToNotKeyguard() {
+ updateStatusBarState(SHADE);
+ }
+
+ private void updateStateToKeyguard() {
+ updateStatusBarState(KEYGUARD);
+ }
+
+ private void updateStatusBarState(int state) {
+ ArgumentCaptor<StatusBarStateController.StateListener> statusBarStateListenerCaptor =
+ ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
+ verify(mStatusBarStateController).addCallback(statusBarStateListenerCaptor.capture());
+ StatusBarStateController.StateListener callback = statusBarStateListenerCaptor.getValue();
+
+ callback.onStateChanged(state);
+ }
+
+ /**
+ * Calls {@link com.android.keyguard.KeyguardUpdateMonitorCallback#onFinishedGoingToSleep(int)}
+ * to ensure values are updated properly.
+ */
+ private void onFinishedGoingToSleep() {
+ ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateCallbackCaptor =
+ ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+ verify(mKeyguardUpdateMonitor).registerCallback(keyguardUpdateCallbackCaptor.capture());
+ KeyguardUpdateMonitorCallback callback = keyguardUpdateCallbackCaptor.getValue();
+
+ callback.onFinishedGoingToSleep(0);
+ }
+
+ private static class TestNotificationPanelViewStateProvider
+ implements NotificationPanelViewController.NotificationPanelViewStateProvider {
+
+ TestNotificationPanelViewStateProvider() {}
+
+ private float mPanelViewExpandedHeight = 100f;
+ private float mQsExpansionFraction = 0f;
+ private boolean mShouldHeadsUpBeVisible = false;
+
+ @Override
+ public float getPanelViewExpandedHeight() {
+ return mPanelViewExpandedHeight;
+ }
+
+ @Override
+ public float getQsExpansionFraction() {
+ return mQsExpansionFraction;
+ }
+
+ @Override
+ public boolean shouldHeadsUpBeVisible() {
+ return mShouldHeadsUpBeVisible;
+ }
+
+ public void setPanelViewExpandedHeight(float panelViewExpandedHeight) {
+ this.mPanelViewExpandedHeight = panelViewExpandedHeight;
+ }
+
+ public void setQsExpansionFraction(float qsExpansionFraction) {
+ this.mQsExpansionFraction = qsExpansionFraction;
+ }
+
+ public void setShouldHeadsUpBeVisible(boolean shouldHeadsUpBeVisible) {
+ this.mShouldHeadsUpBeVisible = shouldHeadsUpBeVisible;
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 7338fad..a8b2990 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -29,6 +29,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
@@ -47,7 +48,6 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
-import android.hardware.biometrics.BiometricSourceType;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
@@ -96,6 +96,7 @@
import com.android.systemui.communal.dagger.CommunalViewComponent;
import com.android.systemui.controls.dagger.ControlsComponent;
import com.android.systemui.doze.DozeLog;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.idle.IdleHostViewController;
@@ -200,6 +201,8 @@
@Mock
private LayoutInflater mLayoutInflater;
@Mock
+ private FeatureFlags mFeatureFlags;
+ @Mock
private DynamicPrivacyController mDynamicPrivacyController;
@Mock
private ShadeController mShadeController;
@@ -241,8 +244,6 @@
@Mock
private ConversationNotificationManager mConversationNotificationManager;
@Mock
- private BiometricUnlockController mBiometricUnlockController;
- @Mock
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock
private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
@@ -441,7 +442,7 @@
.thenReturn(mKeyguardClockSwitchController);
when(mKeyguardStatusViewComponent.getKeyguardStatusViewController())
.thenReturn(mKeyguardStatusViewController);
- when(mKeyguardStatusBarViewComponentFactory.build(any()))
+ when(mKeyguardStatusBarViewComponentFactory.build(any(), any()))
.thenReturn(mKeyguardStatusBarViewComponent);
when(mKeyguardStatusBarViewComponent.getKeyguardStatusBarViewController())
.thenReturn(mKeyguardStatusBarViewController);
@@ -467,6 +468,7 @@
mResources,
new Handler(Looper.getMainLooper()),
mLayoutInflater,
+ mFeatureFlags,
coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController,
mFalsingManager, new FalsingCollectorFake(),
mNotificationLockscreenUserManager, mNotificationEntryManager,
@@ -476,7 +478,7 @@
mCommunalSourceMonitor, mMetricsLogger, mActivityManager, mConfigurationController,
() -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager,
mConversationNotificationManager, mMediaHiearchyManager,
- mBiometricUnlockController, mStatusBarKeyguardViewManager,
+ mStatusBarKeyguardViewManager,
mNotificationStackScrollLayoutController,
mKeyguardStatusViewComponentFactory,
mKeyguardQsUserSwitchComponentFactory,
@@ -574,20 +576,6 @@
}
@Test
- public void testKeyguardStatusBarVisibility_hiddenForBypass() {
- when(mUpdateMonitor.shouldListenForFace()).thenReturn(true);
- mNotificationPanelViewController.mKeyguardUpdateCallback.onBiometricRunningStateChanged(
- true, BiometricSourceType.FACE);
- verify(mKeyguardStatusBar, never()).setVisibility(View.VISIBLE);
-
- when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
- mNotificationPanelViewController.mKeyguardUpdateCallback.onFinishedGoingToSleep(0);
- mNotificationPanelViewController.mKeyguardUpdateCallback.onBiometricRunningStateChanged(
- true, BiometricSourceType.FACE);
- verify(mKeyguardStatusBar, never()).setVisibility(View.VISIBLE);
- }
-
- @Test
public void testA11y_initializeNode() {
AccessibilityNodeInfo nodeInfo = new AccessibilityNodeInfo();
mAccessibiltyDelegate.onInitializeAccessibilityNodeInfo(mView, nodeInfo);
@@ -867,9 +855,10 @@
@Test
public void testCommunalhostViewControllerInit() {
+ verify(mCommunalHostViewController, times(1)).init();
clearInvocations(mCommunalHostViewController);
givenViewAttached();
- verify(mCommunalHostViewController).init();
+ verify(mCommunalHostViewController, never()).init();
}
@Test
@@ -890,8 +879,6 @@
clearInvocations(mCommunalHostViewController);
givenViewDetached();
verify(mCommunalSourceMonitor).removeCallback(any());
- verify(mCommunalHostViewController).show(sourceCapture.capture());
- assertThat(sourceCapture.getValue()).isEqualTo(null);
}
@Test
@@ -918,6 +905,28 @@
anyBoolean(), anyBoolean(), anyInt());
}
+ @Test
+ public void testCommunalAlphaUpdate() {
+ // Verify keyguard content alpha changes are propagate. Note the actual value set is not
+ // checked since an interpolation is applied to the incoming value.
+ mNotificationPanelViewController.setKeyguardOnlyContentAlpha(0.8f);
+ verify(mCommunalHostViewController).setAlpha(anyFloat());
+ }
+
+ @Test
+ public void testCommunalPositionUpdate() {
+ // Verify that the communal position is updated on interaction with the
+ // NotificationPanelViewController. Note that there a number of paths where the position
+ // might be updated and therefore the check isn't strictly on a single invocation.
+ clearInvocations(mCommunalHostViewController);
+ final View.OnLayoutChangeListener layoutChangeListener =
+ mNotificationPanelViewController.createLayoutChangeListener();
+ mNotificationPanelViewController.mStatusBarStateController.setState(KEYGUARD);
+ layoutChangeListener.onLayoutChange(mView, 0, 0, 200, 200, 0, 0, 200, 200);
+ verify(mCommunalHostViewController, atLeast(1))
+ .updatePosition(anyInt(), anyBoolean());
+ }
+
private void triggerPositionClockAndNotifications() {
mNotificationPanelViewController.closeQs();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index bcc257d..3d887dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -46,7 +46,6 @@
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -92,7 +91,7 @@
@Mock private NotificationPanelViewController mNotificationPanelViewController;
@Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
@Mock private NotificationShadeDepthController mNotificationShadeDepthController;
- @Mock private SuperStatusBarViewFactory mStatusBarViewFactory;
+ @Mock private StatusBarWindowView mStatusBarWindowView;
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -140,7 +139,7 @@
mNotificationShadeDepthController,
mView,
mNotificationPanelViewController,
- mStatusBarViewFactory,
+ mStatusBarWindowView,
mNotificationStackScrollLayoutController,
mStatusBarKeyguardViewManager);
mController.setupExpandedStatusBar();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 9d316b1..5ebe900 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -712,7 +712,7 @@
public void qsExpansion_half_clippingQs() {
reset(mScrimBehind);
mScrimController.setClipsQsScrim(true);
- mScrimController.setQsPosition(0.5f, 999 /* value doesn't matter */);
+ mScrimController.setQsPosition(0.25f, 999 /* value doesn't matter */);
finishAnimationsImmediately();
assertScrimAlpha(Map.of(
@@ -1138,7 +1138,7 @@
@Test
public void testScrimsVisible_whenShadeVisibleOnLockscreen() {
mScrimController.transitionTo(ScrimState.KEYGUARD);
- mScrimController.setQsPosition(0.5f, 300);
+ mScrimController.setQsPosition(0.25f, 300);
assertScrimAlpha(Map.of(
mScrimBehind, SEMI_TRANSPARENT,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
index 10eb71f..1503af8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
@@ -16,32 +16,54 @@
package com.android.systemui.statusbar.phone
+import android.content.Context
+import android.content.res.Configuration
import android.graphics.Rect
import android.test.suitebuilder.annotation.SmallTest
+import android.view.Display
import android.view.DisplayCutout
-import android.view.WindowMetrics
import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.leak.RotationUtils
import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
import com.android.systemui.util.leak.RotationUtils.ROTATION_NONE
import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE
import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN
import com.android.systemui.util.leak.RotationUtils.Rotation
+import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Test
+import org.mockito.ArgumentMatchers.any
import org.mockito.Mock
import org.mockito.Mockito.`when`
+import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
@SmallTest
class StatusBarContentInsetsProviderTest : SysuiTestCase() {
+
@Mock private lateinit var dc: DisplayCutout
- @Mock private lateinit var windowMetrics: WindowMetrics
+ @Mock private lateinit var contextMock: Context
+ @Mock private lateinit var display: Display
+ private lateinit var configurationController: ConfigurationController
+
+ private val configuration = Configuration()
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
+ `when`(contextMock.display).thenReturn(display)
+
+ context.ensureTestableResources()
+ `when`(contextMock.resources).thenReturn(context.resources)
+ `when`(contextMock.resources.configuration).thenReturn(configuration)
+ `when`(contextMock.createConfigurationContext(any())).thenAnswer {
+ context.createConfigurationContext(it.arguments[0] as Configuration)
+ }
+
+ configurationController = ConfigurationControllerImpl(contextMock)
}
@Test
@@ -55,15 +77,13 @@
val chipWidth = 30
val dotWidth = 10
- `when`(windowMetrics.bounds).thenReturn(screenBounds)
-
var isRtl = false
var targetRotation = ROTATION_NONE
var bounds = calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
null,
- windowMetrics,
+ screenBounds,
sbHeightPortrait,
minLeftPadding,
minRightPadding)
@@ -92,7 +112,7 @@
currentRotation,
targetRotation,
dc,
- windowMetrics,
+ screenBounds,
sbHeightLandscape,
minLeftPadding,
minRightPadding)
@@ -127,7 +147,6 @@
val sbHeightLandscape = 60
val currentRotation = ROTATION_NONE
- `when`(windowMetrics.bounds).thenReturn(screenBounds)
`when`(dc.boundingRects).thenReturn(listOf(dcBounds))
// THEN rotations which share a short side should use the greater value between rounded
@@ -142,7 +161,7 @@
currentRotation,
targetRotation,
dc,
- windowMetrics,
+ screenBounds,
sbHeightPortrait,
minLeftPadding,
minRightPadding)
@@ -159,7 +178,7 @@
currentRotation,
targetRotation,
dc,
- windowMetrics,
+ screenBounds,
sbHeightLandscape,
minLeftPadding,
minRightPadding)
@@ -178,7 +197,7 @@
currentRotation,
targetRotation,
dc,
- windowMetrics,
+ screenBounds,
sbHeightPortrait,
minLeftPadding,
minRightPadding)
@@ -196,7 +215,7 @@
currentRotation,
targetRotation,
dc,
- windowMetrics,
+ screenBounds,
sbHeightLandscape,
minLeftPadding,
minRightPadding)
@@ -219,7 +238,6 @@
val sbHeightLandscape = 60
val currentRotation = ROTATION_NONE
- `when`(windowMetrics.bounds).thenReturn(screenBounds)
`when`(dc.boundingRects).thenReturn(listOf(dcBounds))
// THEN only the landscape/seascape rotations should avoid the cutout area because of the
@@ -234,7 +252,7 @@
currentRotation,
targetRotation,
dc,
- windowMetrics,
+ screenBounds,
sbHeightPortrait,
minLeftPadding,
minRightPadding)
@@ -251,7 +269,7 @@
currentRotation,
targetRotation,
dc,
- windowMetrics,
+ screenBounds,
sbHeightLandscape,
minLeftPadding,
minRightPadding)
@@ -268,7 +286,7 @@
currentRotation,
targetRotation,
dc,
- windowMetrics,
+ screenBounds,
sbHeightPortrait,
minLeftPadding,
minRightPadding)
@@ -285,7 +303,7 @@
currentRotation,
targetRotation,
dc,
- windowMetrics,
+ screenBounds,
sbHeightLandscape,
minLeftPadding,
minRightPadding)
@@ -303,8 +321,6 @@
val sbHeightPortrait = 100
val sbHeightLandscape = 60
- `when`(windowMetrics.bounds).thenReturn(screenBounds)
-
// THEN content insets should only use rounded corner padding
var targetRotation = ROTATION_NONE
var expectedBounds = Rect(minLeftPadding,
@@ -316,7 +332,7 @@
currentRotation,
targetRotation,
null, /* no cutout */
- windowMetrics,
+ screenBounds,
sbHeightPortrait,
minLeftPadding,
minRightPadding)
@@ -332,7 +348,7 @@
currentRotation,
targetRotation,
null, /* no cutout */
- windowMetrics,
+ screenBounds,
sbHeightLandscape,
minLeftPadding,
minRightPadding)
@@ -348,7 +364,7 @@
currentRotation,
targetRotation,
null, /* no cutout */
- windowMetrics,
+ screenBounds,
sbHeightPortrait,
minLeftPadding,
minRightPadding)
@@ -364,7 +380,7 @@
currentRotation,
targetRotation,
null, /* no cutout */
- windowMetrics,
+ screenBounds,
sbHeightLandscape,
minLeftPadding,
minRightPadding)
@@ -382,7 +398,6 @@
val sbHeightLandscape = 60
val currentRotation = ROTATION_NONE
- `when`(windowMetrics.bounds).thenReturn(screenBounds)
`when`(dc.boundingRects).thenReturn(listOf(dcBounds))
// THEN left should be set to the display cutout width, and right should use the minRight
@@ -396,7 +411,7 @@
currentRotation,
targetRotation,
dc,
- windowMetrics,
+ screenBounds,
sbHeightPortrait,
minLeftPadding,
minRightPadding)
@@ -404,6 +419,67 @@
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
+ @Test
+ fun testDisplayChanged_returnsUpdatedInsets() {
+ // GIVEN: get insets on the first display and switch to the second display
+ val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
+ mock(DumpManager::class.java))
+
+ givenDisplay(
+ screenBounds = Rect(0, 0, 1080, 2160),
+ displayUniqueId = "1"
+ )
+ val firstDisplayInsets = provider.getStatusBarContentInsetsForRotation(ROTATION_NONE)
+ givenDisplay(
+ screenBounds = Rect(0, 0, 800, 600),
+ displayUniqueId = "2"
+ )
+ configurationController.onConfigurationChanged(configuration)
+
+ // WHEN: get insets on the second display
+ val secondDisplayInsets = provider.getStatusBarContentInsetsForRotation(ROTATION_NONE)
+
+ // THEN: insets are updated
+ assertThat(firstDisplayInsets).isNotEqualTo(secondDisplayInsets)
+ }
+
+ @Test
+ fun testDisplayChangedAndReturnedBack_returnsTheSameInsets() {
+ // GIVEN: get insets on the first display, switch to the second display,
+ // get insets and switch back
+ val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
+ mock(DumpManager::class.java))
+ givenDisplay(
+ screenBounds = Rect(0, 0, 1080, 2160),
+ displayUniqueId = "1"
+ )
+ val firstDisplayInsetsFirstCall = provider
+ .getStatusBarContentInsetsForRotation(ROTATION_NONE)
+ givenDisplay(
+ screenBounds = Rect(0, 0, 800, 600),
+ displayUniqueId = "2"
+ )
+ configurationController.onConfigurationChanged(configuration)
+ provider.getStatusBarContentInsetsForRotation(ROTATION_NONE)
+ givenDisplay(
+ screenBounds = Rect(0, 0, 1080, 2160),
+ displayUniqueId = "1"
+ )
+ configurationController.onConfigurationChanged(configuration)
+
+ // WHEN: get insets on the first display again
+ val firstDisplayInsetsSecondCall = provider
+ .getStatusBarContentInsetsForRotation(ROTATION_NONE)
+
+ // THEN: insets for the first and second calls for the first display are the same
+ assertThat(firstDisplayInsetsFirstCall).isEqualTo(firstDisplayInsetsSecondCall)
+ }
+
+ private fun givenDisplay(screenBounds: Rect, displayUniqueId: String) {
+ `when`(display.uniqueId).thenReturn(displayUniqueId)
+ configuration.windowConfiguration.maxBounds = screenBounds
+ }
+
private fun assertRects(
expected: Rect,
actual: Rect,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 71c61ab..2f7e39b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -109,7 +109,6 @@
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -140,12 +139,12 @@
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
+import com.android.systemui.unfold.config.UnfoldTransitionConfig;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.MessageRouterImpl;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.wmshell.BubblesManager;
-import com.android.systemui.unfold.config.UnfoldTransitionConfig;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.startingsurface.StartingSurface;
@@ -160,8 +159,6 @@
import java.io.PrintWriter;
import java.util.Optional;
-import javax.inject.Provider;
-
import dagger.Lazy;
@SmallTest
@@ -235,12 +232,11 @@
@Mock private ViewMediatorCallback mKeyguardVieMediatorCallback;
@Mock private VolumeComponent mVolumeComponent;
@Mock private CommandQueue mCommandQueue;
- @Mock private Provider<StatusBarComponent.Builder> mStatusBarComponentBuilderProvider;
- @Mock private StatusBarComponent.Builder mStatusBarComponentBuilder;
+ @Mock private StatusBarComponent.Factory mStatusBarComponentFactory;
@Mock private StatusBarComponent mStatusBarComponent;
@Mock private PluginManager mPluginManager;
@Mock private LegacySplitScreen mLegacySplitScreen;
- @Mock private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
+ @Mock private StatusBarWindowView mStatusBarWindowView;
@Mock private LightsOutNotifController mLightsOutNotifController;
@Mock private ViewMediatorCallback mViewMediatorCallback;
@Mock private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
@@ -341,8 +337,7 @@
when(mLockscreenWallpaperLazy.get()).thenReturn(mLockscreenWallpaper);
when(mBiometricUnlockControllerLazy.get()).thenReturn(mBiometricUnlockController);
- when(mStatusBarComponentBuilderProvider.get()).thenReturn(mStatusBarComponentBuilder);
- when(mStatusBarComponentBuilder.build()).thenReturn(mStatusBarComponent);
+ when(mStatusBarComponentFactory.create()).thenReturn(mStatusBarComponent);
when(mStatusBarComponent.getNotificationShadeWindowViewController()).thenReturn(
mNotificationShadeWindowViewController);
@@ -407,13 +402,13 @@
mDozeScrimController,
mVolumeComponent,
mCommandQueue,
- mStatusBarComponentBuilderProvider,
+ mStatusBarComponentFactory,
mPluginManager,
Optional.of(mLegacySplitScreen),
mLightsOutNotifController,
mStatusBarNotificationActivityStarterBuilder,
mShadeController,
- mSuperStatusBarViewFactory,
+ mStatusBarWindowView,
mStatusBarKeyguardViewManager,
mViewMediatorCallback,
mInitController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 21c4a17..c488ee9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -70,6 +70,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
@@ -241,7 +242,9 @@
mMockBd,
mDemoModeController,
mCarrierConfigTracker,
- mFeatureFlags);
+ mFeatureFlags,
+ mock(DumpManager.class)
+ );
setupNetworkController();
// Trigger blank callbacks to always get the current state (some tests don't trigger
@@ -309,7 +312,8 @@
mock(AccessPointControllerImpl.class),
mock(DataUsageController.class), mMockSubDefaults,
mock(DeviceProvisionedController.class), mMockBd, mDemoModeController,
- mCarrierConfigTracker, mFeatureFlags);
+ mCarrierConfigTracker, mFeatureFlags,
+ mock(DumpManager.class));
setupNetworkController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index bc4c2b6..3433a14 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -21,6 +21,7 @@
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.DataUsageController;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.util.CarrierConfigTracker;
import org.junit.Test;
@@ -113,7 +114,7 @@
mock(AccessPointControllerImpl.class),
mock(DataUsageController.class), mMockSubDefaults,
mock(DeviceProvisionedController.class), mMockBd, mDemoModeController,
- mock(CarrierConfigTracker.class), mFeatureFlags);
+ mock(CarrierConfigTracker.class), mFeatureFlags, mock(DumpManager.class));
setupNetworkController();
setupDefaultSignal();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index 5090b0d..4ff1301 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -41,6 +41,7 @@
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.R;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.util.CarrierConfigTracker;
import org.junit.Test;
@@ -67,7 +68,8 @@
Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
- mDemoModeController, mock(CarrierConfigTracker.class), mFeatureFlags);
+ mDemoModeController, mock(CarrierConfigTracker.class), mFeatureFlags,
+ mock(DumpManager.class));
setupNetworkController();
verifyLastMobileDataIndicators(false, -1, 0);
@@ -87,7 +89,8 @@
Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
- mDemoModeController, mock(CarrierConfigTracker.class), mFeatureFlags);
+ mDemoModeController, mock(CarrierConfigTracker.class), mFeatureFlags,
+ mock(DumpManager.class));
mNetworkController.registerListeners();
// Wait for the main looper to execute the previous command
@@ -155,7 +158,8 @@
Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
- mDemoModeController, mock(CarrierConfigTracker.class), mFeatureFlags);
+ mDemoModeController, mock(CarrierConfigTracker.class), mFeatureFlags,
+ mock(DumpManager.class));
setupNetworkController();
// No Subscriptions.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
index 9c47f19..e6dc4db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
@@ -96,6 +96,8 @@
DumpManager mDumpManager;
@Mock
OverlayManagerTransaction.Builder mTransactionBuilder;
+ @Mock
+ Runnable mOnOverlaysApplied;
private ThemeOverlayApplier mManager;
private boolean mGetOverlayInfoEnabled = true;
@@ -103,7 +105,8 @@
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
- mManager = new ThemeOverlayApplier(mOverlayManager, MoreExecutors.directExecutor(),
+ mManager = new ThemeOverlayApplier(mOverlayManager,
+ MoreExecutors.directExecutor(), MoreExecutors.directExecutor(),
LAUNCHER_PACKAGE, THEMEPICKER_PACKAGE, mDumpManager) {
@Override
protected OverlayManagerTransaction.Builder getTransactionBuilder() {
@@ -173,7 +176,7 @@
@Test
public void allCategoriesSpecified_allEnabledExclusively() {
mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
- TEST_USER_HANDLES);
+ TEST_USER_HANDLES, mOnOverlaysApplied);
verify(mOverlayManager).commit(any());
for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) {
@@ -185,7 +188,7 @@
@Test
public void allCategoriesSpecified_sysuiCategoriesAlsoAppliedToSysuiUser() {
mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
- TEST_USER_HANDLES);
+ TEST_USER_HANDLES, mOnOverlaysApplied);
for (Map.Entry<String, OverlayIdentifier> entry : ALL_CATEGORIES_MAP.entrySet()) {
if (SYSTEM_USER_CATEGORIES.contains(entry.getKey())) {
@@ -202,8 +205,9 @@
public void allCategoriesSpecified_enabledForAllUserHandles() {
Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES);
mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
- userHandles);
+ userHandles, mOnOverlaysApplied);
+ verify(mOnOverlaysApplied).run();
for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) {
verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true),
eq(TEST_USER.getIdentifier()));
@@ -219,7 +223,7 @@
Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES);
mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
- userHandles);
+ userHandles, mOnOverlaysApplied);
for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) {
verify(mTransactionBuilder, never()).setEnabled(eq(overlayPackage), eq(true),
@@ -233,7 +237,7 @@
mock(FabricatedOverlay.class)
};
mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, pendingCreation,
- TEST_USER.getIdentifier(), TEST_USER_HANDLES);
+ TEST_USER.getIdentifier(), TEST_USER_HANDLES, mOnOverlaysApplied);
for (FabricatedOverlay overlay : pendingCreation) {
verify(mTransactionBuilder).registerFabricatedOverlay(eq(overlay));
@@ -247,7 +251,7 @@
categoryToPackage.remove(OVERLAY_CATEGORY_ICON_ANDROID);
mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER.getIdentifier(),
- TEST_USER_HANDLES);
+ TEST_USER_HANDLES, mOnOverlaysApplied);
for (OverlayIdentifier overlayPackage : categoryToPackage.values()) {
verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true),
@@ -264,7 +268,7 @@
@Test
public void zeroCategoriesSpecified_allDisabled() {
mManager.applyCurrentUserOverlays(Maps.newArrayMap(), null, TEST_USER.getIdentifier(),
- TEST_USER_HANDLES);
+ TEST_USER_HANDLES, mOnOverlaysApplied);
for (String category : THEME_CATEGORIES) {
verify(mTransactionBuilder).setEnabled(
@@ -279,7 +283,7 @@
categoryToPackage.put("blah.category", new OverlayIdentifier("com.example.blah.category"));
mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER.getIdentifier(),
- TEST_USER_HANDLES);
+ TEST_USER_HANDLES, mOnOverlaysApplied);
verify(mTransactionBuilder, never()).setEnabled(
eq(new OverlayIdentifier("com.example.blah.category")), eq(false),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index f6a54936..cd911cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -161,7 +161,7 @@
ArgumentCaptor.forClass(Map.class);
verify(mThemeOverlayApplier)
- .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
+ .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any());
// Assert that we received the colors that we were expecting
assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
@@ -183,7 +183,7 @@
mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED));
mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK),
null, null), WallpaperManager.FLAG_SYSTEM);
- verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
}
@Test
@@ -204,7 +204,7 @@
ArgumentCaptor.forClass(Map.class);
verify(mThemeOverlayApplier)
- .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
+ .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any());
// Assert that we received the colors that we were expecting
assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
@@ -240,7 +240,7 @@
.isFalse();
verify(mThemeOverlayApplier)
- .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
}
@Test
@@ -270,7 +270,7 @@
"android.theme.customization.color_both\":\"0")).isTrue();
verify(mThemeOverlayApplier)
- .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
}
@Test
@@ -300,7 +300,7 @@
"android.theme.customization.color_both\":\"1")).isTrue();
verify(mThemeOverlayApplier)
- .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
}
@Test
@@ -327,7 +327,7 @@
assertThat(updatedSetting.getValue().contains("android.theme.customization.color_index"))
.isFalse();
verify(mThemeOverlayApplier)
- .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
}
@Test
@@ -354,7 +354,7 @@
assertThat(updatedSetting.getValue().contains("android.theme.customization.color_index"))
.isFalse();
verify(mThemeOverlayApplier)
- .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
}
@Test
@@ -382,7 +382,7 @@
eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture());
verify(mThemeOverlayApplier)
- .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
}
@Test
@@ -411,14 +411,14 @@
verify(mThemeOverlayApplier, never())
- .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
}
@Test
public void onProfileAdded_setsTheme() {
mBroadcastReceiver.getValue().onReceive(null,
new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
- verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
}
@Test
@@ -428,7 +428,7 @@
mBroadcastReceiver.getValue().onReceive(null,
new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
verify(mThemeOverlayApplier)
- .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
}
@Test
@@ -438,7 +438,7 @@
mBroadcastReceiver.getValue().onReceive(null,
new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
verify(mThemeOverlayApplier, never())
- .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
}
@Test
@@ -450,7 +450,7 @@
Color.valueOf(Color.BLUE), null);
mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
- verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
// Regression test: null events should not reset the internal state and allow colors to be
// applied again.
@@ -458,11 +458,11 @@
mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED));
mColorsListener.getValue().onColorsChanged(null, WallpaperManager.FLAG_SYSTEM);
verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(),
- any());
+ any(), any());
mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.GREEN),
null, null), WallpaperManager.FLAG_SYSTEM);
verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(),
- any());
+ any(), any());
}
@Test
@@ -499,7 +499,7 @@
verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture());
// Colors were applied during controller initialization.
- verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
clearInvocations(mThemeOverlayApplier);
}
@@ -533,7 +533,7 @@
verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture());
// Colors were applied during controller initialization.
- verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
clearInvocations(mThemeOverlayApplier);
WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
@@ -542,12 +542,12 @@
// Defers event because we already have initial colors.
verify(mThemeOverlayApplier, never())
- .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
// Then event happens after setup phase is over.
when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
mDeviceProvisionedListener.getValue().onUserSetupChanged();
- verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
}
@Test
@@ -568,11 +568,11 @@
Color.valueOf(Color.RED), null);
mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
verify(mThemeOverlayApplier, never())
- .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
mWakefulnessLifecycle.dispatchFinishedGoingToSleep();
verify(mThemeOverlayApplier, never())
- .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
}
@Test
@@ -592,10 +592,10 @@
Color.valueOf(Color.RED), null);
mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
verify(mThemeOverlayApplier, never())
- .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
mWakefulnessLifecycleObserver.getValue().onFinishedGoingToSleep();
- verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
}
@Test
@@ -614,7 +614,7 @@
ArgumentCaptor.forClass(Map.class);
verify(mThemeOverlayApplier)
- .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
+ .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any());
// Assert that we received the colors that we were expecting
assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index fd05c23..a9cd0ed 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -3432,7 +3432,8 @@
mConnectionId = service.mId;
- mClient = AccessibilityInteractionClient.getInstance(mContext);
+ mClient = AccessibilityInteractionClient.getInstance(/* initializeCache= */false,
+ mContext);
mClient.addConnection(mConnectionId, service);
//TODO: (multi-display) We need to support multiple displays.
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
index 95f3560..b095e3e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
@@ -433,13 +433,28 @@
MotionEvent.BUTTON_PRIMARY, 1.0f, 1.0f, mLastMotionEvent.getDeviceId(), 0,
mLastMotionEvent.getSource(), mLastMotionEvent.getFlags());
- // The only real difference between these two events is the action flag.
+ MotionEvent pressEvent = MotionEvent.obtain(downEvent);
+ pressEvent.setAction(MotionEvent.ACTION_BUTTON_PRESS);
+ pressEvent.setActionButton(MotionEvent.BUTTON_PRIMARY);
+
+ MotionEvent releaseEvent = MotionEvent.obtain(downEvent);
+ releaseEvent.setAction(MotionEvent.ACTION_BUTTON_RELEASE);
+ releaseEvent.setActionButton(MotionEvent.BUTTON_PRIMARY);
+ releaseEvent.setButtonState(0);
+
MotionEvent upEvent = MotionEvent.obtain(downEvent);
upEvent.setAction(MotionEvent.ACTION_UP);
+ upEvent.setButtonState(0);
AutoclickController.super.onMotionEvent(downEvent, downEvent, mEventPolicyFlags);
downEvent.recycle();
+ AutoclickController.super.onMotionEvent(pressEvent, pressEvent, mEventPolicyFlags);
+ pressEvent.recycle();
+
+ AutoclickController.super.onMotionEvent(releaseEvent, releaseEvent, mEventPolicyFlags);
+ releaseEvent.recycle();
+
AutoclickController.super.onMotionEvent(upEvent, upEvent, mEventPolicyFlags);
upEvent.recycle();
}
diff --git a/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java b/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java
index 5f9aaae..9801407 100644
--- a/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java
+++ b/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java
@@ -40,6 +40,7 @@
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
@@ -47,6 +48,7 @@
import android.view.accessibility.AccessibilityManager;
import com.android.internal.R;
+import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.ImageUtils;
@@ -71,6 +73,7 @@
@VisibleForTesting
protected static final String ACTION_DISMISS_NOTIFICATION =
TAG + ".ACTION_DISMISS_NOTIFICATION";
+ private static final String EXTRA_TIME_FOR_LOGGING = "start_time_to_log_a11y_tool";
private static final int SEND_NOTIFICATION_DELAY_HOURS = 24;
/** Current enabled accessibility services. */
@@ -179,7 +182,8 @@
intent.setPackage(context.getPackageName())
.setIdentifier(serviceComponentName.flattenToShortString())
.putExtra(Intent.EXTRA_COMPONENT_NAME, serviceComponentName)
- .putExtra(Intent.EXTRA_USER_ID, userId);
+ .putExtra(Intent.EXTRA_USER_ID, userId)
+ .putExtra(Intent.EXTRA_TIME, SystemClock.elapsedRealtime());
return intent;
}
@@ -214,11 +218,24 @@
if (TextUtils.isEmpty(action) || componentName == null) {
return;
}
+ final long startTimeMills = intent.getLongExtra(Intent.EXTRA_TIME, 0);
+ final long durationMills =
+ startTimeMills > 0 ? SystemClock.elapsedRealtime() - startTimeMills : 0;
final int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_SYSTEM);
if (ACTION_SEND_NOTIFICATION.equals(action)) {
- trySendNotification(userId, componentName);
+ if (trySendNotification(userId, componentName)) {
+ AccessibilityStatsLogUtils.logNonA11yToolServiceWarningReported(
+ componentName.getPackageName(),
+ AccessibilityStatsLogUtils.ACCESSIBILITY_PRIVACY_WARNING_STATUS_SHOWN,
+ durationMills);
+ }
} else if (ACTION_A11Y_SETTINGS.equals(action)) {
- launchSettings(userId, componentName);
+ if (tryLaunchSettings(userId, componentName)) {
+ AccessibilityStatsLogUtils.logNonA11yToolServiceWarningReported(
+ componentName.getPackageName(),
+ AccessibilityStatsLogUtils.ACCESSIBILITY_PRIVACY_WARNING_STATUS_CLICKED,
+ durationMills);
+ }
mNotificationManager.cancel(componentName.flattenToShortString(),
NOTE_A11Y_VIEW_AND_CONTROL_ACCESS);
onNotificationCanceled(userId, componentName);
@@ -240,12 +257,12 @@
}
}
- private void trySendNotification(int userId, ComponentName componentName) {
+ private boolean trySendNotification(int userId, ComponentName componentName) {
if (!AccessibilitySecurityPolicy.POLICY_WARNING_ENABLED) {
- return;
+ return false;
}
if (userId != mCurrentUserId) {
- return;
+ return false;
}
List<AccessibilityServiceInfo> enabledServiceInfos = getEnabledServiceInfos();
@@ -264,23 +281,27 @@
android.R.dimen.app_icon_size);
sendNotification(userId, componentName, displayName,
ImageUtils.buildScaledBitmap(drawable, size, size));
+ return true;
}
break;
}
}
+ return false;
}
- private void launchSettings(int userId, ComponentName componentName) {
+ private boolean tryLaunchSettings(int userId, ComponentName componentName) {
if (userId != mCurrentUserId) {
- return;
+ return false;
}
final Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.putExtra(Intent.EXTRA_COMPONENT_NAME, componentName.flattenToShortString());
+ intent.putExtra(EXTRA_TIME_FOR_LOGGING, SystemClock.elapsedRealtime());
final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId(
mContext.getDisplayId()).toBundle();
mContext.startActivityAsUser(intent, bundle, UserHandle.of(userId));
mContext.getSystemService(StatusBarManager.class).collapsePanels();
+ return true;
}
protected void onNotificationCanceled(int userId, ComponentName componentName) {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 689890f..6505c20 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -154,7 +154,7 @@
"android.hardware.configstore-V1.0-java",
"android.hardware.contexthub-V1.0-java",
"android.hardware.rebootescrow-V1-java",
- "android.hardware.soundtrigger-V2.4-java",
+ "android.hardware.soundtrigger-V2.3-java",
"android.hardware.power.stats-V1-java",
"android.hidl.manager-V1.2-java",
"capture_state_listener-aidl-java",
@@ -207,10 +207,3 @@
name: "protolog.conf.json.gz",
src: ":services.core.json.gz",
}
-
-filegroup {
- name: "services.core-sources-deviceconfig-interface",
- srcs: [
- "java/com/android/server/utils/DeviceConfigInterface.java",
- ],
-}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 0d3b064..608cbf2 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -25,9 +25,9 @@
import static android.os.UserHandle.USER_SYSTEM;
import android.Manifest;
+import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
-import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -93,7 +93,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Locale;
@@ -804,35 +803,6 @@
return mIsHearingAidProfileSupported;
}
- @Override
- /** @hide */
- public java.util.List<String> getSystemConfigEnabledProfilesForPackage(String packageName) {
- if (Binder.getCallingUid() != Process.BLUETOOTH_UID) {
- Slog.w(TAG, "getSystemConfigEnabledProfilesForPackage(): not allowed for non-bluetooth");
- return null;
- }
-
- SystemConfig systemConfig = SystemConfig.getInstance();
- if (systemConfig == null) {
- return null;
- }
-
- android.util.ArrayMap<String, Boolean> componentEnabledStates =
- systemConfig.getComponentsEnabledStates(packageName);
- if (componentEnabledStates == null) {
- return null;
- }
-
- ArrayList enabledProfiles = new ArrayList<String>();
- for (Map.Entry<String, Boolean> entry : componentEnabledStates.entrySet()) {
- if (entry.getValue()) {
- enabledProfiles.add(entry.getKey());
- }
- }
-
- return enabledProfiles;
- }
-
private boolean isDeviceProvisioned() {
return Settings.Global.getInt(mContentResolver, Settings.Global.DEVICE_PROVISIONED,
0) != 0;
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index 9e8b9c6..3288ca8 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -23,21 +23,6 @@
"file_patterns": ["NotificationManagerService\\.java"]
},
{
- "name": "CtsContentTestCases",
- "options": [
- {
- "include-filter": "android.content.cts.ClipboardManagerTest"
- },
- {
- "include-filter": "android.content.cts.ClipDataTest"
- },
- {
- "include-filter": "android.content.cts.ClipDescriptionTest"
- }
- ],
- "file_patterns": ["ClipboardService\\.java"]
- },
- {
"name": "FrameworksMockingServicesTests",
"options": [
{
@@ -59,6 +44,21 @@
{
"name": "CtsScopedStorageDeviceOnlyTest",
"file_patterns": ["StorageManagerService\\.java"]
+ },
+ {
+ "name": "CtsContentTestCases",
+ "options": [
+ {
+ "include-filter": "android.content.cts.ClipboardManagerTest"
+ },
+ {
+ "include-filter": "android.content.cts.ClipDataTest"
+ },
+ {
+ "include-filter": "android.content.cts.ClipDescriptionTest"
+ }
+ ],
+ "file_patterns": ["ClipboardService\\.java"]
}
]
}
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 449f02e..3c6b682 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -492,7 +492,7 @@
for (int uid : uidsToRemove) {
FrameworkStatsLog.write(FrameworkStatsLog.ISOLATED_UID_CHANGED, -1, uid,
FrameworkStatsLog.ISOLATED_UID_CHANGED__EVENT__REMOVED);
- mStats.removeIsolatedUidLocked(uid, SystemClock.elapsedRealtime(),
+ mStats.maybeRemoveIsolatedUidLocked(uid, SystemClock.elapsedRealtime(),
SystemClock.uptimeMillis());
}
mStats.clearPendingRemovedUidsLocked();
diff --git a/services/core/java/com/android/server/am/CacheOomRanker.java b/services/core/java/com/android/server/am/CacheOomRanker.java
index 1ead7e3..bd7ee01 100644
--- a/services/core/java/com/android/server/am/CacheOomRanker.java
+++ b/services/core/java/com/android/server/am/CacheOomRanker.java
@@ -243,6 +243,7 @@
float lruWeight;
float usesWeight;
float rssWeight;
+ int preserveTopNApps;
int[] lruPositions;
RankedProcessRecord[] scoredProcessRecords;
@@ -250,6 +251,7 @@
lruWeight = mLruWeight;
usesWeight = mUsesWeight;
rssWeight = mRssWeight;
+ preserveTopNApps = mPreserveTopNApps;
lruPositions = mLruPositions;
scoredProcessRecords = mScoredProcessRecords;
}
@@ -276,19 +278,19 @@
++numProcessesEvaluated;
}
- // Count how many apps we're not re-ranking (up to mPreserveTopNApps).
+ // Count how many apps we're not re-ranking (up to preserveTopNApps).
int numProcessesNotReRanked = 0;
while (numProcessesEvaluated < lruProcessServiceStart
- && numProcessesNotReRanked < mPreserveTopNApps) {
+ && numProcessesNotReRanked < preserveTopNApps) {
ProcessRecord process = lruList.get(numProcessesEvaluated);
if (appCanBeReRanked(process)) {
numProcessesNotReRanked++;
}
numProcessesEvaluated++;
}
- // Exclude the top `mPreserveTopNApps` apps from re-ranking.
- if (numProcessesNotReRanked < mPreserveTopNApps) {
- numProcessesReRanked -= mPreserveTopNApps - numProcessesNotReRanked;
+ // Exclude the top `preserveTopNApps` apps from re-ranking.
+ if (numProcessesNotReRanked < preserveTopNApps) {
+ numProcessesReRanked -= preserveTopNApps - numProcessesNotReRanked;
if (numProcessesReRanked < 0) {
numProcessesReRanked = 0;
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index e94276c..d1e56a0 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -72,6 +72,7 @@
import static com.android.server.am.ProcessList.TAG_PROCESS_OBSERVERS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityThread;
@@ -114,6 +115,8 @@
import com.android.server.wm.WindowProcessController;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -171,6 +174,27 @@
@EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S)
static final long USE_SHORT_FGS_USAGE_INTERACTION_TIME = 183972877L;
+ static final int CACHED_COMPAT_CHANGE_PROCESS_CAPABILITY = 0;
+ static final int CACHED_COMPAT_CHANGE_CAMERA_MICROPHONE_CAPABILITY = 1;
+ static final int CACHED_COMPAT_CHANGE_USE_SHORT_FGS_USAGE_INTERACTION_TIME = 2;
+
+ @IntDef(prefix = { "CACHED_COMPAT_CHANGE_" }, value = {
+ CACHED_COMPAT_CHANGE_PROCESS_CAPABILITY,
+ CACHED_COMPAT_CHANGE_CAMERA_MICROPHONE_CAPABILITY,
+ CACHED_COMPAT_CHANGE_USE_SHORT_FGS_USAGE_INTERACTION_TIME,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ static @interface CachedCompatChangeId{}
+
+ /**
+ * Mapping from CACHED_COMPAT_CHANGE_* to the actual compat change id.
+ */
+ static final long[] CACHED_COMPAT_CHANGE_IDS_MAPPING = new long[] {
+ PROCESS_CAPABILITY_CHANGE_ID,
+ CAMERA_MICROPHONE_CAPABILITY_CHANGE_ID,
+ USE_SHORT_FGS_USAGE_INTERACTION_TIME,
+ };
+
/**
* For some direct access we need to power manager.
*/
@@ -396,6 +420,12 @@
return mPlatformCompatCache;
}
+ boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId, ApplicationInfo app,
+ boolean defaultValue) {
+ return getPlatformCompatCache().isChangeEnabled(
+ CACHED_COMPAT_CHANGE_IDS_MAPPING[cachedCompatChangeId], app, defaultValue);
+ }
+
OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids) {
this(service, processList, activeUids, createAdjusterThread());
}
@@ -1962,12 +1992,8 @@
(fgsType & FOREGROUND_SERVICE_TYPE_LOCATION)
!= 0 ? PROCESS_CAPABILITY_FOREGROUND_LOCATION : 0;
- boolean enabled = false;
- try {
- enabled = getPlatformCompatCache().isChangeEnabled(
- CAMERA_MICROPHONE_CAPABILITY_CHANGE_ID, s.appInfo);
- } catch (RemoteException e) {
- }
+ final boolean enabled = state.getCachedCompatChange(
+ CACHED_COMPAT_CHANGE_CAMERA_MICROPHONE_CAPABILITY);
if (enabled) {
capabilityFromFGS |=
(fgsType & FOREGROUND_SERVICE_TYPE_CAMERA)
@@ -2189,12 +2215,8 @@
// to client's state.
clientProcState = PROCESS_STATE_BOUND_TOP;
state.bumpAllowStartFgsState(PROCESS_STATE_BOUND_TOP);
- boolean enabled = false;
- try {
- enabled = getPlatformCompatCache().isChangeEnabled(
- PROCESS_CAPABILITY_CHANGE_ID, client.info);
- } catch (RemoteException e) {
- }
+ final boolean enabled = cstate.getCachedCompatChange(
+ CACHED_COMPAT_CHANGE_PROCESS_CAPABILITY);
if (enabled) {
if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
// TOP process passes all capabilities to the service.
@@ -2845,8 +2867,8 @@
state.setProcStateChanged(true);
}
} else if (state.hasReportedInteraction()) {
- final boolean fgsInteractionChangeEnabled = getPlatformCompatCache().isChangeEnabled(
- USE_SHORT_FGS_USAGE_INTERACTION_TIME, app.info, false);
+ final boolean fgsInteractionChangeEnabled = state.getCachedCompatChange(
+ CACHED_COMPAT_CHANGE_USE_SHORT_FGS_USAGE_INTERACTION_TIME);
final long interactionThreshold = fgsInteractionChangeEnabled
? mConstants.USAGE_STATS_INTERACTION_INTERVAL_POST_S
: mConstants.USAGE_STATS_INTERACTION_INTERVAL_PRE_S;
@@ -2856,8 +2878,8 @@
maybeUpdateUsageStatsLSP(app, nowElapsed);
}
} else {
- final boolean fgsInteractionChangeEnabled = getPlatformCompatCache().isChangeEnabled(
- USE_SHORT_FGS_USAGE_INTERACTION_TIME, app.info, false);
+ final boolean fgsInteractionChangeEnabled = state.getCachedCompatChange(
+ CACHED_COMPAT_CHANGE_USE_SHORT_FGS_USAGE_INTERACTION_TIME);
final long interactionThreshold = fgsInteractionChangeEnabled
? mConstants.SERVICE_USAGE_INTERACTION_TIME_POST_S
: mConstants.SERVICE_USAGE_INTERACTION_TIME_PRE_S;
@@ -2976,8 +2998,8 @@
if (mService.mUsageStatsService == null) {
return;
}
- final boolean fgsInteractionChangeEnabled = getPlatformCompatCache().isChangeEnabled(
- USE_SHORT_FGS_USAGE_INTERACTION_TIME, app.info, false);
+ final boolean fgsInteractionChangeEnabled = state.getCachedCompatChange(
+ CACHED_COMPAT_CHANGE_USE_SHORT_FGS_USAGE_INTERACTION_TIME);
boolean isInteraction;
// To avoid some abuse patterns, we are going to be careful about what we consider
// to be an app interaction. Being the top activity doesn't count while the display
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 8d3e442..a32fe02 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -281,7 +281,8 @@
EventLog.writeEvent(EventLogTags.AM_ANR, mApp.userId, pid, mApp.processName,
mApp.info.flags, annotation);
- if (mService.mTraceErrorLogger.isAddErrorIdEnabled()) {
+ if (mService.mTraceErrorLogger != null
+ && mService.mTraceErrorLogger.isAddErrorIdEnabled()) {
errorId = mService.mTraceErrorLogger.generateErrorId();
mService.mTraceErrorLogger.addErrorIdToTrace(mApp.processName, errorId);
} else {
@@ -427,7 +428,7 @@
float loadingProgress = 1;
IncrementalMetrics incrementalMetrics = null;
final PackageManagerInternal packageManagerInternal = mService.getPackageManagerInternal();
- if (mApp.info != null && mApp.info.packageName != null) {
+ if (mApp.info != null && mApp.info.packageName != null && packageManagerInternal != null) {
IncrementalStatesInfo incrementalStatesInfo =
packageManagerInternal.getIncrementalStatesInfo(
mApp.info.packageName, mApp.uid, mApp.userId);
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index 46144f5..7b9a356e 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -21,6 +21,7 @@
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
+import static com.android.server.am.OomAdjuster.CachedCompatChangeId;
import static com.android.server.am.ProcessRecord.TAG;
import android.annotation.ElapsedRealtimeLong;
@@ -422,6 +423,16 @@
@GuardedBy("mService")
private int mCachedIsReceivingBroadcast = VALUE_INVALID;
+ /**
+ * Cache the return value of PlatformCompat.isChangeEnabled().
+ */
+ @GuardedBy("mService")
+ private int[] mCachedCompatChanges = new int[] {
+ VALUE_INVALID, // CACHED_COMPAT_CHANGE_PROCESS_CAPABILITY
+ VALUE_INVALID, // CACHED_COMPAT_CHANGE_CAMERA_MICROPHONE_CAPABILITY
+ VALUE_INVALID, // CACHED_COMPAT_CHANGE_USE_SHORT_FGS_USAGE_INTERACTION_TIME
+ };
+
@GuardedBy("mService")
private int mCachedAdj = ProcessList.INVALID_ADJ;
@GuardedBy("mService")
@@ -611,6 +622,10 @@
@GuardedBy({"mService", "mProcLock"})
void setSetProcState(int setProcState) {
+ if (ActivityManager.isProcStateCached(mSetProcState)
+ && !ActivityManager.isProcStateCached(setProcState)) {
+ mCacheOomRankerUseCount++;
+ }
mSetProcState = setProcState;
}
@@ -874,12 +889,7 @@
@GuardedBy("mService")
void setCached(boolean cached) {
- if (mCached != cached) {
- mCached = cached;
- if (cached) {
- ++mCacheOomRankerUseCount;
- }
- }
+ mCached = cached;
}
@GuardedBy("mService")
@@ -1054,6 +1064,16 @@
}
@GuardedBy("mService")
+ boolean getCachedCompatChange(@CachedCompatChangeId int cachedCompatChangeId) {
+ if (mCachedCompatChanges[cachedCompatChangeId] == VALUE_INVALID) {
+ mCachedCompatChanges[cachedCompatChangeId] = mService.mOomAdjuster
+ .isChangeEnabled(cachedCompatChangeId, mApp.info, false /* default */)
+ ? VALUE_TRUE : VALUE_FALSE;
+ }
+ return mCachedCompatChanges[cachedCompatChangeId] == VALUE_TRUE;
+ }
+
+ @GuardedBy("mService")
void computeOomAdjFromActivitiesIfNecessary(OomAdjuster.ComputeOomAdjWindowCallback callback,
int adj, boolean foregroundActivities, boolean hasVisibleActivities, int procState,
int schedGroup, int appUid, int logUid, int processCurTop) {
@@ -1133,6 +1153,9 @@
mCurSchedGroup = mSetSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
mCurProcState = mCurRawProcState = mSetProcState = mAllowStartFgsState =
PROCESS_STATE_NONEXISTENT;
+ for (int i = 0; i < mCachedCompatChanges.length; i++) {
+ mCachedCompatChanges[i] = VALUE_INVALID;
+ }
}
@GuardedBy("mService")
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index fa7eae3..256c472 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -594,7 +594,8 @@
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("UM.onBeforeUnlockUser-" + userId);
- mInjector.getUserManager().onBeforeUnlockUser(userId);
+ final ArraySet<String> reconciledPackages =
+ mInjector.getUserManager().onBeforeUnlockUser(userId);
t.traceEnd();
synchronized (mLock) {
// Do not proceed if unexpected state
@@ -603,6 +604,9 @@
}
}
mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+ t.traceBegin("UM.onUserStateRunningUnlocking-" + userId);
+ mInjector.getUserManager().onUserStateRunningUnlocking(userId, reconciledPackages);
+ t.traceEnd();
uss.mUnlockProgress.setProgress(20);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index bd7baab..f5d29fc 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -5271,6 +5271,10 @@
// TODO investigate internal users due to deprecation of SDK API
/** @see AudioManager#setBluetoothA2dpOn(boolean) */
public void setBluetoothA2dpOn(boolean on) {
+ if (!checkAudioSettingsPermission("setBluetoothA2dpOn()")) {
+ return;
+ }
+
// for logging only
final int uid = Binder.getCallingUid();
final int pid = Binder.getCallingPid();
@@ -5296,6 +5300,10 @@
/** @see AudioManager#startBluetoothSco() */
public void startBluetoothSco(IBinder cb, int targetSdkVersion) {
+ if (!checkAudioSettingsPermission("startBluetoothSco()")) {
+ return;
+ }
+
final int uid = Binder.getCallingUid();
final int pid = Binder.getCallingPid();
final int scoAudioMode =
@@ -5318,6 +5326,10 @@
/** @see AudioManager#startBluetoothScoVirtualCall() */
public void startBluetoothScoVirtualCall(IBinder cb) {
+ if (!checkAudioSettingsPermission("startBluetoothScoVirtualCall()")) {
+ return;
+ }
+
final int uid = Binder.getCallingUid();
final int pid = Binder.getCallingPid();
final String eventSource = new StringBuilder("startBluetoothScoVirtualCall()")
@@ -8207,14 +8219,33 @@
//==========================================================================================
private final SpatializerHelper mSpatializerHelper = new SpatializerHelper();
+ private void enforceModifyDefaultAudioEffectsPermission() {
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Missing MODIFY_DEFAULT_AUDIO_EFFECTS permission");
+ }
+ }
+
/** @see AudioManager#getSpatializerImmersiveAudioLevel() */
public int getSpatializerImmersiveAudioLevel() {
- return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ return mSpatializerHelper.getImmersiveAudioLevel();
}
/** @see Spatializer#isEnabled() */
public boolean isSpatializerEnabled() {
- return false;
+ return mSpatializerHelper.isEnabled();
+ }
+
+ /** @see Spatializer#isAvailable() */
+ public boolean isSpatializerAvailable() {
+ return mSpatializerHelper.isAvailable();
+ }
+
+ /** @see Spatializer#setSpatializerEnabled(boolean) */
+ public void setSpatializerEnabled(boolean enabled) {
+ enforceModifyDefaultAudioEffectsPermission();
+ mSpatializerHelper.setEnabled(enabled);
}
/** @see Spatializer#canBeSpatialized() */
@@ -8247,39 +8278,18 @@
/** @see Spatializer#addSpatializerCompatibleAudioDevice(AudioDeviceAttributes) */
public void addSpatializerCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
- enforceModifyAudioRoutingPermission();
+ enforceModifyDefaultAudioEffectsPermission();
Objects.requireNonNull(ada);
mSpatializerHelper.addCompatibleAudioDevice(ada);
}
/** @see Spatializer#removeSpatializerCompatibleAudioDevice(AudioDeviceAttributes) */
public void removeSpatializerCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
- enforceModifyAudioRoutingPermission();
+ enforceModifyDefaultAudioEffectsPermission();
Objects.requireNonNull(ada);
mSpatializerHelper.removeCompatibleAudioDevice(ada);
}
- /** @see AudioManager#setSpatializerFeatureEnabled(boolean) */
- public void setSpatializerFeatureEnabled(boolean enabled) {
- if (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Missing MODIFY_DEFAULT_AUDIO_EFFECTS permission");
- }
- mSpatializerHelper.setEnabled(enabled);
- }
-
- /** @see Spatializer#setEnabledForDevice(boolean, AudioDeviceAttributes)
- * @see Spatializer#setEnabled(boolean)
- */
- public void setSpatializerEnabledForDevice(boolean enabled,
- @NonNull AudioDeviceAttributes ada) {
- enforceModifyAudioRoutingPermission();
- Objects.requireNonNull(ada);
- mSpatializerHelper.setEnabledForDevice(enabled, ada);
- }
-
-
//==========================================================================================
private boolean readCameraSoundForced() {
return SystemProperties.getBoolean("audio.camerasound.force", false) ||
diff --git a/services/core/java/com/android/server/audio/RotationHelper.java b/services/core/java/com/android/server/audio/RotationHelper.java
index ad72166..872b1fe 100644
--- a/services/core/java/com/android/server/audio/RotationHelper.java
+++ b/services/core/java/com/android/server/audio/RotationHelper.java
@@ -21,20 +21,26 @@
import android.media.AudioSystem;
import android.os.Handler;
import android.util.Log;
+import android.view.Display;
import android.view.Surface;
import android.view.WindowManager;
/**
* Class to handle device rotation events for AudioService, and forward device rotation
- * to the audio HALs through AudioSystem.
+ * and current active ID to the audio HALs through AudioSystem.
*
* The role of this class is to monitor device orientation changes, and upon rotation,
* verify the UI orientation. In case of a change, send the new orientation, in increments
* of 90deg, through AudioSystem.
*
+ * Another role of this class is to track current active display ID changes. In case of a
+ * change, send the new active display ID through AudioSystem.
+ *
* Note that even though we're responding to device orientation events, we always
* query the display rotation so audio stays in sync with video/dialogs. This is
* done with .getDefaultDisplay().getRotation() from WINDOW_SERVICE.
+ *
+ * We also monitor current display ID and audio is able to know which display is active.
*/
class RotationHelper {
@@ -43,7 +49,9 @@
private static AudioDisplayListener sDisplayListener;
private static final Object sRotationLock = new Object();
+ private static final Object sActiveDisplayLock = new Object();
private static int sDeviceRotation = Surface.ROTATION_0; // R/W synchronized on sRotationLock
+ private static int sDisplayId = Display.DEFAULT_DISPLAY; // synchronized on sActiveDisplayLock
private static Context sContext;
private static Handler sHandler;
@@ -112,6 +120,18 @@
}
/**
+ * Query current display active id and publish the change if any.
+ */
+ static void updateActiveDisplayId(int displayId) {
+ synchronized (sActiveDisplayLock) {
+ if (displayId != Display.DEFAULT_DISPLAY && sDisplayId != displayId) {
+ sDisplayId = displayId;
+ AudioSystem.setParameters("active_displayId=" + sDisplayId);
+ }
+ }
+ }
+
+ /**
* Uses android.hardware.display.DisplayManager.DisplayListener
*/
final static class AudioDisplayListener implements DisplayManager.DisplayListener {
@@ -126,6 +146,7 @@
@Override
public void onDisplayChanged(int displayId) {
+ updateActiveDisplayId(displayId);
updateOrientation();
}
}
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 1b68f84..708d9e1 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -17,11 +17,11 @@
package com.android.server.audio;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioFormat;
import android.media.ISpatializerCallback;
+import android.media.Spatializer;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
@@ -34,13 +34,12 @@
*/
public class SpatializerHelper {
- private static final String TAG = "AS.NotificationHelper";
+ private static final String TAG = "AS.SpatializerHelper";
//---------------------------------------------------------------
// audio device compatibility / enabled
private final ArrayList<AudioDeviceAttributes> mCompatibleAudioDevices = new ArrayList<>(0);
- private final ArrayList<AudioDeviceAttributes> mEnabledAudioDevices = new ArrayList<>(0);
/**
* @return a shallow copy of the list of compatible audio devices
@@ -53,25 +52,10 @@
if (!mCompatibleAudioDevices.contains(ada)) {
mCompatibleAudioDevices.add(ada);
}
- // by default, adding a compatible device enables it
- if (!mEnabledAudioDevices.contains(ada)) {
- mEnabledAudioDevices.add(ada);
- }
}
synchronized void removeCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
mCompatibleAudioDevices.remove(ada);
- mEnabledAudioDevices.remove(ada);
- }
-
- synchronized void setEnabledForDevice(boolean enabled, @NonNull AudioDeviceAttributes ada) {
- if (enabled) {
- if (!mEnabledAudioDevices.contains(ada)) {
- mEnabledAudioDevices.add(ada);
- }
- } else {
- mEnabledAudioDevices.remove(ada);
- }
}
//------------------------------------------------------
@@ -79,15 +63,35 @@
// global state of feature
boolean mFeatureEnabled = false;
+ // initialized state, checked after each audio_server start
+ boolean mInitialized = false;
+
+ synchronized boolean isEnabled() {
+ return mFeatureEnabled;
+ }
+
+ synchronized boolean isAvailable() {
+ if (!mInitialized) {
+ return false;
+ }
+ // TODO check device compatibility
+ // ...
+ return true;
+ }
synchronized void setEnabled(boolean enabled) {
final boolean oldState = mFeatureEnabled;
mFeatureEnabled = enabled;
if (oldState != enabled) {
- dispatchState();
+ dispatchEnabledState();
}
}
+ public int getImmersiveAudioLevel() {
+ // TODO replace placeholder code with actual effect discovery
+ return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ }
+
final RemoteCallbackList<ISpatializerCallback> mStateCallbacks =
new RemoteCallbackList<ISpatializerCallback>();
@@ -101,16 +105,14 @@
mStateCallbacks.unregister(callback);
}
- private synchronized void dispatchState() {
- // TODO check enabled state based on available devices
- // (current implementation takes state as-is and dispatches it to listeners
+ private synchronized void dispatchEnabledState() {
final int nbCallbacks = mStateCallbacks.beginBroadcast();
for (int i = 0; i < nbCallbacks; i++) {
try {
mStateCallbacks.getBroadcastItem(i)
- .dispatchSpatializerStateChanged(mFeatureEnabled);
+ .dispatchSpatializerEnabledChanged(mFeatureEnabled);
} catch (RemoteException e) {
- Log.e(TAG, "Error in dispatchSpatializerStateChanged", e);
+ Log.e(TAG, "Error in dispatchSpatializerEnabledChanged", e);
}
}
mStateCallbacks.finishBroadcast();
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 0cd2e3d..b42f898 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -206,7 +206,7 @@
}
@Override
- public void authenticate(IBinder token, long sessionId, int userId,
+ public long authenticate(IBinder token, long sessionId, int userId,
IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo)
throws RemoteException {
// Only allow internal clients to authenticate with a different userId.
@@ -223,18 +223,18 @@
if (!checkAppOps(callingUid, opPackageName, "authenticate()")) {
authenticateFastFail("Denied by app ops: " + opPackageName, receiver);
- return;
+ return -1;
}
if (token == null || receiver == null || opPackageName == null || promptInfo == null) {
authenticateFastFail(
"Unable to authenticate, one or more null arguments", receiver);
- return;
+ return -1;
}
if (!Utils.isForeground(callingUid, callingPid)) {
authenticateFastFail("Caller is not foreground: " + opPackageName, receiver);
- return;
+ return -1;
}
if (promptInfo.containsTestConfigurations()) {
@@ -251,7 +251,7 @@
final long identity = Binder.clearCallingIdentity();
try {
- mBiometricService.authenticate(
+ return mBiometricService.authenticate(
token, sessionId, userId, receiver, opPackageName, promptInfo);
} finally {
Binder.restoreCallingIdentity(identity);
@@ -270,7 +270,7 @@
}
@Override
- public void cancelAuthentication(IBinder token, String opPackageName)
+ public void cancelAuthentication(IBinder token, String opPackageName, long requestId)
throws RemoteException {
checkPermission();
@@ -281,7 +281,7 @@
final long identity = Binder.clearCallingIdentity();
try {
- mBiometricService.cancelAuthentication(token, opPackageName);
+ mBiometricService.cancelAuthentication(token, opPackageName, requestId);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index bdde980..0da6a1b 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -128,6 +128,7 @@
@VisibleForTesting final IBinder mToken;
// Info to be shown on BiometricDialog when all cookies are returned.
@VisibleForTesting final PromptInfo mPromptInfo;
+ private final long mRequestId;
private final long mOperationId;
private final int mUserId;
private final IBiometricSensorReceiver mSensorReceiver;
@@ -142,6 +143,8 @@
private @BiometricMultiSensorMode int mMultiSensorMode;
private @MultiSensorState int mMultiSensorState;
private int[] mSensors;
+ // TODO(b/197265902): merge into state
+ private boolean mCancelled;
// For explicit confirmation, do not send to keystore until the user has confirmed
// the authentication.
private byte[] mTokenEscrow;
@@ -162,6 +165,7 @@
@NonNull ClientDeathReceiver clientDeathReceiver,
@NonNull PreAuthInfo preAuthInfo,
@NonNull IBinder token,
+ long requestId,
long operationId,
int userId,
@NonNull IBiometricSensorReceiver sensorReceiver,
@@ -179,6 +183,7 @@
mClientDeathReceiver = clientDeathReceiver;
mPreAuthInfo = preAuthInfo;
mToken = token;
+ mRequestId = requestId;
mOperationId = operationId;
mUserId = userId;
mSensorReceiver = sensorReceiver;
@@ -187,6 +192,7 @@
mPromptInfo = promptInfo;
mDebugEnabled = debugEnabled;
mFingerprintSensorProperties = fingerprintSensorProperties;
+ mCancelled = false;
try {
mClientReceiver.asBinder().linkToDeath(this, 0 /* flags */);
@@ -233,7 +239,7 @@
Slog.v(TAG, "waiting for cooking for sensor: " + sensor.id);
}
sensor.goToStateWaitingForCookie(requireConfirmation, mToken, mOperationId,
- mUserId, mSensorReceiver, mOpPackageName, cookie,
+ mUserId, mSensorReceiver, mOpPackageName, mRequestId, cookie,
mPromptInfo.isAllowBackgroundAuthentication());
}
}
@@ -255,8 +261,9 @@
true /* credentialAllowed */,
false /* requireConfirmation */,
mUserId,
- mOpPackageName,
mOperationId,
+ mOpPackageName,
+ mRequestId,
mMultiSensorMode);
} else if (!mPreAuthInfo.eligibleSensors.isEmpty()) {
// Some combination of biometric or biometric|credential is requested
@@ -270,6 +277,11 @@
}
void onCookieReceived(int cookie) {
+ if (mCancelled) {
+ Slog.w(TAG, "Received cookie but already cancelled (ignoring): " + cookie);
+ return;
+ }
+
for (BiometricSensor sensor : mPreAuthInfo.eligibleSensors) {
sensor.goToStateCookieReturnedIfCookieMatches(cookie);
}
@@ -301,8 +313,9 @@
mPreAuthInfo.shouldShowCredential(),
requireConfirmation,
mUserId,
- mOpPackageName,
mOperationId,
+ mOpPackageName,
+ mRequestId,
mMultiSensorMode);
mState = STATE_AUTH_STARTED;
} catch (RemoteException e) {
@@ -369,7 +382,7 @@
final boolean shouldCancel = filter.apply(sensor);
Slog.d(TAG, "sensorId: " + sensor.id + ", shouldCancel: " + shouldCancel);
if (shouldCancel) {
- sensor.goToStateCancelling(mToken, mOpPackageName);
+ sensor.goToStateCancelling(mToken, mOpPackageName, mRequestId);
}
} catch (RemoteException e) {
Slog.e(TAG, "Unable to cancel authentication");
@@ -425,8 +438,9 @@
true /* credentialAllowed */,
false /* requireConfirmation */,
mUserId,
- mOpPackageName,
mOperationId,
+ mOpPackageName,
+ mRequestId,
mMultiSensorMode);
} else {
mClientReceiver.onError(modality, error, vendorCode);
@@ -775,6 +789,8 @@
* @return true if this AuthSession is finished, e.g. should be set to null
*/
boolean onCancelAuthSession(boolean force) {
+ mCancelled = true;
+
final boolean authStarted = mState == STATE_AUTH_CALLED
|| mState == STATE_AUTH_STARTED
|| mState == STATE_AUTH_STARTED_UI_SHOWING;
@@ -820,6 +836,7 @@
return Utils.isCredentialRequested(mPromptInfo);
}
+ @VisibleForTesting
boolean allCookiesReceived() {
final int remainingCookies = mPreAuthInfo.numSensorsWaitingForCookie();
Slog.d(TAG, "Remaining cookies: " + remainingCookies);
@@ -839,6 +856,10 @@
return mState;
}
+ long getRequestId() {
+ return mRequestId;
+ }
+
private int statsModality() {
int modality = 0;
@@ -901,7 +922,9 @@
@Override
public String toString() {
return "State: " + mState
+ + ", cancelled: " + mCancelled
+ ", isCrypto: " + isCrypto()
- + ", PreAuthInfo: " + mPreAuthInfo;
+ + ", PreAuthInfo: " + mPreAuthInfo
+ + ", requestId: " + mRequestId;
}
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricSensor.java b/services/core/java/com/android/server/biometrics/BiometricSensor.java
index 8a842b5..0333c3e 100644
--- a/services/core/java/com/android/server/biometrics/BiometricSensor.java
+++ b/services/core/java/com/android/server/biometrics/BiometricSensor.java
@@ -108,11 +108,11 @@
void goToStateWaitingForCookie(boolean requireConfirmation, IBinder token, long sessionId,
int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName,
- int cookie, boolean allowBackgroundAuthentication)
+ long requestId, int cookie, boolean allowBackgroundAuthentication)
throws RemoteException {
mCookie = cookie;
impl.prepareForAuthentication(requireConfirmation, token,
- sessionId, userId, sensorReceiver, opPackageName, mCookie,
+ sessionId, userId, sensorReceiver, opPackageName, requestId, mCookie,
allowBackgroundAuthentication);
mSensorState = STATE_WAITING_FOR_COOKIE;
}
@@ -129,8 +129,9 @@
mSensorState = STATE_AUTHENTICATING;
}
- void goToStateCancelling(IBinder token, String opPackageName) throws RemoteException {
- impl.cancelAuthenticationFromService(token, opPackageName);
+ void goToStateCancelling(IBinder token, String opPackageName, long requestId)
+ throws RemoteException {
+ impl.cancelAuthenticationFromService(token, opPackageName, requestId);
mSensorState = STATE_CANCELING;
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index b1d300c..e0775d4 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -83,6 +83,7 @@
import java.util.Map;
import java.util.Random;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
/**
* System service that arbitrates the modality for BiometricPrompt to use.
@@ -115,6 +116,7 @@
final SettingObserver mSettingObserver;
private final List<EnabledOnKeyguardCallback> mEnabledOnKeyguardCallbacks;
private final Random mRandom = new Random();
+ @NonNull private final AtomicLong mRequestCounter;
@VisibleForTesting
IStatusBarService mStatusBarService;
@@ -194,6 +196,7 @@
SomeArgs args = (SomeArgs) msg.obj;
handleAuthenticate(
(IBinder) args.arg1 /* token */,
+ (long) args.arg6 /* requestId */,
(long) args.arg2 /* operationId */,
args.argi1 /* userid */,
(IBiometricServiceReceiver) args.arg3 /* receiver */,
@@ -204,7 +207,9 @@
}
case MSG_CANCEL_AUTHENTICATION: {
- handleCancelAuthentication();
+ SomeArgs args = (SomeArgs) msg.obj;
+ handleCancelAuthentication((long) args.arg3 /* requestId */);
+ args.recycle();
break;
}
@@ -683,13 +688,13 @@
}
@Override // Binder call
- public void authenticate(IBinder token, long operationId, int userId,
+ public long authenticate(IBinder token, long operationId, int userId,
IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo) {
checkInternalPermission();
if (token == null || receiver == null || opPackageName == null || promptInfo == null) {
Slog.e(TAG, "Unable to authenticate, one or more null arguments");
- return;
+ return -1;
}
if (!Utils.isValidAuthenticatorConfig(promptInfo)) {
@@ -706,6 +711,8 @@
}
}
+ final long requestId = mRequestCounter.incrementAndGet();
+
SomeArgs args = SomeArgs.obtain();
args.arg1 = token;
args.arg2 = operationId;
@@ -713,15 +720,23 @@
args.arg3 = receiver;
args.arg4 = opPackageName;
args.arg5 = promptInfo;
+ args.arg6 = requestId;
mHandler.obtainMessage(MSG_AUTHENTICATE, args).sendToTarget();
+
+ return requestId;
}
@Override // Binder call
- public void cancelAuthentication(IBinder token, String opPackageName) {
+ public void cancelAuthentication(IBinder token, String opPackageName, long requestId) {
checkInternalPermission();
- mHandler.obtainMessage(MSG_CANCEL_AUTHENTICATION).sendToTarget();
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = token;
+ args.arg2 = opPackageName;
+ args.arg3 = requestId;
+
+ mHandler.obtainMessage(MSG_CANCEL_AUTHENTICATION, args).sendToTarget();
}
@Override // Binder call
@@ -1111,6 +1126,10 @@
return Settings.Secure.getInt(context.getContentResolver(),
CoexCoordinator.FACE_HAPTIC_DISABLE, 1) != 0;
}
+
+ public AtomicLong getRequestGenerator() {
+ return new AtomicLong(0);
+ }
}
/**
@@ -1136,6 +1155,7 @@
mEnabledOnKeyguardCallbacks = new ArrayList<>();
mSettingObserver = mInjector.getSettingObserver(context, mHandler,
mEnabledOnKeyguardCallbacks);
+ mRequestCounter = mInjector.getRequestGenerator();
// TODO(b/193089985) This logic lives here (outside of CoexCoordinator) so that it doesn't
// need to depend on context. We can remove this code once the advanced logic is enabled
@@ -1349,7 +1369,7 @@
mCurrentAuthSession.onCookieReceived(cookie);
}
- private void handleAuthenticate(IBinder token, long operationId, int userId,
+ private void handleAuthenticate(IBinder token, long requestId, long operationId, int userId,
IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo) {
mHandler.post(() -> {
try {
@@ -1360,7 +1380,8 @@
final Pair<Integer, Integer> preAuthStatus = preAuthInfo.getPreAuthenticateStatus();
Slog.d(TAG, "handleAuthenticate: modality(" + preAuthStatus.first
- + "), status(" + preAuthStatus.second + "), preAuthInfo: " + preAuthInfo);
+ + "), status(" + preAuthStatus.second + "), preAuthInfo: " + preAuthInfo
+ + " requestId: " + requestId);
if (preAuthStatus.second == BiometricConstants.BIOMETRIC_SUCCESS) {
// If BIOMETRIC_WEAK or BIOMETRIC_STRONG are allowed, but not enrolled, but
@@ -1372,8 +1393,8 @@
promptInfo.setAuthenticators(Authenticators.DEVICE_CREDENTIAL);
}
- authenticateInternal(token, operationId, userId, receiver, opPackageName,
- promptInfo, preAuthInfo);
+ authenticateInternal(token, requestId, operationId, userId, receiver,
+ opPackageName, promptInfo, preAuthInfo);
} else {
receiver.onError(preAuthStatus.first /* modality */,
preAuthStatus.second /* errorCode */,
@@ -1394,7 +1415,7 @@
* Note that this path is NOT invoked when the BiometricPrompt "Try again" button is pressed.
* In that case, see {@link #handleOnTryAgainPressed()}.
*/
- private void authenticateInternal(IBinder token, long operationId, int userId,
+ private void authenticateInternal(IBinder token, long requestId, long operationId, int userId,
IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo,
PreAuthInfo preAuthInfo) {
Slog.d(TAG, "Creating authSession with authRequest: " + preAuthInfo);
@@ -1412,9 +1433,9 @@
final boolean debugEnabled = mInjector.isDebugEnabled(getContext(), userId);
mCurrentAuthSession = new AuthSession(getContext(), mStatusBarService, mSysuiReceiver,
- mKeyStore, mRandom, mClientDeathReceiver, preAuthInfo, token, operationId, userId,
- mBiometricSensorReceiver, receiver, opPackageName, promptInfo, debugEnabled,
- mInjector.getFingerprintSensorProperties(getContext()));
+ mKeyStore, mRandom, mClientDeathReceiver, preAuthInfo, token, requestId,
+ operationId, userId, mBiometricSensorReceiver, receiver, opPackageName, promptInfo,
+ debugEnabled, mInjector.getFingerprintSensorProperties(getContext()));
try {
mCurrentAuthSession.goToInitialState();
} catch (RemoteException e) {
@@ -1422,11 +1443,21 @@
}
}
- private void handleCancelAuthentication() {
+ private void handleCancelAuthentication(long requestId) {
if (mCurrentAuthSession == null) {
Slog.e(TAG, "handleCancelAuthentication: AuthSession is null");
return;
}
+ if (mCurrentAuthSession.getRequestId() != requestId) {
+ // TODO: actually cancel the operation
+ // This can happen if the operation has been queued, but is cancelled before
+ // it reaches the head of the scheduler. Consider it a programming error for now
+ // and ignore it.
+ Slog.e(TAG, "handleCancelAuthentication: AuthSession mismatch current requestId: "
+ + mCurrentAuthSession.getRequestId() + " cancel for: " + requestId
+ + " (ignoring cancellation)");
+ return;
+ }
final boolean finished = mCurrentAuthSession.onCancelAuthSession(false /* force */);
if (finished) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index 3eb6f4a..0a1c77b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -101,6 +101,7 @@
private final int mSensorId; // sensorId as configured by the framework
@Nullable private IBinder mToken;
+ private long mRequestId;
@Nullable private ClientMonitorCallbackConverter mListener;
// Currently only used for authentication client. The cookie generated by BiometricService
// is never 0.
@@ -154,6 +155,7 @@
mSequentialId = sCount++;
mContext = context;
mToken = token;
+ mRequestId = -1;
mListener = listener;
mTargetUserId = userId;
mOwner = owner;
@@ -258,6 +260,29 @@
return mSensorId;
}
+ /** Unique request id. */
+ public final long getRequestId() {
+ return mRequestId;
+ }
+
+ /** If a unique id has been set via {@link #setRequestId(long)} */
+ public final boolean hasRequestId() {
+ return mRequestId > 0;
+ }
+
+ /**
+ * A unique identifier used to tie this operation to a request (i.e an API invocation).
+ *
+ * Subclasses should not call this method if this operation does not have a direct
+ * correspondence to a request and {@link #hasRequestId()} will return false.
+ */
+ protected final void setRequestId(long id) {
+ if (id <= 0) {
+ throw new IllegalArgumentException("request id must be positive");
+ }
+ mRequestId = id;
+ }
+
@VisibleForTesting
public Callback getCallback() {
return mCallback;
@@ -270,6 +295,7 @@
+ ", proto=" + getProtoEnum()
+ ", owner=" + getOwnerString()
+ ", cookie=" + getCookie()
+ + ", requestId=" + getRequestId()
+ ", userId=" + getTargetUserId() + "}";
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index feb9e2a..361ec40 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -643,22 +643,18 @@
/**
* Requests to cancel authentication or detection.
* @param token from the caller, should match the token passed in when requesting authentication
+ * @param requestId the id returned when requesting authentication
*/
- public void cancelAuthenticationOrDetection(IBinder token) {
- if (mCurrentOperation == null) {
- Slog.e(getTag(), "Unable to cancel authentication, null operation");
- return;
- }
- final boolean isCorrectClient = isAuthenticationOrDetectionOperation(mCurrentOperation);
- final boolean tokenMatches = mCurrentOperation.mClientMonitor.getToken() == token;
+ public void cancelAuthenticationOrDetection(IBinder token, long requestId) {
+ Slog.d(getTag(), "cancelAuthenticationOrDetection, requestId: " + requestId
+ + " current: " + mCurrentOperation
+ + " stack size: " + mPendingOperations.size());
- Slog.d(getTag(), "cancelAuthenticationOrDetection, isCorrectClient: " + isCorrectClient
- + ", tokenMatches: " + tokenMatches);
-
- if (isCorrectClient && tokenMatches) {
+ if (mCurrentOperation != null
+ && canCancelAuthOperation(mCurrentOperation, token, requestId)) {
Slog.d(getTag(), "Cancelling: " + mCurrentOperation);
cancelInternal(mCurrentOperation);
- } else if (!isCorrectClient) {
+ } else {
// Look through the current queue for all authentication clients for the specified
// token, and mark them as STATE_WAITING_IN_QUEUE_CANCELING. Note that we're marking
// all of them, instead of just the first one, since the API surface currently doesn't
@@ -666,8 +662,7 @@
// process. However, this generally does not happen anyway, and would be a class of
// bugs on its own.
for (Operation operation : mPendingOperations) {
- if (isAuthenticationOrDetectionOperation(operation)
- && operation.mClientMonitor.getToken() == token) {
+ if (canCancelAuthOperation(operation, token, requestId)) {
Slog.d(getTag(), "Marking " + operation
+ " as STATE_WAITING_IN_QUEUE_CANCELING");
operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING;
@@ -676,10 +671,26 @@
}
}
- private boolean isAuthenticationOrDetectionOperation(@NonNull Operation operation) {
- final boolean isAuthentication = operation.mClientMonitor
- instanceof AuthenticationConsumer;
- final boolean isDetection = operation.mClientMonitor instanceof DetectionConsumer;
+ private static boolean canCancelAuthOperation(Operation operation, IBinder token,
+ long requestId) {
+ // TODO: restrict callers that can cancel without requestId (negative value)?
+ return isAuthenticationOrDetectionOperation(operation)
+ && operation.mClientMonitor.getToken() == token
+ && isMatchingRequestId(operation, requestId);
+ }
+
+ // By default, monitors are not associated with a request id to retain the original
+ // behavior (i.e. if no requestId is explicitly set then assume it matches)
+ private static boolean isMatchingRequestId(Operation operation, long requestId) {
+ return !operation.mClientMonitor.hasRequestId()
+ || operation.mClientMonitor.getRequestId() == requestId;
+ }
+
+ private static boolean isAuthenticationOrDetectionOperation(@NonNull Operation operation) {
+ final boolean isAuthentication =
+ operation.mClientMonitor instanceof AuthenticationConsumer;
+ final boolean isDetection =
+ operation.mClientMonitor instanceof DetectionConsumer;
return isAuthentication || isDetection;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
index 0bc4f1b..b2fd46d1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
@@ -61,10 +61,11 @@
@Override
public void prepareForAuthentication(boolean requireConfirmation, IBinder token,
long operationId, int userId, IBiometricSensorReceiver sensorReceiver,
- String opPackageName, int cookie, boolean allowBackgroundAuthentication)
+ String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication)
throws RemoteException {
mFaceService.prepareForAuthentication(mSensorId, requireConfirmation, token, operationId,
- userId, sensorReceiver, opPackageName, cookie, allowBackgroundAuthentication);
+ userId, sensorReceiver, opPackageName, requestId, cookie,
+ allowBackgroundAuthentication);
}
@Override
@@ -73,9 +74,9 @@
}
@Override
- public void cancelAuthenticationFromService(IBinder token, String opPackageName)
+ public void cancelAuthenticationFromService(IBinder token, String opPackageName, long requestId)
throws RemoteException {
- mFaceService.cancelAuthenticationFromService(mSensorId, token, opPackageName);
+ mFaceService.cancelAuthenticationFromService(mSensorId, token, opPackageName, requestId);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 12d6b08..675ee545 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -250,7 +250,7 @@
}
@Override // Binder call
- public void authenticate(final IBinder token, final long operationId, int userId,
+ public long authenticate(final IBinder token, final long operationId, int userId,
final IFaceServiceReceiver receiver, final String opPackageName,
boolean isKeyguardBypassEnabled) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
@@ -270,38 +270,38 @@
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for authenticate");
- return;
+ return -1;
}
- provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
+ return provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
0 /* cookie */,
new ClientMonitorCallbackConverter(receiver), opPackageName, restricted,
statsClient, isKeyguard, isKeyguardBypassEnabled);
}
@Override // Binder call
- public void detectFace(final IBinder token, final int userId,
+ public long detectFace(final IBinder token, final int userId,
final IFaceServiceReceiver receiver, final String opPackageName) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
if (!Utils.isKeyguard(getContext(), opPackageName)) {
Slog.w(TAG, "detectFace called from non-sysui package: " + opPackageName);
- return;
+ return -1;
}
if (!Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) {
// If this happens, something in KeyguardUpdateMonitor is wrong. This should only
// ever be invoked when the user is encrypted or lockdown.
Slog.e(TAG, "detectFace invoked when user is not encrypted or lockdown");
- return;
+ return -1;
}
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for detectFace");
- return;
+ return -1;
}
- provider.second.scheduleFaceDetect(provider.first, token, userId,
+ return provider.second.scheduleFaceDetect(provider.first, token, userId,
new ClientMonitorCallbackConverter(receiver), opPackageName,
BiometricsProtoEnums.CLIENT_KEYGUARD);
}
@@ -309,8 +309,8 @@
@Override // Binder call
public void prepareForAuthentication(int sensorId, boolean requireConfirmation,
IBinder token, long operationId, int userId,
- IBiometricSensorReceiver sensorReceiver, String opPackageName, int cookie,
- boolean allowBackgroundAuthentication) {
+ IBiometricSensorReceiver sensorReceiver, String opPackageName, long requestId,
+ int cookie, boolean allowBackgroundAuthentication) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
final ServiceProvider provider = getProviderForSensor(sensorId);
@@ -322,9 +322,9 @@
final boolean isKeyguardBypassEnabled = false; // only valid for keyguard clients
final boolean restricted = true; // BiometricPrompt is always restricted
provider.scheduleAuthenticate(sensorId, token, operationId, userId, cookie,
- new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, restricted,
- BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, allowBackgroundAuthentication,
- isKeyguardBypassEnabled);
+ new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, requestId,
+ restricted, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
+ allowBackgroundAuthentication, isKeyguardBypassEnabled);
}
@Override // Binder call
@@ -341,7 +341,8 @@
}
@Override // Binder call
- public void cancelAuthentication(final IBinder token, final String opPackageName) {
+ public void cancelAuthentication(final IBinder token, final String opPackageName,
+ final long requestId) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
@@ -350,11 +351,12 @@
return;
}
- provider.second.cancelAuthentication(provider.first, token);
+ provider.second.cancelAuthentication(provider.first, token, requestId);
}
@Override // Binder call
- public void cancelFaceDetect(final IBinder token, final String opPackageName) {
+ public void cancelFaceDetect(final IBinder token, final String opPackageName,
+ final long requestId) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
if (!Utils.isKeyguard(getContext(), opPackageName)) {
Slog.w(TAG, "cancelFaceDetect called from non-sysui package: "
@@ -368,12 +370,12 @@
return;
}
- provider.second.cancelFaceDetect(provider.first, token);
+ provider.second.cancelFaceDetect(provider.first, token, requestId);
}
@Override // Binder call
public void cancelAuthenticationFromService(int sensorId, final IBinder token,
- final String opPackageName) {
+ final String opPackageName, final long requestId) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
final ServiceProvider provider = getProviderForSensor(sensorId);
@@ -382,7 +384,7 @@
return;
}
- provider.cancelAuthentication(sensorId, token);
+ provider.cancelAuthentication(sensorId, token, requestId);
}
@Override // Binder call
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
index 93ab1b6..e099ba3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
@@ -101,18 +101,23 @@
void cancelEnrollment(int sensorId, @NonNull IBinder token);
- void scheduleFaceDetect(int sensorId, @NonNull IBinder token, int userId,
+ long scheduleFaceDetect(int sensorId, @NonNull IBinder token, int userId,
@NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
int statsClient);
- void cancelFaceDetect(int sensorId, @NonNull IBinder token);
+ void cancelFaceDetect(int sensorId, @NonNull IBinder token, long requestId);
- void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId,
+ long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId,
int cookie, @NonNull ClientMonitorCallbackConverter callback,
@NonNull String opPackageName, boolean restricted, int statsClient,
boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled);
- void cancelAuthentication(int sensorId, @NonNull IBinder token);
+ void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId,
+ int cookie, @NonNull ClientMonitorCallbackConverter callback,
+ @NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
+ boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled);
+
+ void cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId);
void scheduleRemove(int sensorId, @NonNull IBinder token, int faceId, int userId,
@NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index d66a279..cbceba6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -65,7 +65,8 @@
@FaceManager.FaceAcquired private int mLastAcquire = FaceManager.FACE_ACQUIRED_UNKNOWN;
FaceAuthenticationClient(@NonNull Context context,
- @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
+ @NonNull LazyDaemon<ISession> lazyDaemon,
+ @NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId,
boolean isStrongBiometric, int statsClient, @NonNull UsageStats usageStats,
@@ -76,6 +77,7 @@
BiometricsProtoEnums.MODALITY_FACE, statsClient, null /* taskStackListener */,
lockoutCache, allowBackgroundAuthentication, true /* shouldVibrate */,
isKeyguardBypassEnabled);
+ setRequestId(requestId);
mUsageStats = usageStats;
mLockoutCache = lockoutCache;
mNotificationManager = context.getSystemService(NotificationManager.class);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
index 1e73ac5..2ef0911 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
@@ -43,11 +43,13 @@
@Nullable private ICancellationSignal mCancellationSignal;
public FaceDetectClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
- @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
+ @NonNull IBinder token, long requestId,
+ @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull String owner, int sensorId, boolean isStrongBiometric, int statsClient) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
true /* shouldVibrate */, BiometricsProtoEnums.MODALITY_FACE,
BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
+ setRequestId(requestId);
mIsStrongBiometric = isStrongBiometric;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 718b9da..4bae775 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -65,6 +65,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
/**
* Provider for a single instance of the {@link IFace} HAL.
@@ -83,6 +84,8 @@
@NonNull private final UsageStats mUsageStats;
@NonNull private final ActivityTaskManager mActivityTaskManager;
@NonNull private final BiometricTaskStackListener mTaskStackListener;
+ // for requests that do not use biometric prompt
+ @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
@Nullable private IFace mDaemon;
@@ -110,8 +113,8 @@
&& !client.isAlreadyDone()) {
Slog.e(getTag(), "Stopping background authentication, top: "
+ topPackage + " currentClient: " + client);
- mSensors.valueAt(i).getScheduler()
- .cancelAuthenticationOrDetection(client.getToken());
+ mSensors.valueAt(i).getScheduler().cancelAuthenticationOrDetection(
+ client.getToken(), client.getRequestId());
}
}
}
@@ -356,34 +359,39 @@
}
@Override
- public void scheduleFaceDetect(int sensorId, @NonNull IBinder token,
+ public long scheduleFaceDetect(int sensorId, @NonNull IBinder token,
int userId, @NonNull ClientMonitorCallbackConverter callback,
@NonNull String opPackageName, int statsClient) {
+ final long id = mRequestCounter.incrementAndGet();
+
mHandler.post(() -> {
final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
final FaceDetectClient client = new FaceDetectClient(mContext,
- mSensors.get(sensorId).getLazySession(), token, callback, userId, opPackageName,
+ mSensors.get(sensorId).getLazySession(),
+ token, id, callback, userId, opPackageName,
sensorId, isStrongBiometric, statsClient);
scheduleForSensor(sensorId, client);
});
+
+ return id;
}
@Override
- public void cancelFaceDetect(int sensorId, @NonNull IBinder token) {
+ public void cancelFaceDetect(int sensorId, @NonNull IBinder token, long requestId) {
mHandler.post(() -> mSensors.get(sensorId).getScheduler()
- .cancelAuthenticationOrDetection(token));
+ .cancelAuthenticationOrDetection(token, requestId));
}
@Override
public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback,
- @NonNull String opPackageName, boolean restricted, int statsClient,
+ @NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled) {
mHandler.post(() -> {
final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
final FaceAuthenticationClient client = new FaceAuthenticationClient(
- mContext, mSensors.get(sensorId).getLazySession(), token, callback, userId,
- operationId, restricted, opPackageName, cookie,
+ mContext, mSensors.get(sensorId).getLazySession(), token, requestId, callback,
+ userId, operationId, restricted, opPackageName, cookie,
false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient,
mUsageStats, mSensors.get(sensorId).getLockoutCache(),
allowBackgroundAuthentication, isKeyguardBypassEnabled);
@@ -392,9 +400,23 @@
}
@Override
- public void cancelAuthentication(int sensorId, @NonNull IBinder token) {
+ public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+ int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback,
+ @NonNull String opPackageName, boolean restricted, int statsClient,
+ boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled) {
+ final long id = mRequestCounter.incrementAndGet();
+
+ scheduleAuthenticate(sensorId, token, operationId, userId, cookie, callback,
+ opPackageName, id, restricted, statsClient,
+ allowBackgroundAuthentication, isKeyguardBypassEnabled);
+
+ return id;
+ }
+
+ @Override
+ public void cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId) {
mHandler.post(() -> mSensors.get(sensorId).getScheduler()
- .cancelAuthenticationOrDetection(token));
+ .cancelAuthenticationOrDetection(token, requestId));
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index d05333d..f4dcbbb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -87,6 +87,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
/**
* Supports a single instance of the {@link android.hardware.biometrics.face.V1_0} or its extended
@@ -115,6 +116,8 @@
@NonNull private final Map<Integer, Long> mAuthenticatorIds;
@Nullable private IBiometricsFace mDaemon;
@NonNull private final HalResultController mHalResultController;
+ // for requests that do not use biometric prompt
+ @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
private int mCurrentUserId = UserHandle.USER_NULL;
private final int mSensorId;
private final List<Long> mGeneratedChallengeCount = new ArrayList<>();
@@ -605,7 +608,7 @@
}
@Override
- public void scheduleFaceDetect(int sensorId, @NonNull IBinder token,
+ public long scheduleFaceDetect(int sensorId, @NonNull IBinder token,
int userId, @NonNull ClientMonitorCallbackConverter callback,
@NonNull String opPackageName, int statsClient) {
throw new IllegalStateException("Face detect not supported by IBiometricsFace@1.0. Did you"
@@ -613,7 +616,7 @@
}
@Override
- public void cancelFaceDetect(int sensorId, @NonNull IBinder token) {
+ public void cancelFaceDetect(int sensorId, @NonNull IBinder token, long requestId) {
throw new IllegalStateException("Face detect not supported by IBiometricsFace@1.0. Did you"
+ "forget to check the supportsFaceDetection flag?");
}
@@ -621,26 +624,38 @@
@Override
public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
int userId, int cookie, @NonNull ClientMonitorCallbackConverter receiver,
- @NonNull String opPackageName, boolean restricted, int statsClient,
+ @NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled) {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorId);
final FaceAuthenticationClient client = new FaceAuthenticationClient(mContext,
- mLazyDaemon, token, receiver, userId, operationId, restricted, opPackageName,
- cookie, false /* requireConfirmation */, mSensorId, isStrongBiometric,
- statsClient, mLockoutTracker, mUsageStats, allowBackgroundAuthentication,
- isKeyguardBypassEnabled);
+ mLazyDaemon, token, requestId, receiver, userId, operationId, restricted,
+ opPackageName, cookie, false /* requireConfirmation */, mSensorId,
+ isStrongBiometric, statsClient, mLockoutTracker, mUsageStats,
+ allowBackgroundAuthentication, isKeyguardBypassEnabled);
mScheduler.scheduleClientMonitor(client);
});
}
@Override
- public void cancelAuthentication(int sensorId, @NonNull IBinder token) {
- mHandler.post(() -> {
- mScheduler.cancelAuthenticationOrDetection(token);
- });
+ public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+ int userId, int cookie, @NonNull ClientMonitorCallbackConverter receiver,
+ @NonNull String opPackageName, boolean restricted, int statsClient,
+ boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled) {
+ final long id = mRequestCounter.incrementAndGet();
+
+ scheduleAuthenticate(sensorId, token, operationId, userId, cookie, receiver,
+ opPackageName, id, restricted, statsClient,
+ allowBackgroundAuthentication, isKeyguardBypassEnabled);
+
+ return id;
+ }
+
+ @Override
+ public void cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId) {
+ mHandler.post(() -> mScheduler.cancelAuthenticationOrDetection(token, requestId));
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
index 33950af..40f2801 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
@@ -57,7 +57,8 @@
private int mLastAcquire;
FaceAuthenticationClient(@NonNull Context context,
- @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
+ @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
+ @NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId,
boolean isStrongBiometric, int statsClient, @NonNull LockoutTracker lockoutTracker,
@@ -68,6 +69,7 @@
BiometricsProtoEnums.MODALITY_FACE, statsClient, null /* taskStackListener */,
lockoutTracker, allowBackgroundAuthentication, true /* shouldVibrate */,
isKeyguardBypassEnabled);
+ setRequestId(requestId);
mUsageStats = usageStats;
final Resources resources = getContext().getResources();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
index 1e59429..52d887a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
@@ -61,10 +61,10 @@
@Override
public void prepareForAuthentication(boolean requireConfirmation, IBinder token,
long operationId, int userId, IBiometricSensorReceiver sensorReceiver,
- String opPackageName, int cookie, boolean allowBackgroundAuthentication)
+ String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication)
throws RemoteException {
mFingerprintService.prepareForAuthentication(mSensorId, token, operationId, userId,
- sensorReceiver, opPackageName, cookie, allowBackgroundAuthentication);
+ sensorReceiver, opPackageName, requestId, cookie, allowBackgroundAuthentication);
}
@Override
@@ -73,9 +73,10 @@
}
@Override
- public void cancelAuthenticationFromService(IBinder token, String opPackageName)
+ public void cancelAuthenticationFromService(IBinder token, String opPackageName, long requestId)
throws RemoteException {
- mFingerprintService.cancelAuthenticationFromService(mSensorId, token, opPackageName);
+ mFingerprintService.cancelAuthenticationFromService(
+ mSensorId, token, opPackageName, requestId);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 85817be..fa02c51 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -245,7 +245,7 @@
@SuppressWarnings("deprecation")
@Override // Binder call
- public void authenticate(final IBinder token, final long operationId,
+ public long authenticate(final IBinder token, final long operationId,
final int sensorId, final int userId, final IFingerprintServiceReceiver receiver,
final String opPackageName) {
final int callingUid = Binder.getCallingUid();
@@ -255,7 +255,7 @@
if (!canUseFingerprint(opPackageName, true /* requireForeground */, callingUid,
callingPid, callingUserId)) {
Slog.w(TAG, "Authenticate rejecting package: " + opPackageName);
- return;
+ return -1;
}
// Keyguard check must be done on the caller's binder identity, since it also checks
@@ -270,7 +270,7 @@
// SafetyNet for b/79776455
EventLog.writeEvent(0x534e4554, "79776455");
Slog.e(TAG, "Authenticate invoked when user is encrypted or lockdown");
- return;
+ return -1;
}
} finally {
Binder.restoreCallingIdentity(identity1);
@@ -290,7 +290,7 @@
}
if (provider == null) {
Slog.w(TAG, "Null provider for authenticate");
- return;
+ return -1;
}
final FingerprintSensorPropertiesInternal sensorProps =
@@ -299,18 +299,17 @@
&& sensorProps != null && sensorProps.isAnyUdfpsType()) {
final long identity2 = Binder.clearCallingIdentity();
try {
- authenticateWithPrompt(operationId, sensorProps, userId, receiver);
+ return authenticateWithPrompt(operationId, sensorProps, userId, receiver);
} finally {
Binder.restoreCallingIdentity(identity2);
}
- } else {
- provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
- 0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName,
- restricted, statsClient, isKeyguard, mFingerprintStateCallback);
}
+ return provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
+ 0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName,
+ restricted, statsClient, isKeyguard, mFingerprintStateCallback);
}
- private void authenticateWithPrompt(
+ private long authenticateWithPrompt(
final long operationId,
@NonNull final FingerprintSensorPropertiesInternal props,
final int userId,
@@ -387,33 +386,33 @@
}
};
- biometricPrompt.authenticateUserForOperation(
+ return biometricPrompt.authenticateUserForOperation(
new CancellationSignal(), executor, promptCallback, userId, operationId);
}
@Override
- public void detectFingerprint(final IBinder token, final int userId,
+ public long detectFingerprint(final IBinder token, final int userId,
final IFingerprintServiceReceiver receiver, final String opPackageName) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
if (!Utils.isKeyguard(getContext(), opPackageName)) {
Slog.w(TAG, "detectFingerprint called from non-sysui package: " + opPackageName);
- return;
+ return -1;
}
if (!Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) {
// If this happens, something in KeyguardUpdateMonitor is wrong. This should only
// ever be invoked when the user is encrypted or lockdown.
Slog.e(TAG, "detectFingerprint invoked when user is not encrypted or lockdown");
- return;
+ return -1;
}
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for detectFingerprint");
- return;
+ return -1;
}
- provider.second.scheduleFingerDetect(provider.first, token, userId,
+ return provider.second.scheduleFingerDetect(provider.first, token, userId,
new ClientMonitorCallbackConverter(receiver), opPackageName,
BiometricsProtoEnums.CLIENT_KEYGUARD, mFingerprintStateCallback);
}
@@ -421,7 +420,7 @@
@Override // Binder call
public void prepareForAuthentication(int sensorId, IBinder token, long operationId,
int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName,
- int cookie, boolean allowBackgroundAuthentication) {
+ long requestId, int cookie, boolean allowBackgroundAuthentication) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
final ServiceProvider provider = getProviderForSensor(sensorId);
@@ -432,9 +431,9 @@
final boolean restricted = true; // BiometricPrompt is always restricted
provider.scheduleAuthenticate(sensorId, token, operationId, userId, cookie,
- new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, restricted,
- BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, allowBackgroundAuthentication,
- mFingerprintStateCallback);
+ new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, requestId,
+ restricted, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
+ allowBackgroundAuthentication, mFingerprintStateCallback);
}
@Override // Binder call
@@ -452,7 +451,8 @@
@Override // Binder call
- public void cancelAuthentication(final IBinder token, final String opPackageName) {
+ public void cancelAuthentication(final IBinder token, final String opPackageName,
+ long requestId) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
@@ -469,11 +469,12 @@
return;
}
- provider.second.cancelAuthentication(provider.first, token);
+ provider.second.cancelAuthentication(provider.first, token, requestId);
}
@Override // Binder call
- public void cancelFingerprintDetect(final IBinder token, final String opPackageName) {
+ public void cancelFingerprintDetect(final IBinder token, final String opPackageName,
+ final long requestId) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
if (!Utils.isKeyguard(getContext(), opPackageName)) {
Slog.w(TAG, "cancelFingerprintDetect called from non-sysui package: "
@@ -489,12 +490,12 @@
return;
}
- provider.second.cancelAuthentication(provider.first, token);
+ provider.second.cancelAuthentication(provider.first, token, requestId);
}
@Override // Binder call
public void cancelAuthenticationFromService(final int sensorId, final IBinder token,
- final String opPackageName) {
+ final String opPackageName, final long requestId) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
@@ -506,7 +507,7 @@
return;
}
- provider.cancelAuthentication(sensorId, token);
+ provider.cancelAuthentication(sensorId, token, requestId);
}
@Override // Binder call
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index 706ac10..b9fcd8e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -95,20 +95,26 @@
void cancelEnrollment(int sensorId, @NonNull IBinder token);
- void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
+ long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
@NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
int statsClient,
@NonNull FingerprintStateCallback fingerprintStateCallback);
void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId,
int cookie, @NonNull ClientMonitorCallbackConverter callback,
+ @NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
+ boolean allowBackgroundAuthentication,
+ @NonNull FingerprintStateCallback fingerprintStateCallback);
+
+ long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId,
+ int cookie, @NonNull ClientMonitorCallbackConverter callback,
@NonNull String opPackageName, boolean restricted, int statsClient,
boolean allowBackgroundAuthentication,
@NonNull FingerprintStateCallback fingerprintStateCallback);
void startPreparedClient(int sensorId, int cookie);
- void cancelAuthentication(int sensorId, @NonNull IBinder token);
+ void cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId);
void scheduleRemove(int sensorId, @NonNull IBinder token,
@NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 37ee76a..9d911e0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -61,7 +61,8 @@
private boolean mIsPointerDown;
FingerprintAuthenticationClient(@NonNull Context context,
- @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
+ @NonNull LazyDaemon<ISession> lazyDaemon,
+ @NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
int sensorId, boolean isStrongBiometric, int statsClient,
@@ -74,6 +75,7 @@
BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener,
lockoutCache, allowBackgroundAuthentication, true /* shouldVibrate */,
false /* isKeyguardBypassEnabled */);
+ setRequestId(requestId);
mLockoutCache = lockoutCache;
mUdfpsOverlayController = udfpsOverlayController;
mSensorProps = sensorProps;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index c5dc449..da91cdd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -47,13 +47,15 @@
@Nullable private ICancellationSignal mCancellationSignal;
FingerprintDetectClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
- @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
+ @NonNull IBinder token, long requestId,
+ @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull String owner, int sensorId,
@Nullable IUdfpsOverlayController udfpsOverlayController, boolean isStrongBiometric,
int statsClient) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
true /* shouldVibrate */, BiometricsProtoEnums.MODALITY_FINGERPRINT,
BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
+ setRequestId(requestId);
mIsStrongBiometric = isStrongBiometric;
mUdfpsOverlayController = udfpsOverlayController;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 102b074..377feca 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -71,6 +71,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
/**
* Provider for a single instance of the {@link IFingerprint} HAL.
@@ -88,6 +89,8 @@
@NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
@NonNull private final ActivityTaskManager mActivityTaskManager;
@NonNull private final BiometricTaskStackListener mTaskStackListener;
+ // for requests that do not use biometric prompt
+ @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
@Nullable private IFingerprint mDaemon;
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
@@ -118,8 +121,8 @@
&& !client.isAlreadyDone()) {
Slog.e(getTag(), "Stopping background authentication, top: "
+ topPackage + " currentClient: " + client);
- mSensors.valueAt(i).getScheduler()
- .cancelAuthenticationOrDetection(client.getToken());
+ mSensors.valueAt(i).getScheduler().cancelAuthenticationOrDetection(
+ client.getToken(), client.getRequestId());
}
}
}
@@ -369,31 +372,35 @@
}
@Override
- public void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
+ public long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
@NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
int statsClient,
@NonNull FingerprintStateCallback fingerprintStateCallback) {
+ final long id = mRequestCounter.incrementAndGet();
+
mHandler.post(() -> {
final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
- mSensors.get(sensorId).getLazySession(), token, callback, userId,
+ mSensors.get(sensorId).getLazySession(), token, id, callback, userId,
opPackageName, sensorId, mUdfpsOverlayController, isStrongBiometric,
statsClient);
scheduleForSensor(sensorId, client, fingerprintStateCallback);
});
+
+ return id;
}
@Override
public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback,
- @NonNull String opPackageName, boolean restricted, int statsClient,
+ @NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
boolean allowBackgroundAuthentication,
@NonNull FingerprintStateCallback fingerprintStateCallback) {
mHandler.post(() -> {
final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
- mContext, mSensors.get(sensorId).getLazySession(), token, callback, userId,
- operationId, restricted, opPackageName, cookie,
+ mContext, mSensors.get(sensorId).getLazySession(), token, requestId, callback,
+ userId, operationId, restricted, opPackageName, cookie,
false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient,
mTaskStackListener, mSensors.get(sensorId).getLockoutCache(),
mUdfpsOverlayController, allowBackgroundAuthentication,
@@ -403,14 +410,29 @@
}
@Override
+ public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+ int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback,
+ @NonNull String opPackageName, boolean restricted, int statsClient,
+ boolean allowBackgroundAuthentication,
+ @NonNull FingerprintStateCallback fingerprintStateCallback) {
+ final long id = mRequestCounter.incrementAndGet();
+
+ scheduleAuthenticate(sensorId, token, operationId, userId, cookie, callback,
+ opPackageName, id, restricted, statsClient, allowBackgroundAuthentication,
+ fingerprintStateCallback);
+
+ return id;
+ }
+
+ @Override
public void startPreparedClient(int sensorId, int cookie) {
mHandler.post(() -> mSensors.get(sensorId).getScheduler().startPreparedClient(cookie));
}
@Override
- public void cancelAuthentication(int sensorId, @NonNull IBinder token) {
+ public void cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId) {
mHandler.post(() -> mSensors.get(sensorId).getScheduler()
- .cancelAuthenticationOrDetection(token));
+ .cancelAuthenticationOrDetection(token, requestId));
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 2f5b5c7..f17bcc8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -88,6 +88,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
/**
* Supports a single instance of the {@link android.hardware.biometrics.fingerprint.V2_1} or
@@ -115,6 +116,8 @@
@NonNull private final HalResultController mHalResultController;
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
@Nullable private ISidefpsController mSidefpsController;
+ // for requests that do not use biometric prompt
+ @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
private int mCurrentUserId = UserHandle.USER_NULL;
private final boolean mIsUdfps;
private final int mSensorId;
@@ -142,7 +145,8 @@
&& !client.isAlreadyDone()) {
Slog.e(TAG, "Stopping background authentication, top: "
+ topPackage + " currentClient: " + client);
- mScheduler.cancelAuthenticationOrDetection(client.getToken());
+ mScheduler.cancelAuthenticationOrDetection(
+ client.getToken(), client.getRequestId());
}
}
});
@@ -591,26 +595,30 @@
}
@Override
- public void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
+ public long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
@NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName,
int statsClient,
@NonNull FingerprintStateCallback fingerprintStateCallback) {
+ final long id = mRequestCounter.incrementAndGet();
+
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId);
final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
- mLazyDaemon, token, listener, userId, opPackageName,
+ mLazyDaemon, token, id, listener, userId, opPackageName,
mSensorProperties.sensorId, mUdfpsOverlayController, isStrongBiometric,
statsClient);
mScheduler.scheduleClientMonitor(client, fingerprintStateCallback);
});
+
+ return id;
}
@Override
public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
int userId, int cookie, @NonNull ClientMonitorCallbackConverter listener,
- @NonNull String opPackageName, boolean restricted, int statsClient,
+ @NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
boolean allowBackgroundAuthentication,
@NonNull FingerprintStateCallback fingerprintStateCallback) {
mHandler.post(() -> {
@@ -618,8 +626,8 @@
final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId);
final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
- mContext, mLazyDaemon, token, listener, userId, operationId, restricted,
- opPackageName, cookie, false /* requireConfirmation */,
+ mContext, mLazyDaemon, token, requestId, listener, userId, operationId,
+ restricted, opPackageName, cookie, false /* requireConfirmation */,
mSensorProperties.sensorId, isStrongBiometric, statsClient,
mTaskStackListener, mLockoutTracker, mUdfpsOverlayController,
allowBackgroundAuthentication, mSensorProperties);
@@ -628,14 +636,29 @@
}
@Override
+ public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+ int userId, int cookie, @NonNull ClientMonitorCallbackConverter listener,
+ @NonNull String opPackageName, boolean restricted, int statsClient,
+ boolean allowBackgroundAuthentication,
+ @NonNull FingerprintStateCallback fingerprintStateCallback) {
+ final long id = mRequestCounter.incrementAndGet();
+
+ scheduleAuthenticate(sensorId, token, operationId, userId, cookie, listener,
+ opPackageName, id, restricted, statsClient, allowBackgroundAuthentication,
+ fingerprintStateCallback);
+
+ return id;
+ }
+
+ @Override
public void startPreparedClient(int sensorId, int cookie) {
mHandler.post(() -> mScheduler.startPreparedClient(cookie));
}
@Override
- public void cancelAuthentication(int sensorId, @NonNull IBinder token) {
+ public void cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId) {
Slog.d(TAG, "cancelAuthentication, sensorId: " + sensorId);
- mHandler.post(() -> mScheduler.cancelAuthenticationOrDetection(token));
+ mHandler.post(() -> mScheduler.cancelAuthenticationOrDetection(token, requestId));
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 5060744..7d95ec0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -59,7 +59,8 @@
private boolean mIsPointerDown;
FingerprintAuthenticationClient(@NonNull Context context,
- @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
+ @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon,
+ @NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
int sensorId, boolean isStrongBiometric, int statsClient,
@@ -73,6 +74,7 @@
BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener,
lockoutTracker, allowBackgroundAuthentication, true /* shouldVibrate */,
false /* isKeyguardBypassEnabled */);
+ setRequestId(requestId);
mLockoutFrameworkImpl = lockoutTracker;
mUdfpsOverlayController = udfpsOverlayController;
mSensorProps = sensorProps;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index 8e73ee6b..147a206 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -52,13 +52,15 @@
private boolean mIsPointerDown;
public FingerprintDetectClient(@NonNull Context context,
- @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
+ @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon,
+ @NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
int sensorId, @Nullable IUdfpsOverlayController udfpsOverlayController,
boolean isStrongBiometric, int statsClient) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
true /* shouldVibrate */, BiometricsProtoEnums.MODALITY_FINGERPRINT,
BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
+ setRequestId(requestId);
mUdfpsOverlayController = udfpsOverlayController;
mIsStrongBiometric = isStrongBiometric;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
index 4918185..5c0c362 100644
--- a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
@@ -59,7 +59,7 @@
@Override
public void prepareForAuthentication(boolean requireConfirmation, IBinder token,
long sessionId, int userId, IBiometricSensorReceiver sensorReceiver,
- String opPackageName, int cookie, boolean allowBackgroundAuthentication)
+ String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication)
throws RemoteException {
}
@@ -68,7 +68,7 @@
}
@Override
- public void cancelAuthenticationFromService(IBinder token, String opPackageName)
+ public void cancelAuthenticationFromService(IBinder token, String opPackageName, long requestId)
throws RemoteException {
}
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 6610e8c..42b676f 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -692,8 +692,11 @@
}
try {
- WindowManagerGlobal.getWindowManagerService().registerDisplayWindowListener(
- mDisplayWindowListener);
+ int[] displayIds = WindowManagerGlobal.getWindowManagerService()
+ .registerDisplayWindowListener(mDisplayWindowListener);
+ for (int i = 0; i < displayIds.length; i++) {
+ mDisplayWindowListener.onDisplayAdded(displayIds[i]);
+ }
} catch (RemoteException e) {
Log.e(TAG, "Failed to register display window listener!");
}
diff --git a/services/core/java/com/android/server/connectivity/PacProxyService.java b/services/core/java/com/android/server/connectivity/PacProxyService.java
index 0070339..b2df535 100644
--- a/services/core/java/com/android/server/connectivity/PacProxyService.java
+++ b/services/core/java/com/android/server/connectivity/PacProxyService.java
@@ -192,7 +192,7 @@
}
/**
- * Updates the PAC Proxy Installer with current Proxy information. This is called by
+ * Updates the PAC Proxy Service with current Proxy information. This is called by
* the ProxyTracker through PacProxyManager before a broadcast takes place to allow
* the PacProxyService to indicate that the broadcast should not be sent and the
* PacProxyService will trigger a new broadcast when it is ready.
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 4c9d0f2..2ae5cbb 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -590,7 +590,7 @@
newIndex = i - newStart;
final float newBacklightVal;
final float newNitsVal;
- isLastValue = mRawBacklight[i] > mBacklightMaximum
+ isLastValue = mRawBacklight[i] >= mBacklightMaximum
|| i >= mRawBacklight.length - 1;
// Clamp beginning and end to valid backlight values.
if (newIndex == 0) {
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index a25cfd8..be4ec71 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -36,11 +36,17 @@
import android.hardware.fingerprint.IUdfpsHbmListener;
import android.net.Uri;
import android.os.Handler;
+import android.os.IThermalEventListener;
+import android.os.IThermalService;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.Temperature;
import android.os.UserHandle;
import android.provider.DeviceConfig;
+import android.provider.DeviceConfigInterface;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.IndentingPrintWriter;
@@ -61,7 +67,6 @@
import com.android.server.sensors.SensorManagerInternal;
import com.android.server.sensors.SensorManagerInternal.ProximityActiveListener;
import com.android.server.statusbar.StatusBarManagerInternal;
-import com.android.server.utils.DeviceConfigInterface;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
@@ -108,6 +113,7 @@
private final UdfpsObserver mUdfpsObserver;
private final SensorObserver mSensorObserver;
private final HbmObserver mHbmObserver;
+ private final SkinThermalStatusObserver mSkinThermalStatusObserver;
private final DeviceConfigInterface mDeviceConfig;
private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
@@ -156,6 +162,7 @@
};
mSensorObserver = new SensorObserver(context, ballotBox, injector);
mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler());
+ mSkinThermalStatusObserver = new SkinThermalStatusObserver(injector, ballotBox);
mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
mDeviceConfig = injector.getDeviceConfig();
mAlwaysRespectAppRequest = false;
@@ -174,6 +181,7 @@
mBrightnessObserver.observe(sensorManager);
mSensorObserver.observe();
mHbmObserver.observe();
+ mSkinThermalStatusObserver.observe();
synchronized (mLock) {
// We may have a listener already registered before the call to start, so go ahead and
// notify them to pick up our newly initialized state.
@@ -606,6 +614,7 @@
mUdfpsObserver.dumpLocked(pw);
mSensorObserver.dumpLocked(pw);
mHbmObserver.dumpLocked(pw);
+ mSkinThermalStatusObserver.dumpLocked(pw);
}
}
@@ -714,7 +723,6 @@
return mUdfpsObserver;
}
-
@VisibleForTesting
DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings(
float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
@@ -950,16 +958,19 @@
// user seeing the display flickering when the switches occur.
public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 8;
+ // Force display to [0, 60HZ] if skin temperature is at or above CRITICAL.
+ public static final int PRIORITY_SKIN_TEMPERATURE = 9;
+
// High-brightness-mode may need a specific range of refresh-rates to function properly.
- public static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 9;
+ public static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 10;
// The proximity sensor needs the refresh rate to be locked in order to function, so this is
// set to a high priority.
- public static final int PRIORITY_PROXIMITY = 10;
+ public static final int PRIORITY_PROXIMITY = 11;
// The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order
// to function, so this needs to be the highest priority of all votes.
- public static final int PRIORITY_UDFPS = 11;
+ public static final int PRIORITY_UDFPS = 12;
// Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and
// APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString.
@@ -1054,6 +1065,8 @@
return "PRIORITY_PROXIMITY";
case PRIORITY_LOW_POWER_MODE:
return "PRIORITY_LOW_POWER_MODE";
+ case PRIORITY_SKIN_TEMPERATURE:
+ return "PRIORITY_SKIN_TEMPERATURE";
case PRIORITY_UDFPS:
return "PRIORITY_UDFPS";
case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
@@ -2311,6 +2324,52 @@
}
}
+ private final class SkinThermalStatusObserver extends IThermalEventListener.Stub {
+ private final BallotBox mBallotBox;
+ private final Injector mInjector;
+
+ private @Temperature.ThrottlingStatus int mStatus = -1;
+
+ SkinThermalStatusObserver(Injector injector, BallotBox ballotBox) {
+ mInjector = injector;
+ mBallotBox = ballotBox;
+ }
+
+ @Override
+ public void notifyThrottling(Temperature temp) {
+ mStatus = temp.getStatus();
+ if (mLoggingEnabled) {
+ Slog.d(TAG, "New thermal throttling status "
+ + ", current thermal status = " + mStatus);
+ }
+ final Vote vote;
+ if (mStatus >= Temperature.THROTTLING_CRITICAL) {
+ vote = Vote.forRefreshRates(0f, 60f);
+ } else {
+ vote = null;
+ }
+ mBallotBox.vote(GLOBAL_ID, Vote.PRIORITY_SKIN_TEMPERATURE, vote);
+ }
+
+ public void observe() {
+ IThermalService thermalService = mInjector.getThermalService();
+ if (thermalService == null) {
+ Slog.w(TAG, "Could not observe thermal status. Service not available");
+ return;
+ }
+ try {
+ thermalService.registerThermalEventListenerWithType(this, Temperature.TYPE_SKIN);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register thermal status listener", e);
+ }
+ }
+
+ void dumpLocked(PrintWriter writer) {
+ writer.println(" SkinThermalStatusObserver:");
+ writer.println(" mStatus: " + mStatus);
+ }
+ }
+
private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
public DeviceConfigDisplaySettings() {
}
@@ -2472,6 +2531,8 @@
BrightnessInfo getBrightnessInfo(int displayId);
boolean isDozeState(Display d);
+
+ IThermalService getThermalService();
}
@VisibleForTesting
@@ -2532,6 +2593,12 @@
return Display.isDozeState(d.getState());
}
+ @Override
+ public IThermalService getThermalService() {
+ return IThermalService.Stub.asInterface(
+ ServiceManager.getService(Context.THERMAL_SERVICE));
+ }
+
private DisplayManager getDisplayManager() {
if (mDisplayManager == null) {
mDisplayManager = mContext.getSystemService(DisplayManager.class);
diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectActionFromPlayback.java b/services/core/java/com/android/server/hdmi/DeviceSelectActionFromPlayback.java
new file mode 100644
index 0000000..b2338e6
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/DeviceSelectActionFromPlayback.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.HdmiPlaybackClient;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Handles an action that selects a logical device as a new active source.
+ *
+ * Triggered by {@link HdmiPlaybackClient}, attempts to select the given target device
+ * for a new active source. A >Routing Change< command is issued in order to select
+ * the target device. If that doesn't succeed a >Set Stream Path< command is sent.
+ * It does its best to wake up the target in standby mode, before issuing the device
+ * select command.
+ */
+final class DeviceSelectActionFromPlayback extends HdmiCecFeatureAction {
+ private static final String TAG = "DeviceSelectActionFromPlayback";
+
+ // Time in milliseconds we wait for the device power status to switch to 'Standby'
+ private static final int TIMEOUT_TRANSIT_TO_STANDBY_MS = 5 * 1000;
+
+ // Time in milliseconds we wait for the device power status to turn to 'On'.
+ private static final int TIMEOUT_POWER_ON_MS = 5 * 1000;
+
+ // The number of times we try to wake up the target device before we give up
+ // and just send <Routing Change>.
+ private static final int LOOP_COUNTER_MAX = 2;
+
+ // State in which we wait for <Report Power Status> to come in response to the command
+ // <Give Device Power Status> we have sent.
+ @VisibleForTesting
+ static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 1;
+
+ // State in which we wait for the device power status to switch to 'Standby'.
+ // We wait till the status becomes 'Standby' before we send <Routing Change>
+ // to wake up the device again.
+ private static final int STATE_WAIT_FOR_DEVICE_TO_TRANSIT_TO_STANDBY = 2;
+
+ // State in which we wait for the device power status to switch to 'on'. We wait
+ // maximum 100 seconds (20 * 5) before we give up and just send <Set Stream Path>.
+ @VisibleForTesting
+ static final int STATE_WAIT_FOR_DEVICE_POWER_ON = 3;
+
+ // State in which we wait for <Active Source> to come in response to the command
+ // <Routing Change> we have sent.
+ @VisibleForTesting
+ static final int STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_ROUTING_CHANGE = 4;
+
+ // State in which we wait for <Active Source> to come in response to the command
+ // <Set Stream Path> we have sent.
+ @VisibleForTesting
+ private static final int STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_SET_STREAM_PATH = 5;
+
+ private final HdmiDeviceInfo mTarget;
+ private final HdmiCecMessage mGivePowerStatus;
+ private final boolean mIsCec20;
+
+ private int mPowerStatusCounter = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param source {@link HdmiCecLocalDevice} instance
+ * @param target target logical device that will be a new active source
+ * @param callback callback object
+ */
+ DeviceSelectActionFromPlayback(HdmiCecLocalDevicePlayback source, HdmiDeviceInfo target,
+ IHdmiControlCallback callback) {
+ this(source, target, callback,
+ source.getDeviceInfo().getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0
+ && target.getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ }
+
+ @VisibleForTesting
+ DeviceSelectActionFromPlayback(HdmiCecLocalDevicePlayback source, HdmiDeviceInfo target,
+ IHdmiControlCallback callback, boolean isCec20) {
+ super(source, callback);
+ mTarget = target;
+ mGivePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ getSourceAddress(), getTargetAddress());
+ mIsCec20 = isCec20;
+ }
+
+ private int getTargetAddress() {
+ return mTarget.getLogicalAddress();
+ }
+
+ private int getTargetPath() {
+ return mTarget.getPhysicalAddress();
+ }
+
+ @Override
+ public boolean start() {
+ // This <Routing Change> message wakes up the target device in most cases.
+ sendRoutingChange();
+
+ if (!mIsCec20) {
+ queryDevicePowerStatus();
+ } else {
+ int targetPowerStatus = HdmiControlManager.POWER_STATUS_UNKNOWN;
+ HdmiDeviceInfo targetDevice = localDevice().mService.getHdmiCecNetwork()
+ .getCecDeviceInfo(getTargetAddress());
+ if (targetDevice != null) {
+ targetPowerStatus = targetDevice.getDevicePowerStatus();
+ }
+ if (targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) {
+ queryDevicePowerStatus();
+ } else if (targetPowerStatus == HdmiControlManager.POWER_STATUS_ON) {
+ mState = STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_ROUTING_CHANGE;
+ addTimer(mState, HdmiConfig.TIMEOUT_MS);
+ return true;
+ }
+ }
+
+ mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
+ addTimer(mState, HdmiConfig.TIMEOUT_MS);
+ return true;
+ }
+
+ private void queryDevicePowerStatus() {
+ sendCommand(
+ mGivePowerStatus,
+ new HdmiControlService.SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ if (error != SendMessageResult.SUCCESS) {
+ finishWithCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED);
+ return;
+ }
+ }
+ });
+ }
+
+ @Override
+ public boolean processCommand(HdmiCecMessage cmd) {
+ if (cmd.getSource() != getTargetAddress()) {
+ return false;
+ }
+ int opcode = cmd.getOpcode();
+ byte[] params = cmd.getParams();
+ if (opcode == Constants.MESSAGE_ACTIVE_SOURCE) {
+ // The target device was successfully set as the active source
+ finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+ return true;
+ }
+ if (mState == STATE_WAIT_FOR_REPORT_POWER_STATUS
+ && opcode == Constants.MESSAGE_REPORT_POWER_STATUS) {
+ return handleReportPowerStatus(params[0]);
+ }
+ return false;
+ }
+
+ private boolean handleReportPowerStatus(int powerStatus) {
+ switch (powerStatus) {
+ case HdmiControlManager.POWER_STATUS_ON:
+ selectDevice();
+ return true;
+ case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY:
+ if (mPowerStatusCounter < 4) {
+ mState = STATE_WAIT_FOR_DEVICE_TO_TRANSIT_TO_STANDBY;
+ addTimer(mState, TIMEOUT_TRANSIT_TO_STANDBY_MS);
+ } else {
+ selectDevice();
+ }
+ return true;
+ case HdmiControlManager.POWER_STATUS_STANDBY:
+ if (mPowerStatusCounter == 0) {
+ sendRoutingChange();
+ mState = STATE_WAIT_FOR_DEVICE_POWER_ON;
+ addTimer(mState, TIMEOUT_POWER_ON_MS);
+ } else {
+ selectDevice();
+ }
+ return true;
+ case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON:
+ if (mPowerStatusCounter < LOOP_COUNTER_MAX) {
+ mState = STATE_WAIT_FOR_DEVICE_POWER_ON;
+ addTimer(mState, TIMEOUT_POWER_ON_MS);
+ } else {
+ selectDevice();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void selectDevice() {
+ sendRoutingChange();
+ mState = STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_ROUTING_CHANGE;
+ addTimer(mState, HdmiConfig.TIMEOUT_MS);
+ }
+
+ @Override
+ void handleTimerEvent(int timeoutState) {
+ if (mState != timeoutState) {
+ Slog.w(TAG, "Timer in a wrong state. Ignored.");
+ return;
+ }
+ switch (mState) {
+ case STATE_WAIT_FOR_REPORT_POWER_STATUS:
+ selectDevice();
+ addTimer(mState, HdmiConfig.TIMEOUT_MS);
+ break;
+ case STATE_WAIT_FOR_DEVICE_POWER_ON:
+ mPowerStatusCounter++;
+ queryDevicePowerStatus();
+ mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
+ addTimer(mState, HdmiConfig.TIMEOUT_MS);
+ break;
+ case STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_ROUTING_CHANGE:
+ sendSetStreamPath();
+ mState = STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_SET_STREAM_PATH;
+ addTimer(mState, HdmiConfig.TIMEOUT_MS);
+ break;
+ case STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_SET_STREAM_PATH:
+ finishWithCallback(HdmiControlManager.RESULT_TIMEOUT);
+ break;
+ }
+ }
+
+ private void sendRoutingChange() {
+ sendCommand(HdmiCecMessageBuilder.buildRoutingChange(getSourceAddress(),
+ playback().getActiveSource().physicalAddress, getTargetPath()));
+ }
+
+ private void sendSetStreamPath() {
+ sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(getSourceAddress(),
+ getTargetPath()));
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java b/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java
index 444d74d..9437c4f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java
@@ -27,7 +27,8 @@
@VisibleForTesting
public class HdmiCecAtomWriter {
- private static final int FEATURE_ABORT_OPCODE_UNKNOWN = 0x100;
+ @VisibleForTesting
+ protected static final int FEATURE_ABORT_OPCODE_UNKNOWN = 0x100;
private static final int ERROR_CODE_UNKNOWN = -1;
/**
@@ -103,26 +104,32 @@
HdmiCecMessage message) {
MessageReportedSpecialArgs specialArgs = new MessageReportedSpecialArgs();
- int keycode = message.getParams()[0];
- if (keycode >= 0x1E && keycode <= 0x29) {
- specialArgs.mUserControlPressedCommand = HdmiStatsEnums.NUMBER;
- } else {
- specialArgs.mUserControlPressedCommand = keycode + 0x100;
+ if (message.getParams().length > 0) {
+ int keycode = message.getParams()[0];
+ if (keycode >= 0x1E && keycode <= 0x29) {
+ specialArgs.mUserControlPressedCommand = HdmiStatsEnums.NUMBER;
+ } else {
+ specialArgs.mUserControlPressedCommand = keycode + 0x100;
+ }
}
return specialArgs;
}
/**
- * Constructs method for constructing the special arguments for a <Feature Abort> message.
+ * Constructs the special arguments for a <Feature Abort> message.
*
* @param message The HDMI CEC message to log
*/
private MessageReportedSpecialArgs createFeatureAbortSpecialArgs(HdmiCecMessage message) {
MessageReportedSpecialArgs specialArgs = new MessageReportedSpecialArgs();
- specialArgs.mFeatureAbortOpcode = message.getParams()[0] & 0xFF; // Unsigned byte
- specialArgs.mFeatureAbortReason = message.getParams()[1] + 10;
+ if (message.getParams().length > 0) {
+ specialArgs.mFeatureAbortOpcode = message.getParams()[0] & 0xFF; // Unsigned byte
+ if (message.getParams().length > 1) {
+ specialArgs.mFeatureAbortReason = message.getParams()[1] + 10;
+ }
+ }
return specialArgs;
}
@@ -135,8 +142,7 @@
*/
private void messageReportedBase(MessageReportedGenericArgs genericArgs,
MessageReportedSpecialArgs specialArgs) {
- FrameworkStatsLog.write(
- FrameworkStatsLog.HDMI_CEC_MESSAGE_REPORTED,
+ writeHdmiCecMessageReportedAtom(
genericArgs.mUid,
genericArgs.mDirection,
genericArgs.mInitiatorLogicalAddress,
@@ -148,6 +154,26 @@
specialArgs.mFeatureAbortReason);
}
+ /**
+ * Writes a HdmiCecMessageReported atom representing an incoming or outgoing HDMI-CEC message.
+ */
+ @VisibleForTesting
+ protected void writeHdmiCecMessageReportedAtom(int uid, int direction,
+ int initiatorLogicalAddress, int destinationLogicalAddress, int opcode,
+ int sendMessageResult, int userControlPressedCommand, int featureAbortOpcode,
+ int featureAbortReason) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.HDMI_CEC_MESSAGE_REPORTED,
+ uid,
+ direction,
+ initiatorLogicalAddress,
+ destinationLogicalAddress,
+ opcode,
+ sendMessageResult,
+ userControlPressedCommand,
+ featureAbortOpcode,
+ featureAbortReason);
+ }
/**
* Writes a HdmiCecActiveSourceChanged atom representing a change in the active source.
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 3509062..f94d220 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -261,6 +261,20 @@
}
@ServiceThreadOnly
+ @VisibleForTesting
+ protected boolean isAlreadyActiveSource(HdmiDeviceInfo targetDevice, int targetAddress,
+ IHdmiControlCallback callback) {
+ ActiveSource active = getActiveSource();
+ if (targetDevice.getDevicePowerStatus() == HdmiControlManager.POWER_STATUS_ON
+ && active.isValid()
+ && targetAddress == active.logicalAddress) {
+ invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
+ return true;
+ }
+ return false;
+ }
+
+ @ServiceThreadOnly
@Constants.HandleMessageResult
protected final int onMessage(HdmiCecMessage message) {
assertRunOnServiceThread();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 4376c9a..744436d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -124,6 +124,39 @@
String.valueOf(addr));
}
+ /**
+ * Performs the action 'device select' or 'one touch play' initiated by a Playback device.
+ *
+ * @param id id of HDMI device to select
+ * @param callback callback object to report the result with
+ */
+ @ServiceThreadOnly
+ void deviceSelect(int id, IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
+ synchronized (mLock) {
+ if (id == getDeviceInfo().getId()) {
+ mService.oneTouchPlay(callback);
+ return;
+ }
+ }
+ HdmiDeviceInfo targetDevice = mService.getHdmiCecNetwork().getDeviceInfo(id);
+ if (targetDevice == null) {
+ invokeCallback(callback, HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE);
+ return;
+ }
+ int targetAddress = targetDevice.getLogicalAddress();
+ if (isAlreadyActiveSource(targetDevice, targetAddress, callback)) {
+ return;
+ }
+ if (!mService.isControlEnabled()) {
+ setActiveSource(targetDevice, "HdmiCecLocalDevicePlayback#deviceSelect()");
+ invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
+ return;
+ }
+ removeAction(DeviceSelectActionFromPlayback.class);
+ addAndStartAction(new DeviceSelectActionFromPlayback(this, targetDevice, callback));
+ }
+
@Override
@ServiceThreadOnly
void onHotplug(int portId, boolean connected) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index bfb387f..8d0a7bd 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -242,11 +242,7 @@
return;
}
int targetAddress = targetDevice.getLogicalAddress();
- ActiveSource active = getActiveSource();
- if (targetDevice.getDevicePowerStatus() == HdmiControlManager.POWER_STATUS_ON
- && active.isValid()
- && targetAddress == active.logicalAddress) {
- invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
+ if (isAlreadyActiveSource(targetDevice, targetAddress, callback)) {
return;
}
if (targetAddress == Constants.ADDR_INTERNAL) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index f993b5d..6927094 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1685,7 +1685,8 @@
return;
}
HdmiCecLocalDeviceTv tv = tv();
- if (tv == null) {
+ HdmiCecLocalDevicePlayback playback = playback();
+ if (tv == null && playback == null) {
if (!mAddressAllocated) {
mSelectRequestBuffer.set(SelectRequestBuffer.newDeviceSelect(
HdmiControlService.this, deviceId, callback));
@@ -1698,20 +1699,24 @@
invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
return;
}
- HdmiMhlLocalDeviceStub device = mMhlController.getLocalDeviceById(deviceId);
- if (device != null) {
- if (device.getPortId() == tv.getActivePortId()) {
- invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
+ if (tv != null) {
+ HdmiMhlLocalDeviceStub device = mMhlController.getLocalDeviceById(deviceId);
+ if (device != null) {
+ if (device.getPortId() == tv.getActivePortId()) {
+ invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
+ return;
+ }
+ // Upon selecting MHL device, we send RAP[Content On] to wake up
+ // the connected mobile device, start routing control to switch ports.
+ // callback is handled by MHL action.
+ device.turnOn(callback);
+ tv.doManualPortSwitching(device.getPortId(), null);
return;
}
- // Upon selecting MHL device, we send RAP[Content On] to wake up
- // the connected mobile device, start routing control to switch ports.
- // callback is handled by MHL action.
- device.turnOn(callback);
- tv.doManualPortSwitching(device.getPortId(), null);
+ tv.deviceSelect(deviceId, callback);
return;
}
- tv.deviceSelect(deviceId, callback);
+ playback.deviceSelect(deviceId, callback);
}
});
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java b/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
index e52e32a..a9b5214 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
@@ -92,6 +92,9 @@
pw.println(" Sets the System Audio Mode feature on or off on TV devices");
pw.println(" setarc [on|off]");
pw.println(" Sets the ARC feature on or off on TV devices");
+ pw.println(" deviceselect <device id>");
+ pw.println(" Switch to device with given id");
+ pw.println(" The device's id is represented by its logical address.");
}
private int handleShellCommand(String cmd) throws RemoteException {
@@ -110,12 +113,30 @@
return setSystemAudioMode(pw);
case "setarc":
return setArcMode(pw);
+ case "deviceselect":
+ return deviceSelect(pw);
}
getErrPrintWriter().println("Unhandled command: " + cmd);
return 1;
}
+ private int deviceSelect(PrintWriter pw) throws RemoteException {
+ if (getRemainingArgsCount() != 1) {
+ throw new IllegalArgumentException("Expected exactly 1 argument.");
+ }
+ int deviceId = Integer.parseInt(getNextArg());
+
+ pw.print("Sending Device Select...");
+ mBinderService.deviceSelect(deviceId, mHdmiControlCallback);
+
+ if (!receiveCallback("Device Select")) {
+ return 1;
+ }
+
+ return mCecResult.get() == HdmiControlManager.RESULT_SUCCESS ? 0 : 1;
+ }
+
private int oneTouchPlay(PrintWriter pw) throws RemoteException {
pw.print("Sending One Touch Play...");
mBinderService.oneTouchPlay(mHdmiControlCallback);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 62b36d0..2813236 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -152,12 +152,9 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.content.PackageMonitor;
-import com.android.internal.inputmethod.CallbackUtils;
-import com.android.internal.inputmethod.IBooleanResultCallback;
+import com.android.internal.infra.AndroidFuture;
import com.android.internal.inputmethod.IInputContentUriToken;
-import com.android.internal.inputmethod.IInputContentUriTokenResultCallback;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
-import com.android.internal.inputmethod.IVoidResultCallback;
import com.android.internal.inputmethod.ImeTracing;
import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.inputmethod.InputMethodDebug;
@@ -2587,14 +2584,12 @@
}
if (mCurToken != null) {
- try {
- if (DEBUG) {
- Slog.v(TAG, "Removing window token: " + mCurToken + " for display: "
- + mCurTokenDisplayId);
- }
- mIWindowManager.removeWindowToken(mCurToken, mCurTokenDisplayId);
- } catch (RemoteException e) {
+ if (DEBUG) {
+ Slog.v(TAG, "Removing window token: " + mCurToken + " for display: "
+ + mCurTokenDisplayId);
}
+ mWindowManagerInternal.removeWindowToken(mCurToken, false /* removeWindows */,
+ false /* animateExit */, mCurTokenDisplayId);
// Set IME window status as invisible when unbind current method.
mImeWindowVis = 0;
mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
@@ -5842,9 +5837,15 @@
@BinderThread
@Override
public void createInputContentUriToken(Uri contentUri, String packageName,
- IInputContentUriTokenResultCallback resultCallback) {
- CallbackUtils.onResult(resultCallback,
- () -> mImms.createInputContentUriToken(mToken, contentUri, packageName));
+ AndroidFuture future /* T=IBinder */) {
+ @SuppressWarnings("unchecked")
+ final AndroidFuture<IBinder> typedFuture = future;
+ try {
+ typedFuture.complete(mImms.createInputContentUriToken(
+ mToken, contentUri, packageName).asBinder());
+ } catch (Throwable e) {
+ typedFuture.completeExceptionally(e);
+ }
}
@BinderThread
@@ -5855,28 +5856,55 @@
@BinderThread
@Override
- public void setInputMethod(String id, IVoidResultCallback resultCallback) {
- CallbackUtils.onResult(resultCallback, () -> mImms.setInputMethod(mToken, id));
+ public void setInputMethod(String id, AndroidFuture future /* T=Void */) {
+ @SuppressWarnings("unchecked")
+ final AndroidFuture<Void> typedFuture = future;
+ try {
+ mImms.setInputMethod(mToken, id);
+ typedFuture.complete(null);
+ } catch (Throwable e) {
+ typedFuture.completeExceptionally(e);
+ }
}
@BinderThread
@Override
public void setInputMethodAndSubtype(String id, InputMethodSubtype subtype,
- IVoidResultCallback resultCallback) {
- CallbackUtils.onResult(resultCallback,
- () -> mImms.setInputMethodAndSubtype(mToken, id, subtype));
+ AndroidFuture future /* T=Void */) {
+ @SuppressWarnings("unchecked")
+ final AndroidFuture<Void> typedFuture = future;
+ try {
+ mImms.setInputMethodAndSubtype(mToken, id, subtype);
+ typedFuture.complete(null);
+ } catch (Throwable e) {
+ typedFuture.completeExceptionally(e);
+ }
}
@BinderThread
@Override
- public void hideMySoftInput(int flags, IVoidResultCallback resultCallback) {
- CallbackUtils.onResult(resultCallback, () -> mImms.hideMySoftInput(mToken, flags));
+ public void hideMySoftInput(int flags, AndroidFuture future /* T=Void */) {
+ @SuppressWarnings("unchecked")
+ final AndroidFuture<Void> typedFuture = future;
+ try {
+ mImms.hideMySoftInput(mToken, flags);
+ typedFuture.complete(null);
+ } catch (Throwable e) {
+ typedFuture.completeExceptionally(e);
+ }
}
@BinderThread
@Override
- public void showMySoftInput(int flags, IVoidResultCallback resultCallback) {
- CallbackUtils.onResult(resultCallback, () -> mImms.showMySoftInput(mToken, flags));
+ public void showMySoftInput(int flags, AndroidFuture future /* T=Void */) {
+ @SuppressWarnings("unchecked")
+ final AndroidFuture<Void> typedFuture = future;
+ try {
+ mImms.showMySoftInput(mToken, flags);
+ typedFuture.complete(null);
+ } catch (Throwable e) {
+ typedFuture.completeExceptionally(e);
+ }
}
@BinderThread
@@ -5887,24 +5915,39 @@
@BinderThread
@Override
- public void switchToPreviousInputMethod(IBooleanResultCallback resultCallback) {
- CallbackUtils.onResult(resultCallback, () -> mImms.switchToPreviousInputMethod(mToken));
+ public void switchToPreviousInputMethod(AndroidFuture future /* T=Boolean */) {
+ @SuppressWarnings("unchecked")
+ final AndroidFuture<Boolean> typedFuture = future;
+ try {
+ typedFuture.complete(mImms.switchToPreviousInputMethod(mToken));
+ } catch (Throwable e) {
+ typedFuture.completeExceptionally(e);
+ }
}
@BinderThread
@Override
public void switchToNextInputMethod(boolean onlyCurrentIme,
- IBooleanResultCallback resultCallback) {
- CallbackUtils.onResult(resultCallback,
- () -> mImms.switchToNextInputMethod(mToken, onlyCurrentIme));
+ AndroidFuture future /* T=Boolean */) {
+ @SuppressWarnings("unchecked")
+ final AndroidFuture<Boolean> typedFuture = future;
+ try {
+ typedFuture.complete(mImms.switchToNextInputMethod(mToken, onlyCurrentIme));
+ } catch (Throwable e) {
+ typedFuture.completeExceptionally(e);
+ }
}
@BinderThread
@Override
- public void shouldOfferSwitchingToNextInputMethod(
- IBooleanResultCallback resultCallback) {
- CallbackUtils.onResult(resultCallback,
- () -> mImms.shouldOfferSwitchingToNextInputMethod(mToken));
+ public void shouldOfferSwitchingToNextInputMethod(AndroidFuture future /* T=Boolean */) {
+ @SuppressWarnings("unchecked")
+ final AndroidFuture<Boolean> typedFuture = future;
+ try {
+ typedFuture.complete(mImms.shouldOfferSwitchingToNextInputMethod(mToken));
+ } catch (Throwable e) {
+ typedFuture.completeExceptionally(e);
+ }
}
@BinderThread
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 7b5a635..cd26fb5 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -1385,7 +1385,7 @@
ipw.println("Event Log:");
ipw.increaseIndent();
- EVENT_LOG.iterate(manager.getName(), ipw::println);
+ EVENT_LOG.iterate(ipw::println, manager.getName());
ipw.decreaseIndent();
return;
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index 3da5a43..489b9b3 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -30,8 +30,6 @@
import android.compat.annotation.EnabledAfter;
import android.content.Context;
import android.content.Intent;
-import android.hardware.contexthub.V1_0.ContextHubMsg;
-import android.hardware.contexthub.V1_0.Result;
import android.hardware.location.ContextHubInfo;
import android.hardware.location.ContextHubManager;
import android.hardware.location.ContextHubTransaction;
@@ -383,23 +381,20 @@
checkNanoappPermsAsync();
}
- ContextHubMsg messageToNanoApp =
- ContextHubServiceUtil.createHidlContextHubMessage(mHostEndPointId, message);
-
- int contextHubId = mAttachedContextHubInfo.getId();
try {
- result = mContextHubProxy.getHub().sendMessageToHub(contextHubId, messageToNanoApp);
+ result = mContextHubProxy.sendMessageToContextHub(
+ mHostEndPointId, mAttachedContextHubInfo.getId(), message);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in sendMessageToNanoApp (target hub ID = "
- + contextHubId + ")", e);
- result = Result.UNKNOWN_FAILURE;
+ + mAttachedContextHubInfo.getId() + ")", e);
+ result = ContextHubTransaction.RESULT_FAILED_UNKNOWN;
}
} else {
Log.e(TAG, "Failed to send message to nanoapp: client connection is closed");
- result = Result.UNKNOWN_FAILURE;
+ result = ContextHubTransaction.RESULT_FAILED_UNKNOWN;
}
- return ContextHubServiceUtil.toTransactionResult(result);
+ return result;
}
/**
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java
index e3522f6..41a406ce 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java
@@ -19,7 +19,6 @@
import android.annotation.IntDef;
import android.app.PendingIntent;
import android.content.Context;
-import android.hardware.contexthub.V1_0.ContextHubMsg;
import android.hardware.location.ContextHubInfo;
import android.hardware.location.IContextHubClient;
import android.hardware.location.IContextHubClientCallback;
@@ -227,37 +226,37 @@
* Handles a message sent from a nanoapp.
*
* @param contextHubId the ID of the hub where the nanoapp sent the message from
+ * @param hostEndpointId The host endpoint ID of the client that this message is for.
* @param message the message send by a nanoapp
* @param nanoappPermissions the set of permissions the nanoapp holds
* @param messagePermissions the set of permissions that should be used for attributing
* permissions when this message is consumed by a client
*/
/* package */ void onMessageFromNanoApp(
- int contextHubId, ContextHubMsg message, List<String> nanoappPermissions,
- List<String> messagePermissions) {
- NanoAppMessage clientMessage = ContextHubServiceUtil.createNanoAppMessage(message);
-
+ int contextHubId, short hostEndpointId, NanoAppMessage message,
+ List<String> nanoappPermissions, List<String> messagePermissions) {
if (DEBUG_LOG_ENABLED) {
- Log.v(TAG, "Received " + clientMessage);
+ Log.v(TAG, "Received " + message);
}
- if (clientMessage.isBroadcastMessage()) {
+ if (message.isBroadcastMessage()) {
// Broadcast messages shouldn't be sent with any permissions tagged per CHRE API
// requirements.
if (!messagePermissions.isEmpty()) {
- Log.wtf(TAG, "Received broadcast message with permissions from " + message.appName);
+ Log.wtf(TAG, "Received broadcast message with permissions from "
+ + message.getNanoAppId());
}
broadcastMessage(
- contextHubId, clientMessage, nanoappPermissions, messagePermissions);
+ contextHubId, message, nanoappPermissions, messagePermissions);
} else {
- ContextHubClientBroker proxy = mHostEndPointIdToClientMap.get(message.hostEndPoint);
+ ContextHubClientBroker proxy = mHostEndPointIdToClientMap.get(hostEndpointId);
if (proxy != null) {
proxy.sendMessageToClient(
- clientMessage, nanoappPermissions, messagePermissions);
+ message, nanoappPermissions, messagePermissions);
} else {
Log.e(TAG, "Cannot send message to unregistered client (host endpoint ID = "
- + message.hostEndPoint + ")");
+ + hostEndpointId + ")");
}
}
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 4d302b1..27a78dd 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -16,6 +16,7 @@
package com.android.server.location.contexthub;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.PendingIntent;
@@ -27,13 +28,6 @@
import android.database.ContentObserver;
import android.hardware.SensorPrivacyManager;
import android.hardware.SensorPrivacyManagerInternal;
-import android.hardware.contexthub.V1_0.AsyncEventType;
-import android.hardware.contexthub.V1_0.ContextHub;
-import android.hardware.contexthub.V1_0.ContextHubMsg;
-import android.hardware.contexthub.V1_0.Result;
-import android.hardware.contexthub.V1_0.TransactionResult;
-import android.hardware.contexthub.V1_2.HubAppInfo;
-import android.hardware.contexthub.V1_2.IContexthubCallback;
import android.hardware.location.ContextHubInfo;
import android.hardware.location.ContextHubMessage;
import android.hardware.location.ContextHubTransaction;
@@ -67,6 +61,8 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
@@ -97,6 +93,20 @@
private static final int OS_APP_INSTANCE = -1;
+ /**
+ * Constants describing an async event from the Context Hub.
+ * {@hide}
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "CONTEXT_HUB_EVENT_" }, value = {
+ CONTEXT_HUB_EVENT_UNKNOWN,
+ CONTEXT_HUB_EVENT_RESTARTED,
+ })
+ public @interface Type { }
+
+ public static final int CONTEXT_HUB_EVENT_UNKNOWN = 0;
+ public static final int CONTEXT_HUB_EVENT_RESTARTED = 1;
+
/*
* Local flag to enable debug logging.
*/
@@ -136,7 +146,7 @@
/**
* Class extending the callback to register with a Context Hub.
*/
- private class ContextHubServiceCallback extends IContexthubCallback.Stub {
+ private class ContextHubServiceCallback implements IContextHubWrapper.ICallback {
private final int mContextHubId;
ContextHubServiceCallback(int contextHubId) {
@@ -144,45 +154,31 @@
}
@Override
- public void handleClientMsg(ContextHubMsg message) {
- handleClientMessageCallback(mContextHubId, message,
- Collections.emptyList() /* nanoappPermissions */,
- Collections.emptyList() /* messagePermissions */);
+ public void handleTransactionResult(int transactionId, boolean success) {
+ handleTransactionResultCallback(mContextHubId, transactionId, success);
}
@Override
- public void handleTxnResult(int transactionId, int result) {
- handleTransactionResultCallback(mContextHubId, transactionId, result);
- }
-
- @Override
- public void handleHubEvent(int eventType) {
+ public void handleContextHubEvent(int eventType) {
handleHubEventCallback(mContextHubId, eventType);
}
@Override
- public void handleAppAbort(long nanoAppId, int abortCode) {
- handleAppAbortCallback(mContextHubId, nanoAppId, abortCode);
+ public void handleNanoappAbort(long nanoappId, int abortCode) {
+ handleAppAbortCallback(mContextHubId, nanoappId, abortCode);
}
@Override
- public void handleAppsInfo(
- ArrayList<android.hardware.contexthub.V1_0.HubAppInfo> nanoAppInfoList) {
- handleQueryAppsCallback(mContextHubId,
- ContextHubServiceUtil.toHubAppInfo_1_2(nanoAppInfoList));
+ public void handleNanoappInfo(List<NanoAppState> nanoappStateList) {
+ handleQueryAppsCallback(mContextHubId, nanoappStateList);
}
@Override
- public void handleClientMsg_1_2(android.hardware.contexthub.V1_2.ContextHubMsg message,
- ArrayList<String> messagePermissions) {
- handleClientMessageCallback(mContextHubId, message.msg_1_0, message.permissions,
+ public void handleNanoappMessage(short hostEndpointId, NanoAppMessage message,
+ List<String> nanoappPermissions, List<String> messagePermissions) {
+ handleClientMessageCallback(mContextHubId, hostEndpointId, message, nanoappPermissions,
messagePermissions);
}
-
- @Override
- public void handleAppsInfo_1_2(ArrayList<HubAppInfo> nanoAppInfoList) {
- handleQueryAppsCallback(mContextHubId, nanoAppInfoList);
- }
}
public ContextHubService(Context context) {
@@ -200,7 +196,7 @@
return;
}
- Pair<List<ContextHub>, List<String>> hubInfo;
+ Pair<List<ContextHubInfo>, List<String>> hubInfo;
try {
hubInfo = mContextHubWrapper.getHubs();
} catch (RemoteException e) {
@@ -214,7 +210,7 @@
mContextHubInfoList = new ArrayList<>(mContextHubIdToInfoMap.values());
mClientManager = new ContextHubClientManager(mContext, mContextHubWrapper);
mTransactionManager = new ContextHubTransactionManager(
- mContextHubWrapper.getHub(), mClientManager, mNanoAppStateManager);
+ mContextHubWrapper, mClientManager, mNanoAppStateManager);
mSensorPrivacyManagerInternal =
LocalServices.getService(SensorPrivacyManagerInternal.class);
@@ -261,7 +257,7 @@
public void onReceive(Context context, Intent intent) {
if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())
|| WifiManager.ACTION_WIFI_SCAN_AVAILABILITY_CHANGED.equals(
- intent.getAction())) {
+ intent.getAction())) {
sendWifiSettingUpdate(false /* forceUpdate */);
}
}
@@ -304,7 +300,7 @@
Log.d(TAG, "User: " + userId + "mic privacy: " + enabled);
sendMicrophoneDisableSettingUpdate(enabled);
}
- });
+ });
}
}
@@ -329,7 +325,7 @@
@Override
public void onHubReset() {
- byte[] data = {TransactionResult.SUCCESS};
+ byte[] data = {android.hardware.contexthub.V1_0.TransactionResult.SUCCESS};
onMessageReceiptOldApi(MSG_HUB_RESET, contextHubId, OS_APP_INSTANCE, data);
}
@@ -560,17 +556,17 @@
/**
* Performs a query at the specified hub.
- *
+ * <p>
* This method should only be invoked internally by the service, either to update the service
* cache or as a result of an explicit query requested by a client through the sendMessage API.
*
* @param contextHubId the ID of the hub to do the query
- * @return the result of the query
+ * @return true if the query succeeded
* @throws IllegalStateException if the transaction queue is full
*/
- private int queryNanoAppsInternal(int contextHubId) {
+ private boolean queryNanoAppsInternal(int contextHubId) {
if (mContextHubWrapper == null) {
- return Result.UNKNOWN_FAILURE;
+ return false;
}
IContextHubTransactionCallback onCompleteCallback =
@@ -579,7 +575,7 @@
contextHubId, onCompleteCallback, getCallingPackageName());
mTransactionManager.addTransaction(transaction);
- return Result.OK;
+ return true;
}
@Override
@@ -605,7 +601,7 @@
boolean success = false;
if (nanoAppHandle == OS_APP_INSTANCE) {
if (msg.getMsgType() == MSG_QUERY_NANO_APPS) {
- success = (queryNanoAppsInternal(contextHubHandle) == Result.OK);
+ success = queryNanoAppsInternal(contextHubHandle);
} else {
Log.e(TAG, "Invalid OS message params of type " + msg.getMsgType());
}
@@ -631,14 +627,16 @@
* Handles a unicast or broadcast message from a nanoapp.
*
* @param contextHubId the ID of the hub the message came from
+ * @param hostEndpointId the host endpoint ID of the client receiving this message
* @param message the message contents
* @param reqPermissions the permissions required to consume this message
*/
private void handleClientMessageCallback(
- int contextHubId, ContextHubMsg message, List<String> nanoappPermissions,
+ int contextHubId, short hostEndpointId, NanoAppMessage message,
+ List<String> nanoappPermissions,
List<String> messagePermissions) {
mClientManager.onMessageFromNanoApp(
- contextHubId, message, nanoappPermissions, messagePermissions);
+ contextHubId, hostEndpointId, message, nanoappPermissions, messagePermissions);
}
/**
@@ -663,7 +661,7 @@
/**
* A helper function to handle an unload response from the Context Hub for the old API.
- *
+ * <p>
* TODO(b/69270990): Remove this once the old APIs are obsolete.
*/
private void handleUnloadResponseOldApi(int contextHubId, int result) {
@@ -677,20 +675,21 @@
*
* @param contextHubId the ID of the hub the response came from
* @param transactionId the ID of the transaction
- * @param result the result of the transaction reported by the hub
+ * @param success true if the transaction succeeded
*/
- private void handleTransactionResultCallback(int contextHubId, int transactionId, int result) {
- mTransactionManager.onTransactionResponse(transactionId, result);
+ private void handleTransactionResultCallback(int contextHubId, int transactionId,
+ boolean success) {
+ mTransactionManager.onTransactionResponse(transactionId, success);
}
/**
* Handles an asynchronous event from a Context Hub.
*
* @param contextHubId the ID of the hub the response came from
- * @param eventType the type of the event as defined in Context Hub HAL AsyncEventType
+ * @param eventType the type of the event as in CONTEXT_HUB_EVENT_*
*/
private void handleHubEventCallback(int contextHubId, int eventType) {
- if (eventType == AsyncEventType.RESTARTED) {
+ if (eventType == CONTEXT_HUB_EVENT_RESTARTED) {
sendLocationSettingUpdate();
sendWifiSettingUpdate(true /* forceUpdate */);
sendAirplaneModeSettingUpdate();
@@ -720,15 +719,12 @@
/**
* Handles a query response from a Context Hub.
*
- * @param contextHubId the ID of the hub of the response
- * @param nanoAppInfoList the list of loaded nanoapps
+ * @param contextHubId the ID of the hub of the response
+ * @param nanoappStateList the list of loaded nanoapps
*/
- private void handleQueryAppsCallback(int contextHubId, List<HubAppInfo> nanoAppInfoList) {
- List<NanoAppState> nanoAppStateList =
- ContextHubServiceUtil.createNanoAppStateList(nanoAppInfoList);
-
- mNanoAppStateManager.updateCache(contextHubId, nanoAppInfoList);
- mTransactionManager.onQueryResponse(nanoAppStateList);
+ private void handleQueryAppsCallback(int contextHubId, List<NanoAppState> nanoappStateList) {
+ mNanoAppStateManager.updateCache(contextHubId, nanoappStateList);
+ mTransactionManager.onQueryResponse(nanoappStateList);
}
/**
@@ -771,9 +767,9 @@
/**
* Creates and registers a PendingIntent client at the service for the specified Context Hub.
*
- * @param contextHubId the ID of the hub this client is attached to
- * @param pendingIntent the PendingIntent associated with this client
- * @param nanoAppId the ID of the nanoapp PendingIntent events will be sent for
+ * @param contextHubId the ID of the hub this client is attached to
+ * @param pendingIntent the PendingIntent associated with this client
+ * @param nanoAppId the ID of the nanoapp PendingIntent events will be sent for
* @param attributionTag an optional attribution tag within the given package
* @return the generated client interface
* @throws IllegalArgumentException if hubInfo does not represent a valid hub
@@ -1093,8 +1089,8 @@
}
/**
- * Obtains the latest microphone disabled setting for the current user
- * and notifies the Context Hub.
+ * Obtains the latest microphone disabled setting for the current user and notifies the Context
+ * Hub.
*/
private void sendMicrophoneDisableSettingUpdateForCurrentUser() {
boolean isEnabled = mSensorPrivacyManagerInternal.isSensorPrivacyEnabled(
@@ -1121,9 +1117,9 @@
}
/**
- * Send a microphone disable settings update whenever the foreground user changes.
- * We always send a settings update regardless of the previous state for the same user
- * since the CHRE framework is expected to handle repeated identical setting update.
+ * Send a microphone disable settings update whenever the foreground user changes. We always
+ * send a settings update regardless of the previous state for the same user since the CHRE
+ * framework is expected to handle repeated identical setting update.
*/
public void onUserChanged() {
Log.d(TAG, "User changed to id: " + getCurrentUserId());
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
index 70f50c3..df6cc05 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
@@ -20,7 +20,7 @@
import android.Manifest;
import android.content.Context;
-import android.hardware.contexthub.V1_0.ContextHub;
+import android.hardware.contexthub.V1_0.AsyncEventType;
import android.hardware.contexthub.V1_0.ContextHubMsg;
import android.hardware.contexthub.V1_0.HostEndPoint;
import android.hardware.contexthub.V1_0.Result;
@@ -52,10 +52,10 @@
* @return the HashMap object
*/
/* package */
- static HashMap<Integer, ContextHubInfo> createContextHubInfoMap(List<ContextHub> hubList) {
+ static HashMap<Integer, ContextHubInfo> createContextHubInfoMap(List<ContextHubInfo> hubList) {
HashMap<Integer, ContextHubInfo> contextHubIdToInfoMap = new HashMap<>();
- for (ContextHub contextHub : hubList) {
- contextHubIdToInfoMap.put(contextHub.hubId, new ContextHubInfo(contextHub));
+ for (ContextHubInfo contextHubInfo : hubList) {
+ contextHubIdToInfoMap.put(contextHubInfo.getId(), contextHubInfo);
}
return contextHubIdToInfoMap;
@@ -257,4 +257,21 @@
}
return newAppInfo;
}
+
+ /**
+ * Converts a HIDL AsyncEventType to the corresponding ContextHubService.CONTEXT_HUB_EVENT_*.
+ *
+ * @param hidlEventType The AsyncEventType value.
+ * @return The converted event type.
+ */
+ /* package */
+ static int toContextHubEvent(int hidlEventType) {
+ switch (hidlEventType) {
+ case AsyncEventType.RESTARTED:
+ return ContextHubService.CONTEXT_HUB_EVENT_RESTARTED;
+ default:
+ Log.e(TAG, "toContextHubEvent: Unknown event type: " + hidlEventType);
+ return ContextHubService.CONTEXT_HUB_EVENT_UNKNOWN;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
index f81208f..abf5a24 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
@@ -16,9 +16,6 @@
package com.android.server.location.contexthub;
-import android.hardware.contexthub.V1_0.IContexthub;
-import android.hardware.contexthub.V1_0.Result;
-import android.hardware.contexthub.V1_0.TransactionResult;
import android.hardware.location.ContextHubTransaction;
import android.hardware.location.IContextHubTransactionCallback;
import android.hardware.location.NanoAppBinary;
@@ -40,7 +37,7 @@
/**
* Manages transactions at the Context Hub Service.
- *
+ * <p>
* This class maintains a queue of transaction requests made to the ContextHubService by clients,
* and executes them through the Context Hub. At any point in time, either the transaction queue is
* empty, or there is a pending transaction that is waiting for an asynchronous response from the
@@ -64,7 +61,7 @@
/*
* The proxy to talk to the Context Hub
*/
- private final IContexthub mContextHubProxy;
+ private final IContextHubWrapper mContextHubProxy;
/*
* The manager for all clients for the service.
@@ -120,7 +117,7 @@
}
/* package */ ContextHubTransactionManager(
- IContexthub contextHubProxy, ContextHubClientManager clientManager,
+ IContextHubWrapper contextHubProxy, ContextHubClientManager clientManager,
NanoAppStateManager nanoAppStateManager) {
mContextHubProxy = contextHubProxy;
mClientManager = clientManager;
@@ -143,15 +140,13 @@
nanoAppBinary.getNanoAppId(), packageName) {
@Override
/* package */ int onTransact() {
- android.hardware.contexthub.V1_0.NanoAppBinary hidlNanoAppBinary =
- ContextHubServiceUtil.createHidlNanoAppBinary(nanoAppBinary);
try {
- return mContextHubProxy.loadNanoApp(
- contextHubId, hidlNanoAppBinary, this.getTransactionId());
+ return mContextHubProxy.loadNanoapp(
+ contextHubId, nanoAppBinary, this.getTransactionId());
} catch (RemoteException e) {
Log.e(TAG, "RemoteException while trying to load nanoapp with ID 0x" +
Long.toHexString(nanoAppBinary.getNanoAppId()), e);
- return Result.UNKNOWN_FAILURE;
+ return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
}
}
@@ -194,12 +189,12 @@
@Override
/* package */ int onTransact() {
try {
- return mContextHubProxy.unloadNanoApp(
+ return mContextHubProxy.unloadNanoapp(
contextHubId, nanoAppId, this.getTransactionId());
} catch (RemoteException e) {
Log.e(TAG, "RemoteException while trying to unload nanoapp with ID 0x" +
Long.toHexString(nanoAppId), e);
- return Result.UNKNOWN_FAILURE;
+ return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
}
}
@@ -237,12 +232,12 @@
@Override
/* package */ int onTransact() {
try {
- return mContextHubProxy.enableNanoApp(
+ return mContextHubProxy.enableNanoapp(
contextHubId, nanoAppId, this.getTransactionId());
} catch (RemoteException e) {
Log.e(TAG, "RemoteException while trying to enable nanoapp with ID 0x" +
Long.toHexString(nanoAppId), e);
- return Result.UNKNOWN_FAILURE;
+ return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
}
}
@@ -274,12 +269,12 @@
@Override
/* package */ int onTransact() {
try {
- return mContextHubProxy.disableNanoApp(
+ return mContextHubProxy.disableNanoapp(
contextHubId, nanoAppId, this.getTransactionId());
} catch (RemoteException e) {
Log.e(TAG, "RemoteException while trying to disable nanoapp with ID 0x" +
Long.toHexString(nanoAppId), e);
- return Result.UNKNOWN_FAILURE;
+ return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
}
}
@@ -310,10 +305,10 @@
@Override
/* package */ int onTransact() {
try {
- return mContextHubProxy.queryApps(contextHubId);
+ return mContextHubProxy.queryNanoapps(contextHubId);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException while trying to query for nanoapps", e);
- return Result.UNKNOWN_FAILURE;
+ return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
}
}
@@ -336,7 +331,7 @@
/**
* Adds a new transaction to the queue.
- *
+ * <p>
* If there was no pending transaction at the time, the transaction that was added will be
* started in this method. If there were too many transactions in the queue, an exception will
* be thrown.
@@ -363,10 +358,10 @@
* Handles a transaction response from a Context Hub.
*
* @param transactionId the transaction ID of the response
- * @param result the result of the transaction as defined by the HAL TransactionResult
+ * @param success true if the transaction succeeded
*/
/* package */
- synchronized void onTransactionResponse(int transactionId, int result) {
+ synchronized void onTransactionResponse(int transactionId, boolean success) {
ContextHubServiceTransaction transaction = mTransactionQueue.peek();
if (transaction == null) {
Log.w(TAG, "Received unexpected transaction response (no transaction pending)");
@@ -378,9 +373,7 @@
return;
}
- transaction.onTransactionComplete(
- (result == TransactionResult.SUCCESS) ?
- ContextHubTransaction.RESULT_SUCCESS :
+ transaction.onTransactionComplete(success ? ContextHubTransaction.RESULT_SUCCESS :
ContextHubTransaction.RESULT_FAILED_AT_HUB);
removeTransactionAndStartNext();
}
@@ -421,11 +414,11 @@
/**
* Pops the front transaction from the queue and starts the next pending transaction request.
- *
+ * <p>
* Removing elements from the transaction queue must only be done through this method. When a
* pending transaction is removed, the timeout timer is cancelled and the transaction is marked
* complete.
- *
+ * <p>
* It is assumed that the transaction queue is non-empty when this method is invoked, and that
* the caller has obtained a lock on this ContextHubTransactionManager object.
*/
@@ -442,21 +435,21 @@
/**
* Starts the next pending transaction request.
- *
+ * <p>
* Starting new transactions must only be done through this method. This method continues to
* process the transaction queue as long as there are pending requests, and no transaction is
* pending.
- *
+ * <p>
* It is assumed that the caller has obtained a lock on this ContextHubTransactionManager
* object.
*/
private void startNextTransaction() {
- int result = Result.UNKNOWN_FAILURE;
- while (result != Result.OK && !mTransactionQueue.isEmpty()) {
+ int result = ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ while (result != ContextHubTransaction.RESULT_SUCCESS && !mTransactionQueue.isEmpty()) {
ContextHubServiceTransaction transaction = mTransactionQueue.peek();
result = transaction.onTransact();
- if (result == Result.OK) {
+ if (result == ContextHubTransaction.RESULT_SUCCESS) {
Runnable onTimeoutFunc = () -> {
synchronized (this) {
if (!transaction.isComplete()) {
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 7be47a4..d733db0 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -15,11 +15,20 @@
*/
package com.android.server.location.contexthub;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.contexthub.V1_0.ContextHub;
+import android.hardware.contexthub.V1_0.ContextHubMsg;
+import android.hardware.contexthub.V1_0.TransactionResult;
import android.hardware.contexthub.V1_1.Setting;
import android.hardware.contexthub.V1_1.SettingValue;
+import android.hardware.contexthub.V1_2.HubAppInfo;
import android.hardware.contexthub.V1_2.IContexthubCallback;
+import android.hardware.location.ContextHubInfo;
+import android.hardware.location.ContextHubTransaction;
+import android.hardware.location.NanoAppBinary;
+import android.hardware.location.NanoAppMessage;
+import android.hardware.location.NanoAppState;
import android.os.RemoteException;
import android.util.Log;
import android.util.Pair;
@@ -36,6 +45,45 @@
private static final String TAG = "IContextHubWrapper";
/**
+ * The callback interface to use in registerCallback.
+ */
+ public interface ICallback {
+ /**
+ * @param transactionId The ID of the transaction that completed.
+ * @param success true if the transaction succeeded.
+ */
+ void handleTransactionResult(int transactionId, boolean success);
+
+ /**
+ * @param eventType The Context Hub event type defined by ContextHubService
+ * .CONTEXT_HUB_EVENT_*.
+ */
+ void handleContextHubEvent(int eventType);
+
+ /**
+ * @param nanoappId The ID of the nanoapp that aborted.
+ * @param abortCode The nanoapp-defined abort code.
+ */
+ void handleNanoappAbort(long nanoappId, int abortCode);
+
+ /**
+ * @param nanoappStateList The list of loaded nanoapps on the Context Hub.
+ */
+ void handleNanoappInfo(List<NanoAppState> nanoappStateList);
+
+ /**
+ * Handles a message from a nanoapp to a ContextHubClient.
+ *
+ * @param hostEndpointId The host endpoint ID of the recipient.
+ * @param message The message from the nanoapp.
+ * @param nanoappPermissions The list of permissions held by the nanoapp.
+ * @param messagePermissions The list of permissions required to receive the message.
+ */
+ void handleNanoappMessage(short hostEndpointId, NanoAppMessage message,
+ List<String> nanoappPermissions, List<String> messagePermissions);
+ }
+
+ /**
* Attempts to connect to the Contexthub HAL 1.0 service, if it exists.
*
* @return A valid IContextHubWrapper if the connection was successful, null otherwise.
@@ -95,18 +143,7 @@
/**
* Calls the appropriate getHubs function depending on the HAL version.
*/
- public abstract Pair<List<ContextHub>, List<String>> getHubs() throws RemoteException;
-
- /**
- * Calls the appropriate registerCallback function depending on the HAL version.
- */
- public abstract void registerCallback(
- int hubId, IContexthubCallback callback) throws RemoteException;
-
- /**
- * @return A valid instance of Contexthub HAL 1.0.
- */
- public abstract android.hardware.contexthub.V1_0.IContexthub getHub();
+ public abstract Pair<List<ContextHubInfo>, List<String>> getHubs() throws RemoteException;
/**
* @return True if this version of the Contexthub HAL supports Location setting notifications.
@@ -147,35 +184,211 @@
public abstract void onAirplaneModeSettingChanged(boolean enabled);
/**
- * @return True if this version of the Contexthub HAL supports microphone
- * disable setting notifications.
+ * @return True if this version of the Contexthub HAL supports microphone disable setting
+ * notifications.
*/
public abstract boolean supportsMicrophoneDisableSettingNotifications();
/**
- * Notifies the Contexthub implementation of a microphone disable setting
- * change.
+ * Notifies the Contexthub implementation of a microphone disable setting change.
*/
public abstract void onMicrophoneDisableSettingChanged(boolean enabled);
- private static class ContextHubWrapperV1_0 extends IContextHubWrapper {
+ /**
+ * Sends a message to the Context Hub.
+ *
+ * @param hostEndpointId The host endpoint ID of the sender.
+ * @param contextHubId The ID of the Context Hub to send the message to.
+ * @param message The message to send.
+ * @return the result of the message sending.
+ */
+ @ContextHubTransaction.Result
+ public abstract int sendMessageToContextHub(
+ short hostEndpointId, int contextHubId, NanoAppMessage message)
+ throws RemoteException;
+
+ /**
+ * Loads a nanoapp on the Context Hub.
+ *
+ * @param contextHubId The ID of the Context Hub to load the nanoapp to.
+ * @param binary The nanoapp binary to load.
+ * @param transactionId The transaction ID of this load.
+ * @return the result of this load transaction.
+ */
+ @ContextHubTransaction.Result
+ public abstract int loadNanoapp(int contextHubId, NanoAppBinary binary,
+ int transactionId) throws RemoteException;
+
+ /**
+ * Unloads a nanoapp on the Context Hub. Semantics are similar to loadNanoapp().
+ */
+ @ContextHubTransaction.Result
+ public abstract int unloadNanoapp(int contextHubId, long nanoappId,
+ int transactionId) throws RemoteException;
+
+ /**
+ * Enables a nanoapp on the Context Hub. Semantics are similar to loadNanoapp().
+ */
+ @ContextHubTransaction.Result
+ public abstract int enableNanoapp(int contextHubId, long nanoappId,
+ int transactionId) throws RemoteException;
+
+ /**
+ * Disables a nanoapp on the Context Hub. Semantics are similar to loadNanoapp().
+ */
+ @ContextHubTransaction.Result
+ public abstract int disableNanoapp(int contextHubId, long nanoappId,
+ int transactionId) throws RemoteException;
+
+ /**
+ * Queries a list of nanoapp from the Context hub.
+ *
+ * @param contextHubId The ID of the Context Hub to query.
+ * @return the result of this query transaction.
+ */
+ @ContextHubTransaction.Result
+ public abstract int queryNanoapps(int contextHubId) throws RemoteException;
+
+ /**
+ * Registers a callback with the Context Hub.
+ *
+ * @param contextHubId The ID of the Context Hub to register the callback with.
+ * @param callback The callback to register.
+ */
+ public abstract void registerCallback(int contextHubId, @NonNull ICallback callback)
+ throws RemoteException;
+
+ /**
+ * An abstract call that defines methods common to all HIDL IContextHubWrappers.
+ */
+ private abstract static class ContextHubWrapperHidl extends IContextHubWrapper {
private android.hardware.contexthub.V1_0.IContexthub mHub;
- ContextHubWrapperV1_0(android.hardware.contexthub.V1_0.IContexthub hub) {
+ protected ICallback mCallback = null;
+
+ protected final ContextHubWrapperHidlCallback mHidlCallback =
+ new ContextHubWrapperHidlCallback();
+
+ protected class ContextHubWrapperHidlCallback extends IContexthubCallback.Stub {
+ @Override
+ public void handleClientMsg(ContextHubMsg message) {
+ mCallback.handleNanoappMessage(
+ message.hostEndPoint,
+ ContextHubServiceUtil.createNanoAppMessage(message),
+ Collections.emptyList() /* nanoappPermissions */,
+ Collections.emptyList() /* messagePermissions */);
+ }
+
+ @Override
+ public void handleTxnResult(int transactionId, int result) {
+ mCallback.handleTransactionResult(transactionId,
+ result == TransactionResult.SUCCESS);
+ }
+
+ @Override
+ public void handleHubEvent(int eventType) {
+ mCallback.handleContextHubEvent(
+ ContextHubServiceUtil.toContextHubEvent(eventType));
+ }
+
+ @Override
+ public void handleAppAbort(long nanoAppId, int abortCode) {
+ mCallback.handleNanoappAbort(nanoAppId, abortCode);
+ }
+
+ @Override
+ public void handleAppsInfo(
+ ArrayList<android.hardware.contexthub.V1_0.HubAppInfo> nanoAppInfoList) {
+ handleAppsInfo_1_2(ContextHubServiceUtil.toHubAppInfo_1_2(nanoAppInfoList));
+ }
+
+ @Override
+ public void handleClientMsg_1_2(android.hardware.contexthub.V1_2.ContextHubMsg message,
+ ArrayList<String> messagePermissions) {
+ mCallback.handleNanoappMessage(
+ message.msg_1_0.hostEndPoint,
+ ContextHubServiceUtil.createNanoAppMessage(message.msg_1_0),
+ message.permissions, messagePermissions);
+ }
+
+ @Override
+ public void handleAppsInfo_1_2(ArrayList<HubAppInfo> nanoAppInfoList) {
+ List<NanoAppState> nanoAppStateList =
+ ContextHubServiceUtil.createNanoAppStateList(nanoAppInfoList);
+ mCallback.handleNanoappInfo(nanoAppStateList);
+ }
+ }
+
+ ContextHubWrapperHidl(android.hardware.contexthub.V1_0.IContexthub hub) {
mHub = hub;
}
- public Pair<List<ContextHub>, List<String>> getHubs() throws RemoteException {
- return new Pair(mHub.getHubs(), new ArrayList<String>());
+ @ContextHubTransaction.Result
+ public int sendMessageToContextHub(
+ short hostEndpointId, int contextHubId, NanoAppMessage message)
+ throws RemoteException {
+ ContextHubMsg messageToNanoApp =
+ ContextHubServiceUtil.createHidlContextHubMessage(hostEndpointId, message);
+ return ContextHubServiceUtil.toTransactionResult(
+ mHub.sendMessageToHub(contextHubId, messageToNanoApp));
}
- public void registerCallback(
- int hubId, IContexthubCallback callback) throws RemoteException {
- mHub.registerCallback(hubId, callback);
+ @ContextHubTransaction.Result
+ public int loadNanoapp(int contextHubId, NanoAppBinary binary,
+ int transactionId) throws RemoteException {
+ android.hardware.contexthub.V1_0.NanoAppBinary hidlNanoAppBinary =
+ ContextHubServiceUtil.createHidlNanoAppBinary(binary);
+ return ContextHubServiceUtil.toTransactionResult(mHub.loadNanoApp(
+ contextHubId, hidlNanoAppBinary, transactionId));
}
- public android.hardware.contexthub.V1_0.IContexthub getHub() {
- return mHub;
+ @ContextHubTransaction.Result
+ public int unloadNanoapp(int contextHubId, long nanoappId, int transactionId)
+ throws RemoteException {
+ return ContextHubServiceUtil.toTransactionResult(mHub.unloadNanoApp(
+ contextHubId, nanoappId, transactionId));
+ }
+
+ @ContextHubTransaction.Result
+ public int enableNanoapp(int contextHubId, long nanoappId, int transactionId)
+ throws RemoteException {
+ return ContextHubServiceUtil.toTransactionResult(mHub.enableNanoApp(
+ contextHubId, nanoappId, transactionId));
+ }
+
+ @ContextHubTransaction.Result
+ public int disableNanoapp(int contextHubId, long nanoappId, int transactionId)
+ throws RemoteException {
+ return ContextHubServiceUtil.toTransactionResult(mHub.disableNanoApp(
+ contextHubId, nanoappId, transactionId));
+ }
+
+ @ContextHubTransaction.Result
+ public int queryNanoapps(int contextHubId) throws RemoteException {
+ return ContextHubServiceUtil.toTransactionResult(
+ mHub.queryApps(contextHubId));
+ }
+
+ public void registerCallback(int contextHubId, ICallback callback) throws RemoteException {
+ mCallback = callback;
+ mHub.registerCallback(contextHubId, mHidlCallback);
+ }
+ }
+
+ private static class ContextHubWrapperV1_0 extends ContextHubWrapperHidl {
+ private android.hardware.contexthub.V1_0.IContexthub mHub;
+
+ ContextHubWrapperV1_0(android.hardware.contexthub.V1_0.IContexthub hub) {
+ super(hub);
+ mHub = hub;
+ }
+
+ public Pair<List<ContextHubInfo>, List<String>> getHubs() throws RemoteException {
+ ArrayList<ContextHubInfo> hubInfoList = new ArrayList<>();
+ for (ContextHub hub : mHub.getHubs()) {
+ hubInfoList.add(new ContextHubInfo(hub));
+ }
+ return new Pair(hubInfoList, new ArrayList<String>());
}
public boolean supportsLocationSettingNotifications() {
@@ -207,24 +420,20 @@
}
}
- private static class ContextHubWrapperV1_1 extends IContextHubWrapper {
+ private static class ContextHubWrapperV1_1 extends ContextHubWrapperHidl {
private android.hardware.contexthub.V1_1.IContexthub mHub;
ContextHubWrapperV1_1(android.hardware.contexthub.V1_1.IContexthub hub) {
+ super(hub);
mHub = hub;
}
- public Pair<List<ContextHub>, List<String>> getHubs() throws RemoteException {
- return new Pair(mHub.getHubs(), new ArrayList<String>());
- }
-
- public void registerCallback(
- int hubId, IContexthubCallback callback) throws RemoteException {
- mHub.registerCallback(hubId, callback);
- }
-
- public android.hardware.contexthub.V1_0.IContexthub getHub() {
- return mHub;
+ public Pair<List<ContextHubInfo>, List<String>> getHubs() throws RemoteException {
+ ArrayList<ContextHubInfo> hubInfoList = new ArrayList<>();
+ for (ContextHub hub : mHub.getHubs()) {
+ hubInfoList.add(new ContextHubInfo(hub));
+ }
+ return new Pair(hubInfoList, new ArrayList<String>());
}
public boolean supportsLocationSettingNotifications() {
@@ -262,36 +471,32 @@
}
}
- private static class ContextHubWrapperV1_2 extends IContextHubWrapper
+ private static class ContextHubWrapperV1_2 extends ContextHubWrapperHidl
implements android.hardware.contexthub.V1_2.IContexthub.getHubs_1_2Callback {
private final android.hardware.contexthub.V1_2.IContexthub mHub;
- private Pair<List<ContextHub>, List<String>> mHubInfo =
+ private Pair<List<ContextHubInfo>, List<String>> mHubInfo =
new Pair<>(Collections.emptyList(), Collections.emptyList());
ContextHubWrapperV1_2(android.hardware.contexthub.V1_2.IContexthub hub) {
+ super(hub);
mHub = hub;
}
@Override
public void onValues(ArrayList<ContextHub> hubs, ArrayList<String> supportedPermissions) {
- mHubInfo = new Pair(hubs, supportedPermissions);
+ ArrayList<ContextHubInfo> hubInfoList = new ArrayList<>();
+ for (ContextHub hub : hubs) {
+ hubInfoList.add(new ContextHubInfo(hub));
+ }
+ mHubInfo = new Pair(hubInfoList, supportedPermissions);
}
- public Pair<List<ContextHub>, List<String>> getHubs() throws RemoteException {
+ public Pair<List<ContextHubInfo>, List<String>> getHubs() throws RemoteException {
mHub.getHubs_1_2(this);
return mHubInfo;
}
- public void registerCallback(
- int hubId, IContexthubCallback callback) throws RemoteException {
- mHub.registerCallback_1_2(hubId, callback);
- }
-
- public android.hardware.contexthub.V1_0.IContexthub getHub() {
- return mHub;
- }
-
public boolean supportsLocationSettingNotifications() {
return true;
}
@@ -331,6 +536,11 @@
enabled ? SettingValue.DISABLED : SettingValue.ENABLED);
}
+ public void registerCallback(int contextHubId, ICallback callback) throws RemoteException {
+ mCallback = callback;
+ mHub.registerCallback_1_2(contextHubId, mHidlCallback);
+ }
+
private void sendSettingChanged(byte setting, byte newValue) {
try {
mHub.onSettingChanged_1_2(setting, newValue);
diff --git a/services/core/java/com/android/server/location/contexthub/NanoAppStateManager.java b/services/core/java/com/android/server/location/contexthub/NanoAppStateManager.java
index 667fb98..b6d5496 100644
--- a/services/core/java/com/android/server/location/contexthub/NanoAppStateManager.java
+++ b/services/core/java/com/android/server/location/contexthub/NanoAppStateManager.java
@@ -17,8 +17,8 @@
package com.android.server.location.contexthub;
import android.annotation.Nullable;
-import android.hardware.contexthub.V1_2.HubAppInfo;
import android.hardware.location.NanoAppInstanceInfo;
+import android.hardware.location.NanoAppState;
import android.util.Log;
import java.util.HashMap;
@@ -31,8 +31,8 @@
* Manages the state of loaded nanoapps at the Context Hubs.
*
* This class maintains a list of nanoapps that have been informed as loaded at the hubs. The state
- * should be updated based on the hub callbacks (defined in IContexthubCallback.hal), as a result
- * of either loadNanoApp, unloadNanoApp, or queryApps.
+ * should be updated based on the hub callbacks (defined in IContexthubCallback.hal), as a result of
+ * either loadNanoApp, unloadNanoApp, or queryApps.
*
* The state tracked by this manager is used by clients of ContextHubService that use the old APIs.
*
@@ -61,7 +61,7 @@
/**
* @param nanoAppHandle the nanoapp handle
* @return the NanoAppInstanceInfo for the given nanoapp, or null if the nanoapp does not exist
- * in the cache
+ * in the cache
*/
@Nullable
/* package */
@@ -83,7 +83,7 @@
/**
* @param contextHubId the ID of the hub to search for the instance
- * @param nanoAppId the unique 64-bit ID of the nanoapp
+ * @param nanoAppId the unique 64-bit ID of the nanoapp
* @return the nanoapp handle, -1 if the nanoapp is not in the cache
*/
/* package */
@@ -99,12 +99,12 @@
/**
* Adds a nanoapp instance to the cache.
- *
+ * <p>
* If the cache already contained the nanoapp, the entry is removed and a new nanoapp handle is
* generated.
*
- * @param contextHubId the ID of the hub the nanoapp is loaded in
- * @param nanoAppId the unique 64-bit ID of the nanoapp
+ * @param contextHubId the ID of the hub the nanoapp is loaded in
+ * @param nanoAppId the unique 64-bit ID of the nanoapp
* @param nanoAppVersion the version of the nanoapp
*/
/* package */
@@ -147,15 +147,17 @@
/**
* Performs a batch update of the nanoapp cache given a nanoapp query response.
*
- * @param contextHubId the ID of the hub the response came from
- * @param nanoAppInfoList the list of loaded nanoapps
+ * @param contextHubId the ID of the hub the response came from
+ * @param nanoappStateList the list of loaded nanoapps
*/
/* package */
- synchronized void updateCache(int contextHubId, List<HubAppInfo> nanoAppInfoList) {
+ synchronized void updateCache(int contextHubId, List<NanoAppState> nanoappStateList) {
HashSet<Long> nanoAppIdSet = new HashSet<>();
- for (HubAppInfo appInfo : nanoAppInfoList) {
- handleQueryAppEntry(contextHubId, appInfo.info_1_0.appId, appInfo.info_1_0.version);
- nanoAppIdSet.add(appInfo.info_1_0.appId);
+ for (NanoAppState nanoappState : nanoappStateList) {
+ handleQueryAppEntry(
+ contextHubId, nanoappState.getNanoAppId(),
+ (int) nanoappState.getNanoAppVersion());
+ nanoAppIdSet.add(nanoappState.getNanoAppId());
}
Iterator<NanoAppInstanceInfo> iterator = mNanoAppHash.values().iterator();
@@ -172,8 +174,8 @@
* If the nanoapp exists in the cache, then the entry is updated. Otherwise, inserts a new
* instance of the nanoapp in the cache. This method should only be invoked from updateCache.
*
- * @param contextHubId the ID of the hub the nanoapp is loaded in
- * @param nanoAppId the unique 64-bit ID of the nanoapp
+ * @param contextHubId the ID of the hub the nanoapp is loaded in
+ * @param nanoAppId the unique 64-bit ID of the nanoapp
* @param nanoAppVersion the version of the nanoapp
*/
private void handleQueryAppEntry(int contextHubId, long nanoAppId, int nanoAppVersion) {
diff --git a/services/core/java/com/android/server/location/eventlog/LocalEventLog.java b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
index 12dd3e6..47146c1 100644
--- a/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
@@ -16,217 +16,193 @@
package com.android.server.location.eventlog;
-import android.annotation.Nullable;
-import android.os.SystemClock;
-import android.util.TimeUtils;
+import static java.lang.Integer.bitCount;
+import android.annotation.Nullable;
+
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
-import java.util.Iterator;
+import java.lang.reflect.Array;
+import java.util.Arrays;
import java.util.NoSuchElementException;
-import java.util.function.Consumer;
+import java.util.Objects;
/**
- * An in-memory event log to support historical event information.
+ * An in-memory event log to support historical event information. The log is of a constant size,
+ * and new events will overwrite old events as the log fills up.
+ *
+ * @param <T> log event type
*/
-public abstract class LocalEventLog {
-
- private interface Log {
- // true if this is a filler element that should not be queried
- boolean isFiller();
- long getTimeDeltaMs();
- String getLogString();
- boolean filter(@Nullable String filter);
- }
-
- private static final class FillerEvent implements Log {
-
- static final long MAX_TIME_DELTA = (1L << 32) - 1;
-
- private final int mTimeDelta;
-
- FillerEvent(long timeDelta) {
- Preconditions.checkArgument(timeDelta >= 0);
- mTimeDelta = (int) timeDelta;
- }
-
- @Override
- public boolean isFiller() {
- return true;
- }
-
- @Override
- public long getTimeDeltaMs() {
- return Integer.toUnsignedLong(mTimeDelta);
- }
-
- @Override
- public String getLogString() {
- throw new AssertionError();
- }
-
- @Override
- public boolean filter(String filter) {
- return false;
- }
- }
+public class LocalEventLog<T> {
/**
- * An abstraction of a log event to be implemented by subclasses.
+ * Consumer of log events for iterating over the log.
+ *
+ * @param <T> log event type
*/
- public abstract static class LogEvent implements Log {
-
- static final long MAX_TIME_DELTA = (1L << 32) - 1;
-
- private final int mTimeDelta;
-
- protected LogEvent(long timeDelta) {
- Preconditions.checkArgument(timeDelta >= 0);
- mTimeDelta = (int) timeDelta;
- }
-
- @Override
- public final boolean isFiller() {
- return false;
- }
-
- @Override
- public final long getTimeDeltaMs() {
- return Integer.toUnsignedLong(mTimeDelta);
- }
-
- @Override
- public boolean filter(String filter) {
- return false;
- }
+ public interface LogConsumer<T> {
+ /** Invoked with a time and a logEvent. */
+ void acceptLog(long time, T logEvent);
}
- // circular buffer of log entries
- private final Log[] mLog;
- private int mLogSize;
- private int mLogEndIndex;
+ // masks for the entries field. 1 bit is used to indicate whether this is a filler event or not,
+ // and 31 bits to store the time delta.
+ private static final int IS_FILLER_MASK = 0b10000000000000000000000000000000;
+ private static final int TIME_DELTA_MASK = 0b01111111111111111111111111111111;
+
+ private static final int IS_FILLER_OFFSET = countTrailingZeros(IS_FILLER_MASK);
+ private static final int TIME_DELTA_OFFSET = countTrailingZeros(TIME_DELTA_MASK);
+
+ static final int MAX_TIME_DELTA = (1 << bitCount(TIME_DELTA_MASK)) - 1;
+
+ private static int countTrailingZeros(int i) {
+ int c = 0;
+ while (i != 0 && (i & 1) == 0) {
+ c++;
+ i = i >>> 1;
+ }
+ return c;
+ }
+
+ private static int createEntry(boolean isFiller, int timeDelta) {
+ Preconditions.checkArgument(timeDelta >= 0 && timeDelta <= MAX_TIME_DELTA);
+ return (((isFiller ? 1 : 0) << IS_FILLER_OFFSET) & IS_FILLER_MASK)
+ | ((timeDelta << TIME_DELTA_OFFSET) & TIME_DELTA_MASK);
+ }
+
+ static int getTimeDelta(int entry) {
+ return (entry & TIME_DELTA_MASK) >>> TIME_DELTA_OFFSET;
+ }
+
+ static boolean isFiller(int entry) {
+ return (entry & IS_FILLER_MASK) != 0;
+ }
+
+ // circular buffer of log entries and events. each entry corrosponds to the log event at the
+ // same index. the log entry holds the filler status and time delta according to the bit masks
+ // above, and the log event is the log event.
+
+ @GuardedBy("this")
+ final int[] mEntries;
+
+ @GuardedBy("this")
+ final @Nullable T[] mLogEvents;
+
+ @GuardedBy("this")
+ int mLogSize;
+
+ @GuardedBy("this")
+ int mLogEndIndex;
// invalid if log is empty
- private long mStartRealtimeMs;
- private long mLastLogRealtimeMs;
- public LocalEventLog(int size) {
+ @GuardedBy("this")
+ long mStartTime;
+
+ @GuardedBy("this")
+ long mLastLogTime;
+
+ @SuppressWarnings("unchecked")
+ public LocalEventLog(int size, Class<T> clazz) {
Preconditions.checkArgument(size > 0);
- mLog = new Log[size];
+
+ mEntries = new int[size];
+ mLogEvents = (T[]) Array.newInstance(clazz, size);
mLogSize = 0;
mLogEndIndex = 0;
- mStartRealtimeMs = -1;
- mLastLogRealtimeMs = -1;
+ mStartTime = -1;
+ mLastLogTime = -1;
}
- /**
- * Should be overridden by subclasses to return a new immutable log event for the given
- * arguments (as passed into {@link #addLogEvent(int, Object...)}.
- */
- protected abstract LogEvent createLogEvent(long timeDelta, int event, Object... args);
-
- /**
- * May be optionally overridden by subclasses if they wish to change how log event time is
- * formatted.
- */
- protected String getTimePrefix(long timeMs) {
- return TimeUtils.logTimeOfDay(timeMs) + ": ";
- }
-
- /**
- * Call to add a new log event at the current time. The arguments provided here will be passed
- * into {@link #createLogEvent(long, int, Object...)} in addition to a time delta, and should be
- * used to construct an appropriate {@link LogEvent} object.
- */
- public synchronized void addLogEvent(int event, Object... args) {
- long timeMs = SystemClock.elapsedRealtime();
+ /** Call to add a new log event at the given time. */
+ protected synchronized void addLog(long time, T logEvent) {
+ Preconditions.checkArgument(logEvent != null);
// calculate delta
long delta = 0;
if (!isEmpty()) {
- delta = timeMs - mLastLogRealtimeMs;
+ delta = time - mLastLogTime;
- // if the delta is invalid, or if the delta is great enough using filler elements would
+ // if the delta is negative, or if the delta is great enough using filler elements would
// result in an empty log anyways, just clear the log and continue, otherwise insert
// filler elements until we have a reasonable delta
- if (delta < 0 || (delta / FillerEvent.MAX_TIME_DELTA) >= mLog.length - 1) {
+ if (delta < 0 || (delta / MAX_TIME_DELTA) >= mEntries.length - 1) {
clear();
delta = 0;
} else {
- while (delta >= LogEvent.MAX_TIME_DELTA) {
- long timeDelta = Math.min(FillerEvent.MAX_TIME_DELTA, delta);
- addLogEventInternal(new FillerEvent(timeDelta));
- delta -= timeDelta;
+ while (delta >= MAX_TIME_DELTA) {
+ addLogEventInternal(true, MAX_TIME_DELTA, null);
+ delta -= MAX_TIME_DELTA;
}
}
}
// for first log entry, set initial times
if (isEmpty()) {
- mStartRealtimeMs = timeMs;
- mLastLogRealtimeMs = mStartRealtimeMs;
+ mStartTime = time;
+ mLastLogTime = mStartTime;
}
- addLogEventInternal(createLogEvent(delta, event, args));
+ addLogEventInternal(false, (int) delta, logEvent);
}
- private void addLogEventInternal(Log event) {
- Preconditions.checkState(mStartRealtimeMs != -1 && mLastLogRealtimeMs != -1);
+ @GuardedBy("this")
+ private void addLogEventInternal(boolean isFiller, int timeDelta, @Nullable T logEvent) {
+ Preconditions.checkArgument(isFiller || logEvent != null);
+ Preconditions.checkState(mStartTime != -1 && mLastLogTime != -1);
- if (mLogSize == mLog.length) {
+ if (mLogSize == mEntries.length) {
// if log is full, size will remain the same, but update the start time
- mStartRealtimeMs += mLog[startIndex()].getTimeDeltaMs();
+ mStartTime += getTimeDelta(mEntries[startIndex()]);
} else {
// otherwise add an item
mLogSize++;
}
// set log and increment end index
- mLog[mLogEndIndex] = event;
+ mEntries[mLogEndIndex] = createEntry(isFiller, timeDelta);
+ mLogEvents[mLogEndIndex] = logEvent;
mLogEndIndex = incrementIndex(mLogEndIndex);
- mLastLogRealtimeMs = mLastLogRealtimeMs + event.getTimeDeltaMs();
+ mLastLogTime = mLastLogTime + timeDelta;
}
/** Clears the log of all entries. */
public synchronized void clear() {
+ // clear entries to allow gc
+ Arrays.fill(mLogEvents, null);
+
mLogEndIndex = 0;
mLogSize = 0;
- mStartRealtimeMs = -1;
- mLastLogRealtimeMs = -1;
+ mStartTime = -1;
+ mLastLogTime = -1;
}
// checks if the log is empty (if empty, times are invalid)
- private synchronized boolean isEmpty() {
+ @GuardedBy("this")
+ private boolean isEmpty() {
return mLogSize == 0;
}
/** Iterates over the event log, passing each log string to the given consumer. */
- public synchronized void iterate(Consumer<String> consumer) {
+ public synchronized void iterate(LogConsumer<? super T> consumer) {
LogIterator it = new LogIterator();
while (it.hasNext()) {
- consumer.accept(it.next());
- }
- }
-
- /**
- * Iterates over the event log, passing each filter-matching log string to the given
- * consumer.
- */
- public synchronized void iterate(String filter, Consumer<String> consumer) {
- LogIterator it = new LogIterator(filter);
- while (it.hasNext()) {
- consumer.accept(it.next());
+ it.next();
+ consumer.acceptLog(it.getTime(), it.getLog());
}
}
// returns the index of the first element
+ @GuardedBy("this")
private int startIndex() {
return wrapIndex(mLogEndIndex - mLogSize);
}
// returns the index after this one
+ @GuardedBy("this")
private int incrementIndex(int index) {
if (index == -1) {
return startIndex();
@@ -238,69 +214,68 @@
}
// rolls over the given index if necessary
+ @GuardedBy("this")
private int wrapIndex(int index) {
// java modulo will keep negative sign, we need to rollover
- return (index % mLog.length + mLog.length) % mLog.length;
+ return (index % mEntries.length + mEntries.length) % mEntries.length;
}
- private class LogIterator implements Iterator<String> {
+ private class LogIterator {
- private final @Nullable String mFilter;
-
- private final long mSystemTimeDeltaMs;
-
- private long mCurrentRealtimeMs;
+ private long mLogTime;
private int mIndex;
private int mCount;
+ private long mCurrentTime;
+ private T mCurrentLogEvent;
+
LogIterator() {
- this(null);
- }
+ synchronized (LocalEventLog.this) {
+ mLogTime = mStartTime;
+ mIndex = -1;
+ mCount = -1;
- LogIterator(@Nullable String filter) {
- mFilter = filter;
- mSystemTimeDeltaMs = System.currentTimeMillis() - SystemClock.elapsedRealtime();
- mCurrentRealtimeMs = mStartRealtimeMs;
- mIndex = -1;
- mCount = -1;
-
- increment();
- }
-
- @Override
- public boolean hasNext() {
- return mCount < mLogSize;
- }
-
- public String next() {
- if (!hasNext()) {
- throw new NoSuchElementException();
+ increment();
}
-
- Log log = mLog[mIndex];
- long timeMs = mCurrentRealtimeMs + log.getTimeDeltaMs() + mSystemTimeDeltaMs;
-
- increment();
-
- return getTimePrefix(timeMs) + log.getLogString();
}
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
+ public boolean hasNext() {
+ synchronized (LocalEventLog.this) {
+ return mCount < mLogSize;
+ }
}
- private void increment() {
- long nextDeltaMs = mIndex == -1 ? 0 : mLog[mIndex].getTimeDeltaMs();
+ public void next() {
+ synchronized (LocalEventLog.this) {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+
+ mCurrentTime = mLogTime + getTimeDelta(mEntries[mIndex]);
+ mCurrentLogEvent = Objects.requireNonNull(mLogEvents[mIndex]);
+
+ increment();
+ }
+ }
+
+ public long getTime() {
+ return mCurrentTime;
+ }
+
+ public T getLog() {
+ return mCurrentLogEvent;
+ }
+
+ @GuardedBy("LocalEventLog.this")
+ private void increment(LogIterator this) {
+ long nextDeltaMs = mIndex == -1 ? 0 : getTimeDelta(mEntries[mIndex]);
do {
- mCurrentRealtimeMs += nextDeltaMs;
+ mLogTime += nextDeltaMs;
mIndex = incrementIndex(mIndex);
if (++mCount < mLogSize) {
- nextDeltaMs = mLog[mIndex].getTimeDeltaMs();
+ nextDeltaMs = getTimeDelta(mEntries[mIndex]);
}
- } while (mCount < mLogSize && (mLog[mIndex].isFiller() || (mFilter != null
- && !mLog[mIndex].filter(mFilter))));
+ } while (mCount < mLogSize && isFiller(mEntries[mIndex]));
}
}
}
-
diff --git a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
index 2178672..94953e0 100644
--- a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
@@ -36,12 +36,15 @@
import android.os.PowerManager.LocationPowerSaveMode;
import android.os.SystemClock;
import android.util.ArrayMap;
+import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
+import java.util.function.Consumer;
+
/** In memory event log for location events. */
-public class LocationEventLog extends LocalEventLog {
+public class LocationEventLog extends LocalEventLog<Object> {
public static final LocationEventLog EVENT_LOG = new LocationEventLog();
@@ -53,28 +56,11 @@
}
}
- private static final int EVENT_USER_SWITCHED = 1;
- private static final int EVENT_LOCATION_ENABLED = 2;
- private static final int EVENT_ADAS_LOCATION_ENABLED = 3;
- private static final int EVENT_PROVIDER_ENABLED = 4;
- private static final int EVENT_PROVIDER_MOCKED = 5;
- private static final int EVENT_PROVIDER_CLIENT_REGISTER = 6;
- private static final int EVENT_PROVIDER_CLIENT_UNREGISTER = 7;
- private static final int EVENT_PROVIDER_CLIENT_FOREGROUND = 8;
- private static final int EVENT_PROVIDER_CLIENT_BACKGROUND = 9;
- private static final int EVENT_PROVIDER_CLIENT_PERMITTED = 10;
- private static final int EVENT_PROVIDER_CLIENT_UNPERMITTED = 11;
- private static final int EVENT_PROVIDER_UPDATE_REQUEST = 12;
- private static final int EVENT_PROVIDER_RECEIVE_LOCATION = 13;
- private static final int EVENT_PROVIDER_DELIVER_LOCATION = 14;
- private static final int EVENT_PROVIDER_STATIONARY_THROTTLED = 15;
- private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 16;
-
@GuardedBy("mAggregateStats")
private final ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> mAggregateStats;
public LocationEventLog() {
- super(getLogSize());
+ super(getLogSize(), Object.class);
mAggregateStats = new ArrayMap<>(4);
}
@@ -109,39 +95,39 @@
/** Logs a user switched event. */
public void logUserSwitched(int userIdFrom, int userIdTo) {
- addLogEvent(EVENT_USER_SWITCHED, userIdFrom, userIdTo);
+ addLogEvent(new UserSwitchedEvent(userIdFrom, userIdTo));
}
/** Logs a location enabled/disabled event. */
public void logLocationEnabled(int userId, boolean enabled) {
- addLogEvent(EVENT_LOCATION_ENABLED, userId, enabled);
+ addLogEvent(new LocationEnabledEvent(userId, enabled));
}
/** Logs a location enabled/disabled event. */
public void logAdasLocationEnabled(int userId, boolean enabled) {
- addLogEvent(EVENT_ADAS_LOCATION_ENABLED, userId, enabled);
+ addLogEvent(new LocationAdasEnabledEvent(userId, enabled));
}
/** Logs a location provider enabled/disabled event. */
public void logProviderEnabled(String provider, int userId, boolean enabled) {
- addLogEvent(EVENT_PROVIDER_ENABLED, provider, userId, enabled);
+ addLogEvent(new ProviderEnabledEvent(provider, userId, enabled));
}
/** Logs a location provider being replaced/unreplaced by a mock provider. */
public void logProviderMocked(String provider, boolean mocked) {
- addLogEvent(EVENT_PROVIDER_MOCKED, provider, mocked);
+ addLogEvent(new ProviderMockedEvent(provider, mocked));
}
/** Logs a new client registration for a location provider. */
public void logProviderClientRegistered(String provider, CallerIdentity identity,
LocationRequest request) {
- addLogEvent(EVENT_PROVIDER_CLIENT_REGISTER, provider, identity, request);
+ addLogEvent(new ProviderClientRegisterEvent(provider, true, identity, request));
getAggregateStats(provider, identity).markRequestAdded(request.getIntervalMillis());
}
/** Logs a client unregistration for a location provider. */
public void logProviderClientUnregistered(String provider, CallerIdentity identity) {
- addLogEvent(EVENT_PROVIDER_CLIENT_UNREGISTER, provider, identity);
+ addLogEvent(new ProviderClientRegisterEvent(provider, false, identity, null));
getAggregateStats(provider, identity).markRequestRemoved();
}
@@ -158,7 +144,7 @@
/** Logs a client for a location provider entering the foreground state. */
public void logProviderClientForeground(String provider, CallerIdentity identity) {
if (D) {
- addLogEvent(EVENT_PROVIDER_CLIENT_FOREGROUND, provider, identity);
+ addLogEvent(new ProviderClientForegroundEvent(provider, true, identity));
}
getAggregateStats(provider, identity).markRequestForeground();
}
@@ -166,7 +152,7 @@
/** Logs a client for a location provider leaving the foreground state. */
public void logProviderClientBackground(String provider, CallerIdentity identity) {
if (D) {
- addLogEvent(EVENT_PROVIDER_CLIENT_BACKGROUND, provider, identity);
+ addLogEvent(new ProviderClientForegroundEvent(provider, false, identity));
}
getAggregateStats(provider, identity).markRequestBackground();
}
@@ -174,32 +160,32 @@
/** Logs a client for a location provider entering the permitted state. */
public void logProviderClientPermitted(String provider, CallerIdentity identity) {
if (D) {
- addLogEvent(EVENT_PROVIDER_CLIENT_PERMITTED, provider, identity);
+ addLogEvent(new ProviderClientPermittedEvent(provider, true, identity));
}
}
/** Logs a client for a location provider leaving the permitted state. */
public void logProviderClientUnpermitted(String provider, CallerIdentity identity) {
if (D) {
- addLogEvent(EVENT_PROVIDER_CLIENT_UNPERMITTED, provider, identity);
+ addLogEvent(new ProviderClientPermittedEvent(provider, false, identity));
}
}
/** Logs a change to the provider request for a location provider. */
public void logProviderUpdateRequest(String provider, ProviderRequest request) {
- addLogEvent(EVENT_PROVIDER_UPDATE_REQUEST, provider, request);
+ addLogEvent(new ProviderUpdateEvent(provider, request));
}
/** Logs a new incoming location for a location provider. */
public void logProviderReceivedLocations(String provider, int numLocations) {
- addLogEvent(EVENT_PROVIDER_RECEIVE_LOCATION, provider, numLocations);
+ addLogEvent(new ProviderReceiveLocationEvent(provider, numLocations));
}
/** Logs a location deliver for a client of a location provider. */
public void logProviderDeliveredLocations(String provider, int numLocations,
CallerIdentity identity) {
if (D) {
- addLogEvent(EVENT_PROVIDER_DELIVER_LOCATION, provider, numLocations, identity);
+ addLogEvent(new ProviderDeliverLocationEvent(provider, numLocations, identity));
}
getAggregateStats(provider, identity).markLocationDelivered();
}
@@ -207,80 +193,47 @@
/** Logs that a provider has entered or exited stationary throttling. */
public void logProviderStationaryThrottled(String provider, boolean throttled,
ProviderRequest request) {
- addLogEvent(EVENT_PROVIDER_STATIONARY_THROTTLED, provider, throttled, request);
+ addLogEvent(new ProviderStationaryThrottledEvent(provider, throttled, request));
}
/** Logs that the location power save mode has changed. */
public void logLocationPowerSaveMode(
@LocationPowerSaveMode int locationPowerSaveMode) {
- addLogEvent(EVENT_LOCATION_POWER_SAVE_MODE_CHANGE, locationPowerSaveMode);
+ addLogEvent(new LocationPowerSaveModeEvent(locationPowerSaveMode));
}
- @Override
- protected LogEvent createLogEvent(long timeDelta, int event, Object... args) {
- switch (event) {
- case EVENT_USER_SWITCHED:
- return new UserSwitchedEvent(timeDelta, (Integer) args[0], (Integer) args[1]);
- case EVENT_LOCATION_ENABLED:
- return new LocationEnabledEvent(timeDelta, (Integer) args[0], (Boolean) args[1]);
- case EVENT_ADAS_LOCATION_ENABLED:
- return new LocationAdasEnabledEvent(timeDelta, (Integer) args[0],
- (Boolean) args[1]);
- case EVENT_PROVIDER_ENABLED:
- return new ProviderEnabledEvent(timeDelta, (String) args[0], (Integer) args[1],
- (Boolean) args[2]);
- case EVENT_PROVIDER_MOCKED:
- return new ProviderMockedEvent(timeDelta, (String) args[0], (Boolean) args[1]);
- case EVENT_PROVIDER_CLIENT_REGISTER:
- return new ProviderClientRegisterEvent(timeDelta, (String) args[0], true,
- (CallerIdentity) args[1], (LocationRequest) args[2]);
- case EVENT_PROVIDER_CLIENT_UNREGISTER:
- return new ProviderClientRegisterEvent(timeDelta, (String) args[0], false,
- (CallerIdentity) args[1], null);
- case EVENT_PROVIDER_CLIENT_FOREGROUND:
- return new ProviderClientForegroundEvent(timeDelta, (String) args[0], true,
- (CallerIdentity) args[1]);
- case EVENT_PROVIDER_CLIENT_BACKGROUND:
- return new ProviderClientForegroundEvent(timeDelta, (String) args[0], false,
- (CallerIdentity) args[1]);
- case EVENT_PROVIDER_CLIENT_PERMITTED:
- return new ProviderClientPermittedEvent(timeDelta, (String) args[0], true,
- (CallerIdentity) args[1]);
- case EVENT_PROVIDER_CLIENT_UNPERMITTED:
- return new ProviderClientPermittedEvent(timeDelta, (String) args[0], false,
- (CallerIdentity) args[1]);
- case EVENT_PROVIDER_UPDATE_REQUEST:
- return new ProviderUpdateEvent(timeDelta, (String) args[0],
- (ProviderRequest) args[1]);
- case EVENT_PROVIDER_RECEIVE_LOCATION:
- return new ProviderReceiveLocationEvent(timeDelta, (String) args[0],
- (Integer) args[1]);
- case EVENT_PROVIDER_DELIVER_LOCATION:
- return new ProviderDeliverLocationEvent(timeDelta, (String) args[0],
- (Integer) args[1], (CallerIdentity) args[2]);
- case EVENT_PROVIDER_STATIONARY_THROTTLED:
- return new ProviderStationaryThrottledEvent(timeDelta, (String) args[0],
- (Boolean) args[1], (ProviderRequest) args[2]);
- case EVENT_LOCATION_POWER_SAVE_MODE_CHANGE:
- return new LocationPowerSaveModeEvent(timeDelta, (Integer) args[0]);
- default:
- throw new AssertionError();
- }
+ private void addLogEvent(Object logEvent) {
+ addLog(SystemClock.elapsedRealtime(), logEvent);
}
- private abstract static class ProviderEvent extends LogEvent {
+ public void iterate(Consumer<String> consumer) {
+ iterate(consumer, null);
+ }
+
+ public void iterate(Consumer<String> consumer, @Nullable String providerFilter) {
+ long systemTimeDeltaMs = System.currentTimeMillis() - SystemClock.elapsedRealtime();
+ StringBuilder builder = new StringBuilder();
+ iterate(
+ (time, logEvent) -> {
+ boolean match = providerFilter == null || (logEvent instanceof ProviderEvent
+ && providerFilter.equals(((ProviderEvent) logEvent).mProvider));
+ if (match) {
+ builder.setLength(0);
+ builder.append(TimeUtils.logTimeOfDay(time + systemTimeDeltaMs));
+ builder.append(": ");
+ builder.append(logEvent);
+ consumer.accept(builder.toString());
+ }
+ });
+ }
+
+ private abstract static class ProviderEvent {
protected final String mProvider;
- ProviderEvent(long timeDelta, String provider) {
- super(timeDelta);
+ ProviderEvent(String provider) {
mProvider = provider;
}
-
- @Override
- public boolean filter(String filter) {
- return mProvider.equals(filter);
- }
}
private static final class ProviderEnabledEvent extends ProviderEvent {
@@ -288,15 +241,15 @@
private final int mUserId;
private final boolean mEnabled;
- ProviderEnabledEvent(long timeDelta, String provider, int userId,
+ ProviderEnabledEvent(String provider, int userId,
boolean enabled) {
- super(timeDelta, provider);
+ super(provider);
mUserId = userId;
mEnabled = enabled;
}
@Override
- public String getLogString() {
+ public String toString() {
return mProvider + " provider [u" + mUserId + "] " + (mEnabled ? "enabled"
: "disabled");
}
@@ -306,13 +259,13 @@
private final boolean mMocked;
- ProviderMockedEvent(long timeDelta, String provider, boolean mocked) {
- super(timeDelta, provider);
+ ProviderMockedEvent(String provider, boolean mocked) {
+ super(provider);
mMocked = mocked;
}
@Override
- public String getLogString() {
+ public String toString() {
if (mMocked) {
return mProvider + " provider added mock provider override";
} else {
@@ -327,16 +280,16 @@
private final CallerIdentity mIdentity;
@Nullable private final LocationRequest mLocationRequest;
- ProviderClientRegisterEvent(long timeDelta, String provider, boolean registered,
+ ProviderClientRegisterEvent(String provider, boolean registered,
CallerIdentity identity, @Nullable LocationRequest locationRequest) {
- super(timeDelta, provider);
+ super(provider);
mRegistered = registered;
mIdentity = identity;
mLocationRequest = locationRequest;
}
@Override
- public String getLogString() {
+ public String toString() {
if (mRegistered) {
return mProvider + " provider +registration " + mIdentity + " -> "
+ mLocationRequest;
@@ -351,15 +304,15 @@
private final boolean mForeground;
private final CallerIdentity mIdentity;
- ProviderClientForegroundEvent(long timeDelta, String provider, boolean foreground,
+ ProviderClientForegroundEvent(String provider, boolean foreground,
CallerIdentity identity) {
- super(timeDelta, provider);
+ super(provider);
mForeground = foreground;
mIdentity = identity;
}
@Override
- public String getLogString() {
+ public String toString() {
return mProvider + " provider client " + mIdentity + " -> "
+ (mForeground ? "foreground" : "background");
}
@@ -370,15 +323,14 @@
private final boolean mPermitted;
private final CallerIdentity mIdentity;
- ProviderClientPermittedEvent(long timeDelta, String provider, boolean permitted,
- CallerIdentity identity) {
- super(timeDelta, provider);
+ ProviderClientPermittedEvent(String provider, boolean permitted, CallerIdentity identity) {
+ super(provider);
mPermitted = permitted;
mIdentity = identity;
}
@Override
- public String getLogString() {
+ public String toString() {
return mProvider + " provider client " + mIdentity + " -> "
+ (mPermitted ? "permitted" : "unpermitted");
}
@@ -388,13 +340,13 @@
private final ProviderRequest mRequest;
- ProviderUpdateEvent(long timeDelta, String provider, ProviderRequest request) {
- super(timeDelta, provider);
+ ProviderUpdateEvent(String provider, ProviderRequest request) {
+ super(provider);
mRequest = request;
}
@Override
- public String getLogString() {
+ public String toString() {
return mProvider + " provider request = " + mRequest;
}
}
@@ -403,13 +355,13 @@
private final int mNumLocations;
- ProviderReceiveLocationEvent(long timeDelta, String provider, int numLocations) {
- super(timeDelta, provider);
+ ProviderReceiveLocationEvent(String provider, int numLocations) {
+ super(provider);
mNumLocations = numLocations;
}
@Override
- public String getLogString() {
+ public String toString() {
return mProvider + " provider received location[" + mNumLocations + "]";
}
}
@@ -419,15 +371,15 @@
private final int mNumLocations;
@Nullable private final CallerIdentity mIdentity;
- ProviderDeliverLocationEvent(long timeDelta, String provider, int numLocations,
+ ProviderDeliverLocationEvent(String provider, int numLocations,
@Nullable CallerIdentity identity) {
- super(timeDelta, provider);
+ super(provider);
mNumLocations = numLocations;
mIdentity = identity;
}
@Override
- public String getLogString() {
+ public String toString() {
return mProvider + " provider delivered location[" + mNumLocations + "] to "
+ mIdentity;
}
@@ -438,33 +390,31 @@
private final boolean mStationaryThrottled;
private final ProviderRequest mRequest;
- ProviderStationaryThrottledEvent(long timeDelta, String provider,
- boolean stationaryThrottled, ProviderRequest request) {
- super(timeDelta, provider);
+ ProviderStationaryThrottledEvent(String provider, boolean stationaryThrottled,
+ ProviderRequest request) {
+ super(provider);
mStationaryThrottled = stationaryThrottled;
mRequest = request;
}
@Override
- public String getLogString() {
+ public String toString() {
return mProvider + " provider stationary/idle " + (mStationaryThrottled ? "throttled"
: "unthrottled") + ", request = " + mRequest;
}
}
- private static final class LocationPowerSaveModeEvent extends LogEvent {
+ private static final class LocationPowerSaveModeEvent {
@LocationPowerSaveMode
private final int mLocationPowerSaveMode;
- LocationPowerSaveModeEvent(long timeDelta,
- @LocationPowerSaveMode int locationPowerSaveMode) {
- super(timeDelta);
+ LocationPowerSaveModeEvent(@LocationPowerSaveMode int locationPowerSaveMode) {
mLocationPowerSaveMode = locationPowerSaveMode;
}
@Override
- public String getLogString() {
+ public String toString() {
String mode;
switch (mLocationPowerSaveMode) {
case LOCATION_MODE_NO_CHANGE:
@@ -490,53 +440,50 @@
}
}
- private static final class UserSwitchedEvent extends LogEvent {
+ private static final class UserSwitchedEvent {
private final int mUserIdFrom;
private final int mUserIdTo;
- UserSwitchedEvent(long timeDelta, int userIdFrom, int userIdTo) {
- super(timeDelta);
+ UserSwitchedEvent(int userIdFrom, int userIdTo) {
mUserIdFrom = userIdFrom;
mUserIdTo = userIdTo;
}
@Override
- public String getLogString() {
+ public String toString() {
return "current user switched from u" + mUserIdFrom + " to u" + mUserIdTo;
}
}
- private static final class LocationEnabledEvent extends LogEvent {
+ private static final class LocationEnabledEvent {
private final int mUserId;
private final boolean mEnabled;
- LocationEnabledEvent(long timeDelta, int userId, boolean enabled) {
- super(timeDelta);
+ LocationEnabledEvent(int userId, boolean enabled) {
mUserId = userId;
mEnabled = enabled;
}
@Override
- public String getLogString() {
+ public String toString() {
return "location [u" + mUserId + "] " + (mEnabled ? "enabled" : "disabled");
}
}
- private static final class LocationAdasEnabledEvent extends LogEvent {
+ private static final class LocationAdasEnabledEvent {
private final int mUserId;
private final boolean mEnabled;
- LocationAdasEnabledEvent(long timeDelta, int userId, boolean enabled) {
- super(timeDelta);
+ LocationAdasEnabledEvent(int userId, boolean enabled) {
mUserId = userId;
mEnabled = enabled;
}
@Override
- public String getLogString() {
+ public String toString() {
return "adas location [u" + mUserId + "] " + (mEnabled ? "enabled" : "disabled");
}
}
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index 557fa89..df372b1 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -290,7 +290,8 @@
combined.getValues(augmentStart, augmentEnd, entry);
}
- final long rawBytes = entry.rxBytes + entry.txBytes;
+ final long rawBytes = (entry.rxBytes + entry.txBytes) == 0 ? 1 :
+ (entry.rxBytes + entry.txBytes);
final long rawRxBytes = entry.rxBytes == 0 ? 1 : entry.rxBytes;
final long rawTxBytes = entry.txBytes == 0 ? 1 : entry.txBytes;
final long targetBytes = augmentPlan.getDataUsageBytes();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 5ffb754..bd2a407 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7451,15 +7451,21 @@
sentAccessibilityEvent = true;
}
if (DBG) Slog.v(TAG, "Interrupting!");
+ boolean isInsistentUpdate = isInsistentUpdate(record);
if (hasValidSound) {
- if (isInCall()) {
- playInCallNotification();
+ if (isInsistentUpdate) {
+ // don't reset insistent sound, it's jarring
beep = true;
} else {
- beep = playSound(record, soundUri);
- }
- if(beep) {
- mSoundNotificationKey = key;
+ if (isInCall()) {
+ playInCallNotification();
+ beep = true;
+ } else {
+ beep = playSound(record, soundUri);
+ }
+ if (beep) {
+ mSoundNotificationKey = key;
+ }
}
}
@@ -7467,9 +7473,13 @@
mAudioManager.getRingerModeInternal()
== AudioManager.RINGER_MODE_SILENT;
if (!isInCall() && hasValidVibrate && !ringerModeSilent) {
- buzz = playVibration(record, vibration, hasValidSound);
- if(buzz) {
- mVibrateNotificationKey = key;
+ if (isInsistentUpdate) {
+ buzz = true;
+ } else {
+ buzz = playVibration(record, vibration, hasValidSound);
+ if (buzz) {
+ mVibrateNotificationKey = key;
+ }
}
}
} else if ((record.getFlags() & Notification.FLAG_INSISTENT) != 0) {
@@ -7573,6 +7583,19 @@
}
@GuardedBy("mNotificationLock")
+ boolean isInsistentUpdate(final NotificationRecord record) {
+ return (Objects.equals(record.getKey(), mSoundNotificationKey)
+ || Objects.equals(record.getKey(), mVibrateNotificationKey))
+ && isCurrentlyInsistent();
+ }
+
+ @GuardedBy("mNotificationLock")
+ boolean isCurrentlyInsistent() {
+ return isLoopingRingtoneNotification(mNotificationsByKey.get(mSoundNotificationKey))
+ || isLoopingRingtoneNotification(mNotificationsByKey.get(mVibrateNotificationKey));
+ }
+
+ @GuardedBy("mNotificationLock")
boolean shouldMuteNotificationLocked(final NotificationRecord record) {
// Suppressed because it's a silent update
final Notification notification = record.getNotification();
@@ -7611,10 +7634,8 @@
return true;
}
- // A looping ringtone, such as an incoming call is playing
- if (isLoopingRingtoneNotification(mNotificationsByKey.get(mSoundNotificationKey))
- || isLoopingRingtoneNotification(
- mNotificationsByKey.get(mVibrateNotificationKey))) {
+ // A different looping ringtone, such as an incoming call is playing
+ if (isCurrentlyInsistent() && !isInsistentUpdate(record)) {
return true;
}
@@ -8758,10 +8779,22 @@
void snoozeNotificationInt(String key, long duration, String snoozeCriterionId,
ManagedServiceInfo listener) {
- String listenerName = listener == null ? null : listener.component.toShortString();
+ if (listener == null) {
+ return;
+ }
+ String listenerName = listener.component.toShortString();
if ((duration <= 0 && snoozeCriterionId == null) || key == null) {
return;
}
+ synchronized (mNotificationLock) {
+ final NotificationRecord r = findInCurrentAndSnoozedNotificationByKeyLocked(key);
+ if (r == null) {
+ return;
+ }
+ if (!listener.enabledAndUserMatches(r.getSbn().getNormalizedUserId())){
+ return;
+ }
+ }
if (DBG) {
Slog.d(TAG, String.format("snooze event(%s, %d, %s, %s)", key, duration,
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 96bde3d..e8a3a81 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -330,8 +330,7 @@
}
}
- if (isShortcutOk(channel) && isDeletionOk(channel)
- && !channel.isSoundMissing()) {
+ if (isShortcutOk(channel) && isDeletionOk(channel)) {
r.channels.put(id, channel);
}
}
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index a44cad8..aa467e7 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -64,7 +64,6 @@
import com.android.internal.security.VerityUtils;
import com.android.server.pm.parsing.pkg.AndroidPackage;
-import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -724,16 +723,20 @@
}
}
+ static final int MIN_BUFFER_SIZE = 4 * 1024;
+ static final int MAX_BUFFER_SIZE = 128 * 1024;
+
private static byte[] getApkChecksum(File file, int type) {
- try (FileInputStream fis = new FileInputStream(file);
- BufferedInputStream bis = new BufferedInputStream(fis)) {
- byte[] dataBytes = new byte[512 * 1024];
+ final int bufferSize = (int) Math.max(MIN_BUFFER_SIZE,
+ Math.min(MAX_BUFFER_SIZE, file.length()));
+ try (FileInputStream fis = new FileInputStream(file)) {
+ final byte[] buffer = new byte[bufferSize];
int nread = 0;
final String algo = getMessageDigestAlgoForChecksumKind(type);
MessageDigest md = MessageDigest.getInstance(algo);
- while ((nread = bis.read(dataBytes)) != -1) {
- md.update(dataBytes, 0, nread);
+ while ((nread = fis.read(buffer)) != -1) {
+ md.update(buffer, 0, nread);
}
return md.digest();
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 15e671c..f552606 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -1583,7 +1583,7 @@
dumpPackageSet(pw, filteringAppId, mForceQueryable, "forceQueryable", " ", expandPackages);
pw.println(" queries via package name:");
dumpQueriesMap(pw, filteringAppId, mQueriesViaPackage, " ", expandPackages);
- pw.println(" queries via intent:");
+ pw.println(" queries via component:");
dumpQueriesMap(pw, filteringAppId, mQueriesViaComponent, " ", expandPackages);
pw.println(" queryable via interaction:");
for (int user : users) {
diff --git a/services/core/java/com/android/server/pm/HandlerParams.java b/services/core/java/com/android/server/pm/HandlerParams.java
index b8c2eb8..ab24ee5 100644
--- a/services/core/java/com/android/server/pm/HandlerParams.java
+++ b/services/core/java/com/android/server/pm/HandlerParams.java
@@ -16,20 +16,35 @@
package com.android.server.pm;
+import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
+
+import android.annotation.NonNull;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInfoLite;
+import android.content.pm.PackageManager;
import android.os.UserHandle;
+import android.util.Pair;
import android.util.Slog;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.TAG;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
abstract class HandlerParams {
/** User handle for the user requesting the information or installation. */
private final UserHandle mUser;
String mTraceMethod;
int mTraceCookie;
+ @NonNull
+ final PackageManagerService mPm;
- HandlerParams(UserHandle user) {
+ // TODO(b/198166813): remove PMS dependency
+ HandlerParams(UserHandle user, PackageManagerService pm) {
mUser = user;
+ mPm = pm;
}
UserHandle getUser() {
@@ -54,4 +69,135 @@
abstract void handleStartCopy();
abstract void handleReturnCode();
+
+ Pair<Integer, String> verifyReplacingVersionCode(PackageInfoLite pkgLite,
+ long requiredInstalledVersionCode, int installFlags) {
+ if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
+ return verifyReplacingVersionCodeForApex(
+ pkgLite, requiredInstalledVersionCode, installFlags);
+ }
+
+ String packageName = pkgLite.packageName;
+ synchronized (mPm.mLock) {
+ // Package which currently owns the data that the new package will own if installed.
+ // If an app is uninstalled while keeping data (e.g. adb uninstall -k), installedPkg
+ // will be null whereas dataOwnerPkg will contain information about the package
+ // which was uninstalled while keeping its data.
+ AndroidPackage dataOwnerPkg = mPm.mPackages.get(packageName);
+ if (dataOwnerPkg == null) {
+ PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+ if (ps != null) {
+ dataOwnerPkg = ps.pkg;
+ }
+ }
+
+ if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) {
+ if (dataOwnerPkg == null) {
+ String errorMsg = "Required installed version code was "
+ + requiredInstalledVersionCode
+ + " but package is not installed";
+ Slog.w(TAG, errorMsg);
+ return Pair.create(
+ PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
+ }
+
+ if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) {
+ String errorMsg = "Required installed version code was "
+ + requiredInstalledVersionCode
+ + " but actual installed version is "
+ + dataOwnerPkg.getLongVersionCode();
+ Slog.w(TAG, errorMsg);
+ return Pair.create(
+ PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
+ }
+ }
+
+ if (dataOwnerPkg != null) {
+ if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
+ dataOwnerPkg.isDebuggable())) {
+ try {
+ checkDowngrade(dataOwnerPkg, pkgLite);
+ } catch (PackageManagerException e) {
+ String errorMsg = "Downgrade detected: " + e.getMessage();
+ Slog.w(TAG, errorMsg);
+ return Pair.create(
+ PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg);
+ }
+ }
+ }
+ }
+ return Pair.create(PackageManager.INSTALL_SUCCEEDED, null);
+ }
+
+ private Pair<Integer, String> verifyReplacingVersionCodeForApex(PackageInfoLite pkgLite,
+ long requiredInstalledVersionCode, int installFlags) {
+ String packageName = pkgLite.packageName;
+
+ final PackageInfo activePackage = mPm.mApexManager.getPackageInfo(packageName,
+ ApexManager.MATCH_ACTIVE_PACKAGE);
+ if (activePackage == null) {
+ String errorMsg = "Attempting to install new APEX package " + packageName;
+ Slog.w(TAG, errorMsg);
+ return Pair.create(PackageManager.INSTALL_FAILED_PACKAGE_CHANGED, errorMsg);
+ }
+
+ final long activeVersion = activePackage.getLongVersionCode();
+ if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST
+ && activeVersion != requiredInstalledVersionCode) {
+ String errorMsg = "Installed version of APEX package " + packageName
+ + " does not match required. Active version: " + activeVersion
+ + " required: " + requiredInstalledVersionCode;
+ Slog.w(TAG, errorMsg);
+ return Pair.create(PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
+ }
+
+ final boolean isAppDebuggable = (activePackage.applicationInfo.flags
+ & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+ final long newVersionCode = pkgLite.getLongVersionCode();
+ if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, isAppDebuggable)
+ && newVersionCode < activeVersion) {
+ String errorMsg = "Downgrade of APEX package " + packageName
+ + " is not allowed. Active version: " + activeVersion
+ + " attempted: " + newVersionCode;
+ Slog.w(TAG, errorMsg);
+ return Pair.create(PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg);
+ }
+
+ return Pair.create(PackageManager.INSTALL_SUCCEEDED, null);
+ }
+
+ /**
+ * Check and throw if the given before/after packages would be considered a
+ * downgrade.
+ */
+ private static void checkDowngrade(AndroidPackage before, PackageInfoLite after)
+ throws PackageManagerException {
+ if (after.getLongVersionCode() < before.getLongVersionCode()) {
+ throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
+ "Update version code " + after.versionCode + " is older than current "
+ + before.getLongVersionCode());
+ } else if (after.getLongVersionCode() == before.getLongVersionCode()) {
+ if (after.baseRevisionCode < before.getBaseRevisionCode()) {
+ throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
+ "Update base revision code " + after.baseRevisionCode
+ + " is older than current " + before.getBaseRevisionCode());
+ }
+
+ if (!ArrayUtils.isEmpty(after.splitNames)) {
+ for (int i = 0; i < after.splitNames.length; i++) {
+ final String splitName = after.splitNames[i];
+ final int j = ArrayUtils.indexOf(before.getSplitNames(), splitName);
+ if (j != -1) {
+ if (after.splitRevisionCodes[i] < before.getSplitRevisionCodes()[j]) {
+ throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
+ "Update split " + splitName + " revision code "
+ + after.splitRevisionCodes[i]
+ + " is older than current "
+ + before.getSplitRevisionCodes()[j]);
+ }
+ }
+ }
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
new file mode 100644
index 0000000..d600ae5
--- /dev/null
+++ b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
@@ -0,0 +1,1325 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
+import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
+import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import static android.os.incremental.IncrementalManager.isIncrementalPath;
+import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
+import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
+import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
+
+import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
+import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME;
+import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME;
+import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
+import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION;
+import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
+import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
+import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
+import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
+import static com.android.server.pm.PackageManagerService.DEBUG_VERIFY;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM;
+import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX;
+import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
+import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_SIGNATURE;
+import static com.android.server.pm.PackageManagerService.TAG;
+import static com.android.server.pm.PackageManagerServiceUtils.decompressFile;
+import static com.android.server.pm.PackageManagerServiceUtils.getCompressedFiles;
+import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime;
+import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
+import static com.android.server.pm.PackageManagerServiceUtils.makeDirRecursive;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.SigningDetails;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
+import android.os.Environment;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.system.ErrnoException;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.F2fsUtils;
+import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.security.VerityUtils;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.EventLogTags;
+import com.android.server.pm.parsing.PackageCacher;
+import com.android.server.pm.parsing.PackageParser2;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.utils.WatchedArrayMap;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.DigestException;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ExecutorService;
+
+/**
+ * Part of PackageManagerService that handles init and system packages. This class still needs
+ * further cleanup and eventually all the installation/scanning related logic will go to another
+ * class.
+ */
+public class InitAndSystemPackageHelper {
+ final PackageManagerService mPm;
+
+ // TODO(b/198166813): remove PMS dependency
+ public InitAndSystemPackageHelper(PackageManagerService pm) {
+ mPm = pm;
+ }
+
+ /**
+ * First part of init dir scanning
+ */
+ // TODO(b/197876467): consolidate this with cleanupSystemPackagesAndInstallStubs
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ public void scanSystemDirs(List<ScanPartition> dirsToScanAsSystem,
+ boolean isUpgrade, PackageParser2 packageParser,
+ ExecutorService executorService, AndroidPackage platformPackage,
+ boolean isPreNMR1Upgrade, int systemParseFlags, int systemScanFlags) {
+ File frameworkDir = new File(Environment.getRootDirectory(), "framework");
+
+ // Collect vendor/product/system_ext overlay packages. (Do this before scanning
+ // any apps.)
+ // For security and version matching reason, only consider overlay packages if they
+ // reside in the right directory.
+ for (int i = dirsToScanAsSystem.size() - 1; i >= 0; i--) {
+ final ScanPartition partition = dirsToScanAsSystem.get(i);
+ if (partition.getOverlayFolder() == null) {
+ continue;
+ }
+ scanDirTracedLI(partition.getOverlayFolder(), systemParseFlags,
+ systemScanFlags | partition.scanFlag, 0,
+ packageParser, executorService, platformPackage, isUpgrade,
+ isPreNMR1Upgrade);
+ }
+
+ scanDirTracedLI(frameworkDir, systemParseFlags,
+ systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
+ packageParser, executorService, platformPackage, isUpgrade, isPreNMR1Upgrade);
+ if (!mPm.mPackages.containsKey("android")) {
+ throw new IllegalStateException(
+ "Failed to load frameworks package; check log for warnings");
+ }
+
+ for (int i = 0, size = dirsToScanAsSystem.size(); i < size; i++) {
+ final ScanPartition partition = dirsToScanAsSystem.get(i);
+ if (partition.getPrivAppFolder() != null) {
+ scanDirTracedLI(partition.getPrivAppFolder(), systemParseFlags,
+ systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
+ packageParser, executorService, platformPackage, isUpgrade,
+ isPreNMR1Upgrade);
+ }
+ scanDirTracedLI(partition.getAppFolder(), systemParseFlags,
+ systemScanFlags | partition.scanFlag, 0,
+ packageParser, executorService, platformPackage, isUpgrade,
+ isPreNMR1Upgrade);
+ }
+ }
+
+ /**
+ * Second part of init dir scanning
+ */
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ public void cleanupSystemPackagesAndInstallStubs(List<ScanPartition> dirsToScanAsSystem,
+ boolean isUpgrade, PackageParser2 packageParser,
+ ExecutorService executorService, boolean onlyCore,
+ WatchedArrayMap<String, PackageSetting> packageSettings,
+ long startTime, File appInstallDir, AndroidPackage platformPackage,
+ boolean isPreNMR1Upgrade, int scanFlags, int systemParseFlags, int systemScanFlags,
+ int[] userIds) {
+ // Prune any system packages that no longer exist.
+ final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
+ // Stub packages must either be replaced with full versions in the /data
+ // partition or be disabled.
+ final List<String> stubSystemApps = new ArrayList<>();
+
+ if (!onlyCore) {
+ // do this first before mucking with mPackages for the "expecting better" case
+ final int numPackages = mPm.mPackages.size();
+ for (int index = 0; index < numPackages; index++) {
+ final AndroidPackage pkg = mPm.mPackages.valueAt(index);
+ if (pkg.isStub()) {
+ stubSystemApps.add(pkg.getPackageName());
+ }
+ }
+
+ // Iterates PackageSettings in reversed order because the item could be removed
+ // during the iteration.
+ for (int index = packageSettings.size() - 1; index >= 0; index--) {
+ final PackageSetting ps = packageSettings.valueAt(index);
+
+ /*
+ * If this is not a system app, it can't be a
+ * disable system app.
+ */
+ if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ continue;
+ }
+
+ /*
+ * If the package is scanned, it's not erased.
+ */
+ final AndroidPackage scannedPkg = mPm.mPackages.get(ps.name);
+ if (scannedPkg != null) {
+ /*
+ * If the system app is both scanned and in the
+ * disabled packages list, then it must have been
+ * added via OTA. Remove it from the currently
+ * scanned package so the previously user-installed
+ * application can be scanned.
+ */
+ if (mPm.mSettings.isDisabledSystemPackageLPr(ps.name)) {
+ logCriticalInfo(Log.WARN,
+ "Expecting better updated system app for " + ps.name
+ + "; removing system app. Last known"
+ + " codePath=" + ps.getPathString()
+ + ", versionCode=" + ps.versionCode
+ + "; scanned versionCode="
+ + scannedPkg.getLongVersionCode());
+ mPm.removePackageLI(scannedPkg, true);
+ mPm.mExpectingBetter.put(ps.name, ps.getPath());
+ }
+
+ continue;
+ }
+
+ if (!mPm.mSettings.isDisabledSystemPackageLPr(ps.name)) {
+ logCriticalInfo(Log.WARN, "System package " + ps.name
+ + " no longer exists; its data will be wiped");
+ mPm.removePackageDataLIF(ps, userIds, null, 0, false);
+ } else {
+ // we still have a disabled system package, but, it still might have
+ // been removed. check the code path still exists and check there's
+ // still a package. the latter can happen if an OTA keeps the same
+ // code path, but, changes the package name.
+ final PackageSetting disabledPs =
+ mPm.mSettings.getDisabledSystemPkgLPr(ps.name);
+ if (disabledPs.getPath() == null || !disabledPs.getPath().exists()
+ || disabledPs.pkg == null) {
+ possiblyDeletedUpdatedSystemApps.add(ps.name);
+ } else {
+ // We're expecting that the system app should remain disabled, but add
+ // it to expecting better to recover in case the data version cannot
+ // be scanned.
+ // TODO(b/197869066): mExpectingBetter should be able to pulled out into
+ // this class and used only by the PMS initialization
+ mPm.mExpectingBetter.put(disabledPs.name, disabledPs.getPath());
+ }
+ }
+ }
+ }
+
+ final int cachedSystemApps = PackageCacher.sCachedPackageReadCount.get();
+
+ // Remove any shared userIDs that have no associated packages
+ mPm.mSettings.pruneSharedUsersLPw();
+ final long systemScanTime = SystemClock.uptimeMillis() - startTime;
+ final int systemPackagesCount = mPm.mPackages.size();
+ Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime
+ + " ms, packageCount: " + systemPackagesCount
+ + " , timePerPackage: "
+ + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount)
+ + " , cached: " + cachedSystemApps);
+ if (isUpgrade && systemPackagesCount > 0) {
+ //CHECKSTYLE:OFF IndentationCheck
+ FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
+ BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME,
+ systemScanTime / systemPackagesCount);
+ //CHECKSTYLE:ON IndentationCheck
+ }
+ if (!onlyCore) {
+ EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
+ SystemClock.uptimeMillis());
+ scanDirTracedLI(appInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,
+ packageParser, executorService, platformPackage, isUpgrade,
+ isPreNMR1Upgrade);
+
+ }
+
+ List<Runnable> unfinishedTasks = executorService.shutdownNow();
+ if (!unfinishedTasks.isEmpty()) {
+ throw new IllegalStateException("Not all tasks finished before calling close: "
+ + unfinishedTasks);
+ }
+
+ if (!onlyCore) {
+ // Remove disable package settings for updated system apps that were
+ // removed via an OTA. If the update is no longer present, remove the
+ // app completely. Otherwise, revoke their system privileges.
+ for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {
+ final String packageName = possiblyDeletedUpdatedSystemApps.get(i);
+ final AndroidPackage pkg = mPm.mPackages.get(packageName);
+ final String msg;
+
+ // remove from the disabled system list; do this first so any future
+ // scans of this package are performed without this state
+ mPm.mSettings.removeDisabledSystemPackageLPw(packageName);
+
+ if (pkg == null) {
+ // should have found an update, but, we didn't; remove everything
+ msg = "Updated system package " + packageName
+ + " no longer exists; removing its data";
+ // Actual deletion of code and data will be handled by later
+ // reconciliation step
+ } else {
+ // found an update; revoke system privileges
+ msg = "Updated system package " + packageName
+ + " no longer exists; rescanning package on data";
+
+ // NOTE: We don't do anything special if a stub is removed from the
+ // system image. But, if we were [like removing the uncompressed
+ // version from the /data partition], this is where it'd be done.
+
+ // remove the package from the system and re-scan it without any
+ // special privileges
+ mPm.removePackageLI(pkg, true);
+ try {
+ final File codePath = new File(pkg.getPath());
+ mPm.scanPackageTracedLI(codePath, 0, scanFlags, 0, null);
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "Failed to parse updated, ex-system package: "
+ + e.getMessage());
+ }
+ }
+
+ // one final check. if we still have a package setting [ie. it was
+ // previously scanned and known to the system], but, we don't have
+ // a package [ie. there was an error scanning it from the /data
+ // partition], completely remove the package data.
+ final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+ if (ps != null && mPm.mPackages.get(packageName) == null) {
+ mPm.removePackageDataLIF(ps, userIds, null, 0, false);
+
+ }
+ logCriticalInfo(Log.WARN, msg);
+ }
+
+ /*
+ * Make sure all system apps that we expected to appear on
+ * the userdata partition actually showed up. If they never
+ * appeared, crawl back and revive the system version.
+ */
+ for (int i = 0; i < mPm.mExpectingBetter.size(); i++) {
+ final String packageName = mPm.mExpectingBetter.keyAt(i);
+ if (!mPm.mPackages.containsKey(packageName)) {
+ final File scanFile = mPm.mExpectingBetter.valueAt(i);
+
+ logCriticalInfo(Log.WARN, "Expected better " + packageName
+ + " but never showed up; reverting to system");
+
+ @ParsingPackageUtils.ParseFlags int reparseFlags = 0;
+ @PackageManagerService.ScanFlags int rescanFlags = 0;
+ for (int i1 = dirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
+ final ScanPartition partition = dirsToScanAsSystem.get(i1);
+ if (partition.containsPrivApp(scanFile)) {
+ reparseFlags = systemParseFlags;
+ rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
+ | partition.scanFlag;
+ break;
+ }
+ if (partition.containsApp(scanFile)) {
+ reparseFlags = systemParseFlags;
+ rescanFlags = systemScanFlags | partition.scanFlag;
+ break;
+ }
+ }
+ if (rescanFlags == 0) {
+ Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
+ continue;
+ }
+ mPm.mSettings.enableSystemPackageLPw(packageName);
+
+ try {
+ final AndroidPackage newPkg = mPm.scanPackageTracedLI(
+ scanFile, reparseFlags, rescanFlags, 0, null);
+ // We rescanned a stub, add it to the list of stubbed system packages
+ if (newPkg.isStub()) {
+ stubSystemApps.add(packageName);
+ }
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "Failed to parse original system package: "
+ + e.getMessage());
+ }
+ }
+ }
+
+ // Uncompress and install any stubbed system applications.
+ // This must be done last to ensure all stubs are replaced or disabled.
+ installSystemStubPackages(stubSystemApps, scanFlags);
+
+ final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get()
+ - cachedSystemApps;
+
+ final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;
+ final int dataPackagesCount = mPm.mPackages.size() - systemPackagesCount;
+ Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
+ + " ms, packageCount: " + dataPackagesCount
+ + " , timePerPackage: "
+ + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
+ + " , cached: " + cachedNonSystemApps);
+ if (isUpgrade && dataPackagesCount > 0) {
+ //CHECKSTYLE:OFF IndentationCheck
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
+ BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME,
+ dataScanTime / dataPackagesCount);
+ //CHECKSTYLE:OFF IndentationCheck
+ }
+ }
+ mPm.mExpectingBetter.clear();
+
+ mPm.mSettings.pruneRenamedPackagesLPw();
+ }
+
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags,
+ long currentTime, PackageParser2 packageParser, ExecutorService executorService,
+ AndroidPackage platformPackage, boolean isUpgrade, boolean isPreNMR1Upgrade) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
+ try {
+ scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService,
+ platformPackage, isUpgrade, isPreNMR1Upgrade);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,
+ PackageParser2 packageParser, ExecutorService executorService,
+ AndroidPackage platformPackage, boolean isUpgrade, boolean isPreNMR1Upgrade) {
+ final File[] files = scanDir.listFiles();
+ if (ArrayUtils.isEmpty(files)) {
+ Log.d(TAG, "No files in app dir " + scanDir);
+ return;
+ }
+
+ if (DEBUG_PACKAGE_SCANNING) {
+ Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags
+ + " flags=0x" + Integer.toHexString(parseFlags));
+ }
+
+ ParallelPackageParser parallelPackageParser =
+ new ParallelPackageParser(packageParser, executorService);
+
+ // Submit files for parsing in parallel
+ int fileCount = 0;
+ for (File file : files) {
+ final boolean isPackage = (isApkFile(file) || file.isDirectory())
+ && !PackageInstallerService.isStageName(file.getName());
+ if (!isPackage) {
+ // Ignore entries which are not packages
+ continue;
+ }
+ parallelPackageParser.submit(file, parseFlags);
+ fileCount++;
+ }
+
+ // Process results one by one
+ for (; fileCount > 0; fileCount--) {
+ ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
+ Throwable throwable = parseResult.throwable;
+ int errorCode = PackageManager.INSTALL_SUCCEEDED;
+ String errorMsg = null;
+
+ if (throwable == null) {
+ // TODO(b/194319951): move lower in the scan chain
+ // Static shared libraries have synthetic package names
+ if (parseResult.parsedPackage.isStaticSharedLibrary()) {
+ PackageManagerService.renameStaticSharedLibraryPackage(
+ parseResult.parsedPackage);
+ }
+ try {
+ addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
+ currentTime, null, platformPackage, isUpgrade,
+ isPreNMR1Upgrade);
+ } catch (PackageManagerException e) {
+ errorCode = e.error;
+ errorMsg = "Failed to scan " + parseResult.scanFile + ": " + e.getMessage();
+ Slog.w(TAG, errorMsg);
+ }
+ } else if (throwable instanceof PackageManagerException) {
+ PackageManagerException e = (PackageManagerException) throwable;
+ errorCode = e.error;
+ errorMsg = "Failed to parse " + parseResult.scanFile + ": " + e.getMessage();
+ Slog.w(TAG, errorMsg);
+ } else {
+ throw new IllegalStateException("Unexpected exception occurred while parsing "
+ + parseResult.scanFile, throwable);
+ }
+
+ if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0 && errorCode != INSTALL_SUCCEEDED) {
+ mPm.mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath(), errorMsg);
+ }
+
+ // Delete invalid userdata apps
+ if ((scanFlags & SCAN_AS_SYSTEM) == 0
+ && errorCode != PackageManager.INSTALL_SUCCEEDED) {
+ logCriticalInfo(Log.WARN,
+ "Deleting invalid package at " + parseResult.scanFile);
+ mPm.removeCodePathLI(parseResult.scanFile);
+ }
+ }
+ }
+
+ /**
+ * Uncompress and install stub applications.
+ * <p>In order to save space on the system partition, some applications are shipped in a
+ * compressed form. In addition the compressed bits for the full application, the
+ * system image contains a tiny stub comprised of only the Android manifest.
+ * <p>During the first boot, attempt to uncompress and install the full application. If
+ * the application can't be installed for any reason, disable the stub and prevent
+ * uncompressing the full application during future boots.
+ * <p>In order to forcefully attempt an installation of a full application, go to app
+ * settings and enable the application.
+ */
+ @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
+ private void installSystemStubPackages(@NonNull List<String> systemStubPackageNames,
+ @PackageManagerService.ScanFlags int scanFlags) {
+ for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) {
+ final String packageName = systemStubPackageNames.get(i);
+ // skip if the system package is already disabled
+ if (mPm.mSettings.isDisabledSystemPackageLPr(packageName)) {
+ systemStubPackageNames.remove(i);
+ continue;
+ }
+ // skip if the package isn't installed (?!); this should never happen
+ final AndroidPackage pkg = mPm.mPackages.get(packageName);
+ if (pkg == null) {
+ systemStubPackageNames.remove(i);
+ continue;
+ }
+ // skip if the package has been disabled by the user
+ final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+ if (ps != null) {
+ final int enabledState = ps.getEnabled(UserHandle.USER_SYSTEM);
+ if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ systemStubPackageNames.remove(i);
+ continue;
+ }
+ }
+
+ // install the package to replace the stub on /system
+ try {
+ installStubPackageLI(pkg, 0, scanFlags);
+ ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
+ UserHandle.USER_SYSTEM, "android");
+ systemStubPackageNames.remove(i);
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "Failed to parse uncompressed system package: " + e.getMessage());
+ }
+
+ // any failed attempt to install the package will be cleaned up later
+ }
+
+ // disable any stub still left; these failed to install the full application
+ for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) {
+ final String pkgName = systemStubPackageNames.get(i);
+ final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName);
+ ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ UserHandle.USER_SYSTEM, "android");
+ logCriticalInfo(Log.ERROR, "Stub disabled; pkg: " + pkgName);
+ }
+ }
+
+ /**
+ * Extract, install and enable a stub package.
+ * <p>If the compressed file can not be extracted / installed for any reason, the stub
+ * APK will be installed and the package will be disabled. To recover from this situation,
+ * the user will need to go into system settings and re-enable the package.
+ */
+ @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
+ public boolean enableCompressedPackage(AndroidPackage stubPkg,
+ @NonNull PackageSetting stubPkgSetting, int defParseFlags,
+ List<ScanPartition> dirsToScanAsSystem) {
+ final int parseFlags = defParseFlags | ParsingPackageUtils.PARSE_CHATTY
+ | ParsingPackageUtils.PARSE_ENFORCE_CODE;
+ synchronized (mPm.mInstallLock) {
+ final AndroidPackage pkg;
+ try (PackageFreezer freezer =
+ mPm.freezePackage(stubPkg.getPackageName(), "setEnabledSetting")) {
+ pkg = installStubPackageLI(stubPkg, parseFlags, 0 /*scanFlags*/);
+ synchronized (mPm.mLock) {
+ mPm.prepareAppDataAfterInstallLIF(pkg);
+ try {
+ mPm.updateSharedLibrariesLocked(pkg, stubPkgSetting, null, null,
+ Collections.unmodifiableMap(mPm.mPackages));
+ } catch (PackageManagerException e) {
+ Slog.w(TAG, "updateAllSharedLibrariesLPw failed: ", e);
+ }
+ mPm.mPermissionManager.onPackageInstalled(pkg,
+ PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT,
+ UserHandle.USER_ALL);
+ mPm.writeSettingsLPrTEMP();
+ }
+ } catch (PackageManagerException e) {
+ // Whoops! Something went very wrong; roll back to the stub and disable the package
+ try (PackageFreezer freezer =
+ mPm.freezePackage(stubPkg.getPackageName(), "setEnabledSetting")) {
+ synchronized (mPm.mLock) {
+ // NOTE: Ensure the system package is enabled; even for a compressed stub.
+ // If we don't, installing the system package fails during scan
+ enableSystemPackageLPw(stubPkg);
+ }
+ installPackageFromSystemLIF(stubPkg.getPath(),
+ mPm.mUserManager.getUserIds() /*allUserHandles*/,
+ null /*origUserHandles*/,
+ true /*writeSettings*/, defParseFlags, dirsToScanAsSystem);
+ } catch (PackageManagerException pme) {
+ // Serious WTF; we have to be able to install the stub
+ Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.getPackageName(),
+ pme);
+ } finally {
+ // Disable the package; the stub by itself is not runnable
+ synchronized (mPm.mLock) {
+ final PackageSetting stubPs = mPm.mSettings.getPackageLPr(
+ stubPkg.getPackageName());
+ if (stubPs != null) {
+ stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED,
+ UserHandle.USER_SYSTEM, "android");
+ }
+ mPm.writeSettingsLPrTEMP();
+ }
+ }
+ return false;
+ }
+ mPm.clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
+ | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ mPm.getDexManager().notifyPackageUpdated(pkg.getPackageName(),
+ pkg.getBaseApkPath(), pkg.getSplitCodePaths());
+ }
+ return true;
+ }
+
+ @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
+ private AndroidPackage installStubPackageLI(AndroidPackage stubPkg,
+ @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags)
+ throws PackageManagerException {
+ if (DEBUG_COMPRESSION) {
+ Slog.i(TAG, "Uncompressing system stub; pkg: " + stubPkg.getPackageName());
+ }
+ // uncompress the binary to its eventual destination on /data
+ final File scanFile = decompressPackage(stubPkg.getPackageName(), stubPkg.getPath());
+ if (scanFile == null) {
+ throw new PackageManagerException(
+ "Unable to decompress stub at " + stubPkg.getPath());
+ }
+ synchronized (mPm.mLock) {
+ mPm.mSettings.disableSystemPackageLPw(stubPkg.getPackageName(), true /*replaced*/);
+ }
+ mPm.removePackageLI(stubPkg, true /*chatty*/);
+ try {
+ return mPm.scanPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null);
+ } catch (PackageManagerException e) {
+ Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(),
+ e);
+ // Remove the failed install
+ mPm.removeCodePathLI(scanFile);
+ throw e;
+ }
+ }
+
+ /**
+ * Decompresses the given package on the system image onto
+ * the /data partition.
+ * @return The directory the package was decompressed into. Otherwise, {@code null}.
+ */
+ @GuardedBy("mPm.mInstallLock")
+ private File decompressPackage(String packageName, String codePath) {
+ final File[] compressedFiles = getCompressedFiles(codePath);
+ if (compressedFiles == null || compressedFiles.length == 0) {
+ if (DEBUG_COMPRESSION) {
+ Slog.i(TAG, "No files to decompress: " + codePath);
+ }
+ return null;
+ }
+ final File dstCodePath =
+ PackageManagerService.getNextCodePath(Environment.getDataAppDirectory(null),
+ packageName);
+ int ret = PackageManager.INSTALL_SUCCEEDED;
+ try {
+ makeDirRecursive(dstCodePath, 0755);
+ for (File srcFile : compressedFiles) {
+ final String srcFileName = srcFile.getName();
+ final String dstFileName = srcFileName.substring(
+ 0, srcFileName.length() - COMPRESSED_EXTENSION.length());
+ final File dstFile = new File(dstCodePath, dstFileName);
+ ret = decompressFile(srcFile, dstFile);
+ if (ret != PackageManager.INSTALL_SUCCEEDED) {
+ logCriticalInfo(Log.ERROR, "Failed to decompress"
+ + "; pkg: " + packageName
+ + ", file: " + dstFileName);
+ break;
+ }
+ }
+ } catch (ErrnoException e) {
+ logCriticalInfo(Log.ERROR, "Failed to decompress"
+ + "; pkg: " + packageName
+ + ", err: " + e.errno);
+ }
+ if (ret == PackageManager.INSTALL_SUCCEEDED) {
+ final File libraryRoot = new File(dstCodePath, LIB_DIR_NAME);
+ NativeLibraryHelper.Handle handle = null;
+ try {
+ handle = NativeLibraryHelper.Handle.create(dstCodePath);
+ ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
+ null /*abiOverride*/, false /*isIncremental*/);
+ } catch (IOException e) {
+ logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
+ + "; pkg: " + packageName);
+ ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ } finally {
+ IoUtils.closeQuietly(handle);
+ }
+ }
+ if (ret == PackageManager.INSTALL_SUCCEEDED) {
+ // NOTE: During boot, we have to delay releasing cblocks for no other reason than
+ // we cannot retrieve the setting {@link Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL}.
+ // When we no longer need to read that setting, cblock release can occur always
+ // occur here directly
+ if (!mPm.mSystemReady) {
+ if (mPm.mReleaseOnSystemReady == null) {
+ mPm.mReleaseOnSystemReady = new ArrayList<>();
+ }
+ mPm.mReleaseOnSystemReady.add(dstCodePath);
+ } else {
+ final ContentResolver resolver = mPm.mContext.getContentResolver();
+ F2fsUtils.releaseCompressedBlocks(resolver, dstCodePath);
+ }
+ }
+ if (ret != PackageManager.INSTALL_SUCCEEDED) {
+ if (!dstCodePath.exists()) {
+ return null;
+ }
+ mPm.removeCodePathLI(dstCodePath);
+ return null;
+ }
+
+ return dstCodePath;
+ }
+
+
+ @GuardedBy("mPm.mLock")
+ private void enableSystemPackageLPw(AndroidPackage pkg) {
+ mPm.mSettings.enableSystemPackageLPw(pkg.getPackageName());
+ }
+
+ /**
+ * Tries to delete system package.
+ */
+ @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
+ public void deleteSystemPackageLIF(DeletePackageAction action, PackageSetting deletedPs,
+ @NonNull int[] allUserHandles, int flags, @Nullable PackageRemovedInfo outInfo,
+ boolean writeSettings, int defParseFlags, List<ScanPartition> dirsToScanAsSystem)
+ throws SystemDeleteException {
+ final boolean applyUserRestrictions = outInfo != null && (outInfo.mOrigUsers != null);
+ final AndroidPackage deletedPkg = deletedPs.pkg;
+ // Confirm if the system package has been updated
+ // An updated system app can be deleted. This will also have to restore
+ // the system pkg from system partition
+ // reader
+ final PackageSetting disabledPs = action.mDisabledPs;
+ if (DEBUG_REMOVE) {
+ Slog.d(TAG, "deleteSystemPackageLI: newPs=" + deletedPkg.getPackageName()
+ + " disabledPs=" + disabledPs);
+ }
+ Slog.d(TAG, "Deleting system pkg from data partition");
+
+ if (DEBUG_REMOVE) {
+ if (applyUserRestrictions) {
+ Slog.d(TAG, "Remembering install states:");
+ for (int userId : allUserHandles) {
+ final boolean finstalled = ArrayUtils.contains(outInfo.mOrigUsers, userId);
+ Slog.d(TAG, " u=" + userId + " inst=" + finstalled);
+ }
+ }
+ }
+
+ if (outInfo != null) {
+ // Delete the updated package
+ outInfo.mIsRemovedPackageSystemUpdate = true;
+ }
+
+ if (disabledPs.versionCode < deletedPs.versionCode) {
+ // Delete data for downgrades
+ flags &= ~PackageManager.DELETE_KEEP_DATA;
+ } else {
+ // Preserve data by setting flag
+ flags |= PackageManager.DELETE_KEEP_DATA;
+ }
+
+ mPm.deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles,
+ outInfo, writeSettings);
+
+ // writer
+ synchronized (mPm.mLock) {
+ // NOTE: The system package always needs to be enabled; even if it's for
+ // a compressed stub. If we don't, installing the system package fails
+ // during scan [scanning checks the disabled packages]. We will reverse
+ // this later, after we've "installed" the stub.
+ // Reinstate the old system package
+ enableSystemPackageLPw(disabledPs.pkg);
+ // Remove any native libraries from the upgraded package.
+ removeNativeBinariesLI(deletedPs);
+ }
+
+ // Install the system package
+ if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
+ try {
+ installPackageFromSystemLIF(disabledPs.getPathString(), allUserHandles,
+ outInfo == null ? null : outInfo.mOrigUsers, writeSettings, defParseFlags,
+ dirsToScanAsSystem);
+ } catch (PackageManagerException e) {
+ Slog.w(TAG, "Failed to restore system package:" + deletedPkg.getPackageName() + ": "
+ + e.getMessage());
+ // TODO(b/194319951): can we avoid this; throw would come from scan...
+ throw new SystemDeleteException(e);
+ } finally {
+ if (disabledPs.pkg.isStub()) {
+ // We've re-installed the stub; make sure it's disabled here. If package was
+ // originally enabled, we'll install the compressed version of the application
+ // and re-enable it afterward.
+ final PackageSetting stubPs = mPm.mSettings.getPackageLPr(
+ deletedPkg.getPackageName());
+ if (stubPs != null) {
+ int userId = action.mUser == null
+ ? UserHandle.USER_ALL : action.mUser.getIdentifier();
+ if (userId == UserHandle.USER_ALL) {
+ for (int aUserId : allUserHandles) {
+ stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, aUserId, "android");
+ }
+ } else if (userId >= UserHandle.USER_SYSTEM) {
+ stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, userId, "android");
+ }
+ }
+ }
+ }
+ }
+
+ private void removeNativeBinariesLI(PackageSetting ps) {
+ if (ps != null) {
+ NativeLibraryHelper.removeNativeBinariesLI(ps.legacyNativeLibraryPathString);
+ }
+ }
+
+ /**
+ * Installs a package that's already on the system partition.
+ */
+ @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
+ private void installPackageFromSystemLIF(@NonNull String codePathString,
+ @NonNull int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings,
+ int defParseFlags, List<ScanPartition> dirsToScanAsSystem)
+ throws PackageManagerException {
+ final File codePath = new File(codePathString);
+ @ParsingPackageUtils.ParseFlags int parseFlags =
+ defParseFlags
+ | ParsingPackageUtils.PARSE_MUST_BE_APK
+ | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
+ @PackageManagerService.ScanFlags int scanFlags = SCAN_AS_SYSTEM;
+ for (int i = dirsToScanAsSystem.size() - 1; i >= 0; i--) {
+ ScanPartition partition = dirsToScanAsSystem.get(i);
+ if (partition.containsFile(codePath)) {
+ scanFlags |= partition.scanFlag;
+ if (partition.containsPrivApp(codePath)) {
+ scanFlags |= SCAN_AS_PRIVILEGED;
+ }
+ break;
+ }
+ }
+
+ final AndroidPackage pkg =
+ mPm.scanPackageTracedLI(codePath, parseFlags, scanFlags, 0 /*currentTime*/, null);
+
+ PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(pkg.getPackageName());
+
+ try {
+ // update shared libraries for the newly re-installed system package
+ mPm.updateSharedLibrariesLocked(pkg, pkgSetting, null, null,
+ Collections.unmodifiableMap(mPm.mPackages));
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
+ }
+
+ mPm.prepareAppDataAfterInstallLIF(pkg);
+
+ // writer
+ synchronized (mPm.mLock) {
+ PackageSetting ps = mPm.mSettings.getPackageLPr(pkg.getPackageName());
+
+ final boolean applyUserRestrictions = origUserHandles != null;
+ if (applyUserRestrictions) {
+ boolean installedStateChanged = false;
+ if (DEBUG_REMOVE) {
+ Slog.d(TAG, "Propagating install state across reinstall");
+ }
+ for (int userId : allUserHandles) {
+ final boolean installed = ArrayUtils.contains(origUserHandles, userId);
+ if (DEBUG_REMOVE) {
+ Slog.d(TAG, " user " + userId + " => " + installed);
+ }
+ if (installed != ps.getInstalled(userId)) {
+ installedStateChanged = true;
+ }
+ ps.setInstalled(installed, userId);
+ if (installed) {
+ ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId);
+ }
+ }
+ // Regardless of writeSettings we need to ensure that this restriction
+ // state propagation is persisted
+ mPm.mSettings.writeAllUsersPackageRestrictionsLPr();
+ if (installedStateChanged) {
+ mPm.mSettings.writeKernelMappingLPr(ps);
+ }
+ }
+
+ // The method below will take care of removing obsolete permissions and granting
+ // install permissions.
+ mPm.mPermissionManager.onPackageInstalled(pkg,
+ PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT,
+ UserHandle.USER_ALL);
+ for (final int userId : allUserHandles) {
+ if (applyUserRestrictions) {
+ mPm.mSettings.writePermissionStateForUserLPr(userId, false);
+ }
+ }
+
+ // can downgrade to reader here
+ if (writeSettings) {
+ mPm.writeSettingsLPrTEMP();
+ }
+ }
+ }
+
+ /**
+ * Adds a new package to the internal data structures during platform initialization.
+ * <p>After adding, the package is known to the system and available for querying.
+ * <p>For packages located on the device ROM [eg. packages located in /system, /vendor,
+ * etc...], additional checks are performed. Basic verification [such as ensuring
+ * matching signatures, checking version codes, etc...] occurs if the package is
+ * identical to a previously known package. If the package fails a signature check,
+ * the version installed on /data will be removed. If the version of the new package
+ * is less than or equal than the version on /data, it will be ignored.
+ * <p>Regardless of the package location, the results are applied to the internal
+ * structures and the package is made available to the rest of the system.
+ * <p>NOTE: The return value should be removed. It's the passed in package object.
+ */
+ @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
+ public AndroidPackage addForInitLI(ParsedPackage parsedPackage,
+ @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+ @Nullable UserHandle user, AndroidPackage platformPackage, boolean isUpgrade,
+ boolean isPreNMR1Upgrade) throws PackageManagerException {
+ final boolean scanSystemPartition =
+ (parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
+ final String renamedPkgName;
+ final PackageSetting disabledPkgSetting;
+ final boolean isSystemPkgUpdated;
+ final boolean pkgAlreadyExists;
+ PackageSetting pkgSetting;
+
+ synchronized (mPm.mLock) {
+ renamedPkgName = mPm.mSettings.getRenamedPackageLPr(parsedPackage.getRealPackage());
+ final String realPkgName = PackageManagerService.getRealPackageName(parsedPackage,
+ renamedPkgName);
+ if (realPkgName != null) {
+ PackageManagerService.ensurePackageRenamed(parsedPackage, renamedPkgName);
+ }
+ final PackageSetting originalPkgSetting = mPm.getOriginalPackageLocked(parsedPackage,
+ renamedPkgName);
+ final PackageSetting installedPkgSetting = mPm.mSettings.getPackageLPr(
+ parsedPackage.getPackageName());
+ pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
+ pkgAlreadyExists = pkgSetting != null;
+ final String disabledPkgName = pkgAlreadyExists
+ ? pkgSetting.name : parsedPackage.getPackageName();
+ if (scanSystemPartition && !pkgAlreadyExists
+ && mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName) != null) {
+ // The updated-package data for /system apk remains inconsistently
+ // after the package data for /data apk is lost accidentally.
+ // To recover it, enable /system apk and install it as non-updated system app.
+ Slog.w(TAG, "Inconsistent package setting of updated system app for "
+ + disabledPkgName + ". To recover it, enable the system app"
+ + "and install it as non-updated system app.");
+ mPm.mSettings.removeDisabledSystemPackageLPw(disabledPkgName);
+ }
+ disabledPkgSetting = mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName);
+ isSystemPkgUpdated = disabledPkgSetting != null;
+
+ if (DEBUG_INSTALL && isSystemPkgUpdated) {
+ Slog.d(TAG, "updatedPkg = " + disabledPkgSetting);
+ }
+
+ final SharedUserSetting sharedUserSetting = (parsedPackage.getSharedUserId() != null)
+ ? mPm.mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
+ 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
+ : null;
+ if (DEBUG_PACKAGE_SCANNING
+ && (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0
+ && sharedUserSetting != null) {
+ Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
+ + " (uid=" + sharedUserSetting.userId + "):"
+ + " packages=" + sharedUserSetting.packages);
+ }
+
+ if (scanSystemPartition) {
+ if (isSystemPkgUpdated) {
+ // we're updating the disabled package, so, scan it as the package setting
+ boolean isPlatformPackage = platformPackage != null
+ && Objects.equals(platformPackage.getPackageName(),
+ parsedPackage.getPackageName());
+ final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
+ null, disabledPkgSetting /* pkgSetting */,
+ null /* disabledPkgSetting */, null /* originalPkgSetting */,
+ null, parseFlags, scanFlags, isPlatformPackage, user, null);
+ PackageManagerService.applyPolicy(parsedPackage, scanFlags,
+ platformPackage, true);
+ final ScanResult scanResult =
+ mPm.scanPackageOnlyLI(request, mPm.mInjector,
+ mPm.mFactoryTest, -1L);
+ if (scanResult.mExistingSettingCopied
+ && scanResult.mRequest.mPkgSetting != null) {
+ scanResult.mRequest.mPkgSetting.updateFrom(scanResult.mPkgSetting);
+ }
+ }
+ }
+ }
+
+ final boolean newPkgChangedPaths = pkgAlreadyExists
+ && !pkgSetting.getPathString().equals(parsedPackage.getPath());
+ final boolean newPkgVersionGreater =
+ pkgAlreadyExists && parsedPackage.getLongVersionCode() > pkgSetting.versionCode;
+ final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
+ && newPkgChangedPaths && newPkgVersionGreater;
+ if (isSystemPkgBetter) {
+ // The version of the application on /system is greater than the version on
+ // /data. Switch back to the application on /system.
+ // It's safe to assume the application on /system will correctly scan. If not,
+ // there won't be a working copy of the application.
+ synchronized (mPm.mLock) {
+ // just remove the loaded entries from package lists
+ mPm.mPackages.remove(pkgSetting.name);
+ }
+
+ logCriticalInfo(Log.WARN,
+ "System package updated;"
+ + " name: " + pkgSetting.name
+ + "; " + pkgSetting.versionCode + " --> "
+ + parsedPackage.getLongVersionCode()
+ + "; " + pkgSetting.getPathString()
+ + " --> " + parsedPackage.getPath());
+
+ final InstallArgs args = mPm.createInstallArgsForExisting(
+ pkgSetting.getPathString(), getAppDexInstructionSets(
+ pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
+ args.cleanUpResourcesLI();
+ synchronized (mPm.mLock) {
+ mPm.mSettings.enableSystemPackageLPw(pkgSetting.name);
+ }
+ }
+
+ // The version of the application on the /system partition is less than or
+ // equal to the version on the /data partition. Throw an exception and use
+ // the application already installed on the /data partition.
+ if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) {
+ // In the case of a skipped package, commitReconciledScanResultLocked is not called to
+ // add the object to the "live" data structures, so this is the final mutation step
+ // for the package. Which means it needs to be finalized here to cache derived fields.
+ // This is relevant for cases where the disabled system package is used for flags or
+ // other metadata.
+ parsedPackage.hideAsFinal();
+ throw new PackageManagerException(Log.WARN, "Package " + parsedPackage.getPackageName()
+ + " at " + parsedPackage.getPath() + " ignored: updated version "
+ + (pkgAlreadyExists ? String.valueOf(pkgSetting.versionCode) : "unknown")
+ + " better than this " + parsedPackage.getLongVersionCode());
+ }
+
+ // Verify certificates against what was last scanned. Force re-collecting certificate in two
+ // special cases:
+ // 1) when scanning system, force re-collect only if system is upgrading.
+ // 2) when scannning /data, force re-collect only if the app is privileged (updated from
+ // preinstall, or treated as privileged, e.g. due to shared user ID).
+ final boolean forceCollect = scanSystemPartition ? isUpgrade
+ : PackageManagerServiceUtils.isApkVerificationForced(pkgSetting);
+ if (DEBUG_VERIFY && forceCollect) {
+ Slog.d(TAG, "Force collect certificate of " + parsedPackage.getPackageName());
+ }
+
+ // Full APK verification can be skipped during certificate collection, only if the file is
+ // in verified partition, or can be verified on access (when apk verity is enabled). In both
+ // cases, only data in Signing Block is verified instead of the whole file.
+ // TODO(b/136132412): skip for Incremental installation
+ final boolean skipVerify = scanSystemPartition
+ || (forceCollect && canSkipForcedPackageVerification(parsedPackage));
+ collectCertificatesLI(pkgSetting, parsedPackage, forceCollect, skipVerify,
+ isPreNMR1Upgrade);
+
+ // Reset profile if the application version is changed
+ maybeClearProfilesForUpgradesLI(pkgSetting, parsedPackage);
+
+ /*
+ * A new system app appeared, but we already had a non-system one of the
+ * same name installed earlier.
+ */
+ boolean shouldHideSystemApp = false;
+ // A new application appeared on /system, but, we already have a copy of
+ // the application installed on /data.
+ if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
+ && !pkgSetting.isSystem()) {
+
+ if (!parsedPackage.getSigningDetails()
+ .checkCapability(pkgSetting.signatures.mSigningDetails,
+ SigningDetails.CertCapabilities.INSTALLED_DATA)
+ && !pkgSetting.signatures.mSigningDetails.checkCapability(
+ parsedPackage.getSigningDetails(),
+ SigningDetails.CertCapabilities.ROLLBACK)) {
+ logCriticalInfo(Log.WARN,
+ "System package signature mismatch;"
+ + " name: " + pkgSetting.name);
+ try (@SuppressWarnings("unused") PackageFreezer freezer = mPm.freezePackage(
+ parsedPackage.getPackageName(),
+ "scanPackageInternalLI")) {
+ mPm.deletePackageLIF(parsedPackage.getPackageName(), null, true,
+ mPm.mUserManager.getUserIds(), 0, null, false);
+ }
+ pkgSetting = null;
+ } else if (newPkgVersionGreater) {
+ // The application on /system is newer than the application on /data.
+ // Simply remove the application on /data [keeping application data]
+ // and replace it with the version on /system.
+ logCriticalInfo(Log.WARN,
+ "System package enabled;"
+ + " name: " + pkgSetting.name
+ + "; " + pkgSetting.versionCode + " --> "
+ + parsedPackage.getLongVersionCode()
+ + "; " + pkgSetting.getPathString() + " --> "
+ + parsedPackage.getPath());
+ InstallArgs args = mPm.createInstallArgsForExisting(
+ pkgSetting.getPathString(), getAppDexInstructionSets(
+ pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
+ synchronized (mPm.mInstallLock) {
+ args.cleanUpResourcesLI();
+ }
+ } else {
+ // The application on /system is older than the application on /data. Hide
+ // the application on /system and the version on /data will be scanned later
+ // and re-added like an update.
+ shouldHideSystemApp = true;
+ logCriticalInfo(Log.INFO,
+ "System package disabled;"
+ + " name: " + pkgSetting.name
+ + "; old: " + pkgSetting.getPathString() + " @ "
+ + pkgSetting.versionCode
+ + "; new: " + parsedPackage.getPath() + " @ "
+ + parsedPackage.getPath());
+ }
+ }
+
+ final ScanResult scanResult = mPm.scanPackageNewLI(parsedPackage, parseFlags, scanFlags
+ | SCAN_UPDATE_SIGNATURE, currentTime, user, null);
+ if (scanResult.mSuccess) {
+ synchronized (mPm.mLock) {
+ boolean appIdCreated = false;
+ try {
+ final String pkgName = scanResult.mPkgSetting.name;
+ final Map<String, ReconciledPackage> reconcileResult =
+ mPm.reconcilePackagesLocked(
+ new ReconcileRequest(
+ Collections.singletonMap(pkgName, scanResult),
+ mPm.mSharedLibraries,
+ mPm.mPackages,
+ Collections.singletonMap(
+ pkgName,
+ mPm.getSettingsVersionForPackage(parsedPackage)),
+ Collections.singletonMap(pkgName,
+ mPm.getSharedLibLatestVersionSetting(scanResult))),
+ mPm.mSettings.getKeySetManagerService(), mPm.mInjector);
+ appIdCreated = mPm.optimisticallyRegisterAppId(scanResult);
+ mPm.commitReconciledScanResultLocked(
+ reconcileResult.get(pkgName), mPm.mUserManager.getUserIds());
+ } catch (PackageManagerException e) {
+ if (appIdCreated) {
+ mPm.cleanUpAppIdCreation(scanResult);
+ }
+ throw e;
+ }
+ }
+ }
+
+ if (shouldHideSystemApp) {
+ synchronized (mPm.mLock) {
+ mPm.mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true);
+ }
+ }
+ if (mPm.mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
+ if (pkgSetting != null && pkgSetting.isPackageLoading()) {
+ // Continue monitoring loading progress of active incremental packages
+ final IncrementalStatesCallback incrementalStatesCallback =
+ new IncrementalStatesCallback(parsedPackage.getPackageName(), mPm);
+ pkgSetting.setIncrementalStatesCallback(incrementalStatesCallback);
+ mPm.mIncrementalManager.registerLoadingProgressCallback(parsedPackage.getPath(),
+ new IncrementalProgressListener(parsedPackage.getPackageName(), mPm));
+ }
+ }
+ return scanResult.mPkgSetting.pkg;
+ }
+
+ /**
+ * Returns if forced apk verification can be skipped for the whole package, including splits.
+ */
+ private boolean canSkipForcedPackageVerification(AndroidPackage pkg) {
+ if (!canSkipForcedApkVerification(pkg.getBaseApkPath())) {
+ return false;
+ }
+ // TODO: Allow base and splits to be verified individually.
+ String[] splitCodePaths = pkg.getSplitCodePaths();
+ if (!ArrayUtils.isEmpty(splitCodePaths)) {
+ for (int i = 0; i < splitCodePaths.length; i++) {
+ if (!canSkipForcedApkVerification(splitCodePaths[i])) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns if forced apk verification can be skipped, depending on current FSVerity setup and
+ * whether the apk contains signed root hash. Note that the signer's certificate still needs to
+ * match one in a trusted source, and should be done separately.
+ */
+ private boolean canSkipForcedApkVerification(String apkPath) {
+ if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) {
+ return VerityUtils.hasFsverity(apkPath);
+ }
+
+ try {
+ final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
+ if (rootHashObserved == null) {
+ return false; // APK does not contain Merkle tree root hash.
+ }
+ synchronized (mPm.mInstallLock) {
+ // Returns whether the observed root hash matches what kernel has.
+ mPm.mInstaller.assertFsverityRootHashMatches(apkPath, rootHashObserved);
+ return true;
+ }
+ } catch (Installer.InstallerException | IOException | DigestException
+ | NoSuchAlgorithmException e) {
+ Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e);
+ }
+ return false;
+ }
+
+ private void collectCertificatesLI(PackageSetting ps, ParsedPackage parsedPackage,
+ boolean forceCollect, boolean skipVerify, boolean mIsPreNMR1Upgrade)
+ throws PackageManagerException {
+ // When upgrading from pre-N MR1, verify the package time stamp using the package
+ // directory and not the APK file.
+ final long lastModifiedTime = mIsPreNMR1Upgrade
+ ? new File(parsedPackage.getPath()).lastModified()
+ : getLastModifiedTime(parsedPackage);
+ final Settings.VersionInfo settingsVersionForPackage =
+ mPm.getSettingsVersionForPackage(parsedPackage);
+ if (ps != null && !forceCollect
+ && ps.getPathString().equals(parsedPackage.getPath())
+ && ps.timeStamp == lastModifiedTime
+ && !PackageManagerService.isCompatSignatureUpdateNeeded(settingsVersionForPackage)
+ && !PackageManagerService.isRecoverSignatureUpdateNeeded(
+ settingsVersionForPackage)) {
+ if (ps.signatures.mSigningDetails.getSignatures() != null
+ && ps.signatures.mSigningDetails.getSignatures().length != 0
+ && ps.signatures.mSigningDetails.getSignatureSchemeVersion()
+ != SigningDetails.SignatureSchemeVersion.UNKNOWN) {
+ // Optimization: reuse the existing cached signing data
+ // if the package appears to be unchanged.
+ parsedPackage.setSigningDetails(
+ new SigningDetails(ps.signatures.mSigningDetails));
+ return;
+ }
+
+ Slog.w(TAG, "PackageSetting for " + ps.name
+ + " is missing signatures. Collecting certs again to recover them.");
+ } else {
+ Slog.i(TAG, parsedPackage.getPath() + " changed; collecting certs"
+ + (forceCollect ? " (forced)" : ""));
+ }
+
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
+ input, parsedPackage, skipVerify);
+ if (result.isError()) {
+ throw new PackageManagerException(
+ result.getErrorCode(), result.getErrorMessage(), result.getException());
+ }
+ parsedPackage.setSigningDetails(result.getResult());
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ /**
+ * Clear the package profile if this was an upgrade and the package
+ * version was updated.
+ */
+ private void maybeClearProfilesForUpgradesLI(
+ @Nullable PackageSetting originalPkgSetting,
+ @NonNull AndroidPackage pkg) {
+ if (originalPkgSetting == null || !mPm.isDeviceUpgrading()) {
+ return;
+ }
+ if (originalPkgSetting.versionCode == pkg.getVersionCode()) {
+ return;
+ }
+
+ mPm.clearAppProfilesLIF(pkg);
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG, originalPkgSetting.name
+ + " clear profile due to version change "
+ + originalPkgSetting.versionCode + " != "
+ + pkg.getVersionCode());
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/InstallParams.java b/services/core/java/com/android/server/pm/InstallParams.java
index 45ce3980..934775a 100644
--- a/services/core/java/com/android/server/pm/InstallParams.java
+++ b/services/core/java/com/android/server/pm/InstallParams.java
@@ -163,14 +163,12 @@
final int mDataLoaderType;
final long mRequiredInstalledVersionCode;
final PackageLite mPackageLite;
- @NonNull final PackageManagerService mPm;
InstallParams(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer,
int installFlags, InstallSource installSource, String volumeUuid,
UserHandle user, String packageAbiOverride, PackageLite packageLite,
PackageManagerService pm) {
- super(user);
- mPm = pm;
+ super(user, pm);
mOriginInfo = originInfo;
mMoveInfo = moveInfo;
mObserver = observer;
@@ -195,8 +193,7 @@
PackageInstaller.SessionParams sessionParams, InstallSource installSource,
UserHandle user, SigningDetails signingDetails, int installerUid,
PackageLite packageLite, PackageManagerService pm) {
- super(user);
- mPm = pm;
+ super(user, pm);
mOriginInfo = OriginInfo.fromStagedFile(stagedDir);
mMoveInfo = null;
mInstallReason = fixUpInstallReason(
@@ -373,7 +370,7 @@
// state can change within this delay and hence we need to re-verify certain conditions.
boolean isStaged = (mInstallFlags & INSTALL_STAGED) != 0;
if (isStaged) {
- Pair<Integer, String> ret = mPm.verifyReplacingVersionCode(
+ Pair<Integer, String> ret = verifyReplacingVersionCode(
pkgLite, mRequiredInstalledVersionCode, mInstallFlags);
mRet = ret.first;
if (mRet != INSTALL_SUCCEEDED) {
@@ -622,7 +619,7 @@
Map<String, ReconciledPackage> reconciledPackages;
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
- reconciledPackages = PackageManagerService.reconcilePackagesLocked(
+ reconciledPackages = mPm.reconcilePackagesLocked(
reconcileRequest, mPm.mSettings.getKeySetManagerService(),
mPm.mInjector);
} catch (ReconcileFailure e) {
@@ -2075,7 +2072,7 @@
throws PackageManagerException {
final Message msg = mPm.mHandler.obtainMessage(INIT_COPY);
final MultiPackageInstallParams params =
- new MultiPackageInstallParams(this, children);
+ new MultiPackageInstallParams(this, children, mPm);
params.setTraceMethod("installStageMultiPackage")
.setTraceCookie(System.identityHashCode(params));
msg.obj = params;
@@ -2107,9 +2104,10 @@
private final List<InstallParams> mChildParams;
private final Map<InstallArgs, Integer> mCurrentState;
- MultiPackageInstallParams(InstallParams parent, List<InstallParams> childParams)
+ MultiPackageInstallParams(InstallParams parent, List<InstallParams> childParams,
+ PackageManagerService pm)
throws PackageManagerException {
- super(parent.getUser());
+ super(parent.getUser(), pm);
if (childParams.size() == 0) {
throw new PackageManagerException("No child sessions found!");
}
@@ -2159,4 +2157,6 @@
installRequests);
}
}
+
+
}
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
new file mode 100644
index 0000000..44a973e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -0,0 +1,783 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
+import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+
+import static com.android.server.pm.PackageManagerService.CHECK_PENDING_INTEGRITY_VERIFICATION;
+import static com.android.server.pm.PackageManagerService.CHECK_PENDING_VERIFICATION;
+import static com.android.server.pm.PackageManagerService.DEBUG_BACKUP;
+import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
+import static com.android.server.pm.PackageManagerService.DEFAULT_VERIFICATION_RESPONSE;
+import static com.android.server.pm.PackageManagerService.DEFERRED_NO_KILL_INSTALL_OBSERVER;
+import static com.android.server.pm.PackageManagerService.DEFERRED_NO_KILL_POST_DELETE;
+import static com.android.server.pm.PackageManagerService.DOMAIN_VERIFICATION;
+import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY;
+import static com.android.server.pm.PackageManagerService.ENABLE_ROLLBACK_STATUS;
+import static com.android.server.pm.PackageManagerService.ENABLE_ROLLBACK_TIMEOUT;
+import static com.android.server.pm.PackageManagerService.INIT_COPY;
+import static com.android.server.pm.PackageManagerService.INSTANT_APP_RESOLUTION_PHASE_TWO;
+import static com.android.server.pm.PackageManagerService.INTEGRITY_VERIFICATION_COMPLETE;
+import static com.android.server.pm.PackageManagerService.PACKAGE_VERIFIED;
+import static com.android.server.pm.PackageManagerService.POST_INSTALL;
+import static com.android.server.pm.PackageManagerService.SEND_PENDING_BROADCAST;
+import static com.android.server.pm.PackageManagerService.SNAPSHOT_UNCORK;
+import static com.android.server.pm.PackageManagerService.TAG;
+import static com.android.server.pm.PackageManagerService.TRACE_SNAPSHOTS;
+import static com.android.server.pm.PackageManagerService.WRITE_PACKAGE_LIST;
+import static com.android.server.pm.PackageManagerService.WRITE_PACKAGE_RESTRICTIONS;
+import static com.android.server.pm.PackageManagerService.WRITE_SETTINGS;
+
+import android.content.Intent;
+import android.content.pm.IPackageInstallObserver2;
+import android.content.pm.InstantAppRequest;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.stats.storage.StorageEnums;
+import android.util.ArrayMap;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.EventLogTags;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import dalvik.system.VMRuntime;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
+/**
+ * Part of PackageManagerService that handles events.
+ */
+public class PackageHandler extends Handler {
+ final PackageManagerService mPm;
+ PackageHandler(Looper looper, PackageManagerService pm) {
+ super(looper);
+ mPm = pm;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ try {
+ doHandleMessage(msg);
+ } finally {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ }
+ }
+
+ void doHandleMessage(Message msg) {
+ switch (msg.what) {
+ case INIT_COPY: {
+ HandlerParams params = (HandlerParams) msg.obj;
+ if (params != null) {
+ if (DEBUG_INSTALL) Slog.i(TAG, "init_copy: " + params);
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
+ System.identityHashCode(params));
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
+ params.startCopy();
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ break;
+ }
+ case SEND_PENDING_BROADCAST: {
+ String[] packages;
+ ArrayList<String>[] components;
+ int size = 0;
+ int[] uids;
+ Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
+ synchronized (mPm.mLock) {
+ size = mPm.mPendingBroadcasts.size();
+ if (size <= 0) {
+ // Nothing to be done. Just return
+ return;
+ }
+ packages = new String[size];
+ components = new ArrayList[size];
+ uids = new int[size];
+ int i = 0; // filling out the above arrays
+
+ for (int n = 0; n < mPm.mPendingBroadcasts.userIdCount(); n++) {
+ final int packageUserId = mPm.mPendingBroadcasts.userIdAt(n);
+ final ArrayMap<String, ArrayList<String>> componentsToBroadcast =
+ mPm.mPendingBroadcasts.packagesForUserId(packageUserId);
+ final int numComponents = componentsToBroadcast.size();
+ for (int index = 0; i < size && index < numComponents; index++) {
+ packages[i] = componentsToBroadcast.keyAt(index);
+ components[i] = componentsToBroadcast.valueAt(index);
+ final PackageSetting ps = mPm.mSettings.getPackageLPr(packages[i]);
+ uids[i] = (ps != null)
+ ? UserHandle.getUid(packageUserId, ps.appId)
+ : -1;
+ i++;
+ }
+ }
+ size = i;
+ mPm.mPendingBroadcasts.clear();
+ }
+ // Send broadcasts
+ for (int i = 0; i < size; i++) {
+ mPm.sendPackageChangedBroadcast(packages[i], true /* dontKillApp */,
+ components[i], uids[i], null /* reason */);
+ }
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ break;
+ }
+ case POST_INSTALL: {
+ if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);
+
+ PackageManagerService.PostInstallData data = mPm.mRunningInstalls.get(msg.arg1);
+ final boolean didRestore = (msg.arg2 != 0);
+ mPm.mRunningInstalls.delete(msg.arg1);
+
+ if (data != null && data.res.mFreezer != null) {
+ data.res.mFreezer.close();
+ }
+
+ if (data != null && data.mPostInstallRunnable != null) {
+ data.mPostInstallRunnable.run();
+ } else if (data != null && data.args != null) {
+ InstallArgs args = data.args;
+ PackageInstalledInfo parentRes = data.res;
+
+ final boolean killApp = (args.mInstallFlags
+ & PackageManager.INSTALL_DONT_KILL_APP) == 0;
+ final boolean virtualPreload = ((args.mInstallFlags
+ & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
+
+ handlePackagePostInstall(parentRes, killApp, virtualPreload,
+ didRestore, args.mInstallSource.installerPackageName,
+ args.mObserver, args.mDataLoaderType);
+
+ // Log tracing if needed
+ if (args.mTraceMethod != null) {
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, args.mTraceMethod,
+ args.mTraceCookie);
+ }
+ } else if (DEBUG_INSTALL) {
+ // No post-install when we run restore from installExistingPackageForUser
+ Slog.i(TAG, "Nothing to do for post-install token " + msg.arg1);
+ }
+
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", msg.arg1);
+ } break;
+ case DEFERRED_NO_KILL_POST_DELETE: {
+ synchronized (mPm.mInstallLock) {
+ InstallArgs args = (InstallArgs) msg.obj;
+ if (args != null) {
+ args.doPostDeleteLI(true);
+ }
+ }
+ } break;
+ case DEFERRED_NO_KILL_INSTALL_OBSERVER: {
+ String packageName = (String) msg.obj;
+ if (packageName != null) {
+ mPm.notifyInstallObserver(packageName);
+ }
+ } break;
+ case WRITE_SETTINGS: {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
+ synchronized (mPm.mLock) {
+ removeMessages(WRITE_SETTINGS);
+ removeMessages(WRITE_PACKAGE_RESTRICTIONS);
+ mPm.writeSettingsLPrTEMP();
+ mPm.mDirtyUsers.clear();
+ }
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ } break;
+ case WRITE_PACKAGE_RESTRICTIONS: {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
+ synchronized (mPm.mLock) {
+ removeMessages(WRITE_PACKAGE_RESTRICTIONS);
+ for (int userId : mPm.mDirtyUsers) {
+ mPm.mSettings.writePackageRestrictionsLPr(userId);
+ }
+ mPm.mDirtyUsers.clear();
+ }
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ } break;
+ case WRITE_PACKAGE_LIST: {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
+ synchronized (mPm.mLock) {
+ removeMessages(WRITE_PACKAGE_LIST);
+ mPm.mSettings.writePackageListLPr(msg.arg1);
+ }
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ } break;
+ case CHECK_PENDING_VERIFICATION: {
+ final int verificationId = msg.arg1;
+ final PackageVerificationState state = mPm.mPendingVerification.get(verificationId);
+
+ if ((state != null) && !state.isVerificationComplete()
+ && !state.timeoutExtended()) {
+ final VerificationParams params = state.getVerificationParams();
+ final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
+
+ String errorMsg = "Verification timed out for " + originUri;
+ Slog.i(TAG, errorMsg);
+
+ final UserHandle user = params.getUser();
+ if (getDefaultVerificationResponse(user)
+ == PackageManager.VERIFICATION_ALLOW) {
+ Slog.i(TAG, "Continuing with installation of " + originUri);
+ state.setVerifierResponse(Binder.getCallingUid(),
+ PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
+ mPm.broadcastPackageVerified(verificationId, originUri,
+ PackageManager.VERIFICATION_ALLOW, null, params.mDataLoaderType,
+ user);
+ } else {
+ mPm.broadcastPackageVerified(verificationId, originUri,
+ PackageManager.VERIFICATION_REJECT, null,
+ params.mDataLoaderType, user);
+ params.setReturnCode(
+ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, errorMsg);
+ state.setVerifierResponse(Binder.getCallingUid(),
+ PackageManager.VERIFICATION_REJECT);
+ }
+
+ if (state.areAllVerificationsComplete()) {
+ mPm.mPendingVerification.remove(verificationId);
+ }
+
+ Trace.asyncTraceEnd(
+ TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
+
+ params.handleVerificationFinished();
+
+ }
+ break;
+ }
+ case CHECK_PENDING_INTEGRITY_VERIFICATION: {
+ final int verificationId = msg.arg1;
+ final PackageVerificationState state = mPm.mPendingVerification.get(verificationId);
+
+ if (state != null && !state.isIntegrityVerificationComplete()) {
+ final VerificationParams params = state.getVerificationParams();
+ final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
+
+ String errorMsg = "Integrity verification timed out for " + originUri;
+ Slog.i(TAG, errorMsg);
+
+ state.setIntegrityVerificationResult(
+ getDefaultIntegrityVerificationResponse());
+
+ if (getDefaultIntegrityVerificationResponse()
+ == PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW) {
+ Slog.i(TAG, "Integrity check times out, continuing with " + originUri);
+ } else {
+ params.setReturnCode(
+ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
+ errorMsg);
+ }
+
+ if (state.areAllVerificationsComplete()) {
+ mPm.mPendingVerification.remove(verificationId);
+ }
+
+ Trace.asyncTraceEnd(
+ TRACE_TAG_PACKAGE_MANAGER,
+ "integrity_verification",
+ verificationId);
+
+ params.handleIntegrityVerificationFinished();
+ }
+ break;
+ }
+ case PACKAGE_VERIFIED: {
+ final int verificationId = msg.arg1;
+
+ final PackageVerificationState state = mPm.mPendingVerification.get(verificationId);
+ if (state == null) {
+ Slog.w(TAG, "Verification with id " + verificationId
+ + " not found."
+ + " It may be invalid or overridden by integrity verification");
+ break;
+ }
+
+ final PackageVerificationResponse response = (PackageVerificationResponse) msg.obj;
+
+ state.setVerifierResponse(response.callerUid, response.code);
+
+ if (state.isVerificationComplete()) {
+ final VerificationParams params = state.getVerificationParams();
+ final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
+
+ if (state.isInstallAllowed()) {
+ mPm.broadcastPackageVerified(verificationId, originUri,
+ response.code, null, params.mDataLoaderType, params.getUser());
+ } else {
+ params.setReturnCode(
+ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
+ "Install not allowed");
+ }
+
+ if (state.areAllVerificationsComplete()) {
+ mPm.mPendingVerification.remove(verificationId);
+ }
+
+ Trace.asyncTraceEnd(
+ TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
+
+ params.handleVerificationFinished();
+ }
+
+ break;
+ }
+ case INTEGRITY_VERIFICATION_COMPLETE: {
+ final int verificationId = msg.arg1;
+
+ final PackageVerificationState state = mPm.mPendingVerification.get(verificationId);
+ if (state == null) {
+ Slog.w(TAG, "Integrity verification with id " + verificationId
+ + " not found. It may be invalid or overridden by verifier");
+ break;
+ }
+
+ final int response = (Integer) msg.obj;
+ final VerificationParams params = state.getVerificationParams();
+ final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
+
+ state.setIntegrityVerificationResult(response);
+
+ if (response == PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW) {
+ Slog.i(TAG, "Integrity check passed for " + originUri);
+ } else {
+ params.setReturnCode(
+ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
+ "Integrity check failed for " + originUri);
+ }
+
+ if (state.areAllVerificationsComplete()) {
+ mPm.mPendingVerification.remove(verificationId);
+ }
+
+ Trace.asyncTraceEnd(
+ TRACE_TAG_PACKAGE_MANAGER,
+ "integrity_verification",
+ verificationId);
+
+ params.handleIntegrityVerificationFinished();
+ break;
+ }
+ case INSTANT_APP_RESOLUTION_PHASE_TWO: {
+ InstantAppResolver.doInstantAppResolutionPhaseTwo(mPm.mContext,
+ mPm.mInstantAppResolverConnection,
+ (InstantAppRequest) msg.obj,
+ mPm.mInstantAppInstallerActivity,
+ mPm.mHandler);
+ break;
+ }
+ case ENABLE_ROLLBACK_STATUS: {
+ final int enableRollbackToken = msg.arg1;
+ final int enableRollbackCode = msg.arg2;
+ final VerificationParams params =
+ mPm.mPendingEnableRollback.get(enableRollbackToken);
+ if (params == null) {
+ Slog.w(TAG, "Invalid rollback enabled token "
+ + enableRollbackToken + " received");
+ break;
+ }
+
+ mPm.mPendingEnableRollback.remove(enableRollbackToken);
+
+ if (enableRollbackCode != PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED) {
+ final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
+ Slog.w(TAG, "Failed to enable rollback for " + originUri);
+ Slog.w(TAG, "Continuing with installation of " + originUri);
+ }
+
+ Trace.asyncTraceEnd(
+ TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken);
+
+ params.handleRollbackEnabled();
+ break;
+ }
+ case ENABLE_ROLLBACK_TIMEOUT: {
+ final int enableRollbackToken = msg.arg1;
+ final int sessionId = msg.arg2;
+ final VerificationParams params =
+ mPm.mPendingEnableRollback.get(enableRollbackToken);
+ if (params != null) {
+ final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
+
+ Slog.w(TAG, "Enable rollback timed out for " + originUri);
+ mPm.mPendingEnableRollback.remove(enableRollbackToken);
+
+ Slog.w(TAG, "Continuing with installation of " + originUri);
+ Trace.asyncTraceEnd(
+ TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken);
+ params.handleRollbackEnabled();
+ Intent rollbackTimeoutIntent = new Intent(
+ Intent.ACTION_CANCEL_ENABLE_ROLLBACK);
+ rollbackTimeoutIntent.putExtra(
+ PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_SESSION_ID,
+ sessionId);
+ rollbackTimeoutIntent.addFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mPm.mContext.sendBroadcastAsUser(rollbackTimeoutIntent, UserHandle.SYSTEM,
+ android.Manifest.permission.PACKAGE_ROLLBACK_AGENT);
+ }
+ break;
+ }
+ case DOMAIN_VERIFICATION: {
+ int messageCode = msg.arg1;
+ Object object = msg.obj;
+ mPm.mDomainVerificationManager.runMessage(messageCode, object);
+ break;
+ }
+ case SNAPSHOT_UNCORK: {
+ int corking = mPm.sSnapshotCorked.decrementAndGet();
+ if (TRACE_SNAPSHOTS && corking == 0) {
+ Log.e(TAG, "snapshot: corking goes to zero in message handler");
+ }
+ break;
+ }
+ }
+ }
+
+ private void handlePackagePostInstall(PackageInstalledInfo res, boolean killApp,
+ boolean virtualPreload, boolean launchedForRestore, String installerPackage,
+ IPackageInstallObserver2 installObserver, int dataLoaderType) {
+ boolean succeeded = res.mReturnCode == PackageManager.INSTALL_SUCCEEDED;
+ final boolean update = res.mRemovedInfo != null && res.mRemovedInfo.mRemovedPackage != null;
+ final String packageName = res.mName;
+ final PackageSetting pkgSetting = succeeded ? mPm.getPackageSetting(packageName) : null;
+ final boolean removedBeforeUpdate = (pkgSetting == null)
+ || (pkgSetting.isSystem() && !pkgSetting.getPathString().equals(
+ res.mPkg.getPath()));
+ if (succeeded && removedBeforeUpdate) {
+ Slog.e(TAG, packageName + " was removed before handlePackagePostInstall "
+ + "could be executed");
+ res.mReturnCode = INSTALL_FAILED_PACKAGE_CHANGED;
+ res.mReturnMsg = "Package was removed before install could complete.";
+
+ // Remove the update failed package's older resources safely now
+ InstallArgs args = res.mRemovedInfo != null ? res.mRemovedInfo.mArgs : null;
+ if (args != null) {
+ synchronized (mPm.mInstallLock) {
+ args.doPostDeleteLI(true);
+ }
+ }
+ mPm.notifyInstallObserver(res, installObserver);
+ return;
+ }
+
+ if (succeeded) {
+ // Clear the uid cache after we installed a new package.
+ mPm.mPerUidReadTimeoutsCache = null;
+
+ // Send the removed broadcasts
+ if (res.mRemovedInfo != null) {
+ res.mRemovedInfo.sendPackageRemovedBroadcasts(killApp, false /*removedBySystem*/);
+ }
+
+ final String installerPackageName =
+ res.mInstallerPackageName != null
+ ? res.mInstallerPackageName
+ : res.mRemovedInfo != null
+ ? res.mRemovedInfo.mInstallerPackageName
+ : null;
+
+ synchronized (mPm.mLock) {
+ mPm.mInstantAppRegistry.onPackageInstalledLPw(res.mPkg, res.mNewUsers);
+ }
+
+ // Determine the set of users who are adding this package for
+ // the first time vs. those who are seeing an update.
+ int[] firstUserIds = EMPTY_INT_ARRAY;
+ int[] firstInstantUserIds = EMPTY_INT_ARRAY;
+ int[] updateUserIds = EMPTY_INT_ARRAY;
+ int[] instantUserIds = EMPTY_INT_ARRAY;
+ final boolean allNewUsers = res.mOrigUsers == null || res.mOrigUsers.length == 0;
+ for (int newUser : res.mNewUsers) {
+ final boolean isInstantApp = pkgSetting.getInstantApp(newUser);
+ if (allNewUsers) {
+ if (isInstantApp) {
+ firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);
+ } else {
+ firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);
+ }
+ continue;
+ }
+ boolean isNew = true;
+ for (int origUser : res.mOrigUsers) {
+ if (origUser == newUser) {
+ isNew = false;
+ break;
+ }
+ }
+ if (isNew) {
+ if (isInstantApp) {
+ firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);
+ } else {
+ firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);
+ }
+ } else {
+ if (isInstantApp) {
+ instantUserIds = ArrayUtils.appendInt(instantUserIds, newUser);
+ } else {
+ updateUserIds = ArrayUtils.appendInt(updateUserIds, newUser);
+ }
+ }
+ }
+
+ // Send installed broadcasts if the package is not a static shared lib.
+ if (res.mPkg.getStaticSharedLibName() == null) {
+ mPm.mProcessLoggingHandler.invalidateBaseApkHash(res.mPkg.getBaseApkPath());
+
+ // Send added for users that see the package for the first time
+ // sendPackageAddedForNewUsers also deals with system apps
+ int appId = UserHandle.getAppId(res.mUid);
+ boolean isSystem = res.mPkg.isSystem();
+ mPm.sendPackageAddedForNewUsers(packageName, isSystem || virtualPreload,
+ virtualPreload /*startReceiver*/, appId, firstUserIds, firstInstantUserIds,
+ dataLoaderType);
+
+ // Send added for users that don't see the package for the first time
+ Bundle extras = new Bundle(1);
+ extras.putInt(Intent.EXTRA_UID, res.mUid);
+ if (update) {
+ extras.putBoolean(Intent.EXTRA_REPLACING, true);
+ }
+ extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
+ // Send to all running apps.
+ final SparseArray<int[]> newBroadcastAllowList;
+
+ synchronized (mPm.mLock) {
+ newBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList(
+ mPm.getPackageSettingInternal(res.mName, Process.SYSTEM_UID),
+ updateUserIds, mPm.mSettings.getPackagesLocked());
+ }
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, 0 /*flags*/,
+ null /*targetPackage*/, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, newBroadcastAllowList, null);
+ if (installerPackageName != null) {
+ // Send to the installer, even if it's not running.
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, 0 /*flags*/,
+ installerPackageName, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
+ }
+ // if the required verifier is defined, but, is not the installer of record
+ // for the package, it gets notified
+ final boolean notifyVerifier = mPm.mRequiredVerifierPackage != null
+ && !mPm.mRequiredVerifierPackage.equals(installerPackageName);
+ if (notifyVerifier) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, 0 /*flags*/,
+ mPm.mRequiredVerifierPackage, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
+ }
+ // If package installer is defined, notify package installer about new
+ // app installed
+ if (mPm.mRequiredInstallerPackage != null) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/,
+ mPm.mRequiredInstallerPackage, null /*finishedReceiver*/,
+ firstUserIds, instantUserIds, null /* broadcastAllowList */, null);
+ }
+
+ // Send replaced for users that don't see the package for the first time
+ if (update) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
+ packageName, extras, 0 /*flags*/,
+ null /*targetPackage*/, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, res.mRemovedInfo.mBroadcastAllowList,
+ null);
+ if (installerPackageName != null) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
+ extras, 0 /*flags*/,
+ installerPackageName, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /*broadcastAllowList*/, null);
+ }
+ if (notifyVerifier) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
+ extras, 0 /*flags*/,
+ mPm.mRequiredVerifierPackage, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /*broadcastAllowList*/, null);
+ }
+ mPm.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
+ null /*package*/, null /*extras*/, 0 /*flags*/,
+ packageName /*targetPackage*/,
+ null /*finishedReceiver*/, updateUserIds, instantUserIds,
+ null /*broadcastAllowList*/,
+ mPm.getTemporaryAppAllowlistBroadcastOptions(REASON_PACKAGE_REPLACED)
+ .toBundle());
+ } else if (launchedForRestore && !res.mPkg.isSystem()) {
+ // First-install and we did a restore, so we're responsible for the
+ // first-launch broadcast.
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, "Post-restore of " + packageName
+ + " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds));
+ }
+ mPm.sendFirstLaunchBroadcast(packageName, installerPackage,
+ firstUserIds, firstInstantUserIds);
+ }
+
+ // Send broadcast package appeared if external for all users
+ if (res.mPkg.isExternalStorage()) {
+ if (!update) {
+ final StorageManager storage = mPm.mInjector.getSystemService(
+ StorageManager.class);
+ VolumeInfo volume =
+ storage.findVolumeByUuid(
+ res.mPkg.getStorageUuid().toString());
+ int packageExternalStorageType =
+ PackageManagerService.getPackageExternalStorageType(volume,
+ res.mPkg.isExternalStorage());
+ // If the package was installed externally, log it.
+ if (packageExternalStorageType != StorageEnums.UNKNOWN) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,
+ packageExternalStorageType, packageName);
+ }
+ }
+ if (DEBUG_INSTALL) {
+ Slog.i(TAG, "upgrading pkg " + res.mPkg + " is external");
+ }
+ final int[] uidArray = new int[]{res.mPkg.getUid()};
+ ArrayList<String> pkgList = new ArrayList<>(1);
+ pkgList.add(packageName);
+ mPm.sendResourcesChangedBroadcast(true, true, pkgList, uidArray, null);
+ }
+ } else if (!ArrayUtils.isEmpty(res.mLibraryConsumers)) { // if static shared lib
+ for (int i = 0; i < res.mLibraryConsumers.size(); i++) {
+ AndroidPackage pkg = res.mLibraryConsumers.get(i);
+ // send broadcast that all consumers of the static shared library have changed
+ mPm.sendPackageChangedBroadcast(pkg.getPackageName(), false /* dontKillApp */,
+ new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
+ pkg.getUid(), null);
+ }
+ }
+
+ // Work that needs to happen on first install within each user
+ if (firstUserIds != null && firstUserIds.length > 0) {
+ for (int userId : firstUserIds) {
+ mPm.restorePermissionsAndUpdateRolesForNewUserInstall(packageName,
+ userId);
+ }
+ }
+
+ if (allNewUsers && !update) {
+ mPm.notifyPackageAdded(packageName, res.mUid);
+ } else {
+ mPm.notifyPackageChanged(packageName, res.mUid);
+ }
+
+ // Log current value of "unknown sources" setting
+ EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
+ getUnknownSourcesSettings());
+
+ // Remove the replaced package's older resources safely now
+ InstallArgs args = res.mRemovedInfo != null ? res.mRemovedInfo.mArgs : null;
+ if (args != null) {
+ if (!killApp) {
+ // If we didn't kill the app, defer the deletion of code/resource files, since
+ // they may still be in use by the running application. This mitigates problems
+ // in cases where resources or code is loaded by a new Activity before
+ // ApplicationInfo changes have propagated to all application threads.
+ mPm.scheduleDeferredNoKillPostDelete(args);
+ } else {
+ synchronized (mPm.mInstallLock) {
+ args.doPostDeleteLI(true);
+ }
+ }
+ } else {
+ // Force a gc to clear up things. Ask for a background one, it's fine to go on
+ // and not block here.
+ VMRuntime.getRuntime().requestConcurrentGC();
+ }
+
+ // Notify DexManager that the package was installed for new users.
+ // The updated users should already be indexed and the package code paths
+ // should not change.
+ // Don't notify the manager for ephemeral apps as they are not expected to
+ // survive long enough to benefit of background optimizations.
+ for (int userId : firstUserIds) {
+ PackageInfo info = mPm.getPackageInfo(packageName, /*flags*/ 0, userId);
+ // There's a race currently where some install events may interleave with an
+ // uninstall. This can lead to package info being null (b/36642664).
+ if (info != null) {
+ mPm.getDexManager().notifyPackageInstalled(info, userId);
+ }
+ }
+ }
+
+ final boolean deferInstallObserver = succeeded && update && !killApp;
+ if (deferInstallObserver) {
+ mPm.scheduleDeferredNoKillInstallObserver(res, installObserver);
+ } else {
+ mPm.notifyInstallObserver(res, installObserver);
+ }
+ }
+
+ /**
+ * Get the default verification agent response code.
+ *
+ * @return default verification response code
+ */
+ private int getDefaultVerificationResponse(UserHandle user) {
+ if (mPm.mUserManager.hasUserRestriction(UserManager.ENSURE_VERIFY_APPS,
+ user.getIdentifier())) {
+ return PackageManager.VERIFICATION_REJECT;
+ }
+ return android.provider.Settings.Global.getInt(mPm.mContext.getContentResolver(),
+ android.provider.Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE,
+ DEFAULT_VERIFICATION_RESPONSE);
+ }
+
+ /**
+ * Get the default integrity verification response code.
+ */
+ private int getDefaultIntegrityVerificationResponse() {
+ // We are not exposing this as a user-configurable setting because we don't want to provide
+ // an easy way to get around the integrity check.
+ return PackageManager.VERIFICATION_REJECT;
+ }
+
+ /**
+ * Get the "allow unknown sources" setting.
+ *
+ * @return the current "allow unknown sources" setting
+ */
+ private int getUnknownSourcesSettings() {
+ return android.provider.Settings.Secure.getIntForUser(mPm.mContext.getContentResolver(),
+ android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS,
+ -1, UserHandle.USER_SYSTEM);
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 958c769..08c95c2 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -144,6 +144,7 @@
import com.android.internal.os.SomeArgs;
import com.android.internal.security.VerityUtils;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
@@ -2983,6 +2984,8 @@
// Verify that all staged packages are internally consistent
final ArraySet<String> stagedSplits = new ArraySet<>();
+ final ArraySet<String> stagedSplitTypes = new ArraySet<>();
+ final ArraySet<String> requiredSplitTypes = new ArraySet<>();
final ArrayMap<String, ApkLite> splitApks = new ArrayMap<>();
final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
for (File addedFile : addedFiles) {
@@ -3038,6 +3041,10 @@
} else {
splitApks.put(apk.getSplitName(), apk);
}
+
+ // Collect the requiredSplitTypes and staged splitTypes
+ CollectionUtils.addAll(requiredSplitTypes, apk.getRequiredSplitTypes());
+ CollectionUtils.addAll(stagedSplitTypes, apk.getSplitTypes());
}
if (removeSplitList.size() > 0) {
@@ -3092,7 +3099,8 @@
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Full install must include a base package");
}
- if (baseApk.isSplitRequired() && stagedSplits.size() <= 1) {
+ if (baseApk.isSplitRequired() && (stagedSplits.size() <= 1
+ || !stagedSplitTypes.containsAll(requiredSplitTypes))) {
throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT,
"Missing split for " + mPackageName);
}
@@ -3130,6 +3138,8 @@
if (mResolvedBaseFile == null) {
mResolvedBaseFile = new File(appInfo.getBaseCodePath());
inheritFileLocked(mResolvedBaseFile);
+ // Collect the requiredSplitTypes from base
+ CollectionUtils.addAll(requiredSplitTypes, existing.getBaseRequiredSplitTypes());
}
// Inherit splits if not overridden.
@@ -3140,6 +3150,10 @@
final boolean splitRemoved = removeSplitList.contains(splitName);
if (!stagedSplits.contains(splitName) && !splitRemoved) {
inheritFileLocked(splitFile);
+ // Collect the requiredSplitTypes and staged splitTypes from splits
+ CollectionUtils.addAll(requiredSplitTypes,
+ existing.getRequiredSplitTypes()[i]);
+ CollectionUtils.addAll(stagedSplitTypes, existing.getSplitTypes()[i]);
}
}
}
@@ -3221,7 +3235,8 @@
final boolean allSplitsRemoved = (existingSplits == removeSplitList.size());
final boolean onlyBaseFileStaged = (stagedSplits.size() == 1
&& stagedSplits.contains(null));
- if (allSplitsRemoved && (stagedSplits.isEmpty() || onlyBaseFileStaged)) {
+ if ((allSplitsRemoved && (stagedSplits.isEmpty() || onlyBaseFileStaged))
+ || !stagedSplitTypes.containsAll(requiredSplitTypes)) {
throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT,
"Missing split for " + mPackageName);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3cd41d3..2c2884f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -39,11 +39,9 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
import static android.content.pm.PackageManager.INSTALL_FAILED_PROCESS_NOT_DEFINED;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
-import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
import static android.content.pm.PackageManager.INSTALL_INTERNAL;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
-import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
import static android.content.pm.PackageManager.MATCH_ALL;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
@@ -74,7 +72,6 @@
import static android.content.pm.PackageManagerInternal.LAST_KNOWN_PACKAGE;
import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
import static android.os.PowerWhitelistManager.REASON_LOCKED_BOOT_COMPLETED;
-import static android.os.PowerWhitelistManager.REASON_PACKAGE_REPLACED;
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.incremental.IncrementalManager.isIncrementalPath;
@@ -86,10 +83,7 @@
import static com.android.internal.annotations.VisibleForTesting.Visibility;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
-import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
-import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME;
import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME;
-import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME;
import static com.android.server.pm.ComponentResolver.RESOLVE_PRIORITY_SORTER;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
@@ -98,13 +92,10 @@
import static com.android.server.pm.PackageManagerServiceUtils.comparePackageSignatures;
import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
-import static com.android.server.pm.PackageManagerServiceUtils.decompressFile;
import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride;
import static com.android.server.pm.PackageManagerServiceUtils.dumpCriticalInfo;
-import static com.android.server.pm.PackageManagerServiceUtils.getCompressedFiles;
import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime;
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
-import static com.android.server.pm.PackageManagerServiceUtils.makeDirRecursive;
import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
import static com.android.server.pm.parsing.PackageInfoUtils.checkUseInstalledOrHidden;
@@ -175,7 +166,6 @@
import android.content.pm.ModuleInfo;
import android.content.pm.PackageChangeEvent;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.ComponentEnabledSetting;
@@ -188,7 +178,6 @@
import android.content.pm.PackageManagerInternal.PackageListObserver;
import android.content.pm.PackageManagerInternal.PrivateResolveFlags;
import android.content.pm.PackagePartitions;
-import android.content.pm.PackagePartitions.SystemPartition;
import android.content.pm.PackageStats;
import android.content.pm.PackageUserState;
import android.content.pm.ParceledListSlice;
@@ -202,7 +191,6 @@
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
import android.content.pm.SigningDetails;
-import android.content.pm.SigningDetails.SignatureSchemeVersion;
import android.content.pm.SigningInfo;
import android.content.pm.StagedApexInfo;
import android.content.pm.SuspendDialogInfo;
@@ -275,7 +263,6 @@
import android.security.KeyStore;
import android.service.pm.PackageServiceDumpProto;
import android.stats.storage.StorageEnums;
-import android.system.ErrnoException;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArrayMap;
@@ -311,13 +298,11 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.F2fsUtils;
-import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
import com.android.internal.content.om.OverlayConfig;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.SomeArgs;
import com.android.internal.policy.AttributeCache;
-import com.android.internal.security.VerityUtils;
import com.android.internal.telephony.CarrierAppUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
@@ -387,7 +372,6 @@
import dalvik.system.VMRuntime;
-import libcore.io.IoUtils;
import libcore.util.EmptyArray;
import libcore.util.HexEncoding;
@@ -408,7 +392,6 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.nio.charset.StandardCharsets;
-import java.security.DigestException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
@@ -489,7 +472,7 @@
static final boolean DEBUG_PREFERRED = false;
static final boolean DEBUG_UPGRADE = false;
static final boolean DEBUG_DOMAIN_VERIFICATION = false;
- private static final boolean DEBUG_BACKUP = false;
+ static final boolean DEBUG_BACKUP = false;
public static final boolean DEBUG_INSTALL = false;
public static final boolean DEBUG_REMOVE = false;
private static final boolean DEBUG_BROADCASTS = false;
@@ -622,7 +605,7 @@
/** Suffix of stub packages on the system partition */
public final static String STUB_SUFFIX = "-Stub";
- private static final int[] EMPTY_INT_ARRAY = new int[0];
+ static final int[] EMPTY_INT_ARRAY = new int[0];
/**
* Timeout (in milliseconds) after which the watchdog should declare that
@@ -674,7 +657,7 @@
* This can be either PackageManager.VERIFICATION_ALLOW or
* PackageManager.VERIFICATION_REJECT.
*/
- private static final int DEFAULT_VERIFICATION_RESPONSE = PackageManager.VERIFICATION_ALLOW;
+ static final int DEFAULT_VERIFICATION_RESPONSE = PackageManager.VERIFICATION_ALLOW;
/**
* Adding an installer package name to a package that does not have one set requires the
@@ -769,7 +752,7 @@
final Handler mHandler;
- private final ProcessLoggingHandler mProcessLoggingHandler;
+ final ProcessLoggingHandler mProcessLoggingHandler;
private final boolean mEnableFreeCacheV2;
@@ -838,7 +821,7 @@
* find updated user-installed versions. Keys are package name, values
* are package location.
*/
- final private ArrayMap<String, File> mExpectingBetter = new ArrayMap<>();
+ final ArrayMap<String, File> mExpectingBetter = new ArrayMap<>();
/**
* Tracks existing packages prior to receiving an OTA. Keys are package name.
@@ -857,7 +840,7 @@
* @see Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL
* @see #systemReady()
*/
- private @Nullable List<File> mReleaseOnSystemReady;
+ @Nullable List<File> mReleaseOnSystemReady;
/**
* Whether or not system app permissions should be promoted from install to runtime.
@@ -900,7 +883,7 @@
final ArrayMap<String, FeatureInfo> mAvailableFeatures;
@Watched
- private final InstantAppRegistry mInstantAppRegistry;
+ final InstantAppRegistry mInstantAppRegistry;
@GuardedBy("mLock")
int mChangedPackagesSequenceNumber;
@@ -943,7 +926,7 @@
new ArrayList<>();
// Cached parsed flag value. Invalidated on each flag change.
- private PerUidReadTimeouts[] mPerUidReadTimeoutsCache;
+ PerUidReadTimeouts[] mPerUidReadTimeoutsCache;
private static final PerUidReadTimeouts[] EMPTY_PER_UID_READ_TIMEOUTS_ARRAY = {};
@@ -1441,7 +1424,7 @@
boolean mResolverReplaced = false;
@NonNull
- private final DomainVerificationManagerInternal mDomainVerificationManager;
+ final DomainVerificationManagerInternal mDomainVerificationManager;
/** The service connection to the ephemeral resolver */
final InstantAppResolverConnection mInstantAppResolverConnection;
@@ -1585,7 +1568,7 @@
final UserManagerService mUserManager;
// Stores a list of users whose package restrictions file needs to be updated
- private final ArraySet<Integer> mDirtyUsers = new ArraySet<>();
+ final ArraySet<Integer> mDirtyUsers = new ArraySet<>();
// Recordkeeping of restore-after-install operations that are currently in flight
// between the Package Manager and the Backup Manager
@@ -2050,6 +2033,13 @@
boolean filterAppAccess(int uid, int callingUid);
@LiveImplementation(override = LiveImplementation.MANDATORY)
void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
+ FindPreferredActivityBodyResult findPreferredActivityInternal(Intent intent,
+ String resolvedType, int flags, List<ResolveInfo> query, boolean always,
+ boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
+ ResolveInfo findPersistentPreferredActivityLP(Intent intent, String resolvedType, int flags,
+ List<ResolveInfo> query, boolean debug, int userId);
}
/**
@@ -2858,7 +2848,24 @@
}
allHomeCandidates.addAll(resolveInfos);
- final String packageName = mDefaultAppProvider.getDefaultHome(userId);
+ String packageName = mDefaultAppProvider.getDefaultHome(userId);
+ if (packageName == null) {
+ // Role changes are not and cannot be atomic because its implementation lives inside
+ // a system app, so when the home role changes, there is a window when the previous
+ // role holder is removed and the new role holder is granted the preferred activity,
+ // but hasn't become the role holder yet. However, this case may be easily hit
+ // because the preferred activity change triggers a broadcast and receivers may try
+ // to get the default home activity there. So we need to fix it for this time
+ // window, and an easy workaround is to fallback to the current preferred activity.
+ final int appId = UserHandle.getAppId(Binder.getCallingUid());
+ final boolean filtered = appId >= Process.FIRST_APPLICATION_UID;
+ FindPreferredActivityBodyResult result = findPreferredActivityInternal(
+ intent, null, 0, resolveInfos, true, false, false, userId, filtered);
+ ResolveInfo preferredResolveInfo = result.mPreferredResolveInfo;
+ if (preferredResolveInfo != null && preferredResolveInfo.activityInfo != null) {
+ packageName = preferredResolveInfo.activityInfo.packageName;
+ }
+ }
if (packageName == null) {
return null;
}
@@ -4610,8 +4617,14 @@
public void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
final String packageName = dumpState.getTargetPackageName();
+ final PackageSetting setting = mSettings.getPackageLPr(packageName);
final boolean checkin = dumpState.isCheckIn();
+ // Return if the package doesn't exist.
+ if (packageName != null && setting == null) {
+ return;
+ }
+
switch (type) {
case DumpState.DUMP_VERSION:
{
@@ -4704,8 +4717,7 @@
case DumpState.DUMP_QUERIES:
{
- final PackageSetting setting = mSettings.getPackageLPr(packageName);
- Integer filteringAppId = setting == null ? null : setting.appId;
+ final Integer filteringAppId = setting == null ? null : setting.appId;
mAppsFilter.dumpQueries(
pw, filteringAppId, dumpState, mUserManager.getUserIds(),
this::getPackagesForUidInternalBody);
@@ -4716,8 +4728,9 @@
{
final android.util.IndentingPrintWriter writer =
new android.util.IndentingPrintWriter(pw);
- if (dumpState.onTitlePrinted()) pw.println();
-
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
writer.println("Domain verification status:");
writer.increaseIndent();
try {
@@ -4734,18 +4747,14 @@
case DumpState.DUMP_DEXOPT:
{
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
- ipw.println();
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
ipw.println("Dexopt state:");
ipw.increaseIndent();
Collection<PackageSetting> pkgSettings;
- if (packageName != null) {
- PackageSetting targetPkgSetting = mSettings.getPackageLPr(packageName);
- if (targetPkgSetting != null) {
- pkgSettings = Collections.singletonList(targetPkgSetting);
- } else {
- ipw.println("Unable to find package: " + packageName);
- return;
- }
+ if (setting != null) {
+ pkgSettings = Collections.singletonList(setting);
} else {
pkgSettings = mSettings.getPackagesLocked().values();
}
@@ -4755,10 +4764,12 @@
if (pkg == null) {
continue;
}
- ipw.println("[" + pkgSetting.name + "]");
+ final String pkgName = pkg.getPackageName();
+ ipw.println("[" + pkgName + "]");
ipw.increaseIndent();
+
mPackageDexOptimizer.dumpDexoptState(ipw, pkg, pkgSetting,
- mDexManager.getPackageUseInfoOrDefault(pkg.getPackageName()));
+ mDexManager.getPackageUseInfoOrDefault(pkgName));
ipw.decreaseIndent();
}
break;
@@ -4767,28 +4778,29 @@
case DumpState.DUMP_COMPILER_STATS:
{
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
- ipw.println();
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
ipw.println("Compiler stats:");
ipw.increaseIndent();
- Collection<AndroidPackage> packages;
- if (packageName != null) {
- AndroidPackage targetPackage = mPackages.get(packageName);
- if (targetPackage != null) {
- packages = Collections.singletonList(targetPackage);
- } else {
- ipw.println("Unable to find package: " + packageName);
- return;
- }
+ Collection<PackageSetting> pkgSettings;
+ if (setting != null) {
+ pkgSettings = Collections.singletonList(setting);
} else {
- packages = mPackages.values();
+ pkgSettings = mSettings.getPackagesLocked().values();
}
- for (AndroidPackage pkg : packages) {
+ for (PackageSetting pkgSetting : pkgSettings) {
+ final AndroidPackage pkg = pkgSetting.getPkg();
+ if (pkg == null) {
+ continue;
+ }
final String pkgName = pkg.getPackageName();
ipw.println("[" + pkgName + "]");
ipw.increaseIndent();
- CompilerStats.PackageStats stats = mCompilerStats.getPackageStats(pkgName);
+ final CompilerStats.PackageStats stats =
+ mCompilerStats.getPackageStats(pkgName);
if (stats == null) {
ipw.println("(No recorded stats)");
} else {
@@ -4800,6 +4812,284 @@
}
} // switch
}
+
+ // The body of findPreferredActivity.
+ protected FindPreferredActivityBodyResult findPreferredActivityBody(
+ Intent intent, String resolvedType, int flags,
+ List<ResolveInfo> query, boolean always,
+ boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered,
+ int callingUid, boolean isDeviceProvisioned) {
+ FindPreferredActivityBodyResult result = new FindPreferredActivityBodyResult();
+
+ flags = updateFlagsForResolve(
+ flags, userId, callingUid, false /*includeInstantApps*/,
+ isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId,
+ resolvedType, flags));
+ intent = updateIntentForResolve(intent);
+
+ // Try to find a matching persistent preferred activity.
+ result.mPreferredResolveInfo = findPersistentPreferredActivityLP(intent,
+ resolvedType, flags, query, debug, userId);
+
+ // If a persistent preferred activity matched, use it.
+ if (result.mPreferredResolveInfo != null) {
+ return result;
+ }
+
+ PreferredIntentResolver pir = mSettings.getPreferredActivities(userId);
+ // Get the list of preferred activities that handle the intent
+ if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for preferred activities...");
+ List<PreferredActivity> prefs = pir != null
+ ? pir.queryIntent(intent, resolvedType,
+ (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
+ userId)
+ : null;
+ if (prefs != null && prefs.size() > 0) {
+
+ // First figure out how good the original match set is.
+ // We will only allow preferred activities that came
+ // from the same match quality.
+ int match = 0;
+
+ if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Figuring out best match...");
+
+ final int N = query.size();
+ for (int j = 0; j < N; j++) {
+ final ResolveInfo ri = query.get(j);
+ if (DEBUG_PREFERRED || debug) {
+ Slog.v(TAG, "Match for " + ri.activityInfo
+ + ": 0x" + Integer.toHexString(match));
+ }
+ if (ri.match > match) {
+ match = ri.match;
+ }
+ }
+
+ if (DEBUG_PREFERRED || debug) {
+ Slog.v(TAG, "Best match: 0x" + Integer.toHexString(match));
+ }
+ match &= IntentFilter.MATCH_CATEGORY_MASK;
+ final int M = prefs.size();
+ for (int i = 0; i < M; i++) {
+ final PreferredActivity pa = prefs.get(i);
+ if (DEBUG_PREFERRED || debug) {
+ Slog.v(TAG, "Checking PreferredActivity ds="
+ + (pa.countDataSchemes() > 0 ? pa.getDataScheme(0) : "<none>")
+ + "\n component=" + pa.mPref.mComponent);
+ pa.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
+ }
+ if (pa.mPref.mMatch != match) {
+ if (DEBUG_PREFERRED || debug) {
+ Slog.v(TAG, "Skipping bad match "
+ + Integer.toHexString(pa.mPref.mMatch));
+ }
+ continue;
+ }
+ // If it's not an "always" type preferred activity and that's what we're
+ // looking for, skip it.
+ if (always && !pa.mPref.mAlways) {
+ if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Skipping mAlways=false entry");
+ continue;
+ }
+ final ActivityInfo ai = getActivityInfo(
+ pa.mPref.mComponent, flags | MATCH_DISABLED_COMPONENTS
+ | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+ userId);
+ if (DEBUG_PREFERRED || debug) {
+ Slog.v(TAG, "Found preferred activity:");
+ if (ai != null) {
+ ai.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
+ } else {
+ Slog.v(TAG, " null");
+ }
+ }
+ final boolean excludeSetupWizardHomeActivity = isHomeIntent(intent)
+ && !isDeviceProvisioned;
+ final boolean allowSetMutation = !excludeSetupWizardHomeActivity
+ && !queryMayBeFiltered;
+ if (ai == null) {
+ // Do not remove launcher's preferred activity during SetupWizard
+ // due to it may not install yet
+ if (!allowSetMutation) {
+ continue;
+ }
+
+ // This previously registered preferred activity
+ // component is no longer known. Most likely an update
+ // to the app was installed and in the new version this
+ // component no longer exists. Clean it up by removing
+ // it from the preferred activities list, and skip it.
+ Slog.w(TAG, "Removing dangling preferred activity: "
+ + pa.mPref.mComponent);
+ pir.removeFilter(pa);
+ result.mChanged = true;
+ continue;
+ }
+ for (int j = 0; j < N; j++) {
+ final ResolveInfo ri = query.get(j);
+ if (!ri.activityInfo.applicationInfo.packageName
+ .equals(ai.applicationInfo.packageName)) {
+ continue;
+ }
+ if (!ri.activityInfo.name.equals(ai.name)) {
+ continue;
+ }
+
+ if (removeMatches && allowSetMutation) {
+ pir.removeFilter(pa);
+ result.mChanged = true;
+ if (DEBUG_PREFERRED) {
+ Slog.v(TAG, "Removing match " + pa.mPref.mComponent);
+ }
+ break;
+ }
+
+ // Okay we found a previously set preferred or last chosen app.
+ // If the result set is different from when this
+ // was created, and is not a subset of the preferred set, we need to
+ // clear it and re-ask the user their preference, if we're looking for
+ // an "always" type entry.
+
+ if (always && !pa.mPref.sameSet(query, excludeSetupWizardHomeActivity)) {
+ if (pa.mPref.isSuperset(query, excludeSetupWizardHomeActivity)) {
+ if (allowSetMutation) {
+ // some components of the set are no longer present in
+ // the query, but the preferred activity can still be reused
+ if (DEBUG_PREFERRED) {
+ Slog.i(TAG, "Result set changed, but PreferredActivity"
+ + " is still valid as only non-preferred"
+ + " components were removed for " + intent
+ + " type " + resolvedType);
+ }
+ // remove obsolete components and re-add the up-to-date
+ // filter
+ PreferredActivity freshPa = new PreferredActivity(pa,
+ pa.mPref.mMatch,
+ pa.mPref.discardObsoleteComponents(query),
+ pa.mPref.mComponent,
+ pa.mPref.mAlways);
+ pir.removeFilter(pa);
+ pir.addFilter(freshPa);
+ result.mChanged = true;
+ } else {
+ if (DEBUG_PREFERRED) {
+ Slog.i(TAG, "Do not remove preferred activity");
+ }
+ }
+ } else {
+ if (allowSetMutation) {
+ Slog.i(TAG,
+ "Result set changed, dropping preferred activity "
+ + "for " + intent + " type "
+ + resolvedType);
+ if (DEBUG_PREFERRED) {
+ Slog.v(TAG,
+ "Removing preferred activity since set changed "
+ + pa.mPref.mComponent);
+ }
+ pir.removeFilter(pa);
+ // Re-add the filter as a "last chosen" entry (!always)
+ PreferredActivity lastChosen = new PreferredActivity(
+ pa, pa.mPref.mMatch, null, pa.mPref.mComponent,
+ false);
+ pir.addFilter(lastChosen);
+ result.mChanged = true;
+ }
+ result.mPreferredResolveInfo = null;
+ return result;
+ }
+ }
+
+ // Yay! Either the set matched or we're looking for the last chosen
+ if (DEBUG_PREFERRED || debug) {
+ Slog.v(TAG, "Returning preferred activity: "
+ + ri.activityInfo.packageName + "/" + ri.activityInfo.name);
+ }
+ result.mPreferredResolveInfo = ri;
+ return result;
+ }
+ }
+ }
+ return result;
+ }
+
+ public final FindPreferredActivityBodyResult findPreferredActivityInternal(
+ Intent intent, String resolvedType, int flags,
+ List<ResolveInfo> query, boolean always,
+ boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
+
+ final int callingUid = Binder.getCallingUid();
+ // Do NOT hold the packages lock; this calls up into the settings provider which
+ // could cause a deadlock.
+ final boolean isDeviceProvisioned =
+ android.provider.Settings.Global.getInt(mContext.getContentResolver(),
+ android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1;
+ // Find the preferred activity - the lock is held inside the method.
+ return findPreferredActivityBody(
+ intent, resolvedType, flags, query, always, removeMatches, debug,
+ userId, queryMayBeFiltered, callingUid, isDeviceProvisioned);
+ }
+
+ public final ResolveInfo findPersistentPreferredActivityLP(Intent intent,
+ String resolvedType,
+ int flags, List<ResolveInfo> query, boolean debug, int userId) {
+ final int N = query.size();
+ PersistentPreferredIntentResolver ppir =
+ mSettings.getPersistentPreferredActivities(userId);
+ // Get the list of persistent preferred activities that handle the intent
+ if (DEBUG_PREFERRED || debug) {
+ Slog.v(TAG, "Looking for persistent preferred activities...");
+ }
+ List<PersistentPreferredActivity> pprefs = ppir != null
+ ? ppir.queryIntent(intent, resolvedType,
+ (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
+ userId)
+ : null;
+ if (pprefs != null && pprefs.size() > 0) {
+ final int M = pprefs.size();
+ for (int i = 0; i < M; i++) {
+ final PersistentPreferredActivity ppa = pprefs.get(i);
+ if (DEBUG_PREFERRED || debug) {
+ Slog.v(TAG, "Checking PersistentPreferredActivity ds="
+ + (ppa.countDataSchemes() > 0 ? ppa.getDataScheme(0) : "<none>")
+ + "\n component=" + ppa.mComponent);
+ ppa.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
+ }
+ final ActivityInfo ai = getActivityInfo(ppa.mComponent,
+ flags | MATCH_DISABLED_COMPONENTS, userId);
+ if (DEBUG_PREFERRED || debug) {
+ Slog.v(TAG, "Found persistent preferred activity:");
+ if (ai != null) {
+ ai.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
+ } else {
+ Slog.v(TAG, " null");
+ }
+ }
+ if (ai == null) {
+ // This previously registered persistent preferred activity
+ // component is no longer known. Ignore it and do NOT remove it.
+ continue;
+ }
+ for (int j = 0; j < N; j++) {
+ final ResolveInfo ri = query.get(j);
+ if (!ri.activityInfo.applicationInfo.packageName
+ .equals(ai.applicationInfo.packageName)) {
+ continue;
+ }
+ if (!ri.activityInfo.name.equals(ai.name)) {
+ continue;
+ }
+ // Found a persistent preference that can handle the intent.
+ if (DEBUG_PREFERRED || debug) {
+ Slog.v(TAG, "Returning persistent preferred activity: "
+ + ri.activityInfo.packageName + "/" + ri.activityInfo.name);
+ }
+ return ri;
+ }
+ }
+ }
+ return null;
+ }
}
/**
@@ -4964,6 +5254,16 @@
super.dump(type, fd, pw, dumpState);
}
}
+ public final FindPreferredActivityBodyResult findPreferredActivityBody(Intent intent,
+ String resolvedType, int flags, List<ResolveInfo> query, boolean always,
+ boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered,
+ int callingUid, boolean isDeviceProvisioned) {
+ synchronized (mLock) {
+ return super.findPreferredActivityBody(intent, resolvedType, flags, query, always,
+ removeMatches, debug, userId, queryMayBeFiltered, callingUid,
+ isDeviceProvisioned);
+ }
+ }
}
/**
@@ -5539,6 +5839,28 @@
current.release();
}
}
+ public final FindPreferredActivityBodyResult findPreferredActivityInternal(Intent intent,
+ String resolvedType, int flags, List<ResolveInfo> query, boolean always,
+ boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
+ ThreadComputer current = live();
+ try {
+ return current.mComputer.findPreferredActivityInternal(intent, resolvedType, flags,
+ query, always, removeMatches, debug, userId, queryMayBeFiltered);
+ } finally {
+ current.release();
+ }
+ }
+ public final ResolveInfo findPersistentPreferredActivityLP(Intent intent,
+ String resolvedType, int flags, List<ResolveInfo> query, boolean debug,
+ int userId) {
+ ThreadComputer current = live();
+ try {
+ return current.mComputer.findPersistentPreferredActivityLP(intent, resolvedType,
+ flags, query, debug, userId);
+ } finally {
+ current.release();
+ }
+ }
}
@@ -5562,7 +5884,7 @@
// If true, the snapshot is corked. Do not create a new snapshot but use the live
// computer. This throttles snapshot creation during periods of churn in Package
// Manager.
- private static final AtomicInteger sSnapshotCorked = new AtomicInteger(0);
+ static final AtomicInteger sSnapshotCorked = new AtomicInteger(0);
/**
* This class records the Computer being used by a thread and the Computer's reference
@@ -5736,665 +6058,8 @@
onChange(null);
}
- class PackageHandler extends Handler {
-
- PackageHandler(Looper looper) {
- super(looper);
- }
-
- public void handleMessage(Message msg) {
- try {
- doHandleMessage(msg);
- } finally {
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- }
- }
-
- void doHandleMessage(Message msg) {
- switch (msg.what) {
- case INIT_COPY: {
- HandlerParams params = (HandlerParams) msg.obj;
- if (params != null) {
- if (DEBUG_INSTALL) Slog.i(TAG, "init_copy: " + params);
- Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
- System.identityHashCode(params));
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
- params.startCopy();
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- break;
- }
- case SEND_PENDING_BROADCAST: {
- String[] packages;
- ArrayList<String>[] components;
- int size = 0;
- int[] uids;
- Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
- synchronized (mLock) {
- size = mPendingBroadcasts.size();
- if (size <= 0) {
- // Nothing to be done. Just return
- return;
- }
- packages = new String[size];
- components = new ArrayList[size];
- uids = new int[size];
- int i = 0; // filling out the above arrays
-
- for (int n = 0; n < mPendingBroadcasts.userIdCount(); n++) {
- final int packageUserId = mPendingBroadcasts.userIdAt(n);
- final ArrayMap<String, ArrayList<String>> componentsToBroadcast =
- mPendingBroadcasts.packagesForUserId(packageUserId);
- final int numComponents = componentsToBroadcast.size();
- for (int index = 0; i < size && index < numComponents; index++) {
- packages[i] = componentsToBroadcast.keyAt(index);
- components[i] = componentsToBroadcast.valueAt(index);
- final PackageSetting ps = mSettings.getPackageLPr(packages[i]);
- uids[i] = (ps != null)
- ? UserHandle.getUid(packageUserId, ps.appId)
- : -1;
- i++;
- }
- }
- size = i;
- mPendingBroadcasts.clear();
- }
- // Send broadcasts
- for (int i = 0; i < size; i++) {
- sendPackageChangedBroadcast(packages[i], true /* dontKillApp */,
- components[i], uids[i], null /* reason */);
- }
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- break;
- }
- case POST_INSTALL: {
- if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);
-
- PostInstallData data = mRunningInstalls.get(msg.arg1);
- final boolean didRestore = (msg.arg2 != 0);
- mRunningInstalls.delete(msg.arg1);
-
- if (data != null && data.res.mFreezer != null) {
- data.res.mFreezer.close();
- }
-
- if (data != null && data.mPostInstallRunnable != null) {
- data.mPostInstallRunnable.run();
- } else if (data != null && data.args != null) {
- InstallArgs args = data.args;
- PackageInstalledInfo parentRes = data.res;
-
- final boolean killApp = (args.mInstallFlags
- & PackageManager.INSTALL_DONT_KILL_APP) == 0;
- final boolean virtualPreload = ((args.mInstallFlags
- & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
-
- handlePackagePostInstall(parentRes, killApp, virtualPreload,
- didRestore, args.mInstallSource.installerPackageName,
- args.mObserver, args.mDataLoaderType);
-
- // Log tracing if needed
- if (args.mTraceMethod != null) {
- Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, args.mTraceMethod,
- args.mTraceCookie);
- }
- } else if (DEBUG_INSTALL) {
- // No post-install when we run restore from installExistingPackageForUser
- Slog.i(TAG, "Nothing to do for post-install token " + msg.arg1);
- }
-
- Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", msg.arg1);
- } break;
- case DEFERRED_NO_KILL_POST_DELETE: {
- synchronized (mInstallLock) {
- InstallArgs args = (InstallArgs) msg.obj;
- if (args != null) {
- args.doPostDeleteLI(true);
- }
- }
- } break;
- case DEFERRED_NO_KILL_INSTALL_OBSERVER: {
- String packageName = (String) msg.obj;
- if (packageName != null) {
- notifyInstallObserver(packageName);
- }
- } break;
- case WRITE_SETTINGS: {
- Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
- synchronized (mLock) {
- removeMessages(WRITE_SETTINGS);
- removeMessages(WRITE_PACKAGE_RESTRICTIONS);
- writeSettingsLPrTEMP();
- mDirtyUsers.clear();
- }
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- } break;
- case WRITE_PACKAGE_RESTRICTIONS: {
- Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
- synchronized (mLock) {
- removeMessages(WRITE_PACKAGE_RESTRICTIONS);
- for (int userId : mDirtyUsers) {
- mSettings.writePackageRestrictionsLPr(userId);
- }
- mDirtyUsers.clear();
- }
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- } break;
- case WRITE_PACKAGE_LIST: {
- Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
- synchronized (mLock) {
- removeMessages(WRITE_PACKAGE_LIST);
- mSettings.writePackageListLPr(msg.arg1);
- }
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- } break;
- case CHECK_PENDING_VERIFICATION: {
- final int verificationId = msg.arg1;
- final PackageVerificationState state = mPendingVerification.get(verificationId);
-
- if ((state != null) && !state.isVerificationComplete()
- && !state.timeoutExtended()) {
- final VerificationParams params = state.getVerificationParams();
- final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
-
- String errorMsg = "Verification timed out for " + originUri;
- Slog.i(TAG, errorMsg);
-
- final UserHandle user = params.getUser();
- if (getDefaultVerificationResponse(user)
- == PackageManager.VERIFICATION_ALLOW) {
- Slog.i(TAG, "Continuing with installation of " + originUri);
- state.setVerifierResponse(Binder.getCallingUid(),
- PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
- broadcastPackageVerified(verificationId, originUri,
- PackageManager.VERIFICATION_ALLOW, null, params.mDataLoaderType,
- user);
- } else {
- broadcastPackageVerified(verificationId, originUri,
- PackageManager.VERIFICATION_REJECT, null,
- params.mDataLoaderType, user);
- params.setReturnCode(
- PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, errorMsg);
- state.setVerifierResponse(Binder.getCallingUid(),
- PackageManager.VERIFICATION_REJECT);
- }
-
- if (state.areAllVerificationsComplete()) {
- mPendingVerification.remove(verificationId);
- }
-
- Trace.asyncTraceEnd(
- TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
-
- params.handleVerificationFinished();
-
- }
- break;
- }
- case CHECK_PENDING_INTEGRITY_VERIFICATION: {
- final int verificationId = msg.arg1;
- final PackageVerificationState state = mPendingVerification.get(verificationId);
-
- if (state != null && !state.isIntegrityVerificationComplete()) {
- final VerificationParams params = state.getVerificationParams();
- final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
-
- String errorMsg = "Integrity verification timed out for " + originUri;
- Slog.i(TAG, errorMsg);
-
- state.setIntegrityVerificationResult(
- getDefaultIntegrityVerificationResponse());
-
- if (getDefaultIntegrityVerificationResponse()
- == PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW) {
- Slog.i(TAG, "Integrity check times out, continuing with " + originUri);
- } else {
- params.setReturnCode(
- PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
- errorMsg);
- }
-
- if (state.areAllVerificationsComplete()) {
- mPendingVerification.remove(verificationId);
- }
-
- Trace.asyncTraceEnd(
- TRACE_TAG_PACKAGE_MANAGER,
- "integrity_verification",
- verificationId);
-
- params.handleIntegrityVerificationFinished();
- }
- break;
- }
- case PACKAGE_VERIFIED: {
- final int verificationId = msg.arg1;
-
- final PackageVerificationState state = mPendingVerification.get(verificationId);
- if (state == null) {
- Slog.w(TAG, "Verification with id " + verificationId
- + " not found."
- + " It may be invalid or overridden by integrity verification");
- break;
- }
-
- final PackageVerificationResponse response = (PackageVerificationResponse) msg.obj;
-
- state.setVerifierResponse(response.callerUid, response.code);
-
- if (state.isVerificationComplete()) {
- final VerificationParams params = state.getVerificationParams();
- final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
-
- if (state.isInstallAllowed()) {
- broadcastPackageVerified(verificationId, originUri,
- response.code, null, params.mDataLoaderType, params.getUser());
- } else {
- params.setReturnCode(
- PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
- "Install not allowed");
- }
-
- if (state.areAllVerificationsComplete()) {
- mPendingVerification.remove(verificationId);
- }
-
- Trace.asyncTraceEnd(
- TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
-
- params.handleVerificationFinished();
- }
-
- break;
- }
- case INTEGRITY_VERIFICATION_COMPLETE: {
- final int verificationId = msg.arg1;
-
- final PackageVerificationState state = mPendingVerification.get(verificationId);
- if (state == null) {
- Slog.w(TAG, "Integrity verification with id " + verificationId
- + " not found. It may be invalid or overridden by verifier");
- break;
- }
-
- final int response = (Integer) msg.obj;
- final VerificationParams params = state.getVerificationParams();
- final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
-
- state.setIntegrityVerificationResult(response);
-
- if (response == PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW) {
- Slog.i(TAG, "Integrity check passed for " + originUri);
- } else {
- params.setReturnCode(
- PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
- "Integrity check failed for " + originUri);
- }
-
- if (state.areAllVerificationsComplete()) {
- mPendingVerification.remove(verificationId);
- }
-
- Trace.asyncTraceEnd(
- TRACE_TAG_PACKAGE_MANAGER,
- "integrity_verification",
- verificationId);
-
- params.handleIntegrityVerificationFinished();
- break;
- }
- case INSTANT_APP_RESOLUTION_PHASE_TWO: {
- InstantAppResolver.doInstantAppResolutionPhaseTwo(mContext,
- mInstantAppResolverConnection,
- (InstantAppRequest) msg.obj,
- mInstantAppInstallerActivity,
- mHandler);
- break;
- }
- case ENABLE_ROLLBACK_STATUS: {
- final int enableRollbackToken = msg.arg1;
- final int enableRollbackCode = msg.arg2;
- final VerificationParams params =
- mPendingEnableRollback.get(enableRollbackToken);
- if (params == null) {
- Slog.w(TAG, "Invalid rollback enabled token "
- + enableRollbackToken + " received");
- break;
- }
-
- mPendingEnableRollback.remove(enableRollbackToken);
-
- if (enableRollbackCode != PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED) {
- final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
- Slog.w(TAG, "Failed to enable rollback for " + originUri);
- Slog.w(TAG, "Continuing with installation of " + originUri);
- }
-
- Trace.asyncTraceEnd(
- TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken);
-
- params.handleRollbackEnabled();
- break;
- }
- case ENABLE_ROLLBACK_TIMEOUT: {
- final int enableRollbackToken = msg.arg1;
- final int sessionId = msg.arg2;
- final VerificationParams params =
- mPendingEnableRollback.get(enableRollbackToken);
- if (params != null) {
- final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
-
- Slog.w(TAG, "Enable rollback timed out for " + originUri);
- mPendingEnableRollback.remove(enableRollbackToken);
-
- Slog.w(TAG, "Continuing with installation of " + originUri);
- Trace.asyncTraceEnd(
- TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken);
- params.handleRollbackEnabled();
- Intent rollbackTimeoutIntent = new Intent(
- Intent.ACTION_CANCEL_ENABLE_ROLLBACK);
- rollbackTimeoutIntent.putExtra(
- PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_SESSION_ID,
- sessionId);
- rollbackTimeoutIntent.addFlags(
- Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcastAsUser(rollbackTimeoutIntent, UserHandle.SYSTEM,
- android.Manifest.permission.PACKAGE_ROLLBACK_AGENT);
- }
- break;
- }
- case DOMAIN_VERIFICATION: {
- int messageCode = msg.arg1;
- Object object = msg.obj;
- mDomainVerificationManager.runMessage(messageCode, object);
- break;
- }
- case SNAPSHOT_UNCORK: {
- int corking = sSnapshotCorked.decrementAndGet();
- if (TRACE_SNAPSHOTS && corking == 0) {
- Log.e(TAG, "snapshot: corking goes to zero in message handler");
- }
- break;
- }
- }
- }
- }
-
- private void handlePackagePostInstall(PackageInstalledInfo res, boolean killApp,
- boolean virtualPreload, boolean launchedForRestore, String installerPackage,
- IPackageInstallObserver2 installObserver, int dataLoaderType) {
- boolean succeeded = res.mReturnCode == PackageManager.INSTALL_SUCCEEDED;
- final boolean update = res.mRemovedInfo != null && res.mRemovedInfo.mRemovedPackage != null;
- final String packageName = res.mName;
- final PackageSetting pkgSetting = succeeded ? getPackageSetting(packageName) : null;
- final boolean removedBeforeUpdate = (pkgSetting == null)
- || (pkgSetting.isSystem() && !pkgSetting.getPathString().equals(
- res.mPkg.getPath()));
- if (succeeded && removedBeforeUpdate) {
- Slog.e(TAG, packageName + " was removed before handlePackagePostInstall "
- + "could be executed");
- res.mReturnCode = INSTALL_FAILED_PACKAGE_CHANGED;
- res.mReturnMsg = "Package was removed before install could complete.";
-
- // Remove the update failed package's older resources safely now
- InstallArgs args = res.mRemovedInfo != null ? res.mRemovedInfo.mArgs : null;
- if (args != null) {
- synchronized (mInstallLock) {
- args.doPostDeleteLI(true);
- }
- }
- notifyInstallObserver(res, installObserver);
- return;
- }
-
- if (succeeded) {
- // Clear the uid cache after we installed a new package.
- mPerUidReadTimeoutsCache = null;
-
- // Send the removed broadcasts
- if (res.mRemovedInfo != null) {
- res.mRemovedInfo.sendPackageRemovedBroadcasts(killApp, false /*removedBySystem*/);
- }
-
- final String installerPackageName =
- res.mInstallerPackageName != null
- ? res.mInstallerPackageName
- : res.mRemovedInfo != null
- ? res.mRemovedInfo.mInstallerPackageName
- : null;
-
- synchronized (mLock) {
- mInstantAppRegistry.onPackageInstalledLPw(res.mPkg, res.mNewUsers);
- }
-
- // Determine the set of users who are adding this package for
- // the first time vs. those who are seeing an update.
- int[] firstUserIds = EMPTY_INT_ARRAY;
- int[] firstInstantUserIds = EMPTY_INT_ARRAY;
- int[] updateUserIds = EMPTY_INT_ARRAY;
- int[] instantUserIds = EMPTY_INT_ARRAY;
- final boolean allNewUsers = res.mOrigUsers == null || res.mOrigUsers.length == 0;
- for (int newUser : res.mNewUsers) {
- final boolean isInstantApp = pkgSetting.getInstantApp(newUser);
- if (allNewUsers) {
- if (isInstantApp) {
- firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);
- } else {
- firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);
- }
- continue;
- }
- boolean isNew = true;
- for (int origUser : res.mOrigUsers) {
- if (origUser == newUser) {
- isNew = false;
- break;
- }
- }
- if (isNew) {
- if (isInstantApp) {
- firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);
- } else {
- firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);
- }
- } else {
- if (isInstantApp) {
- instantUserIds = ArrayUtils.appendInt(instantUserIds, newUser);
- } else {
- updateUserIds = ArrayUtils.appendInt(updateUserIds, newUser);
- }
- }
- }
-
- // Send installed broadcasts if the package is not a static shared lib.
- if (res.mPkg.getStaticSharedLibName() == null) {
- mProcessLoggingHandler.invalidateBaseApkHash(res.mPkg.getBaseApkPath());
-
- // Send added for users that see the package for the first time
- // sendPackageAddedForNewUsers also deals with system apps
- int appId = UserHandle.getAppId(res.mUid);
- boolean isSystem = res.mPkg.isSystem();
- sendPackageAddedForNewUsers(packageName, isSystem || virtualPreload,
- virtualPreload /*startReceiver*/, appId, firstUserIds, firstInstantUserIds,
- dataLoaderType);
-
- // Send added for users that don't see the package for the first time
- Bundle extras = new Bundle(1);
- extras.putInt(Intent.EXTRA_UID, res.mUid);
- if (update) {
- extras.putBoolean(Intent.EXTRA_REPLACING, true);
- }
- extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
- // Send to all running apps.
- final SparseArray<int[]> newBroadcastAllowList;
-
- synchronized (mLock) {
- newBroadcastAllowList = mAppsFilter.getVisibilityAllowList(
- getPackageSettingInternal(res.mName, Process.SYSTEM_UID),
- updateUserIds, mSettings.getPackagesLocked());
- }
- sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, 0 /*flags*/,
- null /*targetPackage*/, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, newBroadcastAllowList, null);
- if (installerPackageName != null) {
- // Send to the installer, even if it's not running.
- sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, 0 /*flags*/,
- installerPackageName, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
- }
- // if the required verifier is defined, but, is not the installer of record
- // for the package, it gets notified
- final boolean notifyVerifier = mRequiredVerifierPackage != null
- && !mRequiredVerifierPackage.equals(installerPackageName);
- if (notifyVerifier) {
- sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, 0 /*flags*/,
- mRequiredVerifierPackage, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
- }
- // If package installer is defined, notify package installer about new
- // app installed
- if (mRequiredInstallerPackage != null) {
- sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/,
- mRequiredInstallerPackage, null /*finishedReceiver*/,
- firstUserIds, instantUserIds, null /* broadcastAllowList */, null);
- }
-
- // Send replaced for users that don't see the package for the first time
- if (update) {
- sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- packageName, extras, 0 /*flags*/,
- null /*targetPackage*/, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, res.mRemovedInfo.mBroadcastAllowList,
- null);
- if (installerPackageName != null) {
- sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, 0 /*flags*/,
- installerPackageName, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /*broadcastAllowList*/, null);
- }
- if (notifyVerifier) {
- sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, 0 /*flags*/,
- mRequiredVerifierPackage, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /*broadcastAllowList*/, null);
- }
- sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
- null /*package*/, null /*extras*/, 0 /*flags*/,
- packageName /*targetPackage*/,
- null /*finishedReceiver*/, updateUserIds, instantUserIds,
- null /*broadcastAllowList*/,
- getTemporaryAppAllowlistBroadcastOptions(REASON_PACKAGE_REPLACED)
- .toBundle());
- } else if (launchedForRestore && !res.mPkg.isSystem()) {
- // First-install and we did a restore, so we're responsible for the
- // first-launch broadcast.
- if (DEBUG_BACKUP) {
- Slog.i(TAG, "Post-restore of " + packageName
- + " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds));
- }
- sendFirstLaunchBroadcast(packageName, installerPackage,
- firstUserIds, firstInstantUserIds);
- }
-
- // Send broadcast package appeared if external for all users
- if (res.mPkg.isExternalStorage()) {
- if (!update) {
- final StorageManager storage = mInjector.getSystemService(
- StorageManager.class);
- VolumeInfo volume =
- storage.findVolumeByUuid(
- res.mPkg.getStorageUuid().toString());
- int packageExternalStorageType =
- getPackageExternalStorageType(volume, res.mPkg.isExternalStorage());
- // If the package was installed externally, log it.
- if (packageExternalStorageType != StorageEnums.UNKNOWN) {
- FrameworkStatsLog.write(
- FrameworkStatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,
- packageExternalStorageType, packageName);
- }
- }
- if (DEBUG_INSTALL) {
- Slog.i(TAG, "upgrading pkg " + res.mPkg + " is external");
- }
- final int[] uidArray = new int[]{res.mPkg.getUid()};
- ArrayList<String> pkgList = new ArrayList<>(1);
- pkgList.add(packageName);
- sendResourcesChangedBroadcast(true, true, pkgList, uidArray, null);
- }
- } else if (!ArrayUtils.isEmpty(res.mLibraryConsumers)) { // if static shared lib
- for (int i = 0; i < res.mLibraryConsumers.size(); i++) {
- AndroidPackage pkg = res.mLibraryConsumers.get(i);
- // send broadcast that all consumers of the static shared library have changed
- sendPackageChangedBroadcast(pkg.getPackageName(), false /* dontKillApp */,
- new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
- pkg.getUid(), null);
- }
- }
-
- // Work that needs to happen on first install within each user
- if (firstUserIds != null && firstUserIds.length > 0) {
- for (int userId : firstUserIds) {
- restorePermissionsAndUpdateRolesForNewUserInstall(packageName,
- userId);
- }
- }
-
- if (allNewUsers && !update) {
- notifyPackageAdded(packageName, res.mUid);
- } else {
- notifyPackageChanged(packageName, res.mUid);
- }
-
- // Log current value of "unknown sources" setting
- EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
- getUnknownSourcesSettings());
-
- // Remove the replaced package's older resources safely now
- InstallArgs args = res.mRemovedInfo != null ? res.mRemovedInfo.mArgs : null;
- if (args != null) {
- if (!killApp) {
- // If we didn't kill the app, defer the deletion of code/resource files, since
- // they may still be in use by the running application. This mitigates problems
- // in cases where resources or code is loaded by a new Activity before
- // ApplicationInfo changes have propagated to all application threads.
- scheduleDeferredNoKillPostDelete(args);
- } else {
- synchronized (mInstallLock) {
- args.doPostDeleteLI(true);
- }
- }
- } else {
- // Force a gc to clear up things. Ask for a background one, it's fine to go on
- // and not block here.
- VMRuntime.getRuntime().requestConcurrentGC();
- }
- // Notify DexManager that the package was installed for new users.
- // The updated users should already be indexed and the package code paths
- // should not change.
- // Don't notify the manager for ephemeral apps as they are not expected to
- // survive long enough to benefit of background optimizations.
- for (int userId : firstUserIds) {
- PackageInfo info = getPackageInfo(packageName, /*flags*/ 0, userId);
- // There's a race currently where some install events may interleave with an
- // uninstall. This can lead to package info being null (b/36642664).
- if (info != null) {
- mDexManager.notifyPackageInstalled(info, userId);
- }
- }
- }
- final boolean deferInstallObserver = succeeded && update && !killApp;
- if (deferInstallObserver) {
- scheduleDeferredNoKillInstallObserver(res, installObserver);
- } else {
- notifyInstallObserver(res, installObserver);
- }
- }
@Override
public void notifyPackagesReplacedReceived(String[] packages) {
@@ -6435,12 +6100,12 @@
}
}
- private void scheduleDeferredNoKillPostDelete(InstallArgs args) {
+ void scheduleDeferredNoKillPostDelete(InstallArgs args) {
Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_POST_DELETE, args);
mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_POST_DELETE_DELAY_MS);
}
- private void scheduleDeferredNoKillInstallObserver(PackageInstalledInfo info,
+ void scheduleDeferredNoKillInstallObserver(PackageInstalledInfo info,
IPackageInstallObserver2 observer) {
String packageName = info.mPkg.getPackageName();
mNoKillInstallObservers.put(packageName, Pair.create(info, observer));
@@ -6528,7 +6193,7 @@
* corresponding {@link StorageEnums} storage type value if it is.
* corresponding {@link StorageEnums} storage type value if it is.
*/
- private static int getPackageExternalStorageType(VolumeInfo packageVolume,
+ static int getPackageExternalStorageType(VolumeInfo packageVolume,
boolean packageIsExternal) {
if (packageVolume != null) {
DiskInfo disk = packageVolume.getDisk();
@@ -6723,7 +6388,7 @@
HandlerThread thread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
thread.start();
- return pm.new PackageHandler(thread.getLooper());
+ return new PackageHandler(thread.getLooper(), pm);
},
new DefaultSystemWrapper(),
LocalServices::getService,
@@ -6829,53 +6494,6 @@
}
}
- @VisibleForTesting
- public static class ScanPartition extends SystemPartition {
- @ScanFlags
- public final int scanFlag;
-
- public ScanPartition(@NonNull SystemPartition partition) {
- super(partition);
- scanFlag = scanFlagForPartition(partition);
- }
-
- /**
- * Creates a partition containing the same folders as the original partition but with a
- * different root folder. The new partition will include the scan flags of the original
- * partition along with any specified additional scan flags.
- */
- public ScanPartition(@NonNull File folder, @NonNull ScanPartition original,
- @ScanFlags int additionalScanFlag) {
- super(folder, original);
- this.scanFlag = original.scanFlag | additionalScanFlag;
- }
-
- private static int scanFlagForPartition(PackagePartitions.SystemPartition partition) {
- switch (partition.type) {
- case PackagePartitions.PARTITION_SYSTEM:
- return 0;
- case PackagePartitions.PARTITION_VENDOR:
- return SCAN_AS_VENDOR;
- case PackagePartitions.PARTITION_ODM:
- return SCAN_AS_ODM;
- case PackagePartitions.PARTITION_OEM:
- return SCAN_AS_OEM;
- case PackagePartitions.PARTITION_PRODUCT:
- return SCAN_AS_PRODUCT;
- case PackagePartitions.PARTITION_SYSTEM_EXT:
- return SCAN_AS_SYSTEM_EXT;
- default:
- throw new IllegalStateException("Unable to determine scan flag for "
- + partition.getFolder());
- }
- }
-
- @Override
- public String toString() {
- return getFolder().getAbsolutePath() + ":" + scanFlag;
- }
- }
-
// Link watchables to the class
private void registerObserver() {
mPackages.registerObserver(mWatcher);
@@ -7215,7 +6833,6 @@
Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!");
}
- File frameworkDir = new File(Environment.getRootDirectory(), "framework");
final VersionInfo ver = mSettings.getInternalVersion();
mIsUpgrade =
@@ -7267,286 +6884,23 @@
// Prepare apex package info before scanning APKs, these information are needed when
// scanning apk in apex.
mApexManager.scanApexPackagesTraced(packageParser, executorService);
- // Collect vendor/product/system_ext overlay packages. (Do this before scanning
- // any apps.)
- // For security and version matching reason, only consider overlay packages if they
- // reside in the right directory.
- for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
- final ScanPartition partition = mDirsToScanAsSystem.get(i);
- if (partition.getOverlayFolder() == null) {
- continue;
- }
- scanDirTracedLI(partition.getOverlayFolder(), systemParseFlags,
- systemScanFlags | partition.scanFlag, 0,
- packageParser, executorService);
- }
- scanDirTracedLI(frameworkDir, systemParseFlags,
- systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
- packageParser, executorService);
- if (!mPackages.containsKey("android")) {
- throw new IllegalStateException(
- "Failed to load frameworks package; check log for warnings");
- }
- for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
- final ScanPartition partition = mDirsToScanAsSystem.get(i);
- if (partition.getPrivAppFolder() != null) {
- scanDirTracedLI(partition.getPrivAppFolder(), systemParseFlags,
- systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
- packageParser, executorService);
- }
- scanDirTracedLI(partition.getAppFolder(), systemParseFlags,
- systemScanFlags | partition.scanFlag, 0,
- packageParser, executorService);
- }
-
+ final InitAndSystemPackageHelper helper = new InitAndSystemPackageHelper(this);
+ helper.scanSystemDirs(mDirsToScanAsSystem, mIsUpgrade,
+ packageParser, executorService, mPlatformPackage, mIsPreNMR1Upgrade,
+ systemParseFlags, systemScanFlags);
// Parse overlay configuration files to set default enable state, mutability, and
// priority of system overlays.
mOverlayConfig = OverlayConfig.initializeSystemInstance(
consumer -> mPmInternal.forEachPackage(
pkg -> consumer.accept(pkg, pkg.isSystem())));
-
- // Prune any system packages that no longer exist.
- final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
- // Stub packages must either be replaced with full versions in the /data
- // partition or be disabled.
- final List<String> stubSystemApps = new ArrayList<>();
final int[] userIds = mUserManager.getUserIds();
- if (!mOnlyCore) {
- // do this first before mucking with mPackages for the "expecting better" case
- final int numPackages = mPackages.size();
- for (int index = 0; index < numPackages; index++) {
- final AndroidPackage pkg = mPackages.valueAt(index);
- if (pkg.isStub()) {
- stubSystemApps.add(pkg.getPackageName());
- }
- }
-
- // Iterates PackageSettings in reversed order because the item could be removed
- // during the iteration.
- for (int index = packageSettings.size() - 1; index >= 0; index--) {
- final PackageSetting ps = packageSettings.valueAt(index);
-
- /*
- * If this is not a system app, it can't be a
- * disable system app.
- */
- if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- continue;
- }
-
- /*
- * If the package is scanned, it's not erased.
- */
- final AndroidPackage scannedPkg = mPackages.get(ps.name);
- if (scannedPkg != null) {
- /*
- * If the system app is both scanned and in the
- * disabled packages list, then it must have been
- * added via OTA. Remove it from the currently
- * scanned package so the previously user-installed
- * application can be scanned.
- */
- if (mSettings.isDisabledSystemPackageLPr(ps.name)) {
- logCriticalInfo(Log.WARN,
- "Expecting better updated system app for " + ps.name
- + "; removing system app. Last known"
- + " codePath=" + ps.getPathString()
- + ", versionCode=" + ps.versionCode
- + "; scanned versionCode=" + scannedPkg.getLongVersionCode());
- removePackageLI(scannedPkg, true);
- mExpectingBetter.put(ps.name, ps.getPath());
- }
-
- continue;
- }
-
- if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
- logCriticalInfo(Log.WARN, "System package " + ps.name
- + " no longer exists; its data will be wiped");
- removePackageDataLIF(ps, userIds, null, 0, false);
- } else {
- // we still have a disabled system package, but, it still might have
- // been removed. check the code path still exists and check there's
- // still a package. the latter can happen if an OTA keeps the same
- // code path, but, changes the package name.
- final PackageSetting disabledPs =
- mSettings.getDisabledSystemPkgLPr(ps.name);
- if (disabledPs.getPath() == null || !disabledPs.getPath().exists()
- || disabledPs.pkg == null) {
- possiblyDeletedUpdatedSystemApps.add(ps.name);
- } else {
- // We're expecting that the system app should remain disabled, but add
- // it to expecting better to recover in case the data version cannot
- // be scanned.
- mExpectingBetter.put(disabledPs.name, disabledPs.getPath());
- }
- }
- }
- }
-
- final int cachedSystemApps = PackageCacher.sCachedPackageReadCount.get();
-
- // Remove any shared userIDs that have no associated packages
- mSettings.pruneSharedUsersLPw();
- final long systemScanTime = SystemClock.uptimeMillis() - startTime;
- final int systemPackagesCount = mPackages.size();
- Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime
- + " ms, packageCount: " + systemPackagesCount
- + " , timePerPackage: "
- + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount)
- + " , cached: " + cachedSystemApps);
- if (mIsUpgrade && systemPackagesCount > 0) {
- //CHECKSTYLE:OFF IndentationCheck
- FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
- BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME,
- systemScanTime / systemPackagesCount);
- //CHECKSTYLE:ON IndentationCheck
- }
- if (!mOnlyCore) {
- EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
- SystemClock.uptimeMillis());
- scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,
- packageParser, executorService);
-
- }
-
+ helper.cleanupSystemPackagesAndInstallStubs(mDirsToScanAsSystem,
+ mIsUpgrade, packageParser, executorService, mOnlyCore, packageSettings,
+ startTime, mAppInstallDir, mPlatformPackage, mIsPreNMR1Upgrade,
+ scanFlags, systemParseFlags, systemScanFlags, userIds);
packageParser.close();
- List<Runnable> unfinishedTasks = executorService.shutdownNow();
- if (!unfinishedTasks.isEmpty()) {
- throw new IllegalStateException("Not all tasks finished before calling close: "
- + unfinishedTasks);
- }
-
- if (!mOnlyCore) {
- // Remove disable package settings for updated system apps that were
- // removed via an OTA. If the update is no longer present, remove the
- // app completely. Otherwise, revoke their system privileges.
- for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {
- final String packageName = possiblyDeletedUpdatedSystemApps.get(i);
- final AndroidPackage pkg = mPackages.get(packageName);
- final String msg;
-
- // remove from the disabled system list; do this first so any future
- // scans of this package are performed without this state
- mSettings.removeDisabledSystemPackageLPw(packageName);
-
- if (pkg == null) {
- // should have found an update, but, we didn't; remove everything
- msg = "Updated system package " + packageName
- + " no longer exists; removing its data";
- // Actual deletion of code and data will be handled by later
- // reconciliation step
- } else {
- // found an update; revoke system privileges
- msg = "Updated system package " + packageName
- + " no longer exists; rescanning package on data";
-
- // NOTE: We don't do anything special if a stub is removed from the
- // system image. But, if we were [like removing the uncompressed
- // version from the /data partition], this is where it'd be done.
-
- // remove the package from the system and re-scan it without any
- // special privileges
- removePackageLI(pkg, true);
- try {
- final File codePath = new File(pkg.getPath());
- scanPackageTracedLI(codePath, 0, scanFlags, 0, null);
- } catch (PackageManagerException e) {
- Slog.e(TAG, "Failed to parse updated, ex-system package: "
- + e.getMessage());
- }
- }
-
- // one final check. if we still have a package setting [ie. it was
- // previously scanned and known to the system], but, we don't have
- // a package [ie. there was an error scanning it from the /data
- // partition], completely remove the package data.
- final PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps != null && mPackages.get(packageName) == null) {
- removePackageDataLIF(ps, userIds, null, 0, false);
-
- }
- logCriticalInfo(Log.WARN, msg);
- }
-
- /*
- * Make sure all system apps that we expected to appear on
- * the userdata partition actually showed up. If they never
- * appeared, crawl back and revive the system version.
- */
- for (int i = 0; i < mExpectingBetter.size(); i++) {
- final String packageName = mExpectingBetter.keyAt(i);
- if (!mPackages.containsKey(packageName)) {
- final File scanFile = mExpectingBetter.valueAt(i);
-
- logCriticalInfo(Log.WARN, "Expected better " + packageName
- + " but never showed up; reverting to system");
-
- @ParseFlags int reparseFlags = 0;
- @ScanFlags int rescanFlags = 0;
- for (int i1 = mDirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
- final ScanPartition partition = mDirsToScanAsSystem.get(i1);
- if (partition.containsPrivApp(scanFile)) {
- reparseFlags = systemParseFlags;
- rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
- | partition.scanFlag;
- break;
- }
- if (partition.containsApp(scanFile)) {
- reparseFlags = systemParseFlags;
- rescanFlags = systemScanFlags | partition.scanFlag;
- break;
- }
- }
- if (rescanFlags == 0) {
- Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
- continue;
- }
- mSettings.enableSystemPackageLPw(packageName);
-
- try {
- final AndroidPackage newPkg = scanPackageTracedLI(
- scanFile, reparseFlags, rescanFlags, 0, null);
- // We rescanned a stub, add it to the list of stubbed system packages
- if (newPkg.isStub()) {
- stubSystemApps.add(packageName);
- }
- } catch (PackageManagerException e) {
- Slog.e(TAG, "Failed to parse original system package: "
- + e.getMessage());
- }
- }
- }
-
- // Uncompress and install any stubbed system applications.
- // This must be done last to ensure all stubs are replaced or disabled.
- installSystemStubPackages(stubSystemApps, scanFlags);
-
- final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get()
- - cachedSystemApps;
-
- final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;
- final int dataPackagesCount = mPackages.size() - systemPackagesCount;
- Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
- + " ms, packageCount: " + dataPackagesCount
- + " , timePerPackage: "
- + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
- + " , cached: " + cachedNonSystemApps);
- if (mIsUpgrade && dataPackagesCount > 0) {
- //CHECKSTYLE:OFF IndentationCheck
- FrameworkStatsLog.write(
- FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
- BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME,
- dataScanTime / dataPackagesCount);
- //CHECKSTYLE:OFF IndentationCheck
- }
- }
- mExpectingBetter.clear();
-
- mSettings.pruneRenamedPackagesLPw();
-
// Resolve the storage manager.
mStorageManagerPackage = getStorageManagerPackageName();
@@ -7637,7 +6991,7 @@
}
List<String> deferPackages = reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL,
UserHandle.USER_SYSTEM, storageFlags, true /* migrateAppData */,
- true /* onlyCoreApps */);
+ true /* onlyCoreApps */, null);
mPrepareAppDataFuture = SystemServerInitThreadPool.submit(() -> {
TimingsTraceLog traceLog = new TimingsTraceLog("SystemServerTimingAsync",
Trace.TRACE_TAG_PACKAGE_MANAGER);
@@ -7840,234 +7194,10 @@
Slog.i(TAG, "Fix for b/169414761 is applied");
}
- /**
- * Uncompress and install stub applications.
- * <p>In order to save space on the system partition, some applications are shipped in a
- * compressed form. In addition the compressed bits for the full application, the
- * system image contains a tiny stub comprised of only the Android manifest.
- * <p>During the first boot, attempt to uncompress and install the full application. If
- * the application can't be installed for any reason, disable the stub and prevent
- * uncompressing the full application during future boots.
- * <p>In order to forcefully attempt an installation of a full application, go to app
- * settings and enable the application.
- */
- private void installSystemStubPackages(@NonNull List<String> systemStubPackageNames,
- @ScanFlags int scanFlags) {
- for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) {
- final String packageName = systemStubPackageNames.get(i);
- // skip if the system package is already disabled
- if (mSettings.isDisabledSystemPackageLPr(packageName)) {
- systemStubPackageNames.remove(i);
- continue;
- }
- // skip if the package isn't installed (?!); this should never happen
- final AndroidPackage pkg = mPackages.get(packageName);
- if (pkg == null) {
- systemStubPackageNames.remove(i);
- continue;
- }
- // skip if the package has been disabled by the user
- final PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps != null) {
- final int enabledState = ps.getEnabled(UserHandle.USER_SYSTEM);
- if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
- systemStubPackageNames.remove(i);
- continue;
- }
- }
- // install the package to replace the stub on /system
- try {
- installStubPackageLI(pkg, 0, scanFlags);
- ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
- UserHandle.USER_SYSTEM, "android");
- systemStubPackageNames.remove(i);
- } catch (PackageManagerException e) {
- Slog.e(TAG, "Failed to parse uncompressed system package: " + e.getMessage());
- }
- // any failed attempt to install the package will be cleaned up later
- }
- // disable any stub still left; these failed to install the full application
- for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) {
- final String pkgName = systemStubPackageNames.get(i);
- final PackageSetting ps = mSettings.getPackageLPr(pkgName);
- ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
- UserHandle.USER_SYSTEM, "android");
- logCriticalInfo(Log.ERROR, "Stub disabled; pkg: " + pkgName);
- }
- }
- /**
- * Extract, install and enable a stub package.
- * <p>If the compressed file can not be extracted / installed for any reason, the stub
- * APK will be installed and the package will be disabled. To recover from this situation,
- * the user will need to go into system settings and re-enable the package.
- */
- private boolean enableCompressedPackage(AndroidPackage stubPkg,
- @NonNull PackageSetting stubPkgSetting) {
- final int parseFlags = mDefParseFlags | ParsingPackageUtils.PARSE_CHATTY
- | ParsingPackageUtils.PARSE_ENFORCE_CODE;
- synchronized (mInstallLock) {
- final AndroidPackage pkg;
- try (PackageFreezer freezer =
- freezePackage(stubPkg.getPackageName(), "setEnabledSetting")) {
- pkg = installStubPackageLI(stubPkg, parseFlags, 0 /*scanFlags*/);
- synchronized (mLock) {
- prepareAppDataAfterInstallLIF(pkg);
- try {
- updateSharedLibrariesLocked(pkg, stubPkgSetting, null, null,
- Collections.unmodifiableMap(mPackages));
- } catch (PackageManagerException e) {
- Slog.w(TAG, "updateAllSharedLibrariesLPw failed: ", e);
- }
- mPermissionManager.onPackageInstalled(pkg,
- PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT,
- UserHandle.USER_ALL);
- writeSettingsLPrTEMP();
- }
- } catch (PackageManagerException e) {
- // Whoops! Something went very wrong; roll back to the stub and disable the package
- try (PackageFreezer freezer =
- freezePackage(stubPkg.getPackageName(), "setEnabledSetting")) {
- synchronized (mLock) {
- // NOTE: Ensure the system package is enabled; even for a compressed stub.
- // If we don't, installing the system package fails during scan
- enableSystemPackageLPw(stubPkg);
- }
- installPackageFromSystemLIF(stubPkg.getPath(),
- mUserManager.getUserIds() /*allUserHandles*/, null /*origUserHandles*/,
- true /*writeSettings*/);
- } catch (PackageManagerException pme) {
- // Serious WTF; we have to be able to install the stub
- Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.getPackageName(),
- pme);
- } finally {
- // Disable the package; the stub by itself is not runnable
- synchronized (mLock) {
- final PackageSetting stubPs = mSettings.getPackageLPr(
- stubPkg.getPackageName());
- if (stubPs != null) {
- stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED,
- UserHandle.USER_SYSTEM, "android");
- }
- writeSettingsLPrTEMP();
- }
- }
- return false;
- }
- clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
- | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
- mDexManager.notifyPackageUpdated(pkg.getPackageName(),
- pkg.getBaseApkPath(), pkg.getSplitCodePaths());
- }
- return true;
- }
-
- private AndroidPackage installStubPackageLI(AndroidPackage stubPkg,
- @ParseFlags int parseFlags, @ScanFlags int scanFlags)
- throws PackageManagerException {
- if (DEBUG_COMPRESSION) {
- Slog.i(TAG, "Uncompressing system stub; pkg: " + stubPkg.getPackageName());
- }
- // uncompress the binary to its eventual destination on /data
- final File scanFile = decompressPackage(stubPkg.getPackageName(), stubPkg.getPath());
- if (scanFile == null) {
- throw new PackageManagerException(
- "Unable to decompress stub at " + stubPkg.getPath());
- }
- synchronized (mLock) {
- mSettings.disableSystemPackageLPw(stubPkg.getPackageName(), true /*replaced*/);
- }
- removePackageLI(stubPkg, true /*chatty*/);
- try {
- return scanPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null);
- } catch (PackageManagerException e) {
- Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(),
- e);
- // Remove the failed install
- removeCodePathLI(scanFile);
- throw e;
- }
- }
-
- /**
- * Decompresses the given package on the system image onto
- * the /data partition.
- * @return The directory the package was decompressed into. Otherwise, {@code null}.
- */
- private File decompressPackage(String packageName, String codePath) {
- final File[] compressedFiles = getCompressedFiles(codePath);
- if (compressedFiles == null || compressedFiles.length == 0) {
- if (DEBUG_COMPRESSION) {
- Slog.i(TAG, "No files to decompress: " + codePath);
- }
- return null;
- }
- final File dstCodePath =
- getNextCodePath(Environment.getDataAppDirectory(null), packageName);
- int ret = PackageManager.INSTALL_SUCCEEDED;
- try {
- makeDirRecursive(dstCodePath, 0755);
- for (File srcFile : compressedFiles) {
- final String srcFileName = srcFile.getName();
- final String dstFileName = srcFileName.substring(
- 0, srcFileName.length() - COMPRESSED_EXTENSION.length());
- final File dstFile = new File(dstCodePath, dstFileName);
- ret = decompressFile(srcFile, dstFile);
- if (ret != PackageManager.INSTALL_SUCCEEDED) {
- logCriticalInfo(Log.ERROR, "Failed to decompress"
- + "; pkg: " + packageName
- + ", file: " + dstFileName);
- break;
- }
- }
- } catch (ErrnoException e) {
- logCriticalInfo(Log.ERROR, "Failed to decompress"
- + "; pkg: " + packageName
- + ", err: " + e.errno);
- }
- if (ret == PackageManager.INSTALL_SUCCEEDED) {
- final File libraryRoot = new File(dstCodePath, LIB_DIR_NAME);
- NativeLibraryHelper.Handle handle = null;
- try {
- handle = NativeLibraryHelper.Handle.create(dstCodePath);
- ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
- null /*abiOverride*/, false /*isIncremental*/);
- } catch (IOException e) {
- logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
- + "; pkg: " + packageName);
- ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
- } finally {
- IoUtils.closeQuietly(handle);
- }
- }
- if (ret == PackageManager.INSTALL_SUCCEEDED) {
- // NOTE: During boot, we have to delay releasing cblocks for no other reason than
- // we cannot retrieve the setting {@link Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL}.
- // When we no longer need to read that setting, cblock release can occur always
- // occur here directly
- if (!mSystemReady) {
- if (mReleaseOnSystemReady == null) {
- mReleaseOnSystemReady = new ArrayList<>();
- }
- mReleaseOnSystemReady.add(dstCodePath);
- } else {
- final ContentResolver resolver = mContext.getContentResolver();
- F2fsUtils.releaseCompressedBlocks(resolver, dstCodePath);
- }
- }
- if (ret != PackageManager.INSTALL_SUCCEEDED) {
- if (!dstCodePath.exists()) {
- return null;
- }
- removeCodePathLI(dstCodePath);
- return null;
- }
-
- return dstCodePath;
- }
@GuardedBy("mLock")
void updateInstantAppInstallerLocked(String modifiedPackage) {
@@ -9017,7 +8147,7 @@
/**
* Update given intent when being used to request {@link ResolveInfo}.
*/
- private Intent updateIntentForResolve(Intent intent) {
+ private static Intent updateIntentForResolve(Intent intent) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
}
@@ -9822,7 +8952,7 @@
return isCompatSignatureUpdateNeeded(getSettingsVersionForPackage(pkg));
}
- private static boolean isCompatSignatureUpdateNeeded(VersionInfo ver) {
+ static boolean isCompatSignatureUpdateNeeded(VersionInfo ver) {
return ver.databaseVersion < DatabaseVersion.SIGNATURE_END_ENTITY;
}
@@ -9830,7 +8960,7 @@
return isRecoverSignatureUpdateNeeded(getSettingsVersionForPackage(pkg));
}
- private static boolean isRecoverSignatureUpdateNeeded(VersionInfo ver) {
+ static boolean isRecoverSignatureUpdateNeeded(VersionInfo ver) {
return ver.databaseVersion < DatabaseVersion.SIGNATURE_MALFORMED_RECOVER;
}
@@ -10370,69 +9500,38 @@
}
@GuardedBy("mLock")
- private ResolveInfo findPersistentPreferredActivityLP(Intent intent, String resolvedType,
+ private ResolveInfo findPersistentPreferredActivityLP(Intent intent,
+ String resolvedType,
int flags, List<ResolveInfo> query, boolean debug, int userId) {
- final int N = query.size();
- PersistentPreferredIntentResolver ppir = mSettings.getPersistentPreferredActivities(userId);
- // Get the list of persistent preferred activities that handle the intent
- if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for presistent preferred activities...");
- List<PersistentPreferredActivity> pprefs = ppir != null
- ? ppir.queryIntent(intent, resolvedType,
- (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
- userId)
- : null;
- if (pprefs != null && pprefs.size() > 0) {
- final int M = pprefs.size();
- for (int i=0; i<M; i++) {
- final PersistentPreferredActivity ppa = pprefs.get(i);
- if (DEBUG_PREFERRED || debug) {
- Slog.v(TAG, "Checking PersistentPreferredActivity ds="
- + (ppa.countDataSchemes() > 0 ? ppa.getDataScheme(0) : "<none>")
- + "\n component=" + ppa.mComponent);
- ppa.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
- }
- final ActivityInfo ai = getActivityInfo(ppa.mComponent,
- flags | MATCH_DISABLED_COMPONENTS, userId);
- if (DEBUG_PREFERRED || debug) {
- Slog.v(TAG, "Found persistent preferred activity:");
- if (ai != null) {
- ai.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
- } else {
- Slog.v(TAG, " null");
- }
- }
- if (ai == null) {
- // This previously registered persistent preferred activity
- // component is no longer known. Ignore it and do NOT remove it.
- continue;
- }
- for (int j=0; j<N; j++) {
- final ResolveInfo ri = query.get(j);
- if (!ri.activityInfo.applicationInfo.packageName
- .equals(ai.applicationInfo.packageName)) {
- continue;
- }
- if (!ri.activityInfo.name.equals(ai.name)) {
- continue;
- }
- // Found a persistent preference that can handle the intent.
- if (DEBUG_PREFERRED || debug) {
- Slog.v(TAG, "Returning persistent preferred activity: " +
- ri.activityInfo.packageName + "/" + ri.activityInfo.name);
- }
- return ri;
- }
- }
- }
- return null;
+ return mComputer.findPersistentPreferredActivityLP(intent,
+ resolvedType,
+ flags, query, debug, userId);
}
- private boolean isHomeIntent(Intent intent) {
+ private static boolean isHomeIntent(Intent intent) {
return ACTION_MAIN.equals(intent.getAction())
&& intent.hasCategory(CATEGORY_HOME)
&& intent.hasCategory(CATEGORY_DEFAULT);
}
+
+ // findPreferredActivityBody returns two items: a "things changed" flag and a
+ // ResolveInfo, which is the preferred activity itself.
+ private static class FindPreferredActivityBodyResult {
+ boolean mChanged;
+ ResolveInfo mPreferredResolveInfo;
+ }
+
+ private FindPreferredActivityBodyResult findPreferredActivityInternal(
+ Intent intent, String resolvedType, int flags,
+ List<ResolveInfo> query, boolean always,
+ boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
+ return mComputer.findPreferredActivityInternal(
+ intent, resolvedType, flags,
+ query, always,
+ removeMatches, debug, userId, queryMayBeFiltered);
+ }
+
ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags,
List<ResolveInfo> query, boolean always,
boolean removeMatches, boolean debug, int userId) {
@@ -10451,206 +9550,22 @@
+ " is holding mLock", new Throwable());
}
if (!mUserManager.exists(userId)) return null;
- final int callingUid = Binder.getCallingUid();
- // Do NOT hold the packages lock; this calls up into the settings provider which
- // could cause a deadlock.
- final boolean isDeviceProvisioned =
- android.provider.Settings.Global.getInt(mContext.getContentResolver(),
- android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1;
- flags = updateFlagsForResolve(
- flags, userId, callingUid, false /*includeInstantApps*/,
- isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
- flags));
- intent = updateIntentForResolve(intent);
- // writer
- synchronized (mLock) {
- // Try to find a matching persistent preferred activity.
- ResolveInfo pri = findPersistentPreferredActivityLP(intent, resolvedType, flags, query,
- debug, userId);
- // If a persistent preferred activity matched, use it.
- if (pri != null) {
- return pri;
+ FindPreferredActivityBodyResult body = findPreferredActivityInternal(
+ intent, resolvedType, flags, query, always,
+ removeMatches, debug, userId, queryMayBeFiltered);
+ if (body.mChanged) {
+ if (DEBUG_PREFERRED) {
+ Slog.v(TAG, "Preferred activity bookkeeping changed; writing restrictions");
}
-
- PreferredIntentResolver pir = mSettings.getPreferredActivities(userId);
- // Get the list of preferred activities that handle the intent
- if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for preferred activities...");
- List<PreferredActivity> prefs = pir != null
- ? pir.queryIntent(intent, resolvedType,
- (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
- userId)
- : null;
- if (prefs != null && prefs.size() > 0) {
- boolean changed = false;
- try {
- // First figure out how good the original match set is.
- // We will only allow preferred activities that came
- // from the same match quality.
- int match = 0;
-
- if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Figuring out best match...");
-
- final int N = query.size();
- for (int j=0; j<N; j++) {
- final ResolveInfo ri = query.get(j);
- if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Match for " + ri.activityInfo
- + ": 0x" + Integer.toHexString(match));
- if (ri.match > match) {
- match = ri.match;
- }
- }
-
- if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Best match: 0x"
- + Integer.toHexString(match));
-
- match &= IntentFilter.MATCH_CATEGORY_MASK;
- final int M = prefs.size();
- for (int i=0; i<M; i++) {
- final PreferredActivity pa = prefs.get(i);
- if (DEBUG_PREFERRED || debug) {
- Slog.v(TAG, "Checking PreferredActivity ds="
- + (pa.countDataSchemes() > 0 ? pa.getDataScheme(0) : "<none>")
- + "\n component=" + pa.mPref.mComponent);
- pa.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
- }
- if (pa.mPref.mMatch != match) {
- if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Skipping bad match "
- + Integer.toHexString(pa.mPref.mMatch));
- continue;
- }
- // If it's not an "always" type preferred activity and that's what we're
- // looking for, skip it.
- if (always && !pa.mPref.mAlways) {
- if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Skipping mAlways=false entry");
- continue;
- }
- final ActivityInfo ai = getActivityInfo(
- pa.mPref.mComponent, flags | MATCH_DISABLED_COMPONENTS
- | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
- userId);
- if (DEBUG_PREFERRED || debug) {
- Slog.v(TAG, "Found preferred activity:");
- if (ai != null) {
- ai.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
- } else {
- Slog.v(TAG, " null");
- }
- }
- final boolean excludeSetupWizardHomeActivity = isHomeIntent(intent)
- && !isDeviceProvisioned;
- final boolean allowSetMutation = !excludeSetupWizardHomeActivity
- && !queryMayBeFiltered;
- if (ai == null) {
- // Do not remove launcher's preferred activity during SetupWizard
- // due to it may not install yet
- if (!allowSetMutation) {
- continue;
- }
-
- // This previously registered preferred activity
- // component is no longer known. Most likely an update
- // to the app was installed and in the new version this
- // component no longer exists. Clean it up by removing
- // it from the preferred activities list, and skip it.
- Slog.w(TAG, "Removing dangling preferred activity: "
- + pa.mPref.mComponent);
- pir.removeFilter(pa);
- changed = true;
- continue;
- }
- for (int j=0; j<N; j++) {
- final ResolveInfo ri = query.get(j);
- if (!ri.activityInfo.applicationInfo.packageName
- .equals(ai.applicationInfo.packageName)) {
- continue;
- }
- if (!ri.activityInfo.name.equals(ai.name)) {
- continue;
- }
-
- if (removeMatches && allowSetMutation) {
- pir.removeFilter(pa);
- changed = true;
- if (DEBUG_PREFERRED) {
- Slog.v(TAG, "Removing match " + pa.mPref.mComponent);
- }
- break;
- }
-
- // Okay we found a previously set preferred or last chosen app.
- // If the result set is different from when this
- // was created, and is not a subset of the preferred set, we need to
- // clear it and re-ask the user their preference, if we're looking for
- // an "always" type entry.
-
- if (always && !pa.mPref.sameSet(query, excludeSetupWizardHomeActivity)) {
- if (pa.mPref.isSuperset(query, excludeSetupWizardHomeActivity)) {
- if (allowSetMutation) {
- // some components of the set are no longer present in
- // the query, but the preferred activity can still be reused
- if (DEBUG_PREFERRED) {
- Slog.i(TAG, "Result set changed, but PreferredActivity"
- + " is still valid as only non-preferred"
- + " components were removed for " + intent
- + " type " + resolvedType);
- }
- // remove obsolete components and re-add the up-to-date
- // filter
- PreferredActivity freshPa = new PreferredActivity(pa,
- pa.mPref.mMatch,
- pa.mPref.discardObsoleteComponents(query),
- pa.mPref.mComponent,
- pa.mPref.mAlways);
- pir.removeFilter(pa);
- pir.addFilter(freshPa);
- changed = true;
- } else {
- if (DEBUG_PREFERRED) {
- Slog.i(TAG, "Do not remove preferred activity");
- }
- }
- } else {
- if (allowSetMutation) {
- Slog.i(TAG,
- "Result set changed, dropping preferred activity "
- + "for " + intent + " type "
- + resolvedType);
- if (DEBUG_PREFERRED) {
- Slog.v(TAG,
- "Removing preferred activity since set changed "
- + pa.mPref.mComponent);
- }
- pir.removeFilter(pa);
- // Re-add the filter as a "last chosen" entry (!always)
- PreferredActivity lastChosen = new PreferredActivity(
- pa, pa.mPref.mMatch, null, pa.mPref.mComponent,
- false);
- pir.addFilter(lastChosen);
- changed = true;
- }
- return null;
- }
- }
-
- // Yay! Either the set matched or we're looking for the last chosen
- if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Returning preferred activity: "
- + ri.activityInfo.packageName + "/" + ri.activityInfo.name);
- return ri;
- }
- }
- } finally {
- if (changed) {
- if (DEBUG_PREFERRED) {
- Slog.v(TAG, "Preferred activity bookkeeping changed; writing restrictions");
- }
- scheduleWritePackageRestrictionsLocked(userId);
- }
- }
+ synchronized (mLock) {
+ scheduleWritePackageRestrictionsLocked(userId);
}
}
- if (DEBUG_PREFERRED || debug) Slog.v(TAG, "No preferred activity to return");
- return null;
+ if ((DEBUG_PREFERRED || debug) && body.mPreferredResolveInfo == null) {
+ Slog.v(TAG, "No preferred activity to return");
+ }
+ return body.mPreferredResolveInfo;
}
/*
@@ -11795,169 +10710,20 @@
return finalList;
}
- private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags,
- long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
- try {
- scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
- private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,
- PackageParser2 packageParser, ExecutorService executorService) {
- final File[] files = scanDir.listFiles();
- if (ArrayUtils.isEmpty(files)) {
- Log.d(TAG, "No files in app dir " + scanDir);
- return;
- }
-
- if (DEBUG_PACKAGE_SCANNING) {
- Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags
- + " flags=0x" + Integer.toHexString(parseFlags));
- }
-
- ParallelPackageParser parallelPackageParser =
- new ParallelPackageParser(packageParser, executorService);
-
- // Submit files for parsing in parallel
- int fileCount = 0;
- for (File file : files) {
- final boolean isPackage = (isApkFile(file) || file.isDirectory())
- && !PackageInstallerService.isStageName(file.getName());
- if (!isPackage) {
- // Ignore entries which are not packages
- continue;
- }
- parallelPackageParser.submit(file, parseFlags);
- fileCount++;
- }
-
- // Process results one by one
- for (; fileCount > 0; fileCount--) {
- ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
- Throwable throwable = parseResult.throwable;
- int errorCode = PackageManager.INSTALL_SUCCEEDED;
- String errorMsg = null;
-
- if (throwable == null) {
- // TODO(toddke): move lower in the scan chain
- // Static shared libraries have synthetic package names
- if (parseResult.parsedPackage.isStaticSharedLibrary()) {
- renameStaticSharedLibraryPackage(parseResult.parsedPackage);
- }
- try {
- addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
- currentTime, null);
- } catch (PackageManagerException e) {
- errorCode = e.error;
- errorMsg = "Failed to scan " + parseResult.scanFile + ": " + e.getMessage();
- Slog.w(TAG, errorMsg);
- }
- } else if (throwable instanceof PackageManagerException) {
- PackageManagerException e = (PackageManagerException) throwable;
- errorCode = e.error;
- errorMsg = "Failed to parse " + parseResult.scanFile + ": " + e.getMessage();
- Slog.w(TAG, errorMsg);
- } else {
- throw new IllegalStateException("Unexpected exception occurred while parsing "
- + parseResult.scanFile, throwable);
- }
-
- if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0 && errorCode != INSTALL_SUCCEEDED) {
- mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath(), errorMsg);
- }
-
- // Delete invalid userdata apps
- if ((scanFlags & SCAN_AS_SYSTEM) == 0
- && errorCode != PackageManager.INSTALL_SUCCEEDED) {
- logCriticalInfo(Log.WARN,
- "Deleting invalid package at " + parseResult.scanFile);
- removeCodePathLI(parseResult.scanFile);
- }
- }
- }
-
public static void reportSettingsProblem(int priority, String msg) {
logCriticalInfo(priority, msg);
}
- private void collectCertificatesLI(PackageSetting ps, ParsedPackage parsedPackage,
- boolean forceCollect, boolean skipVerify) throws PackageManagerException {
- // When upgrading from pre-N MR1, verify the package time stamp using the package
- // directory and not the APK file.
- final long lastModifiedTime = mIsPreNMR1Upgrade
- ? new File(parsedPackage.getPath()).lastModified()
- : getLastModifiedTime(parsedPackage);
- final VersionInfo settingsVersionForPackage = getSettingsVersionForPackage(parsedPackage);
- if (ps != null && !forceCollect
- && ps.getPathString().equals(parsedPackage.getPath())
- && ps.timeStamp == lastModifiedTime
- && !isCompatSignatureUpdateNeeded(settingsVersionForPackage)
- && !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) {
- if (ps.signatures.mSigningDetails.getSignatures() != null
- && ps.signatures.mSigningDetails.getSignatures().length != 0
- && ps.signatures.mSigningDetails.getSignatureSchemeVersion()
- != SignatureSchemeVersion.UNKNOWN) {
- // Optimization: reuse the existing cached signing data
- // if the package appears to be unchanged.
- parsedPackage.setSigningDetails(
- new SigningDetails(ps.signatures.mSigningDetails));
- return;
- }
- Slog.w(TAG, "PackageSetting for " + ps.name
- + " is missing signatures. Collecting certs again to recover them.");
- } else {
- Slog.i(TAG, parsedPackage.getPath() + " changed; collecting certs"
- + (forceCollect ? " (forced)" : ""));
- }
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
- final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
- final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
- input, parsedPackage, skipVerify);
- if (result.isError()) {
- throw new PackageManagerException(
- result.getErrorCode(), result.getErrorMessage(), result.getException());
- }
- parsedPackage.setSigningDetails(result.getResult());
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
- /**
- * Clear the package profile if this was an upgrade and the package
- * version was updated.
- */
- private void maybeClearProfilesForUpgradesLI(
- @Nullable PackageSetting originalPkgSetting,
- @NonNull AndroidPackage pkg) {
- if (originalPkgSetting == null || !isDeviceUpgrading()) {
- return;
- }
- if (originalPkgSetting.versionCode == pkg.getVersionCode()) {
- return;
- }
-
- clearAppProfilesLIF(pkg);
- if (DEBUG_INSTALL) {
- Slog.d(TAG, originalPkgSetting.name
- + " clear profile due to version change "
- + originalPkgSetting.versionCode + " != "
- + pkg.getVersionCode());
- }
- }
/**
* Traces a package scan.
* @see #scanPackageLI(File, int, int, long, UserHandle)
*/
@GuardedBy({"mInstallLock", "mLock"})
- private AndroidPackage scanPackageTracedLI(File scanFile, final int parseFlags,
+ AndroidPackage scanPackageTracedLI(File scanFile, final int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
try {
@@ -11989,318 +10755,9 @@
renameStaticSharedLibraryPackage(parsedPackage);
}
- return addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user);
- }
-
- /**
- * Returns if forced apk verification can be skipped for the whole package, including splits.
- */
- private boolean canSkipForcedPackageVerification(AndroidPackage pkg) {
- if (!canSkipForcedApkVerification(pkg.getBaseApkPath())) {
- return false;
- }
- // TODO: Allow base and splits to be verified individually.
- String[] splitCodePaths = pkg.getSplitCodePaths();
- if (!ArrayUtils.isEmpty(splitCodePaths)) {
- for (int i = 0; i < splitCodePaths.length; i++) {
- if (!canSkipForcedApkVerification(splitCodePaths[i])) {
- return false;
- }
- }
- }
- return true;
- }
-
- /**
- * Returns if forced apk verification can be skipped, depending on current FSVerity setup and
- * whether the apk contains signed root hash. Note that the signer's certificate still needs to
- * match one in a trusted source, and should be done separately.
- */
- private boolean canSkipForcedApkVerification(String apkPath) {
- if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) {
- return VerityUtils.hasFsverity(apkPath);
- }
-
- try {
- final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
- if (rootHashObserved == null) {
- return false; // APK does not contain Merkle tree root hash.
- }
- synchronized (mInstallLock) {
- // Returns whether the observed root hash matches what kernel has.
- mInstaller.assertFsverityRootHashMatches(apkPath, rootHashObserved);
- return true;
- }
- } catch (InstallerException | IOException | DigestException |
- NoSuchAlgorithmException e) {
- Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e);
- }
- return false;
- }
-
- /**
- * Adds a new package to the internal data structures during platform initialization.
- * <p>After adding, the package is known to the system and available for querying.
- * <p>For packages located on the device ROM [eg. packages located in /system, /vendor,
- * etc...], additional checks are performed. Basic verification [such as ensuring
- * matching signatures, checking version codes, etc...] occurs if the package is
- * identical to a previously known package. If the package fails a signature check,
- * the version installed on /data will be removed. If the version of the new package
- * is less than or equal than the version on /data, it will be ignored.
- * <p>Regardless of the package location, the results are applied to the internal
- * structures and the package is made available to the rest of the system.
- * <p>NOTE: The return value should be removed. It's the passed in package object.
- */
- @GuardedBy({"mInstallLock", "mLock"})
- private AndroidPackage addForInitLI(ParsedPackage parsedPackage,
- @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
- @Nullable UserHandle user)
- throws PackageManagerException {
- final boolean scanSystemPartition =
- (parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
- final String renamedPkgName;
- final PackageSetting disabledPkgSetting;
- final boolean isSystemPkgUpdated;
- final boolean pkgAlreadyExists;
- PackageSetting pkgSetting;
-
- synchronized (mLock) {
- renamedPkgName = mSettings.getRenamedPackageLPr(parsedPackage.getRealPackage());
- final String realPkgName = getRealPackageName(parsedPackage, renamedPkgName);
- if (realPkgName != null) {
- ensurePackageRenamed(parsedPackage, renamedPkgName);
- }
- final PackageSetting originalPkgSetting = getOriginalPackageLocked(parsedPackage,
- renamedPkgName);
- final PackageSetting installedPkgSetting = mSettings.getPackageLPr(
- parsedPackage.getPackageName());
- pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
- pkgAlreadyExists = pkgSetting != null;
- final String disabledPkgName = pkgAlreadyExists
- ? pkgSetting.name : parsedPackage.getPackageName();
- if (scanSystemPartition && !pkgAlreadyExists
- && mSettings.getDisabledSystemPkgLPr(disabledPkgName) != null) {
- // The updated-package data for /system apk remains inconsistently
- // after the package data for /data apk is lost accidentally.
- // To recover it, enable /system apk and install it as non-updated system app.
- Slog.w(TAG, "Inconsistent package setting of updated system app for "
- + disabledPkgName + ". To recover it, enable the system app"
- + "and install it as non-updated system app.");
- mSettings.removeDisabledSystemPackageLPw(disabledPkgName);
- }
- disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(disabledPkgName);
- isSystemPkgUpdated = disabledPkgSetting != null;
-
- if (DEBUG_INSTALL && isSystemPkgUpdated) {
- Slog.d(TAG, "updatedPkg = " + disabledPkgSetting);
- }
-
- final SharedUserSetting sharedUserSetting = (parsedPackage.getSharedUserId() != null)
- ? mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
- 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
- : null;
- if (DEBUG_PACKAGE_SCANNING
- && (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0
- && sharedUserSetting != null) {
- Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
- + " (uid=" + sharedUserSetting.userId + "):"
- + " packages=" + sharedUserSetting.packages);
- }
-
- if (scanSystemPartition) {
- if (isSystemPkgUpdated) {
- // we're updating the disabled package, so, scan it as the package setting
- boolean isPlatformPackage = mPlatformPackage != null
- && Objects.equals(mPlatformPackage.getPackageName(),
- parsedPackage.getPackageName());
- final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
- null, disabledPkgSetting /* pkgSetting */,
- null /* disabledPkgSetting */, null /* originalPkgSetting */,
- null, parseFlags, scanFlags, isPlatformPackage, user, null);
- applyPolicy(parsedPackage, scanFlags, mPlatformPackage, true);
- final ScanResult scanResult =
- scanPackageOnlyLI(request, mInjector, mFactoryTest, -1L);
- if (scanResult.mExistingSettingCopied
- && scanResult.mRequest.mPkgSetting != null) {
- scanResult.mRequest.mPkgSetting.updateFrom(scanResult.mPkgSetting);
- }
- }
- }
- }
-
- final boolean newPkgChangedPaths = pkgAlreadyExists
- && !pkgSetting.getPathString().equals(parsedPackage.getPath());
- final boolean newPkgVersionGreater =
- pkgAlreadyExists && parsedPackage.getLongVersionCode() > pkgSetting.versionCode;
- final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
- && newPkgChangedPaths && newPkgVersionGreater;
- if (isSystemPkgBetter) {
- // The version of the application on /system is greater than the version on
- // /data. Switch back to the application on /system.
- // It's safe to assume the application on /system will correctly scan. If not,
- // there won't be a working copy of the application.
- synchronized (mLock) {
- // just remove the loaded entries from package lists
- mPackages.remove(pkgSetting.name);
- }
-
- logCriticalInfo(Log.WARN,
- "System package updated;"
- + " name: " + pkgSetting.name
- + "; " + pkgSetting.versionCode + " --> " + parsedPackage.getLongVersionCode()
- + "; " + pkgSetting.getPathString()
- + " --> " + parsedPackage.getPath());
-
- final InstallArgs args = createInstallArgsForExisting(
- pkgSetting.getPathString(), getAppDexInstructionSets(
- pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
- args.cleanUpResourcesLI();
- synchronized (mLock) {
- mSettings.enableSystemPackageLPw(pkgSetting.name);
- }
- }
-
- // The version of the application on the /system partition is less than or
- // equal to the version on the /data partition. Throw an exception and use
- // the application already installed on the /data partition.
- if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) {
- // In the case of a skipped package, commitReconciledScanResultLocked is not called to
- // add the object to the "live" data structures, so this is the final mutation step
- // for the package. Which means it needs to be finalized here to cache derived fields.
- // This is relevant for cases where the disabled system package is used for flags or
- // other metadata.
- parsedPackage.hideAsFinal();
- throw new PackageManagerException(Log.WARN, "Package " + parsedPackage.getPackageName()
- + " at " + parsedPackage.getPath() + " ignored: updated version "
- + (pkgAlreadyExists ? String.valueOf(pkgSetting.versionCode) : "unknown")
- + " better than this " + parsedPackage.getLongVersionCode());
- }
-
- // Verify certificates against what was last scanned. Force re-collecting certificate in two
- // special cases:
- // 1) when scanning system, force re-collect only if system is upgrading.
- // 2) when scannning /data, force re-collect only if the app is privileged (updated from
- // preinstall, or treated as privileged, e.g. due to shared user ID).
- final boolean forceCollect = scanSystemPartition ? mIsUpgrade
- : PackageManagerServiceUtils.isApkVerificationForced(pkgSetting);
- if (DEBUG_VERIFY && forceCollect) {
- Slog.d(TAG, "Force collect certificate of " + parsedPackage.getPackageName());
- }
-
- // Full APK verification can be skipped during certificate collection, only if the file is
- // in verified partition, or can be verified on access (when apk verity is enabled). In both
- // cases, only data in Signing Block is verified instead of the whole file.
- // TODO(b/136132412): skip for Incremental installation
- final boolean skipVerify = scanSystemPartition
- || (forceCollect && canSkipForcedPackageVerification(parsedPackage));
- collectCertificatesLI(pkgSetting, parsedPackage, forceCollect, skipVerify);
-
- // Reset profile if the application version is changed
- maybeClearProfilesForUpgradesLI(pkgSetting, parsedPackage);
-
- /*
- * A new system app appeared, but we already had a non-system one of the
- * same name installed earlier.
- */
- boolean shouldHideSystemApp = false;
- // A new application appeared on /system, but, we already have a copy of
- // the application installed on /data.
- if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
- && !pkgSetting.isSystem()) {
-
- if (!parsedPackage.getSigningDetails()
- .checkCapability(pkgSetting.signatures.mSigningDetails,
- SigningDetails.CertCapabilities.INSTALLED_DATA)
- && !pkgSetting.signatures.mSigningDetails.checkCapability(
- parsedPackage.getSigningDetails(),
- SigningDetails.CertCapabilities.ROLLBACK)) {
- logCriticalInfo(Log.WARN,
- "System package signature mismatch;"
- + " name: " + pkgSetting.name);
- try (@SuppressWarnings("unused") PackageFreezer freezer = freezePackage(
- parsedPackage.getPackageName(),
- "scanPackageInternalLI")) {
- deletePackageLIF(parsedPackage.getPackageName(), null, true,
- mUserManager.getUserIds(), 0, null, false);
- }
- pkgSetting = null;
- } else if (newPkgVersionGreater) {
- // The application on /system is newer than the application on /data.
- // Simply remove the application on /data [keeping application data]
- // and replace it with the version on /system.
- logCriticalInfo(Log.WARN,
- "System package enabled;"
- + " name: " + pkgSetting.name
- + "; " + pkgSetting.versionCode + " --> "
- + parsedPackage.getLongVersionCode()
- + "; " + pkgSetting.getPathString() + " --> "
- + parsedPackage.getPath());
- InstallArgs args = createInstallArgsForExisting(
- pkgSetting.getPathString(), getAppDexInstructionSets(
- pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
- synchronized (mInstallLock) {
- args.cleanUpResourcesLI();
- }
- } else {
- // The application on /system is older than the application on /data. Hide
- // the application on /system and the version on /data will be scanned later
- // and re-added like an update.
- shouldHideSystemApp = true;
- logCriticalInfo(Log.INFO,
- "System package disabled;"
- + " name: " + pkgSetting.name
- + "; old: " + pkgSetting.getPathString() + " @ "
- + pkgSetting.versionCode
- + "; new: " + parsedPackage.getPath() + " @ "
- + parsedPackage.getPath());
- }
- }
-
- final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags, scanFlags
- | SCAN_UPDATE_SIGNATURE, currentTime, user, null);
- if (scanResult.mSuccess) {
- synchronized (mLock) {
- boolean appIdCreated = false;
- try {
- final String pkgName = scanResult.mPkgSetting.name;
- final Map<String, ReconciledPackage> reconcileResult = reconcilePackagesLocked(
- new ReconcileRequest(
- Collections.singletonMap(pkgName, scanResult),
- mSharedLibraries,
- mPackages,
- Collections.singletonMap(
- pkgName, getSettingsVersionForPackage(parsedPackage)),
- Collections.singletonMap(pkgName,
- getSharedLibLatestVersionSetting(scanResult))),
- mSettings.getKeySetManagerService(), mInjector);
- appIdCreated = optimisticallyRegisterAppId(scanResult);
- commitReconciledScanResultLocked(
- reconcileResult.get(pkgName), mUserManager.getUserIds());
- } catch (PackageManagerException e) {
- if (appIdCreated) {
- cleanUpAppIdCreation(scanResult);
- }
- throw e;
- }
- }
- }
-
- if (shouldHideSystemApp) {
- synchronized (mLock) {
- mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true);
- }
- }
- if (mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
- if (pkgSetting != null && pkgSetting.isPackageLoading()) {
- // Continue monitoring loading progress of active incremental packages
- final IncrementalStatesCallback incrementalStatesCallback =
- new IncrementalStatesCallback(parsedPackage.getPackageName(), this);
- pkgSetting.setIncrementalStatesCallback(incrementalStatesCallback);
- mIncrementalManager.registerLoadingProgressCallback(parsedPackage.getPath(),
- new IncrementalProgressListener(parsedPackage.getPackageName(), this));
- }
- }
- return scanResult.mPkgSetting.pkg;
+ final InitAndSystemPackageHelper helper = new InitAndSystemPackageHelper(this);
+ return helper.addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user,
+ mPlatformPackage, mIsUpgrade, mIsPreNMR1Upgrade);
}
// TODO:(b/135203078): Move to parsing
@@ -13264,7 +11721,7 @@
}
}
- private void clearAppProfilesLIF(AndroidPackage pkg) {
+ void clearAppProfilesLIF(AndroidPackage pkg) {
if (pkg == null) {
Slog.wtf(TAG, "Package was null!", new Throwable());
return;
@@ -13333,7 +11790,7 @@
}
@GuardedBy("mLock")
- private void updateSharedLibrariesLocked(AndroidPackage pkg, PackageSetting pkgSetting,
+ void updateSharedLibrariesLocked(AndroidPackage pkg, PackageSetting pkgSetting,
@Nullable AndroidPackage changingLib, @Nullable PackageSetting changingLibSetting,
Map<String, AndroidPackage> availablePackages)
throws PackageManagerException {
@@ -13743,7 +12200,7 @@
// method. Also, we need to solve the problem of potentially creating a new shared user
// setting. That can probably be done later and patch things up after the fact.
@GuardedBy({"mInstallLock", "mLock"})
- private ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
+ ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
@@ -13956,7 +12413,7 @@
* <p>This may differ from the package's actual name if the application has already
* been installed under one of this package's original names.
*/
- private static @Nullable String getRealPackageName(@NonNull AndroidPackage pkg,
+ static @Nullable String getRealPackageName(@NonNull AndroidPackage pkg,
@Nullable String renamedPkgName) {
if (isPackageRenamed(pkg, renamedPkgName)) {
return pkg.getRealPackage();
@@ -13978,7 +12435,7 @@
* shared user [if any].
*/
@GuardedBy("mLock")
- private @Nullable PackageSetting getOriginalPackageLocked(@NonNull AndroidPackage pkg,
+ @Nullable PackageSetting getOriginalPackageLocked(@NonNull AndroidPackage pkg,
@Nullable String renamedPkgName) {
if (isPackageRenamed(pkg, renamedPkgName)) {
return null;
@@ -14017,7 +12474,7 @@
* <p>When we've already installed the package under an original name, update
* the new package so we can continue to have the old name.
*/
- private static void ensurePackageRenamed(@NonNull ParsedPackage parsedPackage,
+ static void ensurePackageRenamed(@NonNull ParsedPackage parsedPackage,
@NonNull String renamedPackageName) {
if (!parsedPackage.getOriginalPackages().contains(renamedPackageName)
|| parsedPackage.getPackageName().equals(renamedPackageName)) {
@@ -14130,7 +12587,7 @@
@GuardedBy("mInstallLock")
@VisibleForTesting
@NonNull
- static ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
+ ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
Injector injector,
boolean isUnderFactoryTest, long currentTime)
throws PackageManagerException {
@@ -14533,7 +12990,7 @@
* Implementation detail: This method must NOT have any side effect. It would
* ideally be static, but, it requires locks to read system state.
*/
- private static void applyPolicy(ParsedPackage parsedPackage,
+ static void applyPolicy(ParsedPackage parsedPackage,
final @ScanFlags int scanFlags, AndroidPackage platformPkg,
boolean isUpdatedSystemApp) {
if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
@@ -15819,8 +14276,7 @@
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
- void sendPackagesSuspendedForUser(String[] pkgList, int[] uidList, int userId,
- boolean suspended) {
+ void sendPackagesSuspendedForUser(String intent, String[] pkgList, int[] uidList, int userId) {
final List<List<String>> pkgsToSend = new ArrayList(pkgList.length);
final List<IntArray> uidsToSend = new ArrayList(pkgList.length);
final List<SparseArray<int[]>> allowListsToSend = new ArrayList(pkgList.length);
@@ -15861,11 +14317,8 @@
extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidsToSend.get(i).toArray());
final SparseArray<int[]> allowList = allowListsToSend.get(i).size() == 0
? null : allowListsToSend.get(i);
- sendPackageBroadcast(
- suspended ? Intent.ACTION_PACKAGES_SUSPENDED
- : Intent.ACTION_PACKAGES_UNSUSPENDED,
- null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null,
- userIds, null, allowList, null);
+ sendPackageBroadcast(intent, null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null,
+ null, userIds, null, allowList, null);
}
}
@@ -16179,6 +14632,8 @@
final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
final IntArray changedUids = new IntArray(packageNames.length);
+ final List<String> modifiedPackagesList = new ArrayList<>(packageNames.length);
+ final IntArray modifiedUids = new IntArray(packageNames.length);
final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
final boolean[] canSuspend = suspended ? canSuspendPackageForUserInternal(packageNames,
userId) : null;
@@ -16206,13 +14661,14 @@
unactionedPackages.add(packageName);
continue;
}
- boolean packageUnsuspended;
+ final boolean packageUnsuspended;
+ final boolean packageModified;
synchronized (mLock) {
if (suspended) {
- pkgSetting.addOrUpdateSuspension(callingPackage, dialogInfo, appExtras,
- launcherExtras, userId);
+ packageModified = pkgSetting.addOrUpdateSuspension(callingPackage,
+ dialogInfo, appExtras, launcherExtras, userId);
} else {
- pkgSetting.removeSuspension(callingPackage, userId);
+ packageModified = pkgSetting.removeSuspension(callingPackage, userId);
}
packageUnsuspended = !suspended && !pkgSetting.getSuspended(userId);
}
@@ -16220,18 +14676,29 @@
changedPackagesList.add(packageName);
changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
}
+ if (packageModified) {
+ modifiedPackagesList.add(packageName);
+ modifiedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
+ }
}
if (!changedPackagesList.isEmpty()) {
- final String[] changedPackages = changedPackagesList.toArray(
- new String[changedPackagesList.size()]);
- sendPackagesSuspendedForUser(changedPackages, changedUids.toArray(), userId, suspended);
+ final String[] changedPackages = changedPackagesList.toArray(new String[0]);
+ sendPackagesSuspendedForUser(
+ suspended ? Intent.ACTION_PACKAGES_SUSPENDED
+ : Intent.ACTION_PACKAGES_UNSUSPENDED,
+ changedPackages, changedUids.toArray(), userId);
sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, userId);
synchronized (mLock) {
scheduleWritePackageRestrictionsLocked(userId);
}
}
- return unactionedPackages.toArray(new String[unactionedPackages.size()]);
+ // Send the suspension changed broadcast to ensure suspension state is not stale.
+ if (!modifiedPackagesList.isEmpty()) {
+ sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
+ modifiedPackagesList.toArray(new String[0]), modifiedUids.toArray(), userId);
+ }
+ return unactionedPackages.toArray(new String[0]);
}
@Override
@@ -16360,7 +14827,8 @@
final String[] packageArray = unsuspendedPackages.toArray(
new String[unsuspendedPackages.size()]);
sendMyPackageSuspendedOrUnsuspended(packageArray, false, userId);
- sendPackagesSuspendedForUser(packageArray, unsuspendedUids.toArray(), userId, false);
+ sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_UNSUSPENDED,
+ packageArray, unsuspendedUids.toArray(), userId);
}
}
@@ -16639,30 +15107,6 @@
return Math.max(timeout, DEFAULT_VERIFICATION_TIMEOUT);
}
-
- /**
- * Get the default verification agent response code.
- *
- * @return default verification response code
- */
- private int getDefaultVerificationResponse(UserHandle user) {
- if (mUserManager.hasUserRestriction(UserManager.ENSURE_VERIFY_APPS, user.getIdentifier())) {
- return PackageManager.VERIFICATION_REJECT;
- }
- return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
- android.provider.Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE,
- DEFAULT_VERIFICATION_RESPONSE);
- }
-
- /**
- * Get the default integrity verification response code.
- */
- private int getDefaultIntegrityVerificationResponse() {
- // We are not exposing this as a user-configurable setting because we don't want to provide
- // an easy way to get around the integrity check.
- return PackageManager.VERIFICATION_REJECT;
- }
-
@Deprecated
@Override
public void verifyIntentFilter(int id, int verificationCode, List<String> failedDomains) {
@@ -16733,17 +15177,6 @@
}
}
- /**
- * Get the "allow unknown sources" setting.
- *
- * @return the current "allow unknown sources" setting
- */
- private int getUnknownSourcesSettings() {
- return android.provider.Settings.Secure.getInt(mContext.getContentResolver(),
- android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS,
- -1);
- }
-
@Override
public void setInstallerPackageName(String targetPackage, String installerPackageName) {
final int callingUid = Binder.getCallingUid();
@@ -17060,7 +15493,7 @@
});
}
- private void sendFirstLaunchBroadcast(String pkgName, String installerPkg,
+ void sendFirstLaunchBroadcast(String pkgName, String installerPkg,
int[] userIds, int[] instantUserIds) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0,
installerPkg, null, userIds, instantUserIds, null /* broadcastAllowList */, null);
@@ -17099,19 +15532,8 @@
return new File(firstLevelDir, packageName + "-" + suffix);
}
- private void removeNativeBinariesLI(PackageSetting ps) {
- if (ps != null) {
- NativeLibraryHelper.removeNativeBinariesLI(ps.legacyNativeLibraryPathString);
- }
- }
-
@GuardedBy("mLock")
- private void enableSystemPackageLPw(AndroidPackage pkg) {
- mSettings.enableSystemPackageLPw(pkg.getPackageName());
- }
-
- @GuardedBy("mLock")
- static Map<String, ReconciledPackage> reconcilePackagesLocked(
+ Map<String, ReconciledPackage> reconcilePackagesLocked(
final ReconcileRequest request, KeySetManagerService ksms, Injector injector)
throws ReconcileFailure {
final Map<String, ScanResult> scannedPackages = request.mScannedPackages;
@@ -17987,7 +16409,10 @@
Slog.i(TAG, "Enabling system stub after removal; pkg: "
+ stubPkg.getPackageName());
}
- enableCompressedPackage(stubPkg, stubPs);
+ final InitAndSystemPackageHelper helper =
+ new InitAndSystemPackageHelper(this);
+ helper.enableCompressedPackage(stubPkg, stubPs, mDefParseFlags,
+ mDirsToScanAsSystem);
} else if (DEBUG_COMPRESSION) {
Slog.i(TAG, "System stub disabled for all users, leaving uncompressed "
+ "after removal; pkg: " + stubPkg.getPackageName());
@@ -18006,7 +16431,7 @@
* make sure this flag is set for partially installed apps. If not its meaningless to
* delete a partially installed application.
*/
- private void removePackageDataLIF(final PackageSetting deletedPs, @NonNull int[] allUserHandles,
+ void removePackageDataLIF(final PackageSetting deletedPs, @NonNull int[] allUserHandles,
PackageRemovedInfo outInfo, int flags, boolean writeSettings) {
String packageName = deletedPs.name;
if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + deletedPs);
@@ -18130,181 +16555,7 @@
return null;
}
- /*
- * Tries to delete system package.
- */
- private void deleteSystemPackageLIF(DeletePackageAction action, PackageSetting deletedPs,
- @NonNull int[] allUserHandles, int flags, @Nullable PackageRemovedInfo outInfo,
- boolean writeSettings)
- throws SystemDeleteException {
- final boolean applyUserRestrictions = outInfo != null && (outInfo.mOrigUsers != null);
- final AndroidPackage deletedPkg = deletedPs.pkg;
- // Confirm if the system package has been updated
- // An updated system app can be deleted. This will also have to restore
- // the system pkg from system partition
- // reader
- final PackageSetting disabledPs = action.mDisabledPs;
- if (DEBUG_REMOVE) Slog.d(TAG, "deleteSystemPackageLI: newPs=" + deletedPkg.getPackageName()
- + " disabledPs=" + disabledPs);
- Slog.d(TAG, "Deleting system pkg from data partition");
-
- if (DEBUG_REMOVE) {
- if (applyUserRestrictions) {
- Slog.d(TAG, "Remembering install states:");
- for (int userId : allUserHandles) {
- final boolean finstalled = ArrayUtils.contains(outInfo.mOrigUsers, userId);
- Slog.d(TAG, " u=" + userId + " inst=" + finstalled);
- }
- }
- }
-
- if (outInfo != null) {
- // Delete the updated package
- outInfo.mIsRemovedPackageSystemUpdate = true;
- }
-
- if (disabledPs.versionCode < deletedPs.versionCode) {
- // Delete data for downgrades
- flags &= ~PackageManager.DELETE_KEEP_DATA;
- } else {
- // Preserve data by setting flag
- flags |= PackageManager.DELETE_KEEP_DATA;
- }
-
- deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles,
- outInfo, writeSettings);
-
- // writer
- synchronized (mLock) {
- // NOTE: The system package always needs to be enabled; even if it's for
- // a compressed stub. If we don't, installing the system package fails
- // during scan [scanning checks the disabled packages]. We will reverse
- // this later, after we've "installed" the stub.
- // Reinstate the old system package
- enableSystemPackageLPw(disabledPs.pkg);
- // Remove any native libraries from the upgraded package.
- removeNativeBinariesLI(deletedPs);
- }
-
- // Install the system package
- if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
- try {
- installPackageFromSystemLIF(disabledPs.getPathString(), allUserHandles,
- outInfo == null ? null : outInfo.mOrigUsers, writeSettings);
- } catch (PackageManagerException e) {
- Slog.w(TAG, "Failed to restore system package:" + deletedPkg.getPackageName() + ": "
- + e.getMessage());
- // TODO(patb): can we avoid this; throw would come from scan...
- throw new SystemDeleteException(e);
- } finally {
- if (disabledPs.pkg.isStub()) {
- // We've re-installed the stub; make sure it's disabled here. If package was
- // originally enabled, we'll install the compressed version of the application
- // and re-enable it afterward.
- final PackageSetting stubPs = mSettings.getPackageLPr(deletedPkg.getPackageName());
- if (stubPs != null) {
- int userId = action.mUser == null
- ? UserHandle.USER_ALL : action.mUser.getIdentifier();
- if (userId == UserHandle.USER_ALL) {
- for (int aUserId : allUserHandles) {
- stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, aUserId, "android");
- }
- } else if (userId >= UserHandle.USER_SYSTEM) {
- stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, userId, "android");
- }
- }
- }
- }
- }
-
- /**
- * Installs a package that's already on the system partition.
- */
- private void installPackageFromSystemLIF(@NonNull String codePathString,
- @NonNull int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings)
- throws PackageManagerException {
- final File codePath = new File(codePathString);
- @ParseFlags int parseFlags =
- mDefParseFlags
- | ParsingPackageUtils.PARSE_MUST_BE_APK
- | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
- @ScanFlags int scanFlags = SCAN_AS_SYSTEM;
- for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
- ScanPartition partition = mDirsToScanAsSystem.get(i);
- if (partition.containsFile(codePath)) {
- scanFlags |= partition.scanFlag;
- if (partition.containsPrivApp(codePath)) {
- scanFlags |= SCAN_AS_PRIVILEGED;
- }
- break;
- }
- }
-
- final AndroidPackage pkg =
- scanPackageTracedLI(codePath, parseFlags, scanFlags, 0 /*currentTime*/, null);
-
- PackageSetting pkgSetting = mSettings.getPackageLPr(pkg.getPackageName());
-
- try {
- // update shared libraries for the newly re-installed system package
- updateSharedLibrariesLocked(pkg, pkgSetting, null, null,
- Collections.unmodifiableMap(mPackages));
- } catch (PackageManagerException e) {
- Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
- }
-
- prepareAppDataAfterInstallLIF(pkg);
-
- // writer
- synchronized (mLock) {
- PackageSetting ps = mSettings.getPackageLPr(pkg.getPackageName());
-
- final boolean applyUserRestrictions = origUserHandles != null;
- if (applyUserRestrictions) {
- boolean installedStateChanged = false;
- if (DEBUG_REMOVE) {
- Slog.d(TAG, "Propagating install state across reinstall");
- }
- for (int userId : allUserHandles) {
- final boolean installed = ArrayUtils.contains(origUserHandles, userId);
- if (DEBUG_REMOVE) {
- Slog.d(TAG, " user " + userId + " => " + installed);
- }
- if (installed != ps.getInstalled(userId)) {
- installedStateChanged = true;
- }
- ps.setInstalled(installed, userId);
- if (installed) {
- ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId);
- }
- }
- // Regardless of writeSettings we need to ensure that this restriction
- // state propagation is persisted
- mSettings.writeAllUsersPackageRestrictionsLPr();
- if (installedStateChanged) {
- mSettings.writeKernelMappingLPr(ps);
- }
- }
-
- // The method below will take care of removing obsolete permissions and granting
- // install permissions.
- mPermissionManager.onPackageInstalled(pkg,
- PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT,
- UserHandle.USER_ALL);
- for (final int userId : allUserHandles) {
- if (applyUserRestrictions) {
- mSettings.writePermissionStateForUserLPr(userId, false);
- }
- }
-
- // can downgrade to reader here
- if (writeSettings) {
- writeSettingsLPrTEMP();
- }
- }
- }
-
- private void deleteInstalledPackageLIF(PackageSetting ps,
+ void deleteInstalledPackageLIF(PackageSetting ps,
boolean deleteCodeAndResources, int flags, @NonNull int[] allUserHandles,
PackageRemovedInfo outInfo, boolean writeSettings) {
synchronized (mLock) {
@@ -18409,7 +16660,7 @@
/*
* This method handles package deletion in general
*/
- private boolean deletePackageLIF(@NonNull String packageName, UserHandle user,
+ boolean deletePackageLIF(@NonNull String packageName, UserHandle user,
boolean deleteCodeAndResources, @NonNull int[] allUserHandles, int flags,
PackageRemovedInfo outInfo, boolean writeSettings) {
final DeletePackageAction action;
@@ -18504,7 +16755,9 @@
if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package: " + ps.name);
// When an updated system application is deleted we delete the existing resources
// as well and fall back to existing code in system partition
- deleteSystemPackageLIF(action, ps, allUserHandles, flags, outInfo, writeSettings);
+ final InitAndSystemPackageHelper helper = new InitAndSystemPackageHelper(this);
+ helper.deleteSystemPackageLIF(action, ps, allUserHandles, flags, outInfo,
+ writeSettings, mDefParseFlags, mDirsToScanAsSystem);
} else {
if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.name);
deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles,
@@ -19112,7 +17365,7 @@
mSettings.clearPackagePreferredActivities(packageName, outUserChanged, userId);
}
- private void restorePermissionsAndUpdateRolesForNewUserInstall(String packageName,
+ void restorePermissionsAndUpdateRolesForNewUserInstall(String packageName,
@UserIdInt int userId) {
// We may also need to apply pending (restored) runtime permission grants
// within these users.
@@ -20190,7 +18443,9 @@
if (isSystemStub
&& (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|| newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
- if (!enableCompressedPackage(deletedPkg, pkgSetting)) {
+ final InitAndSystemPackageHelper helper = new InitAndSystemPackageHelper(this);
+ if (!helper.enableCompressedPackage(deletedPkg, pkgSetting, mDefParseFlags,
+ mDirsToScanAsSystem)) {
Slog.w(TAG, "Failed setApplicationEnabledSetting: failed to enable "
+ "commpressed package " + setting.getPackageName());
updateAllowed[i] = false;
@@ -20373,7 +18628,7 @@
}
}
- private void sendPackageChangedBroadcast(String packageName,
+ void sendPackageChangedBroadcast(String packageName,
boolean dontKillApp, ArrayList<String> componentNames, int packageUid, String reason) {
if (DEBUG_INSTALL)
Log.v(TAG, "Sending package changed: package=" + packageName + " components="
@@ -21090,15 +19345,24 @@
final String packageName = dumpState.getTargetPackageName();
final boolean checkin = dumpState.isCheckIn();
+
+ // Return if the package doesn't exist.
+ if (packageName != null
+ && getPackageSetting(packageName) == null
+ && !mApexManager.isApexPackage(packageName)) {
+ pw.println("Unable to find package: " + packageName);
+ return;
+ }
+
if (checkin) {
pw.println("vers,1");
}
// reader
- if (dumpState.isDumping(DumpState.DUMP_VERSION) && packageName == null) {
- if (!checkin) {
- dump(DumpState.DUMP_VERSION, fd, pw, dumpState);
- }
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_VERSION)
+ && packageName == null) {
+ dump(DumpState.DUMP_VERSION, fd, pw, dumpState);
}
if (!checkin
@@ -21129,7 +19393,8 @@
ipw.decreaseIndent();
}
- if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) {
+ if (dumpState.isDumping(DumpState.DUMP_VERIFIERS)
+ && packageName == null) {
final String requiredVerifierPackage = mRequiredVerifierPackage;
if (!checkin) {
if (dumpState.onTitlePrinted()) {
@@ -21150,14 +19415,16 @@
}
}
- if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER) && packageName == null) {
+ if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER)
+ && packageName == null) {
final DomainVerificationProxy proxy = mDomainVerificationManager.getProxy();
final ComponentName verifierComponent = proxy.getComponentName();
if (verifierComponent != null) {
String verifierPackageName = verifierComponent.getPackageName();
if (!checkin) {
- if (dumpState.onTitlePrinted())
+ if (dumpState.onTitlePrinted()) {
pw.println();
+ }
pw.println("Domain Verifier:");
pw.print(" Using: ");
pw.print(verifierPackageName);
@@ -21177,11 +19444,13 @@
}
}
- if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
+ if (dumpState.isDumping(DumpState.DUMP_LIBS)
+ && packageName == null) {
dump(DumpState.DUMP_LIBS, fd, pw, dumpState);
}
- if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) {
+ if (dumpState.isDumping(DumpState.DUMP_FEATURES)
+ && packageName == null) {
if (dumpState.onTitlePrinted()) {
pw.println();
}
@@ -21191,12 +19460,7 @@
synchronized (mAvailableFeatures) {
for (FeatureInfo feat : mAvailableFeatures.values()) {
- if (checkin) {
- pw.print("feat,");
- pw.print(feat.name);
- pw.print(",");
- pw.println(feat.version);
- } else {
+ if (!checkin) {
pw.print(" ");
pw.print(feat.name);
if (feat.version > 0) {
@@ -21204,55 +19468,73 @@
pw.print(feat.version);
}
pw.println();
+ } else {
+ pw.print("feat,");
+ pw.print(feat.name);
+ pw.print(",");
+ pw.println(feat.version);
}
}
}
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
synchronized (mLock) {
mComponentResolver.dumpActivityResolvers(pw, dumpState, packageName);
}
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
synchronized (mLock) {
mComponentResolver.dumpReceiverResolvers(pw, dumpState, packageName);
}
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
synchronized (mLock) {
mComponentResolver.dumpServiceResolvers(pw, dumpState, packageName);
}
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
synchronized (mLock) {
mComponentResolver.dumpProviderResolvers(pw, dumpState, packageName);
}
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
dump(DumpState.DUMP_PREFERRED, fd, pw, dumpState);
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)
+ && packageName == null) {
dump(DumpState.DUMP_PREFERRED_XML, fd, pw, dumpState);
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
dump(DumpState.DUMP_DOMAIN_PREFERRED, fd, pw, dumpState);
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
- mSettings.dumpPermissions(pw, packageName, permissionNames, dumpState);
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
+ synchronized (mLock) {
+ mSettings.dumpPermissions(pw, packageName, permissionNames, dumpState);
+ }
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
synchronized (mLock) {
mComponentResolver.dumpContentProviders(pw, dumpState, packageName);
}
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
synchronized (mLock) {
mSettings.getKeySetManagerService().dumpLPr(pw, packageName, dumpState);
}
@@ -21267,7 +19549,8 @@
}
}
- if (dumpState.isDumping(DumpState.DUMP_QUERIES)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_QUERIES)) {
dump(DumpState.DUMP_QUERIES, fd, pw, dumpState);
}
@@ -21279,8 +19562,12 @@
}
}
- if (dumpState.isDumping(DumpState.DUMP_CHANGES)) {
- if (dumpState.onTitlePrinted()) pw.println();
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_CHANGES)
+ && packageName == null) {
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
pw.println("Package Changes:");
synchronized (mLock) {
pw.print(" Sequence number="); pw.println(mChangedPackagesSequenceNumber);
@@ -21306,11 +19593,14 @@
}
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_FROZEN) && packageName == null) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_FROZEN)
+ && packageName == null) {
// XXX should handle packageName != null by dumping only install data that
// the given package is involved with.
- if (dumpState.onTitlePrinted()) pw.println();
-
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
ipw.println();
ipw.println("Frozen packages:");
@@ -21327,9 +19617,12 @@
ipw.decreaseIndent();
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_VOLUMES) && packageName == null) {
- if (dumpState.onTitlePrinted()) pw.println();
-
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_VOLUMES)
+ && packageName == null) {
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
ipw.println();
ipw.println("Loaded volumes:");
@@ -21346,52 +19639,65 @@
ipw.decreaseIndent();
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
&& packageName == null) {
synchronized (mLock) {
mComponentResolver.dumpServicePermissions(pw, dumpState);
}
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_DEXOPT)) {
- if (dumpState.onTitlePrinted()) pw.println();
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_DEXOPT)) {
dump(DumpState.DUMP_DEXOPT, fd, pw, dumpState);
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)) {
- if (dumpState.onTitlePrinted()) pw.println();
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)) {
dump(DumpState.DUMP_COMPILER_STATS, fd, pw, dumpState);
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) {
- if (dumpState.onTitlePrinted()) pw.println();
- synchronized (mLock) {
- mSettings.dumpReadMessagesLPr(pw, dumpState);
+ if (dumpState.isDumping(DumpState.DUMP_MESSAGES)
+ && packageName == null) {
+ if (!checkin) {
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
+ synchronized (mLock) {
+ mSettings.dumpReadMessagesLPr(pw, dumpState);
+ }
+ pw.println();
+ pw.println("Package warning messages:");
+ dumpCriticalInfo(pw, null);
+ } else {
+ dumpCriticalInfo(pw, "msg,");
}
- pw.println();
- pw.println("Package warning messages:");
- dumpCriticalInfo(pw, null);
- }
-
- if (checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES)) {
- dumpCriticalInfo(pw, "msg,");
}
// PackageInstaller should be called outside of mPackages lock
- if (!checkin && dumpState.isDumping(DumpState.DUMP_INSTALLS) && packageName == null) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_INSTALLS)
+ && packageName == null) {
// XXX should handle packageName != null by dumping only install data that
// the given package is involved with.
- if (dumpState.onTitlePrinted()) pw.println();
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
mInstallerService.dump(new IndentingPrintWriter(pw, " ", 120));
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_APEX)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_APEX)
+ && (packageName == null || mApexManager.isApexPackage(packageName))) {
mApexManager.dump(pw, packageName);
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_PER_UID_READ_TIMEOUTS)
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_PER_UID_READ_TIMEOUTS)
&& packageName == null) {
- pw.println();
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
pw.println("Per UID read timeouts:");
pw.println(" Default timeouts flag: " + getDefaultTimeouts());
pw.println(" Known digesters list flag: " + getKnownDigestersList());
@@ -21408,7 +19714,12 @@
}
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_SNAPSHOT_STATISTICS)) {
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_SNAPSHOT_STATISTICS)
+ && packageName == null) {
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
pw.println("Snapshot statistics");
if (!mSnapshotEnabled) {
pw.println(" Snapshots disabled");
@@ -21636,7 +19947,8 @@
try {
sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber, flags);
synchronized (mInstallLock) {
- reconcileAppsDataLI(volumeUuid, user.id, flags, true /* migrateAppData */);
+ reconcileAppsDataLI(volumeUuid, user.id, flags, true /* migrateAppData */,
+ null);
}
} catch (IllegalStateException e) {
// Device was probably ejected, and we'll process that event momentarily
@@ -21825,21 +20137,32 @@
* <p>
* Verifies that directories exist and that ownership and labeling is
* correct for all installed apps on all mounted volumes.
+ *
+ * @param reconciledPackages A set that will be populated with package names that have
+ * successfully had their data reconciled. Any package names already
+ * contained will be skipped. Because this must be mutable when
+ * non-null, it is typed {@link ArraySet} to prevent accidental
+ * usage of {@link Collections#emptySet()}. Null can be passed if the
+ * caller doesn't need this functionality.
*/
- void reconcileAppsData(int userId, int flags, boolean migrateAppsData) {
+ @NonNull
+ void reconcileAppsData(int userId, int flags, boolean migrateAppsData,
+ @Nullable ArraySet<String> reconciledPackages) {
final StorageManager storage = mInjector.getSystemService(StorageManager.class);
for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
final String volumeUuid = vol.getFsUuid();
synchronized (mInstallLock) {
- reconcileAppsDataLI(volumeUuid, userId, flags, migrateAppsData);
+ reconcileAppsDataLI(volumeUuid, userId, flags, migrateAppsData,
+ reconciledPackages);
}
}
}
@GuardedBy("mInstallLock")
private void reconcileAppsDataLI(String volumeUuid, int userId, int flags,
- boolean migrateAppData) {
- reconcileAppsDataLI(volumeUuid, userId, flags, migrateAppData, false /* onlyCoreApps */);
+ boolean migrateAppData, @Nullable ArraySet<String> reconciledPackages) {
+ reconcileAppsDataLI(volumeUuid, userId, flags, migrateAppData, false /* onlyCoreApps */,
+ reconciledPackages);
}
/**
@@ -21854,7 +20177,8 @@
*/
@GuardedBy("mInstallLock")
private List<String> reconcileAppsDataLI(String volumeUuid, int userId, int flags,
- boolean migrateAppData, boolean onlyCoreApps) {
+ boolean migrateAppData, boolean onlyCoreApps,
+ @Nullable ArraySet<String> reconciledPackages) {
Slog.v(TAG, "reconcileAppsData for " + volumeUuid + " u" + userId + " 0x"
+ Integer.toHexString(flags) + " migrateAppData=" + migrateAppData);
List<String> result = onlyCoreApps ? new ArrayList<>() : null;
@@ -21917,6 +20241,9 @@
int preparedCount = 0;
for (PackageSetting ps : packages) {
final String packageName = ps.name;
+ if (reconciledPackages != null && reconciledPackages.contains(packageName)) {
+ continue;
+ }
if (ps.pkg == null) {
Slog.w(TAG, "Odd, missing scanned package " + packageName);
// TODO: might be due to legacy ASEC apps; we should circle back
@@ -21932,6 +20259,10 @@
if (ps.getInstalled(userId)) {
prepareAppDataAndMigrate(batch, ps.pkg, userId, flags, migrateAppData);
preparedCount++;
+
+ if (reconciledPackages != null) {
+ reconciledPackages.add(packageName);
+ }
}
}
executeBatchLI(batch);
@@ -22818,137 +21149,6 @@
}
}
- Pair<Integer, String> verifyReplacingVersionCode(PackageInfoLite pkgLite,
- long requiredInstalledVersionCode, int installFlags) {
- if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
- return verifyReplacingVersionCodeForApex(
- pkgLite, requiredInstalledVersionCode, installFlags);
- }
-
- String packageName = pkgLite.packageName;
- synchronized (mLock) {
- // Package which currently owns the data that the new package will own if installed.
- // If an app is uninstalled while keeping data (e.g. adb uninstall -k), installedPkg
- // will be null whereas dataOwnerPkg will contain information about the package
- // which was uninstalled while keeping its data.
- AndroidPackage dataOwnerPkg = mPackages.get(packageName);
- if (dataOwnerPkg == null) {
- PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps != null) {
- dataOwnerPkg = ps.pkg;
- }
- }
-
- if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) {
- if (dataOwnerPkg == null) {
- String errorMsg = "Required installed version code was "
- + requiredInstalledVersionCode
- + " but package is not installed";
- Slog.w(TAG, errorMsg);
- return Pair.create(
- PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
- }
-
- if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) {
- String errorMsg = "Required installed version code was "
- + requiredInstalledVersionCode
- + " but actual installed version is "
- + dataOwnerPkg.getLongVersionCode();
- Slog.w(TAG, errorMsg);
- return Pair.create(
- PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
- }
- }
-
- if (dataOwnerPkg != null) {
- if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
- dataOwnerPkg.isDebuggable())) {
- try {
- checkDowngrade(dataOwnerPkg, pkgLite);
- } catch (PackageManagerException e) {
- String errorMsg = "Downgrade detected: " + e.getMessage();
- Slog.w(TAG, errorMsg);
- return Pair.create(
- PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg);
- }
- }
- }
- }
- return Pair.create(PackageManager.INSTALL_SUCCEEDED, null);
- }
-
- private Pair<Integer, String> verifyReplacingVersionCodeForApex(PackageInfoLite pkgLite,
- long requiredInstalledVersionCode, int installFlags) {
- String packageName = pkgLite.packageName;
-
- final PackageInfo activePackage = mApexManager.getPackageInfo(packageName,
- ApexManager.MATCH_ACTIVE_PACKAGE);
- if (activePackage == null) {
- String errorMsg = "Attempting to install new APEX package " + packageName;
- Slog.w(TAG, errorMsg);
- return Pair.create(PackageManager.INSTALL_FAILED_PACKAGE_CHANGED, errorMsg);
- }
-
- final long activeVersion = activePackage.getLongVersionCode();
- if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST
- && activeVersion != requiredInstalledVersionCode) {
- String errorMsg = "Installed version of APEX package " + packageName
- + " does not match required. Active version: " + activeVersion
- + " required: " + requiredInstalledVersionCode;
- Slog.w(TAG, errorMsg);
- return Pair.create(PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
- }
-
- final boolean isAppDebuggable = (activePackage.applicationInfo.flags
- & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
- final long newVersionCode = pkgLite.getLongVersionCode();
- if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, isAppDebuggable)
- && newVersionCode < activeVersion) {
- String errorMsg = "Downgrade of APEX package " + packageName
- + " is not allowed. Active version: " + activeVersion
- + " attempted: " + newVersionCode;
- Slog.w(TAG, errorMsg);
- return Pair.create(PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg);
- }
-
- return Pair.create(PackageManager.INSTALL_SUCCEEDED, null);
- }
-
- /**
- * Check and throw if the given before/after packages would be considered a
- * downgrade.
- */
- private static void checkDowngrade(AndroidPackage before, PackageInfoLite after)
- throws PackageManagerException {
- if (after.getLongVersionCode() < before.getLongVersionCode()) {
- throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
- "Update version code " + after.versionCode + " is older than current "
- + before.getLongVersionCode());
- } else if (after.getLongVersionCode() == before.getLongVersionCode()) {
- if (after.baseRevisionCode < before.getBaseRevisionCode()) {
- throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
- "Update base revision code " + after.baseRevisionCode
- + " is older than current " + before.getBaseRevisionCode());
- }
-
- if (!ArrayUtils.isEmpty(after.splitNames)) {
- for (int i = 0; i < after.splitNames.length; i++) {
- final String splitName = after.splitNames[i];
- final int j = ArrayUtils.indexOf(before.getSplitNames(), splitName);
- if (j != -1) {
- if (after.splitRevisionCodes[i] < before.getSplitRevisionCodes()[j]) {
- throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
- "Update split " + splitName + " revision code "
- + after.splitRevisionCodes[i]
- + " is older than current "
- + before.getSplitRevisionCodes()[j]);
- }
- }
- }
- }
- }
- }
-
private static class MoveCallbacks extends Handler {
private static final int MSG_CREATED = 1;
private static final int MSG_STATUS_CHANGED = 2;
@@ -24560,7 +22760,7 @@
return mComputer.getPackageSetting(packageName);
}
- private PackageSetting getPackageSettingInternal(String packageName, int callingUid) {
+ PackageSetting getPackageSettingInternal(String packageName, int callingUid) {
return mComputer.getPackageSettingInternal(packageName, callingUid);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 4862021..e4f6398 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -581,8 +581,12 @@
apkLiteResult.getException());
}
final ApkLite apkLite = apkLiteResult.getResult();
- final PackageLite pkgLite = new PackageLite(null, apkLite.getPath(), apkLite, null,
- null, null, null, null, null, apkLite.getTargetSdkVersion());
+ final PackageLite pkgLite = new PackageLite(null, apkLite.getPath(), apkLite,
+ null /* splitNames */, null /* isFeatureSplits */,
+ null /* usesSplitNames */, null /* configForSplit */,
+ null /* splitApkPaths */, null /* splitRevisionCodes */,
+ apkLite.getTargetSdkVersion(), null /* requiredSplitTypes */,
+ null /* splitTypes */);
sessionSize += PackageHelper.calculateInstalledSize(pkgLite,
params.sessionParams.abiOverride, fd.getFileDescriptor());
} catch (IOException e) {
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 717f3d5..d9c4d316 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -455,7 +455,7 @@
return state.suspendParams != null && state.suspendParams.containsKey(suspendingPackage);
}
- void addOrUpdateSuspension(String suspendingPackage, SuspendDialogInfo dialogInfo,
+ boolean addOrUpdateSuspension(String suspendingPackage, SuspendDialogInfo dialogInfo,
PersistableBundle appExtras, PersistableBundle launcherExtras, int userId) {
final PackageUserState existingUserState = modifyUserState(userId);
final PackageUserState.SuspendParams newSuspendParams =
@@ -464,21 +464,27 @@
if (existingUserState.suspendParams == null) {
existingUserState.suspendParams = new ArrayMap<>();
}
- existingUserState.suspendParams.put(suspendingPackage, newSuspendParams);
+ final PackageUserState.SuspendParams oldSuspendParams =
+ existingUserState.suspendParams.put(suspendingPackage, newSuspendParams);
existingUserState.suspended = true;
onChanged();
+ return !Objects.equals(oldSuspendParams, newSuspendParams);
}
- void removeSuspension(String suspendingPackage, int userId) {
+ boolean removeSuspension(String suspendingPackage, int userId) {
+ boolean wasModified = false;
final PackageUserState existingUserState = modifyUserState(userId);
if (existingUserState.suspendParams != null) {
- existingUserState.suspendParams.remove(suspendingPackage);
+ if (existingUserState.suspendParams.remove(suspendingPackage) != null) {
+ wasModified = true;
+ }
if (existingUserState.suspendParams.size() == 0) {
existingUserState.suspendParams = null;
}
}
existingUserState.suspended = (existingUserState.suspendParams != null);
onChanged();
+ return wasModified;
}
void removeSuspension(Predicate<String> suspendingPackagePredicate, int userId) {
diff --git a/services/core/java/com/android/server/pm/ScanPartition.java b/services/core/java/com/android/server/pm/ScanPartition.java
new file mode 100644
index 0000000..e1d2b3b
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ScanPartition.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static com.android.server.pm.PackageManagerService.SCAN_AS_ODM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_OEM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_PRODUCT;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM_EXT;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_VENDOR;
+
+import android.annotation.NonNull;
+import android.content.pm.PackagePartitions;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+
+/**
+ * List of partitions to be scanned during system boot
+ */
+@VisibleForTesting
+public class ScanPartition extends PackagePartitions.SystemPartition {
+ @PackageManagerService.ScanFlags
+ public final int scanFlag;
+
+ public ScanPartition(@NonNull PackagePartitions.SystemPartition partition) {
+ super(partition);
+ scanFlag = scanFlagForPartition(partition);
+ }
+
+ /**
+ * Creates a partition containing the same folders as the original partition but with a
+ * different root folder. The new partition will include the scan flags of the original
+ * partition along with any specified additional scan flags.
+ */
+ public ScanPartition(@NonNull File folder, @NonNull ScanPartition original,
+ @PackageManagerService.ScanFlags int additionalScanFlag) {
+ super(folder, original);
+ this.scanFlag = original.scanFlag | additionalScanFlag;
+ }
+
+ private static int scanFlagForPartition(PackagePartitions.SystemPartition partition) {
+ switch (partition.type) {
+ case PackagePartitions.PARTITION_SYSTEM:
+ return 0;
+ case PackagePartitions.PARTITION_VENDOR:
+ return SCAN_AS_VENDOR;
+ case PackagePartitions.PARTITION_ODM:
+ return SCAN_AS_ODM;
+ case PackagePartitions.PARTITION_OEM:
+ return SCAN_AS_OEM;
+ case PackagePartitions.PARTITION_PRODUCT:
+ return SCAN_AS_PRODUCT;
+ case PackagePartitions.PARTITION_SYSTEM_EXT:
+ return SCAN_AS_SYSTEM_EXT;
+ default:
+ throw new IllegalStateException("Unable to determine scan flag for "
+ + partition.getFolder());
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getFolder().getAbsolutePath() + ":" + scanFlag;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index a854aa0..b111bbf 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3089,8 +3089,7 @@
// Read preferred apps from .../etc/preferred-apps directories.
int size = PackageManagerService.SYSTEM_PARTITIONS.size();
for (int index = 0; index < size; index++) {
- PackageManagerService.ScanPartition partition =
- PackageManagerService.SYSTEM_PARTITIONS.get(index);
+ ScanPartition partition = PackageManagerService.SYSTEM_PARTITIONS.get(index);
File preferredDir = new File(partition.getFolder(), "etc/preferred-apps");
if (!preferredDir.exists() || !preferredDir.isDirectory()) {
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index cc5187e..95c9191 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -104,20 +104,6 @@
"file_patterns": ["(/|^)ShortcutService\\.java"]
},
{
- "name": "CtsContentTestCases",
- "options": [
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "include-filter": "android.content.pm.cts"
- }
- ]
- },
- {
"name": "GtsContentTestCases",
"options": [
{
@@ -183,6 +169,22 @@
]
}
],
+ "presubmit-large": [
+ {
+ "name": "CtsContentTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "include-filter": "android.content.pm.cts"
+ }
+ ]
+ }
+ ],
"postsubmit": [
{
"name": "CtsPermissionTestCases",
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index e8182e0..40e0526 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -4805,7 +4805,7 @@
mUserDataPreparer.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_DE);
t.traceEnd();
t.traceBegin("reconcileAppsData");
- mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_DE, migrateAppsData);
+ mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_DE, migrateAppsData, null);
t.traceEnd();
if (userId != UserHandle.USER_SYSTEM) {
@@ -4821,11 +4821,14 @@
/**
* Called right before a user is unlocked. This gives us a chance to prepare
* app storage.
+ *
+ * @return set of packages that reconciled app data
*/
- public void onBeforeUnlockUser(@UserIdInt int userId) {
+ @NonNull public ArraySet<String> onBeforeUnlockUser(@UserIdInt int userId) {
UserInfo userInfo = getUserInfo(userId);
if (userInfo == null) {
- return;
+ // PMS requires mutable set, so the API uses ArraySet to prevent Collections.emptySet()
+ return new ArraySet<>();
}
final int userSerial = userInfo.serialNumber;
// Migrate only if build fingerprints mismatch
@@ -4836,8 +4839,33 @@
mUserDataPreparer.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
t.traceEnd();
- t.traceBegin("reconcileAppsData-" + userId);
- mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE, migrateAppsData);
+ final ArraySet<String> reconciledPackages = new ArraySet<>();
+ t.traceBegin("reconcileAppsDataFirstPass-" + userId);
+ mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE, migrateAppsData,
+ reconciledPackages);
+ t.traceEnd();
+ return reconciledPackages;
+ }
+
+ /**
+ * Called right after a user state is moved to {@link UserState#STATE_RUNNING_UNLOCKING}. This
+ * gives us a chance to reconcile app data for apps installed since
+ * {@link #onBeforeUnlockUser(int)} was called.
+ *
+ * @param previouslyReconciledPackages the result from {@link #onBeforeUnlockUser(int)}
+ */
+ public void onUserStateRunningUnlocking(@UserIdInt int userId,
+ @NonNull ArraySet<String> previouslyReconciledPackages) {
+ final UserInfo userInfo = getUserInfo(userId);
+ if (userInfo == null) {
+ return;
+ }
+ // Migrate only if build fingerprints mismatch
+ boolean migrateAppsData = !Build.FINGERPRINT.equals(userInfo.lastLoggedInFingerprint);
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("reconcileAppsDataSecondPass-" + userId);
+ mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE, migrateAppsData,
+ previouslyReconciledPackages);
t.traceEnd();
}
diff --git a/services/core/java/com/android/server/pm/VerificationParams.java b/services/core/java/com/android/server/pm/VerificationParams.java
index 3c499de..dae4038 100644
--- a/services/core/java/com/android/server/pm/VerificationParams.java
+++ b/services/core/java/com/android/server/pm/VerificationParams.java
@@ -131,13 +131,12 @@
private String mErrorMessage = null;
final PackageLite mPackageLite;
- final PackageManagerService mPm;
VerificationParams(UserHandle user, File stagedDir, IPackageInstallObserver2 observer,
PackageInstaller.SessionParams sessionParams, InstallSource installSource,
int installerUid, SigningDetails signingDetails, int sessionId, PackageLite lite,
PackageManagerService pm) {
- super(user);
+ super(user, pm);
mOriginInfo = OriginInfo.fromStagedFile(stagedDir);
mObserver = observer;
mInstallFlags = sessionParams.installFlags;
@@ -155,7 +154,6 @@
? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE;
mSessionId = sessionId;
mPackageLite = lite;
- mPm = pm;
}
@Override
@@ -168,7 +166,7 @@
PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags, mPackageAbiOverride);
- Pair<Integer, String> ret = mPm.verifyReplacingVersionCode(
+ Pair<Integer, String> ret = verifyReplacingVersionCode(
pkgLite, mRequiredInstalledVersionCode, mInstallFlags);
setReturnCode(ret.first, ret.second);
if (mRet != INSTALL_SUCCEEDED) {
@@ -707,7 +705,7 @@
public void verifyStage(List<VerificationParams> children)
throws PackageManagerException {
final MultiPackageVerificationParams params =
- new MultiPackageVerificationParams(this, children);
+ new MultiPackageVerificationParams(this, children, mPm);
mPm.mHandler.post(params::startCopy);
}
@@ -720,9 +718,9 @@
private final List<VerificationParams> mChildParams;
private final Map<VerificationParams, Integer> mVerificationState;
- MultiPackageVerificationParams(VerificationParams parent, List<VerificationParams> children)
- throws PackageManagerException {
- super(parent.getUser());
+ MultiPackageVerificationParams(VerificationParams parent, List<VerificationParams> children,
+ PackageManagerService pm) throws PackageManagerException {
+ super(parent.getUser(), pm);
if (children.size() == 0) {
throw new PackageManagerException("No child sessions found!");
}
diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java
index 11aed8d..75f3725 100644
--- a/services/core/java/com/android/server/pm/permission/Permission.java
+++ b/services/core/java/com/android/server/pm/permission/Permission.java
@@ -476,9 +476,10 @@
r.append("DUP:");
r.append(permissionInfo.name);
}
- if (permission.isRuntime() && (ownerChanged || wasNonRuntime)) {
- // If this is a runtime permission and the owner has changed, or this wasn't a runtime
- // permission, then permission state should be cleaned up
+ if ((permission.isInternal() && ownerChanged)
+ || (permission.isRuntime() && (ownerChanged || wasNonRuntime))) {
+ // If this is an internal/runtime permission and the owner has changed, or this wasn't a
+ // runtime permission, then permission state should be cleaned up.
permission.mDefinitionChanged = true;
}
if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) {
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 74497f7..904e889 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1655,7 +1655,8 @@
isRolePermission = permission.isRole();
}
final boolean mayRevokeRolePermission = isRolePermission
- && mayManageRolePermission(callingUid);
+ // Allow ourselves to revoke role permissions due to definition changes.
+ && (callingUid == Process.myUid() || mayManageRolePermission(callingUid));
final boolean isRuntimePermission;
synchronized (mLock) {
@@ -2333,11 +2334,13 @@
for (int permNum = 0; permNum < numPermissions; permNum++) {
final String permName = permissionsToRevoke.get(permNum);
+ final boolean isInternalPermission;
synchronized (mLock) {
final Permission bp = mRegistry.getPermission(permName);
- if (bp == null || !bp.isRuntime()) {
+ if (bp == null || !(bp.isInternal() || bp.isRuntime())) {
continue;
}
+ isInternalPermission = bp.isInternal();
}
mPackageManagerInt.forEachPackage(pkg -> {
final String packageName = pkg.getPackageName();
@@ -2357,12 +2360,18 @@
if (permissionState == PackageManager.PERMISSION_GRANTED
&& (flags & flagMask) == 0) {
final int uid = UserHandle.getUid(userId, appId);
- EventLog.writeEvent(0x534e4554, "154505240", uid,
- "Revoking permission " + permName + " from package "
- + packageName + " due to definition change");
- EventLog.writeEvent(0x534e4554, "168319670", uid,
- "Revoking permission " + permName + " from package "
- + packageName + " due to definition change");
+ if (isInternalPermission) {
+ EventLog.writeEvent(0x534e4554, "195338390", uid,
+ "Revoking permission " + permName + " from package "
+ + packageName + " due to definition change");
+ } else {
+ EventLog.writeEvent(0x534e4554, "154505240", uid,
+ "Revoking permission " + permName + " from package "
+ + packageName + " due to definition change");
+ EventLog.writeEvent(0x534e4554, "168319670", uid,
+ "Revoking permission " + permName + " from package "
+ + packageName + " due to definition change");
+ }
Slog.e(TAG, "Revoking permission " + permName + " from package "
+ packageName + " due to definition change");
try {
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index ff6511f..27a16e9 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -82,6 +82,7 @@
public final class DeviceStateProviderImpl implements DeviceStateProvider,
InputManagerInternal.LidSwitchCallback, SensorEventListener {
private static final String TAG = "DeviceStateProviderImpl";
+ private static final boolean DEBUG = false;
private static final BooleanSupplier TRUE_BOOLEAN_SUPPLIER = () -> true;
private static final BooleanSupplier FALSE_BOOLEAN_SUPPLIER = () -> false;
@@ -213,6 +214,10 @@
for (int i = 0; i < stateConditions.size(); i++) {
final int state = deviceStates.get(i).getIdentifier();
+ if (DEBUG) {
+ Slog.d(TAG, "Evaluating conditions for device state " + state
+ + " (" + deviceStates.get(i).getName() + ")");
+ }
final Conditions conditions = stateConditions.get(i);
if (conditions == null) {
mStateConditions.put(state, TRUE_BOOLEAN_SUPPLIER);
@@ -233,6 +238,9 @@
if (lidSwitchCondition != null) {
suppliers.add(new LidSwitchBooleanSupplier(lidSwitchCondition.getOpen()));
lidSwitchRequired = true;
+ if (DEBUG) {
+ Slog.d(TAG, "Lid switch required");
+ }
}
List<SensorCondition> sensorConditions = conditions.getSensor();
@@ -249,6 +257,11 @@
break;
}
+ if (DEBUG) {
+ Slog.d(TAG, "Found sensor with type: " + expectedSensorType
+ + " (" + expectedSensorName + ")");
+ }
+
suppliers.add(new SensorBooleanSupplier(foundSensor, sensorCondition.getValue()));
sensorsRequired.add(foundSensor);
}
@@ -343,6 +356,10 @@
int newState = mOrderedStates[0].getIdentifier();
for (int i = 0; i < mOrderedStates.length; i++) {
int state = mOrderedStates[i].getIdentifier();
+ if (DEBUG) {
+ Slog.d(TAG, "Checking conditions for " + mOrderedStates[i].getName() + "("
+ + i + ")");
+ }
boolean conditionSatisfied;
try {
conditionSatisfied = mStateConditions.get(state).getAsBoolean();
@@ -350,10 +367,16 @@
// Failed to compute the current state based on current available data. Return
// with the expectation that notifyDeviceStateChangedIfNeeded() will be called
// when a callback with the missing data is triggered.
+ if (DEBUG) {
+ Slog.d(TAG, "Unable to check current state", e);
+ }
return;
}
if (conditionSatisfied) {
+ if (DEBUG) {
+ Slog.d(TAG, "Device State conditions satisfied, transition to " + state);
+ }
newState = state;
break;
}
@@ -375,6 +398,9 @@
synchronized (mLock) {
mIsLidOpen = lidOpen;
}
+ if (DEBUG) {
+ Slog.d(TAG, "Lid switch state: " + (lidOpen ? "open" : "closed"));
+ }
notifyDeviceStateChangedIfNeeded();
}
@@ -460,6 +486,9 @@
private boolean adheresToRange(float value, @NonNull NumericRange range) {
final BigDecimal min = range.getMin_optional();
if (min != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "value: " + value + ", constraint min: " + min.floatValue());
+ }
if (value <= min.floatValue()) {
return false;
}
@@ -467,6 +496,10 @@
final BigDecimal minInclusive = range.getMinInclusive_optional();
if (minInclusive != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "value: " + value + ", constraint min-inclusive: "
+ + minInclusive.floatValue());
+ }
if (value < minInclusive.floatValue()) {
return false;
}
@@ -474,6 +507,9 @@
final BigDecimal max = range.getMax_optional();
if (max != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "value: " + value + ", constraint max: " + max.floatValue());
+ }
if (value >= max.floatValue()) {
return false;
}
@@ -481,6 +517,10 @@
final BigDecimal maxInclusive = range.getMaxInclusive_optional();
if (maxInclusive != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "value: " + value + ", constraint max-inclusive: "
+ + maxInclusive.floatValue());
+ }
if (value > maxInclusive.floatValue()) {
return false;
}
diff --git a/services/core/java/com/android/server/policy/ModifierShortcutManager.java b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
index a0771c0..e857d32 100644
--- a/services/core/java/com/android/server/policy/ModifierShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
@@ -48,7 +48,7 @@
* <li> Returning particular kind of application intent by special key.
*/
class ModifierShortcutManager {
- private static final String TAG = "ShortcutManager";
+ private static final String TAG = "WindowManager";
private static final String TAG_BOOKMARKS = "bookmarks";
private static final String TAG_BOOKMARK = "bookmark";
@@ -188,7 +188,7 @@
| PackageManager.MATCH_UNINSTALLED_PACKAGES);
} catch (PackageManager.NameNotFoundException e1) {
Log.w(TAG, "Unable to add bookmark: " + packageName
- + "/" + className, e);
+ + "/" + className + " not found.");
continue;
}
}
@@ -213,10 +213,8 @@
mIntentShortcuts.put(shortcutChar, shortcut);
}
}
- } catch (XmlPullParserException e) {
- Log.w(TAG, "Got exception parsing bookmarks.", e);
- } catch (IOException e) {
- Log.w(TAG, "Got exception parsing bookmarks.", e);
+ } catch (XmlPullParserException | IOException e) {
+ Log.e(TAG, "Got exception parsing bookmarks.", e);
}
}
@@ -302,7 +300,7 @@
Slog.w(TAG, "Dropping application launch key because "
+ "the activity to which it is registered was not found: "
+ "keyCode=" + KeyEvent.keyCodeToString(keyCode) + ","
- + " category=" + category, ex);
+ + " category=" + category);
}
return true;
} else {
@@ -318,7 +316,7 @@
} catch (ActivityNotFoundException ex) {
Slog.w(TAG, "Dropping shortcut key combination because "
+ "the activity to which it is registered was not found: "
- + "META+ or SEARCH" + KeyEvent.keyCodeToString(keyCode), ex);
+ + "META+ or SEARCH" + KeyEvent.keyCodeToString(keyCode));
}
return true;
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
index c170603..47bd72a 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
@@ -23,6 +23,7 @@
import android.hardware.soundtrigger.V2_3.ISoundTriggerHw;
import android.media.audio.common.AidlConversion;
import android.media.audio.common.AudioConfig;
+import android.media.audio.common.AudioConfigBase;
import android.media.audio.common.AudioOffloadInfo;
import android.media.soundtrigger.AudioCapabilities;
import android.media.soundtrigger.ConfidenceLevel;
@@ -351,14 +352,8 @@
AudioConfig hidl2aidlAudioConfig(
@NonNull android.hardware.audio.common.V2_0.AudioConfig hidlConfig, boolean isInput) {
AudioConfig aidlConfig = new AudioConfig();
- aidlConfig.sampleRateHz = hidlConfig.sampleRateHz;
- // Relies on the fact that HIDL AudioChannelMask uses the same constant values as
- // system/audio.h.
- aidlConfig.channelMask = AidlConversion.legacy2aidl_audio_channel_mask_t_AudioChannelLayout(
- hidlConfig.channelMask, isInput);
- // Relies on the fact that HIDL AudioFormat uses the same constant values as system/audio.h.
- aidlConfig.format = AidlConversion.legacy2aidl_audio_format_t_AudioFormatDescription(
- hidlConfig.format);
+ aidlConfig.base = hidl2aidlAudioConfigBase(hidlConfig.sampleRateHz, hidlConfig.channelMask,
+ hidlConfig.format, isInput);
aidlConfig.offloadInfo = hidl2aidlOffloadInfo(hidlConfig.offloadInfo);
aidlConfig.frameCount = hidlConfig.frameCount;
return aidlConfig;
@@ -368,26 +363,36 @@
AudioOffloadInfo hidl2aidlOffloadInfo(
@NonNull android.hardware.audio.common.V2_0.AudioOffloadInfo hidlInfo) {
AudioOffloadInfo aidlInfo = new AudioOffloadInfo();
- aidlInfo.sampleRateHz = hidlInfo.sampleRateHz;
- // Relies on the fact that HIDL AudioChannelMask uses the same constant values as
- // system/audio.h.
- aidlInfo.channelMask = AidlConversion.legacy2aidl_audio_channel_mask_t_AudioChannelLayout(
- hidlInfo.channelMask, false /*isInput*/);
- // Relies on the fact that HIDL AudioFormat uses the same constant values as system/audio.h.
- aidlInfo.format = AidlConversion.legacy2aidl_audio_format_t_AudioFormatDescription(
- hidlInfo.format);
+ aidlInfo.base = hidl2aidlAudioConfigBase(hidlInfo.sampleRateHz, hidlInfo.channelMask,
+ hidlInfo.format, false /*isInput*/);
aidlInfo.streamType = AidlConversion.legacy2aidl_audio_stream_type_t_AudioStreamType(
hidlInfo.streamType);
aidlInfo.bitRatePerSecond = hidlInfo.bitRatePerSecond;
- aidlInfo.durationMicroseconds = hidlInfo.durationMicroseconds;
+ aidlInfo.durationUs = hidlInfo.durationMicroseconds;
aidlInfo.hasVideo = hidlInfo.hasVideo;
aidlInfo.isStreaming = hidlInfo.isStreaming;
aidlInfo.bitWidth = hidlInfo.bitWidth;
- aidlInfo.bufferSize = hidlInfo.bufferSize;
+ aidlInfo.offloadBufferSize = hidlInfo.bufferSize;
aidlInfo.usage = AidlConversion.legacy2aidl_audio_usage_t_AudioUsage(hidlInfo.usage);
return aidlInfo;
}
+ // Ideally we would want to convert AudioConfigBase as a unit, however
+ // HIDL V2 lacks this type, so convert by field instead.
+ static @NonNull
+ AudioConfigBase hidl2aidlAudioConfigBase(int sampleRateHz, int channelMask, int format,
+ boolean isInput) {
+ AudioConfigBase aidlBase = new AudioConfigBase();
+ aidlBase.sampleRate = sampleRateHz;
+ // Relies on the fact that HIDL AudioChannelMask uses the same constant values as
+ // system/audio.h.
+ aidlBase.channelMask = AidlConversion.legacy2aidl_audio_channel_mask_t_AudioChannelLayout(
+ channelMask, isInput);
+ // Relies on the fact that HIDL AudioFormat uses the same constant values as system/audio.h.
+ aidlBase.format = AidlConversion.legacy2aidl_audio_format_t_AudioFormatDescription(format);
+ return aidlBase;
+ }
+
@Nullable
static ModelParameterRange hidl2aidlModelParameterRange(
android.hardware.soundtrigger.V2_3.ModelParameterRange hidlRange) {
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
index 7a1f775..c638201 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
@@ -60,7 +60,6 @@
private @Nullable android.hardware.soundtrigger.V2_1.ISoundTriggerHw mUnderlying_2_1;
private @Nullable android.hardware.soundtrigger.V2_2.ISoundTriggerHw mUnderlying_2_2;
private @Nullable android.hardware.soundtrigger.V2_3.ISoundTriggerHw mUnderlying_2_3;
- private @Nullable android.hardware.soundtrigger.V2_4.ISoundTriggerHw mUnderlying_2_4;
// HAL <=2.1 requires us to pass a callback argument to startRecognition. We will store the one
// passed on load and then pass it on start. We don't bother storing the callback on newer
@@ -89,13 +88,10 @@
ICaptureStateNotifier notifier) {
SoundTriggerHw2Compat compat = new SoundTriggerHw2Compat(binder, rebootRunnable);
ISoundTriggerHal result = compat;
- // Add max model limiter for versions <2.4.
- if (compat.mUnderlying_2_4 == null) {
- result = new SoundTriggerHalMaxModelLimiter(result,
- compat.mProperties.maxSoundModels);
- }
- // Add concurrent capture handler for versions <2.4 which do not support concurrent capture.
- if (compat.mUnderlying_2_4 == null && !compat.mProperties.concurrentCapture) {
+ // Add max model limiter for versions.
+ result = new SoundTriggerHalMaxModelLimiter(result, compat.mProperties.maxSoundModels);
+ // Add concurrent capture handler for HALs which do not support concurrent capture.
+ if (!compat.mProperties.concurrentCapture) {
result = new SoundTriggerHalConcurrentCaptureHandler(result, notifier);
}
return result;
@@ -113,21 +109,11 @@
// version, so we go down the versions in descending order to find the latest one supported,
// and then simply up-cast it to obtain all the versions that are earlier.
- // Attempt 2.4
- android.hardware.soundtrigger.V2_4.ISoundTriggerHw as2_4 =
- android.hardware.soundtrigger.V2_4.ISoundTriggerHw.asInterface(binder);
- if (as2_4 != null) {
- mUnderlying_2_0 =
- mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = mUnderlying_2_4 = as2_4;
- return;
- }
-
// Attempt 2.3
android.hardware.soundtrigger.V2_3.ISoundTriggerHw as2_3 =
android.hardware.soundtrigger.V2_3.ISoundTriggerHw.asInterface(binder);
if (as2_3 != null) {
mUnderlying_2_0 = mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = as2_3;
- mUnderlying_2_4 = null;
return;
}
@@ -136,7 +122,7 @@
android.hardware.soundtrigger.V2_2.ISoundTriggerHw.asInterface(binder);
if (as2_2 != null) {
mUnderlying_2_0 = mUnderlying_2_1 = mUnderlying_2_2 = as2_2;
- mUnderlying_2_3 = mUnderlying_2_4 = null;
+ mUnderlying_2_3 = null;
return;
}
@@ -145,7 +131,7 @@
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.asInterface(binder);
if (as2_1 != null) {
mUnderlying_2_0 = mUnderlying_2_1 = as2_1;
- mUnderlying_2_2 = mUnderlying_2_3 = mUnderlying_2_4 = null;
+ mUnderlying_2_2 = mUnderlying_2_3 = null;
return;
}
@@ -154,7 +140,7 @@
android.hardware.soundtrigger.V2_0.ISoundTriggerHw.asInterface(binder);
if (as2_0 != null) {
mUnderlying_2_0 = as2_0;
- mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = mUnderlying_2_4 = null;
+ mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = null;
return;
}
@@ -213,16 +199,8 @@
@Override
public void registerCallback(GlobalCallback callback) {
- try {
- try {
- as2_4().registerGlobalCallback(new GlobalCallbackWrapper(callback));
- } catch (NotSupported e) {
- // In versions < 2.4 the events represented by this callback don't exist, we can
- // safely ignore this.
- }
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
+ // In versions 2.x the events represented by this callback don't exist, we can
+ // safely ignore this.
}
@Override
@@ -232,29 +210,18 @@
try {
AtomicInteger retval = new AtomicInteger(-1);
AtomicInteger handle = new AtomicInteger(0);
-
try {
- as2_4().loadSoundModel_2_4(hidlModel, new ModelCallbackWrapper(callback),
+ as2_1().loadSoundModel_2_1(hidlModel, new ModelCallbackWrapper(callback),
+ 0,
(r, h) -> {
retval.set(r);
handle.set(h);
});
- handleHalStatusAllowBusy(retval.get(), "loadSoundModel_2_4");
- } catch (NotSupported e) {
- // Fall-back to the 2.1 version:
- try {
- as2_1().loadSoundModel_2_1(hidlModel, new ModelCallbackWrapper(callback),
- 0,
- (r, h) -> {
- retval.set(r);
- handle.set(h);
- });
- handleHalStatus(retval.get(), "loadSoundModel_2_1");
- mModelCallbacks.put(handle.get(), callback);
- } catch (NotSupported ee) {
- // Fall-back to the 2.0 version:
- return loadSoundModel_2_0(hidlModel, callback);
- }
+ handleHalStatus(retval.get(), "loadSoundModel_2_1");
+ mModelCallbacks.put(handle.get(), callback);
+ } catch (NotSupported ee) {
+ // Fall-back to the 2.0 version:
+ return loadSoundModel_2_0(hidlModel, callback);
}
return handle.get();
} catch (RemoteException e) {
@@ -270,27 +237,17 @@
AtomicInteger retval = new AtomicInteger(-1);
AtomicInteger handle = new AtomicInteger(0);
try {
- as2_4().loadPhraseSoundModel_2_4(hidlModel, new ModelCallbackWrapper(callback),
+ as2_1().loadPhraseSoundModel_2_1(hidlModel, new ModelCallbackWrapper(callback),
+ 0,
(r, h) -> {
retval.set(r);
handle.set(h);
});
- handleHalStatusAllowBusy(retval.get(), "loadPhraseSoundModel_2_4");
- } catch (NotSupported e) {
- // Fall-back to the 2.1 version:
- try {
- as2_1().loadPhraseSoundModel_2_1(hidlModel, new ModelCallbackWrapper(callback),
- 0,
- (r, h) -> {
- retval.set(r);
- handle.set(h);
- });
- handleHalStatus(retval.get(), "loadPhraseSoundModel_2_1");
- mModelCallbacks.put(handle.get(), callback);
- } catch (NotSupported ee) {
- // Fall-back to the 2.0 version:
- return loadPhraseSoundModel_2_0(hidlModel, callback);
- }
+ handleHalStatus(retval.get(), "loadPhraseSoundModel_2_1");
+ mModelCallbacks.put(handle.get(), callback);
+ } catch (NotSupported ee) {
+ // Fall-back to the 2.0 version:
+ return loadPhraseSoundModel_2_0(hidlModel, callback);
}
return handle.get();
} catch (RemoteException e) {
@@ -328,17 +285,11 @@
ConversionUtil.aidl2hidlRecognitionConfig(config, deviceHandle, ioHandle);
try {
try {
- int retval = as2_4().startRecognition_2_4(modelHandle, hidlConfig);
- handleHalStatusAllowBusy(retval, "startRecognition_2_4");
- } catch (NotSupported e) {
- // Fall-back to the 2.3 version:
- try {
- int retval = as2_3().startRecognition_2_3(modelHandle, hidlConfig);
- handleHalStatus(retval, "startRecognition_2_3");
- } catch (NotSupported ee) {
- // Fall-back to the 2.0 version:
- startRecognition_2_1(modelHandle, hidlConfig);
- }
+ int retval = as2_3().startRecognition_2_3(modelHandle, hidlConfig);
+ handleHalStatus(retval, "startRecognition_2_3");
+ } catch (NotSupported ee) {
+ // Fall-back to the 2.0 version:
+ startRecognition_2_1(modelHandle, hidlConfig);
}
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
@@ -551,14 +502,6 @@
return mUnderlying_2_3;
}
- private @NonNull
- android.hardware.soundtrigger.V2_4.ISoundTriggerHw as2_4() throws NotSupported {
- if (mUnderlying_2_4 == null) {
- throw new NotSupported("Underlying driver version < 2.4");
- }
- return mUnderlying_2_4;
- }
-
/**
* A checked exception representing the requested interface version not being supported.
* At the public interface layer, use {@link #throwAsRecoverableException()} to propagate it to
@@ -580,22 +523,8 @@
}
}
- private static class GlobalCallbackWrapper extends
- android.hardware.soundtrigger.V2_4.ISoundTriggerHwGlobalCallback.Stub {
- private final @NonNull GlobalCallback mDelegate;
-
- private GlobalCallbackWrapper(@NonNull GlobalCallback delegate) {
- mDelegate = delegate;
- }
-
- @Override
- public void onResourcesAvailable() {
- mDelegate.onResourcesAvailable();
- }
- }
-
private static class ModelCallbackWrapper extends
- android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback.Stub {
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.Stub {
private final @NonNull ModelCallback mDelegate;
private ModelCallbackWrapper(
@@ -604,11 +533,6 @@
}
@Override
- public void modelUnloaded(int modelHandle) {
- mDelegate.modelUnloaded(modelHandle);
- }
-
- @Override
public void recognitionCallback_2_1(
android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent event,
int cookie) {
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index ff7e903..b58ca1f 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -785,13 +785,14 @@
@Override
public void showAuthenticationDialog(PromptInfo promptInfo, IBiometricSysuiReceiver receiver,
int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation,
- int userId, String opPackageName, long operationId,
+ int userId, long operationId, String opPackageName, long requestId,
@BiometricMultiSensorMode int multiSensorConfig) {
enforceBiometricDialog();
if (mBar != null) {
try {
mBar.showAuthenticationDialog(promptInfo, receiver, sensorIds, credentialAllowed,
- requireConfirmation, userId, opPackageName, operationId, multiSensorConfig);
+ requireConfirmation, userId, operationId, opPackageName, requestId,
+ multiSensorConfig);
} catch (RemoteException ex) {
}
}
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 83085cc..1ebb722 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -114,6 +114,8 @@
private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
+ private final List<Message> mPendingTvinputInfoEvents = new LinkedList<>();
+
// Calls to mListener should happen here.
private final Handler mHandler = new ListenerHandler();
@@ -229,7 +231,16 @@
connection.getInputStateLocked(), 0, inputId).sendToTarget();
}
}
- }
+ } else {
+ Message msg = mHandler.obtainMessage(ListenerHandler.TVINPUT_INFO_ADDED,
+ deviceId, cableConnectionStatus, connection);
+ for (Iterator<Message> it = mPendingTvinputInfoEvents.iterator(); it.hasNext();) {
+ if (it.next().arg1 == deviceId) {
+ it.remove();
+ }
+ }
+ mPendingTvinputInfoEvents.add(msg);
+ }
ITvInputHardwareCallback callback = connection.getCallbackLocked();
if (callback != null) {
try {
@@ -288,6 +299,8 @@
}
mHardwareInputIdMap.put(deviceId, info.getId());
mInputMap.put(info.getId(), info);
+ processPendingTvInputInfoEventsLocked();
+ Slog.d(TAG,"deviceId ="+ deviceId+", tvinputinfo = "+info);
// Process pending state changes
@@ -530,6 +543,20 @@
}
}
+
+ private void processPendingTvInputInfoEventsLocked() {
+ for (Iterator<Message> it = mPendingTvinputInfoEvents.iterator(); it.hasNext(); ) {
+ Message msg = it.next();
+ int deviceId = msg.arg1;
+ String inputId = mHardwareInputIdMap.get(deviceId);
+ if (inputId != null) {
+ msg.sendToTarget();
+ it.remove();
+ }
+ }
+ }
+
+
private void updateVolume() {
mCurrentMaxIndex = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
mCurrentIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
@@ -1182,6 +1209,7 @@
private static final int HDMI_DEVICE_ADDED = 4;
private static final int HDMI_DEVICE_REMOVED = 5;
private static final int HDMI_DEVICE_UPDATED = 6;
+ private static final int TVINPUT_INFO_ADDED = 7;
@Override
public final void handleMessage(Message msg) {
@@ -1226,6 +1254,29 @@
}
break;
}
+ case TVINPUT_INFO_ADDED: {
+ int deviceId = msg.arg1;
+ int cableConnectionStatus = msg.arg2;
+ Connection connection =(Connection)msg.obj;
+
+ int previousConfigsLength = connection.getConfigsLengthLocked();
+ int previousCableConnectionStatus = connection.getInputStateLocked();
+ String inputId = mHardwareInputIdMap.get(deviceId);
+
+ if (inputId != null) {
+ if (connection.updateCableConnectionStatusLocked(cableConnectionStatus)) {
+ if (previousCableConnectionStatus != connection.getInputStateLocked()) {
+ mListener.onStateChanged(inputId, connection.getInputStateLocked());
+ }
+ } else {
+ if ((previousConfigsLength == 0)
+ != (connection.getConfigsLengthLocked() == 0)) {
+ mListener.onStateChanged(inputId, connection.getInputStateLocked());
+ }
+ }
+ }
+ break;
+ }
default: {
Slog.w(TAG, "Unhandled message: " + msg);
break;
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 36a854e..2894708 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -2304,10 +2304,9 @@
public void requestChannelBrowsable(Uri channelUri, int userId)
throws RemoteException {
final String callingPackageName = getCallingPackageName();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, "requestChannelBrowsable");
final long identity = Binder.clearCallingIdentity();
- final int callingUid = Binder.getCallingUid();
- final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
- userId, "requestChannelBrowsable");
try {
Intent intent = new Intent(TvContract.ACTION_CHANNEL_BROWSABLE_REQUESTED);
List<ResolveInfo> list = getContext().getPackageManager()
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 22f0ada..8fb81fa 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -922,8 +922,10 @@
}
if (clientId == fe.getOwnerClientId()) {
ClientProfile ownerClient = getClientProfile(fe.getOwnerClientId());
- for (int shareOwnerId : ownerClient.getShareFeClientIds()) {
- clearFrontendAndClientMapping(getClientProfile(shareOwnerId));
+ if (ownerClient != null) {
+ for (int shareOwnerId : ownerClient.getShareFeClientIds()) {
+ clearFrontendAndClientMapping(getClientProfile(shareOwnerId));
+ }
}
}
clearFrontendAndClientMapping(getClientProfile(clientId));
@@ -1039,20 +1041,13 @@
@VisibleForTesting
protected boolean reclaimResource(int reclaimingClientId,
@TunerResourceManager.TunerResourceType int resourceType) {
- if (DEBUG) {
- Slog.d(TAG, "Reclaiming resources because higher priority client request resource type "
- + resourceType);
- }
- try {
- mListeners.get(reclaimingClientId).getListener().onReclaimResources();
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to reclaim resources on client " + reclaimingClientId, e);
- return false;
- }
// Reclaim all the resources of the share owners of the frontend that is used by the current
// resource reclaimed client.
ClientProfile profile = getClientProfile(reclaimingClientId);
+ if (profile == null) {
+ return true;
+ }
Set<Integer> shareFeClientIds = profile.getShareFeClientIds();
for (int clientId : shareFeClientIds) {
try {
@@ -1063,6 +1058,17 @@
}
clearAllResourcesAndClientMapping(getClientProfile(clientId));
}
+
+ if (DEBUG) {
+ Slog.d(TAG, "Reclaiming resources because higher priority client request resource type "
+ + resourceType + ", clientId:" + reclaimingClientId);
+ }
+ try {
+ mListeners.get(reclaimingClientId).getListener().onReclaimResources();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to reclaim resources on client " + reclaimingClientId, e);
+ return false;
+ }
clearAllResourcesAndClientMapping(profile);
return true;
}
@@ -1319,13 +1325,21 @@
}
private void clearFrontendAndClientMapping(ClientProfile profile) {
+ if (profile == null) {
+ return;
+ }
for (Integer feId : profile.getInUseFrontendHandles()) {
FrontendResource fe = getFrontendResource(feId);
- if (fe.getOwnerClientId() == profile.getId()) {
+ int ownerClientId = fe.getOwnerClientId();
+ if (ownerClientId == profile.getId()) {
fe.removeOwner();
continue;
}
- getClientProfile(fe.getOwnerClientId()).stopSharingFrontend(profile.getId());
+ ClientProfile ownerClientProfile = getClientProfile(ownerClientId);
+ if (ownerClientProfile != null) {
+ ownerClientProfile.stopSharingFrontend(profile.getId());
+ }
+
}
profile.releaseFrontend();
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index 69cc90bf..efccd57 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -304,7 +304,7 @@
}
}
- /** Turns off the vibrator.This will affect the state of {@link #isVibrating()}. */
+ /** Turns off the vibrator. This will affect the state of {@link #isVibrating()}. */
public void off() {
synchronized (mLock) {
mNativeWrapper.off();
@@ -313,6 +313,15 @@
}
}
+ /**
+ * Resets the vibrator hardware to a default state.
+ * This turns the vibrator off, which will affect the state of {@link #isVibrating()}.
+ */
+ public void reset() {
+ setExternalControl(false);
+ off();
+ }
+
@Override
public String toString() {
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 0efc940..567463f 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -219,7 +219,7 @@
// fresh boot.
mNativeWrapper.cancelSynced();
for (int i = 0; i < mVibrators.size(); i++) {
- mVibrators.valueAt(i).off();
+ mVibrators.valueAt(i).reset();
}
IntentFilter filter = new IntentFilter();
@@ -1380,8 +1380,8 @@
// At this point we have an externally controlled vibration playing already.
// Since the interface defines that only one externally controlled vibration can
// play at a time, we need to first mute the ongoing vibration and then return
- // a scale from this function for the new one. Ee can be assured that the
- // ongoing it will be muted in favor of the new vibration.
+ // a scale from this function for the new one, so we can be assured that the
+ // ongoing will be muted in favor of the new vibration.
//
// Note that this doesn't support multiple concurrent external controls, as we
// would need to mute the old one still if it came from a different controller.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index cf6a38f..63ab1bb 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1354,14 +1354,8 @@
updatePictureInPictureMode(null, false);
} else {
mLastReportedMultiWindowMode = inMultiWindowMode;
- // If the activity is in stopping or stopped state, for instance, it's in the
- // split screen task and not the top one, the last configuration it should keep
- // is the one before multi-window mode change.
- final State state = getState();
- if (state != STOPPED && state != STOPPING) {
- ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS,
- true /* ignoreVisibility */);
- }
+ ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS,
+ false /* ignoreVisibility */);
}
}
}
@@ -2595,6 +2589,13 @@
return task != null ? task.getOrganizedTask() : null;
}
+ /** Returns the organized parent {@link TaskFragment}. */
+ @Nullable
+ TaskFragment getOrganizedTaskFragment() {
+ final TaskFragment parent = getTaskFragment();
+ return parent != null ? parent.getOrganizedTaskFragment() : null;
+ }
+
@Override
@Nullable
TaskDisplayArea getDisplayArea() {
@@ -4307,7 +4308,7 @@
// The activity now gets access to the data associated with this Intent.
mAtmService.mUgmInternal.grantUriPermissionUncheckedFromIntent(intentGrants,
getUriPermissionsLocked());
- final ReferrerIntent rintent = new ReferrerIntent(intent, referrer);
+ final ReferrerIntent rintent = new ReferrerIntent(intent, getFilteredReferrer(referrer));
boolean unsent = true;
final boolean isTopActivityWhileSleeping = isTopRunningActivity() && isSleeping();
@@ -4893,6 +4894,10 @@
// getting visible so we also wait for them.
forAllWindows(mWmService::makeWindowFreezingScreenIfNeededLocked, true);
}
+ // dispatchTaskInfoChangedIfNeeded() right after ActivityRecord#setVisibility() can report
+ // the stale visible state, because the state will be updated after the app transition.
+ // So tries to report the actual visible state again where the state is changed.
+ if (task != null) task.dispatchTaskInfoChangedIfNeeded(false /* force */);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"commitVisibility: %s: visible=%b mVisibleRequested=%b", this,
isVisible(), mVisibleRequested);
@@ -6960,7 +6965,7 @@
Rect appRect;
if (win != null) {
insets = win.getInsetsStateWithVisibilityOverride().calculateInsets(
- win.getFrame(), Type.systemBars(), false /* ignoreVisibility */);
+ win.getFrame(), Type.systemBars(), false /* ignoreVisibility */).toRect();
appRect = new Rect(win.getFrame());
appRect.inset(insets);
} else {
@@ -8413,7 +8418,9 @@
startFreezingScreenLocked(globalChanges);
}
forceNewConfig = false;
- preserveWindow &= isResizeOnlyChange(changes);
+ // Do not preserve window if it is freezing screen because the original window won't be
+ // able to update drawn state that causes freeze timeout.
+ preserveWindow &= isResizeOnlyChange(changes) && !mFreezingScreen;
final boolean hasResizeChange = hasResizeChange(changes & ~info.getRealConfigChanged());
if (hasResizeChange) {
final boolean isDragResizing = task.isDragResizing();
@@ -8817,6 +8824,19 @@
}
/**
+ * Gets the referrer package name with respect to package visibility. This method returns null
+ * if the given package is not visible to this activity.
+ */
+ String getFilteredReferrer(String referrerPackage) {
+ if (referrerPackage == null || (!referrerPackage.equals(packageName)
+ && mWmService.mPmInternal.filterAppAccess(
+ referrerPackage, info.applicationInfo.uid, mUserId))) {
+ return null;
+ }
+ return referrerPackage;
+ }
+
+ /**
* Determines whether this ActivityRecord can turn the screen on. It checks whether the flag
* {@link ActivityRecord#getTurnScreenOnFlag} is set and checks whether the ActivityRecord
* should be visible depending on Keyguard state.
@@ -9213,7 +9233,7 @@
return null;
}
final Rect insets = mainWindow.getInsetsStateWithVisibilityOverride().calculateInsets(
- task.getBounds(), Type.systemBars(), false /* ignoreVisibility */);
+ task.getBounds(), Type.systemBars(), false /* ignoreVisibility */).toRect();
InsetUtils.addInsets(insets, getLetterboxInsets());
return new RemoteAnimationTarget(task.mTaskId, record.getMode(),
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index e593c1c..ea242bb 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -857,9 +857,9 @@
// and override configs.
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
- r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
- r.getSavedState(), r.getPersistentSavedState(), results, newIntents,
- r.takeOptions(), isTransitionForward,
+ r.getFilteredReferrer(r.launchedFromPackage), task.voiceInteractor,
+ proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(),
+ results, newIntents, r.takeOptions(), isTransitionForward,
proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
r.createFixedRotationAdjustmentsIfNeeded(), r.shareableActivityToken,
r.getLaunchedFromBubble(), fragmentToken));
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 6aea848..929ac56f 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -40,6 +40,7 @@
import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
@@ -476,6 +477,7 @@
mNextAppTransitionAnimationsSpecsFuture = null;
mDefaultNextAppTransitionAnimationSpec = null;
mAnimationFinishedCallback = null;
+ mOverrideTaskTransition = false;
mNextAppTransitionIsSync = false;
}
@@ -941,7 +943,7 @@
"applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS: "
+ "anim=%s transit=%s isEntrance=true Callers=%s",
a, appTransitionOldToString(transit), Debug.getCallers(3));
- } else if (transit == TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE) {
+ } else if (isChangeTransitOld(transit)) {
// In the absence of a specific adapter, we just want to keep everything stationary.
a = new AlphaAnimation(1.f, 1.f);
a.setDuration(WindowChangeAnimationSpec.ANIMATION_DURATION);
@@ -1338,6 +1340,9 @@
case TRANSIT_OLD_TASK_FRAGMENT_CLOSE: {
return "TRANSIT_OLD_TASK_FRAGMENT_CLOSE";
}
+ case TRANSIT_OLD_TASK_FRAGMENT_CHANGE: {
+ return "TRANSIT_OLD_TASK_FRAGMENT_CHANGE";
+ }
default: {
return "<UNKNOWN: " + transition + ">";
}
@@ -1595,7 +1600,8 @@
}
static boolean isChangeTransitOld(@TransitionOldType int transit) {
- return transit == TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
+ return transit == TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE
+ || transit == TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
}
static boolean isClosingTransitOld(@TransitionOldType int transit) {
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 6745c69..7a42351 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -39,6 +39,7 @@
import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
@@ -85,6 +86,7 @@
import android.view.WindowManager.TransitionOldType;
import android.view.WindowManager.TransitionType;
import android.view.animation.Animation;
+import android.window.ITaskFragmentOrganizer;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
@@ -204,8 +206,8 @@
mDisplayContent.mOpeningApps);
final @TransitionOldType int transit = getTransitCompatType(
- mDisplayContent.mAppTransition,
- mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+ mDisplayContent.mAppTransition, mDisplayContent.mOpeningApps,
+ mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers,
mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper(),
mDisplayContent.mSkipAppTransitionAnimation);
mDisplayContent.mSkipAppTransitionAnimation = false;
@@ -232,7 +234,11 @@
final ActivityRecord topChangingApp =
getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */);
final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);
- overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
+
+ // Check if there is any override
+ if (!overrideWithTaskFragmentRemoteAnimation(transit, activityTypes)) {
+ overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
+ }
final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mOpeningApps)
|| containsVoiceInteraction(mDisplayContent.mOpeningApps);
@@ -286,6 +292,7 @@
* @param appTransition {@link AppTransition} for managing app transition state.
* @param openingApps {@link ActivityRecord}s which are becoming visible.
* @param closingApps {@link ActivityRecord}s which are becoming invisible.
+ * @param changingContainers {@link WindowContainer}s which are changed in configuration.
* @param wallpaperTarget If non-null, this is the currently visible window that is associated
* with the wallpaper.
* @param oldWallpaper The currently visible window that is associated with the wallpaper in
@@ -294,8 +301,8 @@
*/
static @TransitionOldType int getTransitCompatType(AppTransition appTransition,
ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps,
- @Nullable WindowState wallpaperTarget, @Nullable WindowState oldWallpaper,
- boolean skipAppTransitionAnimation) {
+ ArraySet<WindowContainer> changingContainers, @Nullable WindowState wallpaperTarget,
+ @Nullable WindowState oldWallpaper, boolean skipAppTransitionAnimation) {
// Determine if closing and opening app token sets are wallpaper targets, in which case
// special animations are needed.
@@ -328,8 +335,18 @@
// Special transitions
// TODO(new-app-transitions): Revisit if those can be rewritten by using flags.
- if (appTransition.containsTransitRequest(TRANSIT_CHANGE)) {
- return TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
+ if (appTransition.containsTransitRequest(TRANSIT_CHANGE) && !changingContainers.isEmpty()) {
+ @TransitContainerType int changingType =
+ getTransitContainerType(changingContainers.valueAt(0));
+ switch (changingType) {
+ case TYPE_TASK:
+ return TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
+ case TYPE_TASK_FRAGMENT:
+ return TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
+ default:
+ throw new IllegalStateException(
+ "TRANSIT_CHANGE with unrecognized changing type=" + changingType);
+ }
}
if ((flags & TRANSIT_FLAG_APP_CRASHED) != 0) {
return TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
@@ -471,6 +488,7 @@
return TYPE_NONE;
}
+ @Nullable
private static WindowManager.LayoutParams getAnimLp(ActivityRecord activity) {
final WindowState mainWindow = activity != null ? activity.findMainWindow() : null;
return mainWindow != null ? mainWindow.mAttrs : null;
@@ -494,6 +512,61 @@
}
/**
+ * Overrides the pending transition with the remote animation defined by the
+ * {@link ITaskFragmentOrganizer} if all windows in the transition are children of
+ * {@link TaskFragment} that are organized by the same organizer.
+ *
+ * @return {@code true} if the transition is overridden.
+ */
+ @VisibleForTesting
+ boolean overrideWithTaskFragmentRemoteAnimation(@TransitionOldType int transit,
+ ArraySet<Integer> activityTypes) {
+ final ArrayList<WindowContainer> allWindows = new ArrayList<>();
+ allWindows.addAll(mDisplayContent.mClosingApps);
+ allWindows.addAll(mDisplayContent.mOpeningApps);
+ allWindows.addAll(mDisplayContent.mChangingContainers);
+
+ // Find the common TaskFragmentOrganizer of all windows.
+ ITaskFragmentOrganizer organizer = null;
+ for (int i = allWindows.size() - 1; i >= 0; i--) {
+ final ActivityRecord r = getAppFromContainer(allWindows.get(i));
+ if (r == null) {
+ return false;
+ }
+ final TaskFragment organizedTaskFragment = r.getOrganizedTaskFragment();
+ final ITaskFragmentOrganizer curOrganizer = organizedTaskFragment != null
+ ? organizedTaskFragment.getTaskFragmentOrganizer()
+ : null;
+ if (curOrganizer == null) {
+ // All windows must below an organized TaskFragment.
+ return false;
+ }
+ if (organizer == null) {
+ organizer = curOrganizer;
+ } else if (!organizer.asBinder().equals(curOrganizer.asBinder())) {
+ // They must be controlled by the same organizer.
+ return false;
+ }
+ }
+
+ final RemoteAnimationDefinition definition = organizer != null
+ ? mDisplayContent.mAtmService.mTaskFragmentOrganizerController
+ .getRemoteAnimationDefinition(organizer)
+ : null;
+ final RemoteAnimationAdapter adapter = definition != null
+ ? definition.getAdapter(transit, activityTypes)
+ : null;
+ if (adapter == null) {
+ return false;
+ }
+ mDisplayContent.mAppTransition.overridePendingAppTransitionRemote(adapter);
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+ "Override with TaskFragment remote animation for transit=%s",
+ AppTransition.appTransitionOldToString(transit));
+ return true;
+ }
+
+ /**
* Overrides the pending transition with the remote animation defined for the transition in the
* set of defined remote animations in the app window token.
*/
@@ -512,13 +585,14 @@
}
static ActivityRecord getAppFromContainer(WindowContainer wc) {
- return wc.asTask() != null ? wc.asTask().getTopNonFinishingActivity()
+ return wc.asTaskFragment() != null ? wc.asTaskFragment().getTopNonFinishingActivity()
: wc.asActivityRecord();
}
/**
* @return The window token that determines the animation theme.
*/
+ @Nullable
private ActivityRecord findAnimLayoutParamsToken(@TransitionOldType int transit,
ArraySet<Integer> activityTypes) {
ActivityRecord result;
@@ -531,7 +605,7 @@
w -> w.getRemoteAnimationDefinition() != null
&& w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes));
if (result != null) {
- return getAppFromContainer(result);
+ return result;
}
result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
w -> w.fillsParent() && w.findMainWindow() != null);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ee075fa..db5e3d0 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1201,10 +1201,10 @@
}
}
- WindowToken removeWindowToken(IBinder binder) {
+ WindowToken removeWindowToken(IBinder binder, boolean animateExit) {
final WindowToken token = mTokenMap.remove(binder);
if (token != null && token.asActivityRecord() == null) {
- token.setExiting();
+ token.setExiting(animateExit);
}
return token;
}
@@ -1282,7 +1282,7 @@
}
void removeAppToken(IBinder binder) {
- final WindowToken token = removeWindowToken(binder);
+ final WindowToken token = removeWindowToken(binder, true /* animateExit */);
if (token == null) {
Slog.w(TAG_WM, "removeAppToken: Attempted to remove non-existing token: " + binder);
return;
@@ -3133,6 +3133,12 @@
return mScreenRotationAnimation;
}
+ /** If the display is in transition, there should be a screenshot covering it. */
+ @Override
+ boolean inTransition() {
+ return mScreenRotationAnimation != null || super.inTransition();
+ }
+
@Override
public void dumpDebug(ProtoOutputStream proto, long fieldId,
@WindowTraceLogLevel int logLevel) {
@@ -5747,6 +5753,13 @@
}
mRemoved = true;
+ if (mMirroredSurface != null) {
+ // Do not wait for the mirrored surface to be garbage collected, but clean up
+ // immediately.
+ mWmService.mTransactionFactory.get().remove(mMirroredSurface).apply();
+ mMirroredSurface = null;
+ }
+
// Only update focus/visibility for the last one because there may be many root tasks are
// reparented and the intermediate states are unnecessary.
if (lastReparentedRootTask != null) {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 1647eba1..3c8827e 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -378,6 +378,8 @@
private PointerLocationView mPointerLocationView;
+ private int mDisplayCutoutTouchableRegionSize;
+
/**
* The area covered by system windows which belong to another display. Forwarded insets is set
* in case this is a virtual display, this is displayed on another display that has insets, and
@@ -1126,8 +1128,21 @@
rect.bottom = rect.top + getStatusBarHeight(displayFrames);
}
};
+ final TriConsumer<DisplayFrames, WindowState, Rect> gestureFrameProvider =
+ (displayFrames, windowState, rect) -> {
+ rect.bottom = rect.top + getStatusBarHeight(displayFrames);
+ final DisplayCutout cutout =
+ displayFrames.mInsetsState.getDisplayCutout();
+ if (cutout != null) {
+ final Rect top = cutout.getBoundingRectTop();
+ if (!top.isEmpty()) {
+ rect.bottom = rect.bottom + mDisplayCutoutTouchableRegionSize;
+ }
+ }
+ };
mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, win, frameProvider);
- mDisplayContent.setInsetProvider(ITYPE_TOP_MANDATORY_GESTURES, win, frameProvider);
+ mDisplayContent.setInsetProvider(
+ ITYPE_TOP_MANDATORY_GESTURES, win, gestureFrameProvider);
mDisplayContent.setInsetProvider(ITYPE_TOP_TAPPABLE_ELEMENT, win, frameProvider);
break;
case TYPE_NAVIGATION_BAR:
@@ -2108,11 +2123,14 @@
mStatusBarHeightForRotation[landscapeRotation] =
mStatusBarHeightForRotation[seascapeRotation] =
res.getDimensionPixelSize(R.dimen.status_bar_height_landscape);
+ mDisplayCutoutTouchableRegionSize = res.getDimensionPixelSize(
+ R.dimen.display_cutout_touchable_region_size);
} else {
mStatusBarHeightForRotation[portraitRotation] =
mStatusBarHeightForRotation[upsideDownRotation] =
mStatusBarHeightForRotation[landscapeRotation] =
mStatusBarHeightForRotation[seascapeRotation] = 0;
+ mDisplayCutoutTouchableRegionSize = 0;
}
// Height of the navigation bar when presented horizontally at bottom
diff --git a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
index b627b33..4141090 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
@@ -19,6 +19,7 @@
import android.content.res.Configuration;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.util.IntArray;
import android.view.IDisplayWindowListener;
/**
@@ -28,23 +29,20 @@
class DisplayWindowListenerController {
RemoteCallbackList<IDisplayWindowListener> mDisplayListeners = new RemoteCallbackList<>();
-// private final ArrayList<DisplayContainerListener> mDisplayListeners = new ArrayList<>();
private final WindowManagerService mService;
DisplayWindowListenerController(WindowManagerService service) {
mService = service;
}
- void registerListener(IDisplayWindowListener listener) {
+ int[] registerListener(IDisplayWindowListener listener) {
synchronized (mService.mGlobalLock) {
mDisplayListeners.register(listener);
- try {
- for (int i = 0; i < mService.mAtmService.mRootWindowContainer.getChildCount();
- ++i) {
- DisplayContent d = mService.mAtmService.mRootWindowContainer.getChildAt(i);
- listener.onDisplayAdded(d.mDisplayId);
- }
- } catch (RemoteException e) { }
+ final IntArray displayIds = new IntArray();
+ mService.mAtmService.mRootWindowContainer.forAllDisplays((displayContent) -> {
+ displayIds.add(displayContent.mDisplayId);
+ });
+ return displayIds.toArray();
}
}
diff --git a/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java b/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java
index 92baadf..5e8d795 100644
--- a/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java
+++ b/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java
@@ -22,12 +22,12 @@
import android.annotation.Nullable;
import android.content.res.Resources;
import android.provider.DeviceConfig;
+import android.provider.DeviceConfigInterface;
import android.util.ArraySet;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
-import com.android.server.utils.DeviceConfigInterface;
import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index f8af43be..f2e6abc 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -580,9 +580,12 @@
if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) {
if (recentsAnimationController.updateInputConsumerForApp(
mRecentsAnimationInputConsumer.mWindowHandle)) {
- mRecentsAnimationInputConsumer.show(mInputTransaction,
- recentsAnimationController.getHighestLayerActivity());
- mAddRecentsAnimationInputConsumerHandle = false;
+ final WindowState highestLayerWindow =
+ recentsAnimationController.getHighestLayerWindow();
+ if (highestLayerWindow != null) {
+ mRecentsAnimationInputConsumer.show(mInputTransaction, highestLayerWindow);
+ mAddRecentsAnimationInputConsumerHandle = false;
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 932cceb..450c2c1 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -48,6 +48,7 @@
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimation.Bounds;
import android.view.WindowInsetsAnimationControlListener;
+import android.view.WindowInsetsAnimationController;
import android.view.WindowManager;
import com.android.internal.R;
@@ -584,8 +585,8 @@
}
@Override
- public void startAnimation(InsetsAnimationControlImpl controller,
- WindowInsetsAnimationControlListener listener, int types,
+ public <T extends InsetsAnimationControlRunner & WindowInsetsAnimationController>
+ void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types,
WindowInsetsAnimation animation,
Bounds bounds) {
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index f3e52f2..f93e085 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -22,7 +22,7 @@
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_INSETS;
import static com.android.server.wm.InsetsSourceProviderProto.CAPTURED_LEASH;
import static com.android.server.wm.InsetsSourceProviderProto.CLIENT_VISIBLE;
import static com.android.server.wm.InsetsSourceProviderProto.CONTROL;
@@ -164,7 +164,8 @@
mWin.cancelAnimation();
mWin.mProvidedInsetsSources.remove(mSource.getType());
}
- ProtoLog.d(WM_DEBUG_IME, "InsetsSource setWin %s", win);
+ ProtoLog.d(WM_DEBUG_WINDOW_INSETS, "InsetsSource setWin %s for type %s", win,
+ InsetsState.typeToString(mSource.getType()));
mWin = win;
mFrameProvider = frameProvider;
mImeFrameProvider = imeFrameProvider;
@@ -343,7 +344,7 @@
updateVisibility();
mControl = new InsetsSourceControl(mSource.getType(), leash, surfacePosition,
mSource.calculateInsets(mWin.getBounds(), true /* ignoreVisibility */));
- ProtoLog.d(WM_DEBUG_IME,
+ ProtoLog.d(WM_DEBUG_WINDOW_INSETS,
"InsetsSource Control %s for target %s", mControl, mControlTarget);
}
@@ -392,8 +393,9 @@
protected void updateVisibility() {
mSource.setVisible(mServerVisible && (isMirroredSource() || mClientVisible));
- ProtoLog.d(WM_DEBUG_IME,
- "InsetsSource updateVisibility serverVisible: %s clientVisible: %s",
+ ProtoLog.d(WM_DEBUG_WINDOW_INSETS,
+ "InsetsSource updateVisibility for %s, serverVisible: %s clientVisible: %s",
+ InsetsState.typeToString(mSource.getType()),
mServerVisible, mClientVisible);
}
@@ -539,7 +541,7 @@
t.setAlpha(animationLeash, 1 /* alpha */);
t.hide(animationLeash);
}
- ProtoLog.i(WM_DEBUG_IME,
+ ProtoLog.i(WM_DEBUG_WINDOW_INSETS,
"ControlAdapter startAnimation mSource: %s controlTarget: %s", mSource,
mControlTarget);
@@ -555,7 +557,7 @@
mControlTarget = null;
mAdapter = null;
setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
- ProtoLog.i(WM_DEBUG_IME,
+ ProtoLog.i(WM_DEBUG_WINDOW_INSETS,
"ControlAdapter onAnimationCancelled mSource: %s mControlTarget: %s",
mSource, mControlTarget);
}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 2c4adcb..fd16a7d 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -16,9 +16,9 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
@@ -137,10 +137,10 @@
return rotatedState;
}
}
- final @WindowingMode int windowingMode = token != null
- ? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
final boolean alwaysOnTop = token != null && token.isAlwaysOnTop();
- return getInsetsForTarget(type, windowingMode, alwaysOnTop, mState);
+ // Always use windowing mode fullscreen when get insets for window metrics to make sure it
+ // contains all insets types.
+ return getInsetsForTarget(type, WINDOWING_MODE_FULLSCREEN, alwaysOnTop, mState);
}
private static @InternalInsetsType
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 6f3edbc..4a8c36f 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -375,7 +375,7 @@
// TODO(b/113840485): Handle app transition for individual display, and apply occluded
// state change to secondary displays.
// For now, only default display fully supports occluded change. Other displays only
- // updates keygaurd sleep token on that display.
+ // updates keyguard sleep token on that display.
if (displayId != DEFAULT_DISPLAY) {
updateKeyguardSleepToken(displayId);
return;
@@ -390,15 +390,6 @@
isDisplayOccluded(DEFAULT_DISPLAY)
? TRANSIT_KEYGUARD_OCCLUDE
: TRANSIT_KEYGUARD_UNOCCLUDE, 0 /* flags */);
- // When the occluding activity also turns on the display, visibility of the activity
- // can be committed before KEYGUARD_OCCLUDE transition is handled.
- // Set mRequestForceTransition flag to make sure that the app transition animation
- // is applied for such case.
- // TODO(b/194243906): Fix this before enabling the remote keyguard animation.
- if (WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation
- && topActivity != null) {
- topActivity.mRequestForceTransition = true;
- }
updateKeyguardSleepToken(DEFAULT_DISPLAY);
mWindowManager.executeAppTransition();
} finally {
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index eb7087c..34b834b 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Color;
@@ -63,7 +64,10 @@
private int mLetterboxActivityCornersRadius;
// Color for {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} letterbox background type.
- private Color mLetterboxBackgroundColor;
+ @Nullable private Color mLetterboxBackgroundColorOverride;
+
+ // Color resource id for {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} letterbox background type.
+ @Nullable private Integer mLetterboxBackgroundColorResourceIdOverride;
@LetterboxBackgroundType
private int mLetterboxBackgroundType;
@@ -81,20 +85,18 @@
// side of the screen and 1.0 to the right side.
private float mLetterboxHorizontalPositionMultiplier;
- LetterboxConfiguration(Context context) {
- mContext = context;
- mFixedOrientationLetterboxAspectRatio = context.getResources().getFloat(
+ LetterboxConfiguration(Context systemUiContext) {
+ mContext = systemUiContext;
+ mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat(
R.dimen.config_fixedOrientationLetterboxAspectRatio);
- mLetterboxActivityCornersRadius = context.getResources().getInteger(
+ mLetterboxActivityCornersRadius = mContext.getResources().getInteger(
R.integer.config_letterboxActivityCornersRadius);
- mLetterboxBackgroundColor = Color.valueOf(context.getResources().getColor(
- R.color.config_letterboxBackgroundColor));
- mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(context);
- mLetterboxBackgroundWallpaperBlurRadius = context.getResources().getDimensionPixelSize(
+ mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext);
+ mLetterboxBackgroundWallpaperBlurRadius = mContext.getResources().getDimensionPixelSize(
R.dimen.config_letterboxBackgroundWallpaperBlurRadius);
- mLetterboxBackgroundWallpaperDarkScrimAlpha = context.getResources().getFloat(
+ mLetterboxBackgroundWallpaperDarkScrimAlpha = mContext.getResources().getFloat(
R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha);
- mLetterboxHorizontalPositionMultiplier = context.getResources().getFloat(
+ mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat(
R.dimen.config_letterboxHorizontalPositionMultiplier);
}
@@ -158,12 +160,20 @@
}
/**
- * Gets color of letterbox background which is used when {@link
+ * Gets color of letterbox background which is used when {@link
* #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
* fallback for other backfround types.
*/
Color getLetterboxBackgroundColor() {
- return mLetterboxBackgroundColor;
+ if (mLetterboxBackgroundColorOverride != null) {
+ return mLetterboxBackgroundColorOverride;
+ }
+ int colorId = mLetterboxBackgroundColorResourceIdOverride != null
+ ? mLetterboxBackgroundColorResourceIdOverride
+ : R.color.config_letterboxBackgroundColor;
+ // Query color dynamically because material colors extracted from wallpaper are updated
+ // when wallpaper is changed.
+ return Color.valueOf(mContext.getResources().getColor(colorId));
}
@@ -173,7 +183,16 @@
* fallback for other backfround types.
*/
void setLetterboxBackgroundColor(Color color) {
- mLetterboxBackgroundColor = color;
+ mLetterboxBackgroundColorOverride = color;
+ }
+
+ /**
+ * Sets color ID of letterbox background which is used when {@link
+ * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
+ * fallback for other backfround types.
+ */
+ void setLetterboxBackgroundColorResourceId(int colorId) {
+ mLetterboxBackgroundColorResourceIdOverride = colorId;
}
/**
@@ -181,8 +200,8 @@
* com.android.internal.R.color.config_letterboxBackgroundColor}.
*/
void resetLetterboxBackgroundColor() {
- mLetterboxBackgroundColor = Color.valueOf(mContext.getResources().getColor(
- com.android.internal.R.color.config_letterboxBackgroundColor));
+ mLetterboxBackgroundColorOverride = null;
+ mLetterboxBackgroundColorResourceIdOverride = null;
}
/**
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index d7dc306..ba85c98 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -581,7 +581,7 @@
contentInsets = targetAppMainWindow
.getInsetsStateWithVisibilityOverride()
.calculateInsets(mTargetActivityRecord.getBounds(), Type.systemBars(),
- false /* ignoreVisibility */);
+ false /* ignoreVisibility */).toRect();
} else {
// If the window for the activity had not yet been created, use the display insets.
mService.getStableInsets(mDisplayId, mTmpRect);
@@ -1103,9 +1103,9 @@
}
/**
- * Returns the activity with the highest layer, or null if none is found.
+ * Returns the window with the highest layer, or null if none is found.
*/
- public ActivityRecord getHighestLayerActivity() {
+ public WindowState getHighestLayerWindow() {
int highestLayer = Integer.MIN_VALUE;
Task highestLayerTask = null;
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
@@ -1116,7 +1116,7 @@
highestLayerTask = adapter.mTask;
}
}
- return highestLayerTask.getTopMostActivity();
+ return highestLayerTask.getTopMostActivity().getTopChild();
}
boolean isAnimatingTask(Task task) {
@@ -1209,7 +1209,7 @@
return null;
}
final Rect insets = mainWindow.getInsetsStateWithVisibilityOverride().calculateInsets(
- mBounds, Type.systemBars(), false /* ignoreVisibility */);
+ mBounds, Type.systemBars(), false /* ignoreVisibility */).toRect();
InsetUtils.addInsets(insets, mainWindow.mActivityRecord.getLetterboxInsets());
final int mode = topApp.getActivityType() == mTargetActivityType
? MODE_OPENING
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index d440a14..d1460f4 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -729,7 +729,12 @@
mScreenshotRotationAnimator = null;
mRotateScreenAnimator = null;
mService.mAnimator.mBulkUpdateParams |= WindowSurfacePlacer.SET_UPDATE_ROTATION;
- kill();
+ if (mDisplayContent.getRotationAnimation() == ScreenRotationAnimation.this) {
+ // It also invokes kill().
+ mDisplayContent.setRotationAnimation(null);
+ } else {
+ kill();
+ }
mService.updateRotation(false, false);
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 73dda74..567936d 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2166,16 +2166,6 @@
bounds.offset(horizontalDiff, verticalDiff);
}
- /**
- * Initializes a change transition. See {@link SurfaceFreezer} for more information.
- */
- private void initializeChangeTransition(Rect startBounds) {
- mDisplayContent.prepareAppTransition(TRANSIT_CHANGE);
- mDisplayContent.mChangingContainers.add(this);
-
- mSurfaceFreezer.freeze(getPendingTransaction(), startBounds);
- }
-
private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
if (mWmService.mDisableTransitionAnimation
|| !isVisible()
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 5363c9b..1b72826 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -33,6 +33,7 @@
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.os.UserHandle.USER_NULL;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_NONE;
@@ -1987,10 +1988,50 @@
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
+ // Task will animate differently.
+ if (mTaskFragmentOrganizer != null) {
+ mTmpPrevBounds.set(getBounds());
+ }
+
super.onConfigurationChanged(newParentConfig);
+
+ if (shouldStartChangeTransition(mTmpPrevBounds)) {
+ initializeChangeTransition(mTmpPrevBounds);
+ }
+
+ if (mTaskFragmentOrganizer != null) {
+ // Update the surface position here instead of in the organizer so that we can make sure
+ // it can be synced with the surface freezer.
+ updateSurfacePosition(getSyncTransaction());
+ }
+
sendTaskFragmentInfoChanged();
}
+ /** Whether we should prepare a transition for this {@link TaskFragment} bounds change. */
+ private boolean shouldStartChangeTransition(Rect startBounds) {
+ if (mWmService.mDisableTransitionAnimation
+ || mDisplayContent == null
+ || mTaskFragmentOrganizer == null
+ || getSurfaceControl() == null
+ // The change transition will be covered by display.
+ || mDisplayContent.inTransition()
+ || !isVisible()) {
+ return false;
+ }
+
+ return !startBounds.equals(getBounds());
+ }
+
+ /**
+ * Initializes a change transition. See {@link SurfaceFreezer} for more information.
+ */
+ void initializeChangeTransition(Rect startBounds) {
+ mDisplayContent.prepareAppTransition(TRANSIT_CHANGE);
+ mDisplayContent.mChangingContainers.add(this);
+ mSurfaceFreezer.freeze(getSyncTransaction(), startBounds);
+ }
+
@Override
void setSurfaceControl(SurfaceControl sc) {
super.setSurfaceControl(sc);
@@ -2056,11 +2097,15 @@
}
@Nullable
- @VisibleForTesting
ITaskFragmentOrganizer getTaskFragmentOrganizer() {
return mTaskFragmentOrganizer;
}
+ @Override
+ boolean isOrganized() {
+ return mTaskFragmentOrganizer != null;
+ }
+
/** Clear {@link #mLastPausedActivity} for all {@link TaskFragment} children */
void clearLastPausedActivity() {
forAllTaskFragments(taskFragment -> taskFragment.mLastPausedActivity = null);
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 690f67c..30d2a32 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -30,6 +30,7 @@
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Slog;
+import android.view.RemoteAnimationDefinition;
import android.view.SurfaceControl;
import android.window.ITaskFragmentOrganizer;
import android.window.ITaskFragmentOrganizerController;
@@ -81,6 +82,13 @@
private final Map<TaskFragment, Configuration> mLastSentTaskFragmentParentConfigs =
new WeakHashMap<>();
+ /**
+ * @see android.window.TaskFragmentOrganizer#registerRemoteAnimations(
+ * RemoteAnimationDefinition)
+ */
+ @Nullable
+ private RemoteAnimationDefinition mRemoteAnimationDefinition;
+
TaskFragmentOrganizerState(ITaskFragmentOrganizer organizer) {
mOrganizer = organizer;
try {
@@ -245,6 +253,61 @@
}
}
+ @Override
+ public void registerRemoteAnimations(ITaskFragmentOrganizer organizer,
+ RemoteAnimationDefinition definition) {
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ synchronized (mGlobalLock) {
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+ "Register remote animations for organizer=%s uid=%d pid=%d",
+ organizer.asBinder(), uid, pid);
+ final TaskFragmentOrganizerState organizerState =
+ mTaskFragmentOrganizerState.get(organizer.asBinder());
+ if (organizerState == null) {
+ throw new IllegalStateException("The organizer hasn't been registered.");
+ }
+ if (organizerState.mRemoteAnimationDefinition != null) {
+ throw new IllegalStateException(
+ "The organizer has already registered remote animations="
+ + organizerState.mRemoteAnimationDefinition);
+ }
+
+ definition.setCallingPidUid(pid, uid);
+ organizerState.mRemoteAnimationDefinition = definition;
+ }
+ }
+
+ @Override
+ public void unregisterRemoteAnimations(ITaskFragmentOrganizer organizer) {
+ final int pid = Binder.getCallingPid();
+ final long uid = Binder.getCallingUid();
+ synchronized (mGlobalLock) {
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+ "Unregister remote animations for organizer=%s uid=%d pid=%d",
+ organizer.asBinder(), uid, pid);
+ final TaskFragmentOrganizerState organizerState =
+ mTaskFragmentOrganizerState.get(organizer.asBinder());
+ if (organizerState == null) {
+ Slog.e(TAG, "The organizer hasn't been registered.");
+ return;
+ }
+
+ organizerState.mRemoteAnimationDefinition = null;
+ }
+ }
+
+ /** Gets the {@link RemoteAnimationDefinition} set on the given organizer if exists. */
+ @Nullable
+ public RemoteAnimationDefinition getRemoteAnimationDefinition(
+ ITaskFragmentOrganizer organizer) {
+ synchronized (mGlobalLock) {
+ final TaskFragmentOrganizerState organizerState =
+ mTaskFragmentOrganizerState.get(organizer.asBinder());
+ return organizerState != null ? organizerState.mRemoteAnimationDefinition : null;
+ }
+ }
+
void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
if (!state.addTaskFragment(taskFragment)) {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 54390dc..2805dce 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -689,7 +689,8 @@
}
static Rect getSystemBarInsets(Rect frame, InsetsState state) {
- return state.calculateInsets(frame, Type.systemBars(), false /* ignoreVisibility */);
+ return state.calculateInsets(
+ frame, Type.systemBars(), false /* ignoreVisibility */).toRect();
}
void dump(PrintWriter pw, String prefix) {
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 194f48f..b54e8b7 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -66,8 +66,8 @@
}
@Override
- void setExiting() {
- super.setExiting();
+ void setExiting(boolean animateExit) {
+ super.setExiting(animateExit);
mDisplayContent.mWallpaperController.removeWallpaperToken(this);
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index b6c8e13..8e3fd0a 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -51,6 +51,7 @@
import static com.android.server.wm.WindowContainerProto.IDENTIFIER;
import static com.android.server.wm.WindowContainerProto.ORIENTATION;
import static com.android.server.wm.WindowContainerProto.SURFACE_ANIMATOR;
+import static com.android.server.wm.WindowContainerProto.SURFACE_CONTROL;
import static com.android.server.wm.WindowContainerProto.VISIBLE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -987,6 +988,10 @@
return mDisplayContent != null && mDisplayContent.mChangingContainers.contains(this);
}
+ boolean inTransition() {
+ return mWmService.mAtmService.getTransitionController().inTransition(this);
+ }
+
void sendAppVisibilityToClients() {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowContainer wc = mChildren.get(i);
@@ -2393,6 +2398,9 @@
if (mSurfaceAnimator.isAnimating()) {
mSurfaceAnimator.dumpDebug(proto, SURFACE_ANIMATOR);
}
+ if (mSurfaceControl != null) {
+ mSurfaceControl.dumpDebug(proto, SURFACE_CONTROL);
+ }
// add children to proto
for (int i = 0; i < getChildCount(); i++) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerConstants.java b/services/core/java/com/android/server/wm/WindowManagerConstants.java
index a5ebf9a..bdbcd16 100644
--- a/services/core/java/com/android/server/wm/WindowManagerConstants.java
+++ b/services/core/java/com/android/server/wm/WindowManagerConstants.java
@@ -21,9 +21,9 @@
import android.provider.AndroidDeviceConfig;
import android.provider.DeviceConfig;
+import android.provider.DeviceConfigInterface;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.utils.DeviceConfigInterface;
import java.io.PrintWriter;
import java.util.Objects;
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 8a24209..b8e303b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -461,8 +461,21 @@
* @param removeWindows Whether to also remove the windows associated with the token.
* @param displayId The display to remove the token from.
*/
+ public final void removeWindowToken(android.os.IBinder token, boolean removeWindows,
+ int displayId) {
+ removeWindowToken(token, removeWindows, true /* animateExit */, displayId);
+ }
+
+ /**
+ * Removes a window token.
+ *
+ * @param token The toke to remove.
+ * @param removeWindows Whether to also remove the windows associated with the token.
+ * @param animateExit Whether to play the windows exit animation after the token removal.
+ * @param displayId The display to remove the token from.
+ */
public abstract void removeWindowToken(android.os.IBinder token, boolean removeWindows,
- int displayId);
+ boolean animateExit, int displayId);
/**
* Registers a listener to be notified about app transition events.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c7af2e8..9458511 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -206,6 +206,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.provider.DeviceConfigInterface;
import android.provider.Settings;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
@@ -300,7 +301,6 @@
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
import com.android.server.power.ShutdownThread;
-import com.android.server.utils.DeviceConfigInterface;
import com.android.server.utils.PriorityDump;
import java.io.BufferedWriter;
@@ -1218,7 +1218,9 @@
mAssistantOnTopOfDream = context.getResources().getBoolean(
com.android.internal.R.bool.config_assistantOnTopOfDream);
- mLetterboxConfiguration = new LetterboxConfiguration(context);
+ mLetterboxConfiguration = new LetterboxConfiguration(
+ // Using SysUI context to have access to Material colors extracted from Wallpaper.
+ ActivityThread.currentActivityThread().getSystemUiContext());
mInputManager = inputManager; // Must be before createDisplayContentLocked.
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
@@ -2809,6 +2811,31 @@
}
+ void removeWindowToken(IBinder binder, boolean removeWindows, boolean animateExit,
+ int displayId) {
+ synchronized (mGlobalLock) {
+ final DisplayContent dc = mRoot.getDisplayContent(displayId);
+
+ if (dc == null) {
+ ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s"
+ + " for non-exiting displayId=%d", binder, displayId);
+ return;
+ }
+ final WindowToken token = dc.removeWindowToken(binder, animateExit);
+ if (token == null) {
+ ProtoLog.w(WM_ERROR,
+ "removeWindowToken: Attempted to remove non-existing token: %s",
+ binder);
+ return;
+ }
+
+ if (removeWindows) {
+ token.removeAllWindowsIfPossible();
+ }
+ dc.getInputMonitor().updateInputWindowsLw(true /* force */);
+ }
+ }
+
@Override
public void removeWindowToken(IBinder binder, int displayId) {
if (!checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()")) {
@@ -2816,23 +2843,7 @@
}
final long origId = Binder.clearCallingIdentity();
try {
- synchronized (mGlobalLock) {
- final DisplayContent dc = mRoot.getDisplayContent(displayId);
-
- if (dc == null) {
- ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s"
- + " for non-exiting displayId=%d", binder, displayId);
- return;
- }
- final WindowToken token = dc.removeWindowToken(binder);
- if (token == null) {
- ProtoLog.w(WM_ERROR,
- "removeWindowToken: Attempted to remove non-existing token: %s",
- binder);
- return;
- }
- dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
- }
+ removeWindowToken(binder, false /* removeWindows */, true /* animateExit */, displayId);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -4330,13 +4341,18 @@
}
}
- /** Registers a hierarchy listener that gets callbacks when the hierarchy changes. */
+ /**
+ * Registers a hierarchy listener that gets callbacks when the hierarchy changes. The listener's
+ * onDisplayAdded() will not be called for the displays returned.
+ *
+ * @return the displayIds for the existing displays
+ */
@Override
- public void registerDisplayWindowListener(IDisplayWindowListener listener) {
+ public int[] registerDisplayWindowListener(IDisplayWindowListener listener) {
mAtmService.enforceTaskPermission("registerDisplayWindowListener");
final long ident = Binder.clearCallingIdentity();
try {
- mDisplayNotificationController.registerListener(listener);
+ return mDisplayNotificationController.registerListener(listener);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -7548,28 +7564,10 @@
}
@Override
- public void removeWindowToken(IBinder binder, boolean removeWindows, int displayId) {
- synchronized (mGlobalLock) {
- if (removeWindows) {
- final DisplayContent dc = mRoot.getDisplayContent(displayId);
- if (dc == null) {
- ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s"
- + " for non-exiting displayId=%d", binder, displayId);
- return;
- }
-
- final WindowToken token = dc.removeWindowToken(binder);
- if (token == null) {
- ProtoLog.w(WM_ERROR,
- "removeWindowToken: Attempted to remove non-existing token: %s",
- binder);
- return;
- }
-
- token.removeAllWindowsIfPossible();
- }
- WindowManagerService.this.removeWindowToken(binder, displayId);
- }
+ public void removeWindowToken(IBinder binder, boolean removeWindows, boolean animateExit,
+ int displayId) {
+ WindowManagerService.this.removeWindowToken(binder, removeWindows, animateExit,
+ displayId);
}
@Override
@@ -8120,9 +8118,8 @@
boolean animateStarting = false;
while (timeoutRemaining > 0) {
// Waiting until all starting windows has finished animating.
- animateStarting = mRoot.forAllActivities(a -> {
- return a.hasStartingWindow();
- });
+ animateStarting = !mAtmService.getTransitionController().isShellTransitionsEnabled()
+ && mRoot.forAllActivities(ActivityRecord::hasStartingWindow);
boolean isAnimating = mAnimator.isAnimationScheduled()
|| mRoot.isAnimating(TRANSITION | CHILDREN, ANIMATION_TYPE_ALL)
|| animateStarting;
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index d5965494..1d1cb70 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -24,6 +24,7 @@
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
+import android.content.res.Resources.NotFoundException;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
@@ -606,7 +607,7 @@
return -1;
} catch (IllegalArgumentException e) {
getErrPrintWriter().println(
- "Error: 'reset' or aspect ratio should be provided as an argument " + e);
+ "Error: aspect ratio should be provided as an argument " + e);
return -1;
}
synchronized (mInternal.mGlobalLock) {
@@ -625,7 +626,7 @@
return -1;
} catch (IllegalArgumentException e) {
getErrPrintWriter().println(
- "Error: 'reset' or corners radius should be provided as an argument " + e);
+ "Error: corners radius should be provided as an argument " + e);
return -1;
}
synchronized (mInternal.mGlobalLock) {
@@ -653,13 +654,13 @@
break;
default:
getErrPrintWriter().println(
- "Error: 'reset', 'solid_color', 'app_color_background' or "
+ "Error: 'solid_color', 'app_color_background' or "
+ "'wallpaper' should be provided as an argument");
return -1;
}
} catch (IllegalArgumentException e) {
getErrPrintWriter().println(
- "Error: 'reset', 'solid_color', 'app_color_background' or "
+ "Error: 'solid_color', 'app_color_background' or "
+ "'wallpaper' should be provided as an argument" + e);
return -1;
}
@@ -669,6 +670,24 @@
return 0;
}
+ private int runSetLetterboxBackgroundColorResource(PrintWriter pw) throws RemoteException {
+ final int colorId;
+ try {
+ String arg = getNextArgRequired();
+ colorId = mInternal.mContext.getResources()
+ .getIdentifier(arg, "color", "com.android.internal");
+ } catch (NotFoundException e) {
+ getErrPrintWriter().println(
+ "Error: color in '@android:color/resource_name' format should be provided as "
+ + "an argument " + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setLetterboxBackgroundColorResourceId(colorId);
+ }
+ return 0;
+ }
+
private int runSetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException {
final Color color;
try {
@@ -676,7 +695,7 @@
color = Color.valueOf(Color.parseColor(arg));
} catch (IllegalArgumentException e) {
getErrPrintWriter().println(
- "Error: 'reset' or color in #RRGGBB format should be provided as "
+ "Error: color in #RRGGBB format should be provided as "
+ "an argument " + e);
return -1;
}
@@ -697,7 +716,7 @@
return -1;
} catch (IllegalArgumentException e) {
getErrPrintWriter().println(
- "Error: 'reset' or blur radius should be provided as an argument " + e);
+ "Error: blur radius should be provided as an argument " + e);
return -1;
}
synchronized (mInternal.mGlobalLock) {
@@ -717,7 +736,7 @@
return -1;
} catch (IllegalArgumentException e) {
getErrPrintWriter().println(
- "Error: 'reset' or alpha should be provided as an argument " + e);
+ "Error: alpha should be provided as an argument " + e);
return -1;
}
synchronized (mInternal.mGlobalLock) {
@@ -736,7 +755,7 @@
return -1;
} catch (IllegalArgumentException e) {
getErrPrintWriter().println(
- "Error: 'reset' or multiplier should be provided as an argument " + e);
+ "Error: multiplier should be provided as an argument " + e);
return -1;
}
synchronized (mInternal.mGlobalLock) {
@@ -764,6 +783,9 @@
case "--backgroundColor":
runSetLetterboxBackgroundColor(pw);
break;
+ case "--backgroundColorResource":
+ runSetLetterboxBackgroundColorResource(pw);
+ break;
case "--wallpaperBlurRadius":
runSetLetterboxBackgroundWallpaperBlurRadius(pw);
break;
@@ -1031,6 +1053,11 @@
pw.println(" is 'solid-color'. Use (set)get-letterbox-style to check and control");
pw.println(" letterbox background type. See Color#parseColor for allowed color");
pw.println(" formats (#RRGGBB and some colors by name, e.g. magenta or olive).");
+ pw.println(" --backgroundColorResource resource_name");
+ pw.println(" Color resource name of letterbox background which is used when");
+ pw.println(" background type is 'solid-color'. Use (set)get-letterbox-style to");
+ pw.println(" check and control background type. Parameter is a color resource");
+ pw.println(" name, for example, @android:color/system_accent2_50.");
pw.println(" --wallpaperBlurRadius radius");
pw.println(" Blur radius for 'wallpaper' letterbox background. If radius <= 0");
pw.println(" both it and R.dimen.config_letterboxBackgroundWallpaperBlurRadius");
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 45c47ba..b568774 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -56,6 +56,7 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
+import android.util.AndroidRuntimeException;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
@@ -698,10 +699,9 @@
.startActivityInTaskFragment(tf, activityIntent, activityOptions,
hop.getCallingActivity());
if (!isStartResultSuccessful(result)) {
- final Throwable exception =
- new ActivityNotFoundException("start activity in taskFragment failed");
sendTaskFragmentOperationFailure(tf.getTaskFragmentOrganizer(),
- errorCallbackToken, exception);
+ errorCallbackToken,
+ convertStartFailureToThrowable(result, activityIntent));
}
break;
case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT:
@@ -1223,4 +1223,21 @@
mService.mTaskFragmentOrganizerController
.onTaskFragmentError(organizer, errorCallbackToken, exception);
}
+
+ private Throwable convertStartFailureToThrowable(int result, Intent intent) {
+ switch (result) {
+ case ActivityManager.START_INTENT_NOT_RESOLVED:
+ case ActivityManager.START_CLASS_NOT_FOUND:
+ return new ActivityNotFoundException("No Activity found to handle " + intent);
+ case ActivityManager.START_PERMISSION_DENIED:
+ return new SecurityException("Permission denied and not allowed to start activity "
+ + intent);
+ case ActivityManager.START_CANCELED:
+ return new AndroidRuntimeException("Activity could not be started for " + intent
+ + " with error code : " + result);
+ default:
+ return new AndroidRuntimeException("Start activity failed with error code : "
+ + result + " when starting " + intent);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 1ca0c7e..22db297 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -114,10 +114,10 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RESIZE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_INSETS;
import static com.android.server.am.ActivityManagerService.MY_PID;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_ENTER;
@@ -2220,11 +2220,18 @@
}
}
- boolean onSetAppExiting() {
+ boolean onSetAppExiting(boolean animateExit) {
final DisplayContent displayContent = getDisplayContent();
boolean changed = false;
- if (isVisibleNow()) {
+ if (!animateExit) {
+ // Hide the window permanently if no window exist animation is performed, so we can
+ // avoid the window surface becoming visible again unexpectedly during the next
+ // relayout.
+ mPermanentlyHidden = true;
+ hide(false /* doAnimation */, false /* requestAnim */);
+ }
+ if (isVisibleNow() && animateExit) {
mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
if (mWmService.mAccessibilityController != null) {
mWmService.mAccessibilityController.onWindowTransition(this, TRANSIT_EXIT);
@@ -2237,7 +2244,7 @@
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowState c = mChildren.get(i);
- changed |= c.onSetAppExiting();
+ changed |= c.onSetAppExiting(animateExit);
}
return changed;
@@ -3978,7 +3985,7 @@
* Called when the insets state changed.
*/
void notifyInsetsChanged() {
- ProtoLog.d(WM_DEBUG_IME, "notifyInsetsChanged for %s ", this);
+ ProtoLog.d(WM_DEBUG_WINDOW_INSETS, "notifyInsetsChanged for %s ", this);
try {
mClient.insetsChanged(getCompatInsetsState(),
hasMoved(),
@@ -3990,7 +3997,7 @@
@Override
public void notifyInsetsControlChanged() {
- ProtoLog.d(WM_DEBUG_IME, "notifyInsetsControlChanged for %s ", this);
+ ProtoLog.d(WM_DEBUG_WINDOW_INSETS, "notifyInsetsControlChanged for %s ", this);
if (mAppDied || mRemoved) {
return;
}
@@ -5952,9 +5959,9 @@
outSurfaceInsets.set(getAttrs().surfaceInsets);
final InsetsState state = getInsetsStateWithVisibilityOverride();
outInsets.set(state.calculateInsets(outFrame, systemBars(),
- false /* ignoreVisibility */));
+ false /* ignoreVisibility */).toRect());
outStableInsets.set(state.calculateInsets(outFrame, systemBars(),
- true /* ignoreVisibility */));
+ true /* ignoreVisibility */).toRect());
}
void setViewVisibility(int viewVisibility) {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index fa32be3..ad351f0 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -26,6 +26,7 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -239,7 +240,7 @@
}
}
- void setExiting() {
+ void setExiting(boolean animateExit) {
if (isEmpty()) {
super.removeImmediately();
return;
@@ -254,11 +255,12 @@
final int count = mChildren.size();
boolean changed = false;
- final boolean delayed = isAnimating(TRANSITION | PARENTS | CHILDREN);
+ final boolean delayed = isAnimating(TRANSITION | PARENTS)
+ || (isAnimating(CHILDREN, ANIMATION_TYPE_WINDOW_ANIMATION) && animateExit);
for (int i = 0; i < count; i++) {
final WindowState win = mChildren.get(i);
- changed |= win.onSetAppExiting();
+ changed |= win.onSetAppExiting(animateExit);
}
final ActivityRecord app = asActivityRecord();
@@ -360,7 +362,7 @@
@Override
void removeImmediately() {
if (mDisplayContent != null) {
- mDisplayContent.removeWindowToken(token);
+ mDisplayContent.removeWindowToken(token, true /* animateExit */);
}
// Needs to occur after the token is removed from the display above to avoid attempt at
// duplicate removal of this window container from it's parent.
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 8c93377..4e4a5c3 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -178,7 +178,7 @@
"android.frameworks.stats-V1-ndk",
"android.system.suspend.control-V1-cpp",
"android.system.suspend.control.internal-cpp",
- "android.system.suspend@1.0",
+ "android.system.suspend-V1-ndk",
"service.incremental",
],
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index a94ad4a..bb9740b 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -337,7 +337,7 @@
void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) override;
bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid, int32_t injectorUid) override;
void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) override;
- void setPointerCapture(bool enabled) override;
+ void setPointerCapture(const PointerCaptureRequest& request) override;
void notifyDropWindow(const sp<IBinder>& token, float x, float y) override;
/* --- PointerControllerPolicyInterface implementation --- */
@@ -372,8 +372,8 @@
// Show touches feature enable/disable.
bool showTouches;
- // Pointer capture feature enable/disable.
- bool pointerCapture;
+ // The latest request to enable or disable Pointer Capture.
+ PointerCaptureRequest pointerCaptureRequest;
// Sprite controller singleton, created on first use.
sp<SpriteController> spriteController;
@@ -417,7 +417,6 @@
mLocked.pointerSpeed = 0;
mLocked.pointerGesturesEnabled = true;
mLocked.showTouches = false;
- mLocked.pointerCapture = false;
mLocked.pointerDisplayId = ADISPLAY_ID_DEFAULT;
}
mInteractive = true;
@@ -446,7 +445,9 @@
dump += StringPrintf(INDENT "Pointer Gestures Enabled: %s\n",
toString(mLocked.pointerGesturesEnabled));
dump += StringPrintf(INDENT "Show Touches: %s\n", toString(mLocked.showTouches));
- dump += StringPrintf(INDENT "Pointer Capture Enabled: %s\n", toString(mLocked.pointerCapture));
+ dump += StringPrintf(INDENT "Pointer Capture: %s, seq=%" PRIu32 "\n",
+ mLocked.pointerCaptureRequest.enable ? "Enabled" : "Disabled",
+ mLocked.pointerCaptureRequest.seq);
}
dump += "\n";
@@ -634,7 +635,7 @@
outConfig->showTouches = mLocked.showTouches;
- outConfig->pointerCapture = mLocked.pointerCapture;
+ outConfig->pointerCaptureRequest = mLocked.pointerCaptureRequest;
outConfig->setDisplayViewports(mLocked.viewports);
@@ -1383,16 +1384,16 @@
checkAndClearExceptionFromCallback(env, "onPointerDownOutsideFocus");
}
-void NativeInputManager::setPointerCapture(bool enabled) {
+void NativeInputManager::setPointerCapture(const PointerCaptureRequest& request) {
{ // acquire lock
AutoMutex _l(mLock);
- if (mLocked.pointerCapture == enabled) {
+ if (mLocked.pointerCaptureRequest == request) {
return;
}
- ALOGV("%s pointer capture.", enabled ? "Enabling" : "Disabling");
- mLocked.pointerCapture = enabled;
+ ALOGV("%s pointer capture.", request.enable ? "Enabling" : "Disabling");
+ mLocked.pointerCaptureRequest = request;
} // release lock
mInputManager->getReader()->requestRefreshConfiguration(
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index ae7ea3c..7fea547 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -18,11 +18,12 @@
//#define LOG_NDEBUG 0
+#include <aidl/android/system/suspend/ISystemSuspend.h>
+#include <aidl/android/system/suspend/IWakeLock.h>
#include <android/hardware/power/1.1/IPower.h>
#include <android/hardware/power/Boost.h>
#include <android/hardware/power/IPower.h>
#include <android/hardware/power/Mode.h>
-#include <android/system/suspend/1.0/ISystemSuspend.h>
#include <android/system/suspend/ISuspendControlService.h>
#include <android/system/suspend/internal/ISuspendControlServiceInternal.h>
#include <nativehelper/JNIHelp.h>
@@ -34,6 +35,7 @@
#include <limits.h>
#include <android-base/chrono_utils.h>
+#include <android/binder_manager.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
#include <binder/IServiceManager.h>
@@ -41,20 +43,20 @@
#include <hardware/power.h>
#include <hardware_legacy/power.h>
#include <hidl/ServiceManagement.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
#include <utils/Timers.h>
#include <utils/misc.h>
-#include <utils/String8.h>
-#include <utils/Log.h>
#include "com_android_server_power_PowerManagerService.h"
+using aidl::android::system::suspend::ISystemSuspend;
+using aidl::android::system::suspend::IWakeLock;
+using aidl::android::system::suspend::WakeLockType;
using android::String8;
using android::hardware::power::Boost;
using android::hardware::power::Mode;
using android::system::suspend::ISuspendControlService;
-using android::system::suspend::V1_0::ISystemSuspend;
-using android::system::suspend::V1_0::IWakeLock;
-using android::system::suspend::V1_0::WakeLockType;
using IPowerV1_1 = android::hardware::power::V1_1::IPower;
using IPowerV1_0 = android::hardware::power::V1_0::IPower;
using IPowerAidl = android::hardware::power::IPower;
@@ -133,20 +135,21 @@
}
}
-static sp<ISystemSuspend> gSuspendHal = nullptr;
+static std::shared_ptr<ISystemSuspend> gSuspendHal = nullptr;
static sp<ISuspendControlService> gSuspendControl = nullptr;
static sp<system::suspend::internal::ISuspendControlServiceInternal> gSuspendControlInternal =
nullptr;
-static sp<IWakeLock> gSuspendBlocker = nullptr;
+static std::shared_ptr<IWakeLock> gSuspendBlocker = nullptr;
static std::mutex gSuspendMutex;
// Assume SystemSuspend HAL is always alive.
// TODO: Force device to restart if SystemSuspend HAL dies.
-sp<ISystemSuspend> getSuspendHal() {
+std::shared_ptr<ISystemSuspend> getSuspendHal() {
static std::once_flag suspendHalFlag;
- std::call_once(suspendHalFlag, [](){
- ::android::hardware::details::waitForHwService(ISystemSuspend::descriptor, "default");
- gSuspendHal = ISystemSuspend::getService();
+ std::call_once(suspendHalFlag, []() {
+ const std::string suspendInstance = std::string() + ISystemSuspend::descriptor + "/default";
+ gSuspendHal = ISystemSuspend::fromBinder(
+ ndk::SpAIBinder(AServiceManager_waitForService(suspendInstance.c_str())));
assert(gSuspendHal != nullptr);
});
return gSuspendHal;
@@ -184,7 +187,7 @@
std::lock_guard<std::mutex> lock(gSuspendMutex);
if (gSuspendBlocker) {
gSuspendBlocker->release();
- gSuspendBlocker.clear();
+ gSuspendBlocker = nullptr;
}
}
}
@@ -192,9 +195,10 @@
void disableAutoSuspend() {
std::lock_guard<std::mutex> lock(gSuspendMutex);
if (!gSuspendBlocker) {
- sp<ISystemSuspend> suspendHal = getSuspendHal();
- gSuspendBlocker = suspendHal->acquireWakeLock(WakeLockType::PARTIAL,
- "PowerManager.SuspendLockout");
+ std::shared_ptr<ISystemSuspend> suspendHal = getSuspendHal();
+ suspendHal->acquireWakeLock(WakeLockType::PARTIAL, "PowerManager.SuspendLockout",
+ &gSuspendBlocker);
+ assert(gSuspendBlocker != nullptr);
}
}
diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp
index 0bd737b..957d7c3 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -65,7 +65,6 @@
"libincremental_manager_aidl-cpp",
"libprotobuf-cpp-lite",
"service.incremental.proto",
- "libutils",
"libvold_binder",
"libc++fs",
"libziparchive_for_incfs",
@@ -78,6 +77,7 @@
"libincfs",
"liblog",
"libpermission",
+ "libutils",
"libz",
],
}
diff --git a/services/incremental/TEST_MAPPING b/services/incremental/TEST_MAPPING
index 6aa8a93..f2ad068 100644
--- a/services/incremental/TEST_MAPPING
+++ b/services/incremental/TEST_MAPPING
@@ -1,20 +1,6 @@
{
"presubmit": [
{
- "name": "CtsContentTestCases",
- "options": [
- {
- "include-filter": "android.content.pm.cts.PackageManagerShellCommandTest"
- },
- {
- "include-filter": "android.content.pm.cts.PackageManagerShellCommandIncrementalTest"
- },
- {
- "include-filter": "android.content.pm.cts.ChecksumsTest"
- }
- ]
- },
- {
"name": "CtsPackageManagerStatsHostTestCases",
"options": [
{
@@ -29,6 +15,20 @@
"presubmit-large": [
{
"name": "CtsInstalledLoadingProgressHostTests"
+ },
+ {
+ "name": "CtsContentTestCases",
+ "options": [
+ {
+ "include-filter": "android.content.pm.cts.PackageManagerShellCommandTest"
+ },
+ {
+ "include-filter": "android.content.pm.cts.PackageManagerShellCommandIncrementalTest"
+ },
+ {
+ "include-filter": "android.content.pm.cts.ChecksumsTest"
+ }
+ ]
}
]
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 46adb32..fe3a77e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -440,7 +440,6 @@
private static final String SYSPROP_START_UPTIME = "sys.system_server.start_uptime";
private Future<?> mZygotePreload;
- private Future<?> mBlobStoreServiceStart;
private final SystemServerDumper mDumper = new SystemServerDumper();
@@ -2262,12 +2261,9 @@
t.traceEnd();
}
- mBlobStoreServiceStart = SystemServerInitThreadPool.submit(() -> {
- final TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
- traceLog.traceBegin(START_BLOB_STORE_SERVICE);
- mSystemServiceManager.startService(BLOB_STORE_MANAGER_SERVICE_CLASS);
- traceLog.traceEnd();
- }, START_BLOB_STORE_SERVICE);
+ t.traceBegin(START_BLOB_STORE_SERVICE);
+ mSystemServiceManager.startService(BLOB_STORE_MANAGER_SERVICE_CLASS);
+ t.traceEnd();
// Dreams (interactive idle-time views, a/k/a screen savers, and doze mode)
t.traceBegin("StartDreamManager");
@@ -2673,9 +2669,6 @@
mSystemServiceManager.startService(APP_COMPAT_OVERRIDES_SERVICE_CLASS);
t.traceEnd();
- ConcurrentUtils.waitForFutureNoInterrupt(mBlobStoreServiceStart,
- START_BLOB_STORE_SERVICE);
-
// These are needed to propagate to the runnable below.
final NetworkManagementService networkManagementF = networkManagement;
final NetworkStatsService networkStatsF = networkStats;
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
index 15dfd26..d0c9242 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
@@ -24,6 +24,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
+import android.app.ActivityManager;
import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.Context;
@@ -581,7 +582,7 @@
}
private ProcessRecord nextProcessRecord(int setAdj, long lastActivityTime, long lastRss,
- int returnedToCacheCount) {
+ int wentToForegroundCount) {
ApplicationInfo ai = new ApplicationInfo();
ai.packageName = "a.package.name" + mNextPackageName++;
ProcessRecord app = new ProcessRecord(mAms, ai, ai.packageName + ":process", mNextUid++);
@@ -593,9 +594,9 @@
app.setLastActivityTime(lastActivityTime);
app.mProfile.setLastRss(lastRss);
app.mState.setCached(false);
- for (int i = 0; i < returnedToCacheCount; ++i) {
- app.mState.setCached(false);
- app.mState.setCached(true);
+ for (int i = 0; i < wentToForegroundCount; ++i) {
+ app.mState.setSetProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ app.mState.setSetProcState(ActivityManager.PROCESS_STATE_CACHED_RECENT);
}
// Sets the thread returned by ProcessRecord#getThread, which we use to check whether the
// app is currently launching.
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 3c97c95..9d89f12 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -103,7 +103,7 @@
val dataAppDirectory: File =
File(Files.createTempDirectory("data").toFile(), "app")
val frameworkSignature: SigningDetails = SigningDetails(arrayOf(generateSpySignature()), 3)
- val systemPartitions: List<PackageManagerService.ScanPartition> =
+ val systemPartitions: List<ScanPartition> =
redirectScanPartitions(PackageManagerService.SYSTEM_PARTITIONS)
val session: StaticMockitoSession
@@ -476,7 +476,7 @@
}
/** Finds the appropriate partition, if available, based on a scan flag unique to it. */
- fun getPartitionFromFlag(scanFlagMask: Int): PackageManagerService.ScanPartition =
+ fun getPartitionFromFlag(scanFlagMask: Int): ScanPartition =
systemPartitions.first { (it.scanFlag and scanFlagMask) != 0 }
@Throws(Exception::class)
@@ -630,11 +630,11 @@
/** Override get*Folder methods to point to temporary local directories */
@Throws(IOException::class)
- private fun redirectScanPartitions(partitions: List<PackageManagerService.ScanPartition>):
- List<PackageManagerService.ScanPartition> {
- val spiedPartitions: MutableList<PackageManagerService.ScanPartition> =
+ private fun redirectScanPartitions(partitions: List<ScanPartition>):
+ List<ScanPartition> {
+ val spiedPartitions: MutableList<ScanPartition> =
ArrayList(partitions.size)
- for (partition: PackageManagerService.ScanPartition in partitions) {
+ for (partition: ScanPartition in partitions) {
val spy = spy(partition)
val newRoot = Files.createTempDirectory(partition.folder.name).toFile()
whenever(spy.overlayFolder).thenReturn(File(newRoot, "overlay"))
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt
index 7a6110b..f17fa62 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt
@@ -79,8 +79,8 @@
mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
mockAllowList(packageSetting2, allowList(10001, 10002, 10003))
- pms.sendPackagesSuspendedForUser(
- packagesToSuspend, uidsToSuspend, TEST_USER_ID, /* suspended = */ true)
+ pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
+ packagesToSuspend, uidsToSuspend, TEST_USER_ID)
verify(pms).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
anyInt(), nullable(), nullable(), any(), nullable(), any(), nullable())
@@ -97,8 +97,8 @@
mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
mockAllowList(packageSetting2, allowList(10001, 10002, 10007))
- pms.sendPackagesSuspendedForUser(
- packagesToSuspend, uidsToSuspend, TEST_USER_ID, /* suspended = */ true)
+ pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
+ packagesToSuspend, uidsToSuspend, TEST_USER_ID)
verify(pms, times(2)).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
anyInt(), nullable(), nullable(), any(), nullable(), any(), nullable())
@@ -118,8 +118,8 @@
mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
mockAllowList(packageSetting2, null)
- pms.sendPackagesSuspendedForUser(
- packagesToSuspend, uidsToSuspend, TEST_USER_ID, /* suspended = */ true)
+ pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
+ packagesToSuspend, uidsToSuspend, TEST_USER_ID)
verify(pms, times(2)).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable())
@@ -133,6 +133,22 @@
}
}
+ @Test
+ @Throws(Exception::class)
+ fun sendPackagesSuspendModifiedForUser() {
+ pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
+ packagesToSuspend, uidsToSuspend, TEST_USER_ID)
+ verify(pms).sendPackageBroadcast(
+ eq(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED), nullable(), bundleCaptor.capture(),
+ anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable())
+
+ var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+ var modifiedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
+ assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
+ assertThat(modifiedUids).asList().containsExactly(
+ packageSetting1.appId, packageSetting2.appId)
+ }
+
private fun allowList(vararg uids: Int) = SparseArray<IntArray>().apply {
this.put(TEST_USER_ID, uids)
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java
new file mode 100644
index 0000000..dd6b744
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.AlarmManager;
+import android.content.Context;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+/** Tests various aspects of the Agent. */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class AgentTest {
+ private MockitoSession mMockingSession;
+ @Mock
+ private CompleteEconomicPolicy mEconomicPolicy;
+ @Mock
+ private Context mContext;
+ @Mock
+ private InternalResourceService mIrs;
+
+ @Before
+ public void setUp() {
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .mockStatic(LocalServices.class)
+ .startMocking();
+ when(mIrs.getContext()).thenReturn(mContext);
+ when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mock(AlarmManager.class));
+ }
+
+ @After
+ public void tearDown() {
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
+ }
+
+ @Test
+ public void testRecordTransaction_UnderMax() {
+ Agent agent = new Agent(mIrs, mEconomicPolicy);
+ Ledger ledger = new Ledger();
+
+ doReturn(1_000_000L).when(mIrs).getMaxCirculationLocked();
+ doReturn(1_000_000L).when(mEconomicPolicy).getMaxSatiatedBalance();
+
+ Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5);
+ agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+ assertEquals(5, ledger.getCurrentBalance());
+
+ transaction = new Ledger.Transaction(0, 0, 0, null, 995);
+ agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+ assertEquals(1000, ledger.getCurrentBalance());
+
+ transaction = new Ledger.Transaction(0, 0, 0, null, -500);
+ agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+ assertEquals(500, ledger.getCurrentBalance());
+
+ transaction = new Ledger.Transaction(0, 0, 0, null, 999_500L);
+ agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+ assertEquals(1_000_000L, ledger.getCurrentBalance());
+
+ transaction = new Ledger.Transaction(0, 0, 0, null, -1_000_001L);
+ agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+ assertEquals(-1, ledger.getCurrentBalance());
+ }
+
+ @Test
+ public void testRecordTransaction_MaxCirculation() {
+ Agent agent = new Agent(mIrs, mEconomicPolicy);
+ Ledger ledger = new Ledger();
+
+ doReturn(1000L).when(mIrs).getMaxCirculationLocked();
+ doReturn(1000L).when(mEconomicPolicy).getMaxSatiatedBalance();
+
+ Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5);
+ agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+ assertEquals(5, ledger.getCurrentBalance());
+
+ transaction = new Ledger.Transaction(0, 0, 0, null, 995);
+ agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+ assertEquals(1000, ledger.getCurrentBalance());
+
+ transaction = new Ledger.Transaction(0, 0, 0, null, -500);
+ agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+ assertEquals(500, ledger.getCurrentBalance());
+
+ transaction = new Ledger.Transaction(0, 0, 0, null, 2000);
+ agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+ assertEquals(1000, ledger.getCurrentBalance());
+
+ // MaxCirculation can change as the battery level changes. Any already allocated ARCSs
+ // shouldn't be removed by recordTransaction().
+ doReturn(900L).when(mIrs).getMaxCirculationLocked();
+
+ transaction = new Ledger.Transaction(0, 0, 0, null, 100);
+ agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+ assertEquals(1000, ledger.getCurrentBalance());
+
+ transaction = new Ledger.Transaction(0, 0, 0, null, -50);
+ agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+ assertEquals(950, ledger.getCurrentBalance());
+
+ transaction = new Ledger.Transaction(0, 0, 0, null, -200);
+ agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+ assertEquals(750, ledger.getCurrentBalance());
+
+ doReturn(800L).when(mIrs).getMaxCirculationLocked();
+
+ transaction = new Ledger.Transaction(0, 0, 0, null, 100);
+ agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+ assertEquals(800, ledger.getCurrentBalance());
+ }
+
+ @Test
+ public void testRecordTransaction_MaxSatiatedBalance() {
+ Agent agent = new Agent(mIrs, mEconomicPolicy);
+ Ledger ledger = new Ledger();
+
+ doReturn(1_000_000L).when(mIrs).getMaxCirculationLocked();
+ doReturn(1000L).when(mEconomicPolicy).getMaxSatiatedBalance();
+
+ Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5);
+ agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+ assertEquals(5, ledger.getCurrentBalance());
+
+ transaction = new Ledger.Transaction(0, 0, 0, null, 995);
+ agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+ assertEquals(1000, ledger.getCurrentBalance());
+
+ transaction = new Ledger.Transaction(0, 0, 0, null, -500);
+ agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+ assertEquals(500, ledger.getCurrentBalance());
+
+ transaction = new Ledger.Transaction(0, 0, 0, null, 999_500L);
+ agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+ assertEquals(1_000, ledger.getCurrentBalance());
+
+ // Shouldn't change in normal operation, but adding test case in case it does.
+ doReturn(900L).when(mEconomicPolicy).getMaxSatiatedBalance();
+
+ transaction = new Ledger.Transaction(0, 0, 0, null, 500);
+ agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+ assertEquals(1_000, ledger.getCurrentBalance());
+
+ transaction = new Ledger.Transaction(0, 0, 0, null, -1001);
+ agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+ assertEquals(-1, ledger.getCurrentBalance());
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/OWNERS b/services/tests/mockingservicestests/src/com/android/server/tare/OWNERS
new file mode 100644
index 0000000..217a5ed
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/OWNERS
@@ -0,0 +1 @@
+include /apex/jobscheduler/service/java/com/android/server/tare/OWNERS
\ No newline at end of file
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index c19155f..11e1230 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -144,7 +144,6 @@
"utils/**/*.java",
"utils/**/*.kt",
"utils-mockito/**/*.kt",
- ":services.core-sources-deviceconfig-interface",
],
static_libs: [
"junit",
@@ -161,7 +160,6 @@
"utils/**/*.java",
"utils/**/*.kt",
"utils-mockito/**/*.kt",
- ":services.core-sources-deviceconfig-interface",
],
static_libs: [
"junit",
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
index 850f881..327a80e 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
@@ -470,6 +470,8 @@
sendCommand(COMMAND_UNBIND_SERVICE, TEST_APP2, TEST_APP1, null);
runShellCommand("cmd deviceidle whitelist -" + TEST_APP1);
runShellCommand("cmd deviceidle whitelist -" + TEST_APP2);
+ am.forceStopPackage(TEST_APP1);
+ am.forceStopPackage(TEST_APP2);
}
}
@@ -700,6 +702,7 @@
am.removeOnUidImportanceListener(uidListener1);
am.removeOnUidImportanceListener(uidListener2);
am.removeOnUidImportanceListener(uidListener3);
+ am.forceStopPackage(TEST_APP1);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
index 0d475c0..91bf4d1 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
@@ -135,7 +135,8 @@
"schema1",
ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
/*forceOverride=*/ false,
- /*schemaVersion=*/ 0);
+ /*schemaVersion=*/ 0,
+ /*setSchemaStatsBuilder=*/ null);
// "schema1" is platform hidden now and package visible to package1
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
@@ -167,7 +168,8 @@
"schema1",
ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
/*forceOverride=*/ false,
- /*schemaVersion=*/ 0);
+ /*schemaVersion=*/ 0,
+ /*setSchemaStatsBuilder=*/ null);
// Check that "schema1" still has the same visibility settings
SystemUtil.runWithShellPermissionIdentity(() -> assertThat(
@@ -241,7 +243,8 @@
"schema1",
ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
/*forceOverride=*/ false,
- /*schemaVersion=*/ 0);
+ /*schemaVersion=*/ 0,
+ /*setSchemaStatsBuilder=*/ null);
// "schema1" is platform hidden now and package accessible
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
@@ -269,7 +272,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ true,
- /*schemaVersion=*/ 0);
+ /*schemaVersion=*/ 0,
+ /*setSchemaStatsBuilder=*/ null);
// Check that "schema1" is no longer considered platform hidden or package accessible
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
@@ -298,7 +302,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*schemaVersion=*/ 0);
+ /*schemaVersion=*/ 0,
+ /*setSchemaStatsBuilder=*/ null);
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
"package",
@@ -333,7 +338,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*schemaVersion=*/ 0);
+ /*schemaVersion=*/ 0,
+ /*setSchemaStatsBuilder=*/ null);
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
"package",
@@ -361,7 +367,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.singletonList("Schema"),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*schemaVersion=*/ 0);
+ /*schemaVersion=*/ 0,
+ /*setSchemaStatsBuilder=*/ null);
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
"package",
@@ -390,7 +397,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*schemaVersion=*/ 0);
+ /*schemaVersion=*/ 0,
+ /*setSchemaStatsBuilder=*/ null);
assertThat(mVisibilityStore
.isSchemaSearchableByCaller(
"package",
@@ -431,7 +439,8 @@
"Schema",
ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
/*forceOverride=*/ false,
- /*schemaVersion=*/ 0);
+ /*schemaVersion=*/ 0,
+ /*setSchemaStatsBuilder=*/ null);
assertThat(mVisibilityStore
.isSchemaSearchableByCaller(
"package",
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
index f40a5ad..dd3b3ec 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
@@ -76,13 +76,14 @@
import java.util.Set;
public class AppSearchImplTest {
- @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
- private AppSearchImpl mAppSearchImpl;
/**
* Always trigger optimize in this class. OptimizeStrategy will be tested in its own test class.
*/
private static final OptimizeStrategy ALWAYS_OPTIMIZE = optimizeInfo -> true;
+ @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+ private AppSearchImpl mAppSearchImpl;
+
@Before
public void setUp() throws Exception {
mAppSearchImpl =
@@ -439,7 +440,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert a document and then remove it to generate garbage.
GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build();
@@ -499,7 +501,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert a valid doc
GenericDocument validDoc =
@@ -591,7 +594,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert a valid doc
appSearchImpl.putDocument(
@@ -626,7 +630,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert document
GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build();
@@ -660,7 +665,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
mAppSearchImpl.setSchema(
"package",
"database2",
@@ -669,7 +675,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert documents
GenericDocument document1 =
@@ -714,7 +721,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert document
GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build();
@@ -756,7 +764,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert package2 schema
List<AppSearchSchema> schema2 =
@@ -769,7 +778,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert package1 document
GenericDocument document =
@@ -812,7 +822,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert package2 schema
List<AppSearchSchema> schema2 =
@@ -825,7 +836,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert package1 document
GenericDocument document =
@@ -889,7 +901,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert two package1 documents
GenericDocument document1 =
@@ -914,7 +927,8 @@
assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
long nextPageToken = searchResultPage.getNextPageToken();
- searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken);
+ searchResultPage =
+ mAppSearchImpl.getNextPage("package1", nextPageToken, /*statsBuilder=*/ null);
assertThat(searchResultPage.getResults()).hasSize(1);
assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
}
@@ -932,7 +946,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert two package1 documents
GenericDocument document1 =
@@ -962,14 +977,17 @@
AppSearchException e =
assertThrows(
AppSearchException.class,
- () -> mAppSearchImpl.getNextPage("package2", nextPageToken));
+ () ->
+ mAppSearchImpl.getNextPage(
+ "package2", nextPageToken, /*statsBuilder=*/ null));
assertThat(e)
.hasMessageThat()
.contains("Package \"package2\" cannot use nextPageToken: " + nextPageToken);
assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
// Can continue getting next page for package1
- searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken);
+ searchResultPage =
+ mAppSearchImpl.getNextPage("package1", nextPageToken, /*statsBuilder=*/ null);
assertThat(searchResultPage.getResults()).hasSize(1);
assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
}
@@ -987,7 +1005,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert two package1 documents
GenericDocument document1 =
@@ -1019,7 +1038,8 @@
assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
long nextPageToken = searchResultPage.getNextPageToken();
- searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken);
+ searchResultPage =
+ mAppSearchImpl.getNextPage("package1", nextPageToken, /*statsBuilder=*/ null);
assertThat(searchResultPage.getResults()).hasSize(1);
assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
}
@@ -1037,7 +1057,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert two package1 documents
GenericDocument document1 =
@@ -1074,14 +1095,17 @@
AppSearchException e =
assertThrows(
AppSearchException.class,
- () -> mAppSearchImpl.getNextPage("package2", nextPageToken));
+ () ->
+ mAppSearchImpl.getNextPage(
+ "package2", nextPageToken, /*statsBuilder=*/ null));
assertThat(e)
.hasMessageThat()
.contains("Package \"package2\" cannot use nextPageToken: " + nextPageToken);
assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
// Can continue getting next page for package1
- searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken);
+ searchResultPage =
+ mAppSearchImpl.getNextPage("package1", nextPageToken, /*statsBuilder=*/ null);
assertThat(searchResultPage.getResults()).hasSize(1);
assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
}
@@ -1099,7 +1123,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert two package1 documents
GenericDocument document1 =
@@ -1132,7 +1157,9 @@
AppSearchException e =
assertThrows(
AppSearchException.class,
- () -> mAppSearchImpl.getNextPage("package1", nextPageToken));
+ () ->
+ mAppSearchImpl.getNextPage(
+ "package1", nextPageToken, /*statsBuilder=*/ null));
assertThat(e)
.hasMessageThat()
.contains("Package \"package1\" cannot use nextPageToken: " + nextPageToken);
@@ -1152,7 +1179,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert two package1 documents
GenericDocument document1 =
@@ -1189,7 +1217,8 @@
assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
// Can continue getting next page for package1
- searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken);
+ searchResultPage =
+ mAppSearchImpl.getNextPage("package1", nextPageToken, /*statsBuilder=*/ null);
assertThat(searchResultPage.getResults()).hasSize(1);
assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
}
@@ -1207,7 +1236,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert two package1 documents
GenericDocument document1 =
@@ -1247,7 +1277,9 @@
AppSearchException e =
assertThrows(
AppSearchException.class,
- () -> mAppSearchImpl.getNextPage("package1", nextPageToken));
+ () ->
+ mAppSearchImpl.getNextPage(
+ "package1", nextPageToken, /*statsBuilder=*/ null));
assertThat(e)
.hasMessageThat()
.contains("Package \"package1\" cannot use nextPageToken: " + nextPageToken);
@@ -1267,7 +1299,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert two package1 documents
GenericDocument document1 =
@@ -1311,7 +1344,8 @@
assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
// Can continue getting next page for package1
- searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken);
+ searchResultPage =
+ mAppSearchImpl.getNextPage("package1", nextPageToken, /*statsBuilder=*/ null);
assertThat(searchResultPage.getResults()).hasSize(1);
assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
}
@@ -1355,7 +1389,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Create expected schemaType proto.
SchemaProto expectedProto =
@@ -1400,7 +1435,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Create incompatible schema
List<AppSearchSchema> newSchemas =
@@ -1416,7 +1452,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ true,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
assertThat(setSchemaResponse.getDeletedTypes()).containsExactly("Text");
assertThat(setSchemaResponse.getIncompatibleTypes()).containsExactly("Email");
}
@@ -1439,7 +1476,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Create expected schemaType proto.
SchemaProto expectedProto =
@@ -1472,8 +1510,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
-
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Check the Document type has been deleted.
assertThat(setSchemaResponse.getDeletedTypes()).containsExactly("Document");
@@ -1486,7 +1524,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ true,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Check Document schema is removed.
expectedProto =
@@ -1524,7 +1563,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
mAppSearchImpl.setSchema(
"package",
"database2",
@@ -1533,7 +1573,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Create expected schemaType proto.
SchemaProto expectedProto =
@@ -1573,7 +1614,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ true,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Create expected schemaType list, database 1 should only contain Email but database 2
// remains in same.
@@ -1618,7 +1660,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert package document
GenericDocument document =
@@ -1680,7 +1723,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
mAppSearchImpl.setSchema(
"packageB",
"database",
@@ -1689,7 +1733,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Verify these two packages is stored in AppSearch
SchemaProto expectedProto =
@@ -1735,7 +1780,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
assertThat(mAppSearchImpl.getPackageToDatabases())
.containsExactlyEntriesIn(expectedMapping);
@@ -1749,7 +1795,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
assertThat(mAppSearchImpl.getPackageToDatabases())
.containsExactlyEntriesIn(expectedMapping);
@@ -1763,7 +1810,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
assertThat(mAppSearchImpl.getPackageToDatabases())
.containsExactlyEntriesIn(expectedMapping);
}
@@ -1822,7 +1870,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert two docs
GenericDocument document1 =
@@ -1973,7 +2022,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Since "package1" doesn't have a document, it get any space attributed to it.
StorageInfo storageInfo = mAppSearchImpl.getStorageInfoForPackage("package1");
@@ -1996,7 +2046,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert document for "package1"
GenericDocument document =
@@ -2012,7 +2063,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert two documents for "package2"
document = new GenericDocument.Builder<>("namespace", "id1", "type").build();
@@ -2061,7 +2113,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// "package2" doesn't exist yet, so it shouldn't have any storage size
StorageInfo storageInfo =
@@ -2084,7 +2137,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Since "package1", "database1" doesn't have a document, it get any space attributed to it.
StorageInfo storageInfo = mAppSearchImpl.getStorageInfoForDatabase("package1", "database1");
@@ -2106,7 +2160,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
mAppSearchImpl.setSchema(
"package1",
"database2",
@@ -2115,7 +2170,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Add a document for "package1", "database1"
GenericDocument document =
@@ -2165,7 +2221,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
appSearchImpl.close();
@@ -2181,7 +2238,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0));
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null));
assertThrows(
IllegalStateException.class, () -> appSearchImpl.getSchema("package", "database"));
@@ -2225,7 +2283,9 @@
assertThrows(
IllegalStateException.class,
- () -> appSearchImpl.getNextPage("package", /*nextPageToken=*/ 1L));
+ () ->
+ appSearchImpl.getNextPage(
+ "package", /*nextPageToken=*/ 1L, /*statsBuilder=*/ null));
assertThrows(
IllegalStateException.class,
@@ -2296,7 +2356,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Add a document and persist it.
GenericDocument document =
@@ -2343,7 +2404,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Add two documents and persist them.
GenericDocument document1 =
@@ -2423,7 +2485,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Add two documents and persist them.
GenericDocument document1 =
@@ -2511,7 +2574,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Add two documents
GenericDocument document1 =
@@ -2562,7 +2626,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert a document which is too large
GenericDocument document =
@@ -2636,7 +2701,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Index a document
mAppSearchImpl.putDocument(
@@ -2723,7 +2789,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Index 3 documents
mAppSearchImpl.putDocument(
@@ -2836,7 +2903,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
mAppSearchImpl.setSchema(
"package1",
"database2",
@@ -2845,7 +2913,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
mAppSearchImpl.setSchema(
"package2",
"database1",
@@ -2854,7 +2923,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
mAppSearchImpl.setSchema(
"package2",
"database2",
@@ -2863,7 +2933,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Index documents in package1/database1
mAppSearchImpl.putDocument(
@@ -3002,7 +3073,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Index 3 documents
mAppSearchImpl.putDocument(
@@ -3131,7 +3203,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Index a document
mAppSearchImpl.putDocument(
@@ -3210,7 +3283,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Index a document
mAppSearchImpl.putDocument(
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
index 7c97687..2ab5fd5 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
@@ -33,6 +33,7 @@
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
import com.android.server.appsearch.external.localstorage.stats.SearchStats;
+import com.android.server.appsearch.external.localstorage.stats.SetSchemaStats;
import com.android.server.appsearch.icing.proto.DeleteStatsProto;
import com.android.server.appsearch.icing.proto.DocumentProto;
import com.android.server.appsearch.icing.proto.InitializeStatsProto;
@@ -41,6 +42,7 @@
import com.android.server.appsearch.icing.proto.PutResultProto;
import com.android.server.appsearch.icing.proto.QueryStatsProto;
import com.android.server.appsearch.icing.proto.ScoringSpecProto;
+import com.android.server.appsearch.icing.proto.SetSchemaResultProto;
import com.android.server.appsearch.icing.proto.StatusProto;
import com.android.server.appsearch.icing.proto.TermMatchType;
@@ -57,14 +59,17 @@
import java.util.List;
public class AppSearchLoggerTest {
- @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
- private AppSearchImpl mAppSearchImpl;
- private TestLogger mLogger;
+ private static final String PACKAGE_NAME = "packageName";
+ private static final String DATABASE = "database";
/**
* Always trigger optimize in this class. OptimizeStrategy will be tested in its own test class.
*/
private static final OptimizeStrategy ALWAYS_OPTIMIZE = optimizeInfo -> true;
+ @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+ private AppSearchImpl mAppSearchImpl;
+ private TestLogger mLogger;
+
@Before
public void setUp() throws Exception {
mAppSearchImpl =
@@ -84,6 +89,7 @@
@Nullable SearchStats mSearchStats;
@Nullable RemoveStats mRemoveStats;
@Nullable OptimizeStats mOptimizeStats;
+ @Nullable SetSchemaStats mSetSchemaStats;
@Override
public void logStats(@NonNull CallStats stats) {
@@ -114,6 +120,11 @@
public void logStats(@NonNull OptimizeStats stats) {
mOptimizeStats = stats;
}
+
+ @Override
+ public void logStats(@NonNull SetSchemaStats stats) {
+ mSetSchemaStats = stats;
+ }
}
@Test
@@ -194,7 +205,7 @@
.setExceededMaxTokenNum(nativeExceededMaxNumTokens)
.build())
.build();
- PutDocumentStats.Builder pBuilder = new PutDocumentStats.Builder("packageName", "database");
+ PutDocumentStats.Builder pBuilder = new PutDocumentStats.Builder(PACKAGE_NAME, DATABASE);
AppSearchLoggerHelper.copyNativeStats(nativePutDocumentStats, pBuilder);
@@ -248,8 +259,8 @@
.setDocumentRetrievalLatencyMs(nativeDocumentRetrievingLatencyMillis)
.build();
SearchStats.Builder qBuilder =
- new SearchStats.Builder(SearchStats.VISIBILITY_SCOPE_LOCAL, "packageName")
- .setDatabase("database");
+ new SearchStats.Builder(SearchStats.VISIBILITY_SCOPE_LOCAL, PACKAGE_NAME)
+ .setDatabase(DATABASE);
AppSearchLoggerHelper.copyNativeStats(nativeQueryStats, qBuilder);
@@ -336,6 +347,35 @@
.isEqualTo(nativeTimeSinceLastOptimizeMillis);
}
+ @Test
+ public void testAppSearchLoggerHelper_testCopyNativeStats_setSchema() {
+ ImmutableList<String> newSchemaTypeChangeList = ImmutableList.of("new1");
+ ImmutableList<String> deletedSchemaTypesList = ImmutableList.of("deleted1", "deleted2");
+ ImmutableList<String> compatibleTypesList = ImmutableList.of("compatible1", "compatible2");
+ ImmutableList<String> indexIncompatibleTypeChangeList = ImmutableList.of("index1");
+ ImmutableList<String> backwardsIncompatibleTypeChangeList = ImmutableList.of("backwards1");
+ SetSchemaResultProto setSchemaResultProto =
+ SetSchemaResultProto.newBuilder()
+ .addAllNewSchemaTypes(newSchemaTypeChangeList)
+ .addAllDeletedSchemaTypes(deletedSchemaTypesList)
+ .addAllFullyCompatibleChangedSchemaTypes(compatibleTypesList)
+ .addAllIndexIncompatibleChangedSchemaTypes(indexIncompatibleTypeChangeList)
+ .addAllIncompatibleSchemaTypes(backwardsIncompatibleTypeChangeList)
+ .build();
+ SetSchemaStats.Builder sBuilder = new SetSchemaStats.Builder(PACKAGE_NAME, DATABASE);
+
+ AppSearchLoggerHelper.copyNativeStats(setSchemaResultProto, sBuilder);
+
+ SetSchemaStats sStats = sBuilder.build();
+ assertThat(sStats.getNewTypeCount()).isEqualTo(newSchemaTypeChangeList.size());
+ assertThat(sStats.getDeletedTypeCount()).isEqualTo(deletedSchemaTypesList.size());
+ assertThat(sStats.getCompatibleTypeChangeCount()).isEqualTo(compatibleTypesList.size());
+ assertThat(sStats.getIndexIncompatibleTypeChangeCount())
+ .isEqualTo(indexIncompatibleTypeChangeList.size());
+ assertThat(sStats.getBackwardsIncompatibleTypeChangeCount())
+ .isEqualTo(backwardsIncompatibleTypeChangeList.size());
+ }
+
//
// Testing actual logging
//
@@ -388,7 +428,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
GenericDocument doc1 = new GenericDocument.Builder<>("namespace", "id1", "Type1").build();
GenericDocument doc2 = new GenericDocument.Builder<>("namespace", "id2", "Type1").build();
appSearchImpl.putDocument(testPackageName, testDatabase, doc1, mLogger);
@@ -439,7 +480,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
// Insert a valid doc
GenericDocument doc1 = new GenericDocument.Builder<>("namespace", "id1", "Type1").build();
@@ -495,7 +537,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
GenericDocument document =
new GenericDocument.Builder<>("namespace", "id", "type")
@@ -542,7 +585,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
GenericDocument document =
new GenericDocument.Builder<>("namespace", "id", "type")
@@ -592,7 +636,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
GenericDocument document1 =
new GenericDocument.Builder<>("namespace", "id1", "type")
.setPropertyString("subject", "testPut example1")
@@ -661,7 +706,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
SearchSpec searchSpec =
new SearchSpec.Builder()
@@ -701,7 +747,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
GenericDocument document =
new GenericDocument.Builder<>(testNamespace, testId, "type").build();
mAppSearchImpl.putDocument(testPackageName, testDatabase, document, /*logger=*/ null);
@@ -735,7 +782,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
GenericDocument document =
new GenericDocument.Builder<>(testNamespace, testId, "type").build();
@@ -780,7 +828,8 @@
/*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
/*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
- /*version=*/ 0);
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
GenericDocument document1 =
new GenericDocument.Builder<>(testNamespace, "id1", "type").build();
GenericDocument document2 =
@@ -800,7 +849,58 @@
assertThat(rStats.getDatabase()).isEqualTo(testDatabase);
assertThat(rStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
// delete by query
- assertThat(rStats.getDeleteType()).isEqualTo(DeleteStatsProto.DeleteType.Code.QUERY_VALUE);
+ assertThat(rStats.getDeleteType())
+ .isEqualTo(DeleteStatsProto.DeleteType.Code.DEPRECATED_QUERY_VALUE);
assertThat(rStats.getDeletedDocumentCount()).isEqualTo(2);
}
+
+ @Test
+ public void testLoggingStats_setSchema() throws Exception {
+ AppSearchSchema schema1 =
+ new AppSearchSchema.Builder("testSchema")
+ .addProperty(
+ new AppSearchSchema.StringPropertyConfig.Builder("subject")
+ .setCardinality(
+ AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
+ .setIndexingType(
+ AppSearchSchema.StringPropertyConfig
+ .INDEXING_TYPE_PREFIXES)
+ .setTokenizerType(
+ AppSearchSchema.StringPropertyConfig
+ .TOKENIZER_TYPE_PLAIN)
+ .build())
+ .build();
+ mAppSearchImpl.setSchema(
+ PACKAGE_NAME,
+ DATABASE,
+ Collections.singletonList(schema1),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
+
+ // create a backwards incompatible schema
+ SetSchemaStats.Builder sStatsBuilder = new SetSchemaStats.Builder(PACKAGE_NAME, DATABASE);
+ AppSearchSchema schema2 = new AppSearchSchema.Builder("testSchema").build();
+ mAppSearchImpl.setSchema(
+ PACKAGE_NAME,
+ DATABASE,
+ Collections.singletonList(schema2),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ sStatsBuilder);
+
+ SetSchemaStats sStats = sStatsBuilder.build();
+ assertThat(sStats.getPackageName()).isEqualTo(PACKAGE_NAME);
+ assertThat(sStats.getDatabase()).isEqualTo(DATABASE);
+ assertThat(sStats.getNewTypeCount()).isEqualTo(0);
+ assertThat(sStats.getCompatibleTypeChangeCount()).isEqualTo(0);
+ assertThat(sStats.getIndexIncompatibleTypeChangeCount()).isEqualTo(1);
+ assertThat(sStats.getBackwardsIncompatibleTypeChangeCount()).isEqualTo(1);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
index c1dc0e4..81aab41 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
@@ -264,17 +264,15 @@
.setMigratedDocumentCount(6)
.setSavedDocumentCount(7)
.build();
- int nativeLatencyMillis = 1;
- int newTypeCount = 2;
- int compatibleTypeChangeCount = 3;
- int indexIncompatibleTypeChangeCount = 4;
- int backwardsIncompatibleTypeChangeCount = 5;
+ int newTypeCount = 1;
+ int compatibleTypeChangeCount = 2;
+ int indexIncompatibleTypeChangeCount = 3;
+ int backwardsIncompatibleTypeChangeCount = 4;
SetSchemaStats sStats =
new SetSchemaStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE)
.setStatusCode(TEST_STATUS_CODE)
.setSchemaMigrationStats(schemaMigrationStats)
.setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
- .setNativeLatencyMillis(nativeLatencyMillis)
.setNewTypeCount(newTypeCount)
.setCompatibleTypeChangeCount(compatibleTypeChangeCount)
.setIndexIncompatibleTypeChangeCount(indexIncompatibleTypeChangeCount)
@@ -287,7 +285,6 @@
assertThat(sStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE);
assertThat(sStats.getSchemaMigrationStats()).isEqualTo(schemaMigrationStats);
assertThat(sStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS);
- assertThat(sStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
assertThat(sStats.getNewTypeCount()).isEqualTo(newTypeCount);
assertThat(sStats.getCompatibleTypeChangeCount()).isEqualTo(compatibleTypeChangeCount);
assertThat(sStats.getIndexIncompatibleTypeChangeCount())
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index 2892bf5..b3f7587 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -74,6 +74,7 @@
public class AuthSessionTest {
private static final String TEST_PACKAGE = "test_package";
+ private static final long TEST_REQUEST_ID = 22;
@Mock private Context mContext;
@Mock private ITrustManager mTrustManager;
@@ -112,6 +113,7 @@
final AuthSession session = createAuthSession(mSensors,
false /* checkDevicePolicyManager */,
Authenticators.BIOMETRIC_STRONG,
+ TEST_REQUEST_ID,
0 /* operationId */,
0 /* userId */);
@@ -133,6 +135,7 @@
final AuthSession session = createAuthSession(mSensors,
false /* checkDevicePolicyManager */,
Authenticators.BIOMETRIC_STRONG,
+ TEST_REQUEST_ID,
operationId,
userId);
assertEquals(mSensors.size(), session.mPreAuthInfo.eligibleSensors.size());
@@ -153,6 +156,7 @@
eq(userId),
eq(mSensorReceiver),
eq(TEST_PACKAGE),
+ eq(TEST_REQUEST_ID),
eq(sensor.getCookie()),
anyBoolean() /* allowBackgroundAuthentication */);
}
@@ -185,6 +189,33 @@
}
@Test
+ public void testCancelReducesAppetiteForCookies() throws Exception {
+ setupFace(0 /* id */, false /* confirmationAlwaysRequired */,
+ mock(IBiometricAuthenticator.class));
+ setupFingerprint(1 /* id */, FingerprintSensorProperties.TYPE_UDFPS_OPTICAL);
+
+ final AuthSession session = createAuthSession(mSensors,
+ false /* checkDevicePolicyManager */,
+ Authenticators.BIOMETRIC_STRONG,
+ TEST_REQUEST_ID,
+ 44 /* operationId */,
+ 2 /* userId */);
+
+ session.goToInitialState();
+
+ for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) {
+ assertEquals(BiometricSensor.STATE_WAITING_FOR_COOKIE, sensor.getSensorState());
+ }
+
+ session.onCancelAuthSession(false /* force */);
+
+ for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) {
+ session.onCookieReceived(sensor.getCookie());
+ assertEquals(BiometricSensor.STATE_CANCELING, sensor.getSensorState());
+ }
+ }
+
+ @Test
public void testMultiAuth_singleSensor_fingerprintSensorStartsAfterDialogAnimationCompletes()
throws Exception {
setupFingerprint(0 /* id */, FingerprintSensorProperties.TYPE_UDFPS_OPTICAL);
@@ -212,6 +243,7 @@
final AuthSession session = createAuthSession(mSensors,
false /* checkDevicePolicyManager */,
Authenticators.BIOMETRIC_STRONG,
+ TEST_REQUEST_ID,
operationId,
userId);
assertEquals(mSensors.size(), session.mPreAuthInfo.eligibleSensors.size());
@@ -238,7 +270,7 @@
// fingerprint sensor does not start even if all cookies are received
assertEquals(STATE_AUTH_STARTED, session.getState());
verify(mStatusBarService).showAuthenticationDialog(any(), any(), any(),
- anyBoolean(), anyBoolean(), anyInt(), any(), anyLong(), anyInt());
+ anyBoolean(), anyBoolean(), anyInt(), anyLong(), any(), anyLong(), anyInt());
// Notify AuthSession that the UI is shown. Then, fingerprint sensor should be started.
session.onDialogAnimatedIn();
@@ -277,6 +309,7 @@
final AuthSession session = createAuthSession(mSensors,
false /* checkDevicePolicyManager */,
Authenticators.BIOMETRIC_STRONG,
+ TEST_REQUEST_ID,
0 /* operationId */,
0 /* userId */);
@@ -285,7 +318,8 @@
sessionConsumer.accept(session);
- verify(faceAuthenticator).cancelAuthenticationFromService(eq(mToken), eq(TEST_PACKAGE));
+ verify(faceAuthenticator).cancelAuthenticationFromService(
+ eq(mToken), eq(TEST_PACKAGE), eq(TEST_REQUEST_ID));
}
private PreAuthInfo createPreAuthInfo(List<BiometricSensor> sensors, int userId,
@@ -302,14 +336,14 @@
private AuthSession createAuthSession(List<BiometricSensor> sensors,
boolean checkDevicePolicyManager, @Authenticators.Types int authenticators,
- long operationId, int userId) throws RemoteException {
+ long requestId, long operationId, int userId) throws RemoteException {
final PromptInfo promptInfo = createPromptInfo(authenticators);
final PreAuthInfo preAuthInfo = createPreAuthInfo(sensors, userId, promptInfo,
checkDevicePolicyManager);
return new AuthSession(mContext, mStatusBarService, mSysuiReceiver, mKeyStore,
- mRandom, mClientDeathReceiver, preAuthInfo, mToken, operationId, userId,
+ mRandom, mClientDeathReceiver, preAuthInfo, mToken, requestId, operationId, userId,
mSensorReceiver, mClientReceiver, TEST_PACKAGE, promptInfo,
false /* debugEnabled */, mFingerprintSensorProps);
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 7c7afb7..69d8e89 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -85,6 +85,7 @@
import org.mockito.MockitoAnnotations;
import java.util.Random;
+import java.util.concurrent.atomic.AtomicLong;
@Presubmit
@SmallTest
@@ -93,6 +94,7 @@
private static final String TAG = "BiometricServiceTest";
private static final String TEST_PACKAGE_NAME = "test_package";
+ private static final long TEST_REQUEST_ID = 44;
private static final String ERROR_HW_UNAVAILABLE = "hw_unavailable";
private static final String ERROR_NOT_RECOGNIZED = "not_recognized";
@@ -151,6 +153,7 @@
.thenReturn(mock(BiometricStrengthController.class));
when(mInjector.getTrustManager()).thenReturn(mTrustManager);
when(mInjector.getDevicePolicyManager(any())).thenReturn(mDevicePolicyManager);
+ when(mInjector.getRequestGenerator()).thenReturn(new AtomicLong(TEST_REQUEST_ID - 1));
when(mResources.getString(R.string.biometric_error_hw_unavailable))
.thenReturn(ERROR_HW_UNAVAILABLE);
@@ -215,8 +218,7 @@
mBiometricService.mCurrentAuthSession.getState());
verify(mBiometricService.mCurrentAuthSession.mPreAuthInfo.eligibleSensors.get(0).impl)
- .cancelAuthenticationFromService(any(),
- any());
+ .cancelAuthenticationFromService(any(), any(), anyLong());
// Simulate ERROR_CANCELED received from HAL
mBiometricService.mBiometricSensorReceiver.onError(
@@ -272,8 +274,9 @@
eq(true) /* credentialAllowed */,
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
+ anyLong() /* operationId */,
eq(TEST_PACKAGE_NAME),
- anyLong() /* sessionId */,
+ eq(TEST_REQUEST_ID),
eq(BIOMETRIC_MULTI_SENSOR_DEFAULT));
}
@@ -357,8 +360,9 @@
eq(false) /* credentialAllowed */,
eq(false) /* requireConfirmation */,
anyInt() /* userId */,
+ anyLong() /* operationId */,
eq(TEST_PACKAGE_NAME),
- anyLong() /* sessionId */,
+ eq(TEST_REQUEST_ID),
eq(BIOMETRIC_MULTI_SENSOR_DEFAULT));
}
@@ -467,6 +471,7 @@
anyInt() /* userId */,
any(IBiometricSensorReceiver.class),
anyString() /* opPackageName */,
+ eq(TEST_REQUEST_ID),
cookieCaptor.capture() /* cookie */,
anyBoolean() /* allowBackgroundAuthentication */);
@@ -488,8 +493,9 @@
eq(false) /* credentialAllowed */,
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
+ anyLong() /* operationId */,
eq(TEST_PACKAGE_NAME),
- anyLong() /* sessionId */,
+ eq(TEST_REQUEST_ID),
eq(BIOMETRIC_MULTI_SENSOR_DEFAULT));
// Hardware authenticated
@@ -543,8 +549,9 @@
eq(true) /* credentialAllowed */,
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
+ anyLong() /* operationId */,
eq(TEST_PACKAGE_NAME),
- anyLong() /* sessionId */,
+ eq(TEST_REQUEST_ID),
eq(BIOMETRIC_MULTI_SENSOR_DEFAULT));
}
@@ -705,8 +712,9 @@
anyBoolean() /* credentialAllowed */,
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
+ anyLong() /* operationId */,
anyString(),
- anyLong() /* sessionId */,
+ anyLong() /* requestId */,
eq(BIOMETRIC_MULTI_SENSOR_DEFAULT));
}
@@ -805,8 +813,9 @@
eq(true) /* credentialAllowed */,
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
+ anyLong() /* operationId */,
eq(TEST_PACKAGE_NAME),
- anyLong() /* sessionId */,
+ eq(TEST_REQUEST_ID),
eq(BIOMETRIC_MULTI_SENSOR_DEFAULT));
}
@@ -885,8 +894,9 @@
eq(true) /* credentialAllowed */,
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
+ anyLong() /* operationId */,
eq(TEST_PACKAGE_NAME),
- anyLong() /* sessionId */,
+ eq(TEST_REQUEST_ID),
eq(BIOMETRIC_MULTI_SENSOR_DEFAULT));
}
@@ -1030,8 +1040,7 @@
eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED),
eq(0 /* vendorCode */));
verify(mBiometricService.mSensors.get(0).impl).cancelAuthenticationFromService(
- any(),
- any());
+ any(), any(), anyLong());
assertNull(mBiometricService.mCurrentAuthSession);
}
@@ -1051,7 +1060,7 @@
waitForIdle();
verify(mBiometricService.mSensors.get(0).impl)
- .cancelAuthenticationFromService(any(), any());
+ .cancelAuthenticationFromService(any(), any(), anyLong());
}
@Test
@@ -1071,7 +1080,7 @@
waitForIdle();
verify(mBiometricService.mSensors.get(0).impl)
- .cancelAuthenticationFromService(any(), any());
+ .cancelAuthenticationFromService(any(), any(), anyLong());
}
@Test
@@ -1088,7 +1097,7 @@
waitForIdle();
verify(mBiometricService.mSensors.get(0).impl)
- .cancelAuthenticationFromService(any(), any());
+ .cancelAuthenticationFromService(any(), any(), anyLong());
verify(mReceiver1).onError(
eq(BiometricAuthenticator.TYPE_FACE),
eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED),
@@ -1126,7 +1135,7 @@
false /* requireConfirmation */, null /* authenticators */);
mBiometricService.mImpl.cancelAuthentication(mBiometricService.mCurrentAuthSession.mToken,
- TEST_PACKAGE_NAME);
+ TEST_PACKAGE_NAME, TEST_REQUEST_ID);
waitForIdle();
// Pretend that the HAL has responded to cancel with ERROR_CANCELED
@@ -1353,8 +1362,8 @@
int authenticators = Authenticators.BIOMETRIC_STRONG;
assertEquals(BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
invokeCanAuthenticate(mBiometricService, authenticators));
- invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
- authenticators);
+ long requestId = invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */, authenticators);
waitForIdle();
verify(mReceiver1).onError(
eq(BiometricAuthenticator.TYPE_FINGERPRINT),
@@ -1366,7 +1375,7 @@
authenticators = Authenticators.BIOMETRIC_WEAK;
assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
invokeCanAuthenticate(mBiometricService, authenticators));
- invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ requestId = invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
false /* requireConfirmation */,
authenticators);
waitForIdle();
@@ -1377,8 +1386,9 @@
eq(false) /* credentialAllowed */,
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
+ anyLong() /* operationId */,
eq(TEST_PACKAGE_NAME),
- anyLong() /* sessionId */,
+ eq(requestId),
eq(BIOMETRIC_MULTI_SENSOR_DEFAULT));
// Requesting strong and credential, when credential is setup
@@ -1387,7 +1397,7 @@
when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
invokeCanAuthenticate(mBiometricService, authenticators));
- invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
+ requestId = invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
false /* requireConfirmation */,
authenticators);
waitForIdle();
@@ -1399,8 +1409,9 @@
eq(true) /* credentialAllowed */,
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
+ anyLong() /* operationId */,
eq(TEST_PACKAGE_NAME),
- anyLong() /* sessionId */,
+ eq(requestId),
eq(BIOMETRIC_MULTI_SENSOR_DEFAULT));
// Un-downgrading the authenticator allows successful strong auth
@@ -1414,7 +1425,7 @@
authenticators = Authenticators.BIOMETRIC_STRONG;
assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
invokeCanAuthenticate(mBiometricService, authenticators));
- invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ requestId = invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
false /* requireConfirmation */, authenticators);
waitForIdle();
verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
@@ -1424,8 +1435,9 @@
eq(false) /* credentialAllowed */,
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
+ anyLong() /* operationId */,
eq(TEST_PACKAGE_NAME),
- anyLong() /* sessionId */,
+ eq(requestId),
eq(BIOMETRIC_MULTI_SENSOR_DEFAULT));
}
@@ -1617,11 +1629,12 @@
mBiometricService.mStatusBarService = mock(IStatusBarService.class);
}
- private void invokeAuthenticateAndStart(IBiometricService.Stub service,
+ private long invokeAuthenticateAndStart(IBiometricService.Stub service,
IBiometricServiceReceiver receiver, boolean requireConfirmation,
Integer authenticators) throws Exception {
// Request auth, creates a pending session
- invokeAuthenticate(service, receiver, requireConfirmation, authenticators);
+ final long requestId = invokeAuthenticate(
+ service, receiver, requireConfirmation, authenticators);
waitForIdle();
startPendingAuthSession(mBiometricService);
@@ -1629,6 +1642,8 @@
assertNotNull(mBiometricService.mCurrentAuthSession);
assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState());
+
+ return requestId;
}
private static void startPendingAuthSession(BiometricService service) throws Exception {
@@ -1644,10 +1659,10 @@
service.mImpl.onReadyForAuthentication(cookie);
}
- private static void invokeAuthenticate(IBiometricService.Stub service,
+ private static long invokeAuthenticate(IBiometricService.Stub service,
IBiometricServiceReceiver receiver, boolean requireConfirmation,
Integer authenticators) throws Exception {
- service.authenticate(
+ return service.authenticate(
new Binder() /* token */,
0 /* operationId */,
0 /* userId */,
@@ -1657,9 +1672,9 @@
false /* checkDevicePolicy */));
}
- private static void invokeAuthenticateForWorkApp(IBiometricService.Stub service,
+ private static long invokeAuthenticateForWorkApp(IBiometricService.Stub service,
IBiometricServiceReceiver receiver, Integer authenticators) throws Exception {
- service.authenticate(
+ return service.authenticate(
new Binder() /* token */,
0 /* operationId */,
0 /* userId */,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index a41f79e..e3e3900 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -40,6 +40,7 @@
import android.testing.TestableContext;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -193,7 +194,7 @@
// Request it to be canceled. The operation can be canceled immediately, and the scheduler
// should go back to idle, since in this case the framework has not even requested the HAL
// to authenticate yet.
- mScheduler.cancelAuthenticationOrDetection(mToken);
+ mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */);
assertNull(mScheduler.mCurrentOperation);
}
@@ -303,7 +304,7 @@
mScheduler.mPendingOperations.getFirst().mState);
// Request cancel before the authentication client has started
- mScheduler.cancelAuthenticationOrDetection(mToken);
+ mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */);
waitForIdle();
assertEquals(Operation.STATE_WAITING_IN_QUEUE_CANCELING,
mScheduler.mPendingOperations.getFirst().mState);
@@ -318,6 +319,107 @@
}
@Test
+ public void testCancels_whenAuthRequestIdNotSet() {
+ testCancelsWhenRequestId(null /* requestId */, 2, true /* started */);
+ }
+
+ @Test
+ public void testCancels_whenAuthRequestIdNotSet_notStarted() {
+ testCancelsWhenRequestId(null /* requestId */, 2, false /* started */);
+ }
+
+ @Test
+ public void testCancels_whenAuthRequestIdMatches() {
+ testCancelsWhenRequestId(200L, 200, true /* started */);
+ }
+
+ @Test
+ public void testCancels_whenAuthRequestIdMatches_noStarted() {
+ testCancelsWhenRequestId(200L, 200, false /* started */);
+ }
+
+ @Test
+ public void testDoesNotCancel_whenAuthRequestIdMismatched() {
+ testCancelsWhenRequestId(10L, 20, true /* started */);
+ }
+
+ @Test
+ public void testDoesNotCancel_whenAuthRequestIdMismatched_notStarted() {
+ testCancelsWhenRequestId(10L, 20, false /* started */);
+ }
+
+ private void testCancelsWhenRequestId(@Nullable Long requestId, long cancelRequestId,
+ boolean started) {
+ final boolean matches = requestId == null || requestId == cancelRequestId;
+ final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
+ final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
+ final TestAuthenticationClient client = new TestAuthenticationClient(
+ mContext, lazyDaemon, mToken, callback);
+ if (requestId != null) {
+ client.setRequestId(requestId);
+ }
+
+ mScheduler.scheduleClientMonitor(client);
+ if (started) {
+ mScheduler.startPreparedClient(client.getCookie());
+ }
+ waitForIdle();
+ mScheduler.cancelAuthenticationOrDetection(mToken, cancelRequestId);
+ waitForIdle();
+
+ assertEquals(matches && started ? 1 : 0, client.mNumCancels);
+
+ if (matches) {
+ if (started) {
+ assertEquals(Operation.STATE_STARTED_CANCELING,
+ mScheduler.mCurrentOperation.mState);
+ }
+ } else {
+ if (started) {
+ assertEquals(Operation.STATE_STARTED,
+ mScheduler.mCurrentOperation.mState);
+ } else {
+ assertEquals(Operation.STATE_WAITING_FOR_COOKIE,
+ mScheduler.mCurrentOperation.mState);
+ }
+ }
+ }
+
+ @Test
+ public void testCancelsPending_whenAuthRequestIdsSet() {
+ final long requestId1 = 10;
+ final long requestId2 = 20;
+ final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
+ final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
+ final TestAuthenticationClient client1 = new TestAuthenticationClient(
+ mContext, lazyDaemon, mToken, callback);
+ client1.setRequestId(requestId1);
+ final TestAuthenticationClient client2 = new TestAuthenticationClient(
+ mContext, lazyDaemon, mToken, callback);
+ client2.setRequestId(requestId2);
+
+ mScheduler.scheduleClientMonitor(client1);
+ mScheduler.scheduleClientMonitor(client2);
+ mScheduler.startPreparedClient(client1.getCookie());
+ waitForIdle();
+ mScheduler.cancelAuthenticationOrDetection(mToken, 9999);
+ waitForIdle();
+
+ assertEquals(Operation.STATE_STARTED,
+ mScheduler.mCurrentOperation.mState);
+ assertEquals(Operation.STATE_WAITING_IN_QUEUE,
+ mScheduler.mPendingOperations.getFirst().mState);
+
+ mScheduler.cancelAuthenticationOrDetection(mToken, requestId2);
+ waitForIdle();
+
+ assertEquals(Operation.STATE_STARTED,
+ mScheduler.mCurrentOperation.mState);
+ assertEquals(Operation.STATE_WAITING_IN_QUEUE_CANCELING,
+ mScheduler.mPendingOperations.getFirst().mState);
+ }
+
+ @Test
public void testInterruptPrecedingClients_whenExpected() {
final BaseClientMonitor interruptableMonitor = mock(BaseClientMonitor.class,
withSettings().extraInterfaces(Interruptable.class));
@@ -377,12 +479,10 @@
@Override
protected void stopHalOperation() {
-
}
@Override
protected void startHalOperation() {
-
}
@Override
@@ -397,6 +497,7 @@
}
private static class TestAuthenticationClient extends AuthenticationClient<Object> {
+ int mNumCancels = 0;
public TestAuthenticationClient(@NonNull Context context,
@NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token,
@@ -428,6 +529,11 @@
public boolean wasUserDetected() {
return false;
}
+
+ public void cancel() {
+ mNumCancels++;
+ super.cancel();
+ }
}
private static class TestClientMonitor2 extends TestClientMonitor {
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 1ac28ab..4564296 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -56,7 +56,11 @@
import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
import android.hardware.fingerprint.IUdfpsHbmListener;
import android.os.Handler;
+import android.os.IThermalEventListener;
+import android.os.IThermalService;
import android.os.Looper;
+import android.os.RemoteException;
+import android.os.Temperature;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
@@ -116,6 +120,8 @@
public SensorManagerInternal mSensorManagerInternalMock;
@Mock
public DisplayManagerInternal mDisplayManagerInternalMock;
+ @Mock
+ public IThermalService mThermalServiceMock;
@Before
public void setUp() throws Exception {
@@ -124,6 +130,7 @@
final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContext);
when(mContext.getContentResolver()).thenReturn(resolver);
mInjector = spy(new FakesInjector());
+ when(mInjector.getThermalService()).thenReturn(mThermalServiceMock);
mHandler = new Handler(Looper.getMainLooper());
LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
@@ -1547,12 +1554,52 @@
assertNull(vote);
}
+ @Test
+ public void testSkinTemperature() throws RemoteException {
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
+ director.start(createMockSensorManager());
+
+ ArgumentCaptor<IThermalEventListener> thermalEventListener =
+ ArgumentCaptor.forClass(IThermalEventListener.class);
+
+ verify(mThermalServiceMock).registerThermalEventListenerWithType(
+ thermalEventListener.capture(), eq(Temperature.TYPE_SKIN));
+ final IThermalEventListener listener = thermalEventListener.getValue();
+
+ // Verify that there is no skin temperature vote initially.
+ Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_SKIN_TEMPERATURE);
+ assertNull(vote);
+
+ // Set the skin temperature to critical and verify that we added a vote.
+ listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_CRITICAL));
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_SKIN_TEMPERATURE);
+ assertVoteForRefreshRateRange(vote, 0f, 60.f);
+
+ // Set the skin temperature to severe and verify that the vote is gone.
+ listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_SEVERE));
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_SKIN_TEMPERATURE);
+ assertNull(vote);
+ }
+
+ private Temperature getSkinTemp(@Temperature.ThrottlingStatus int status) {
+ return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status);
+ }
+
private void assertVoteForRefreshRate(Vote vote, float refreshRate) {
assertThat(vote).isNotNull();
final RefreshRateRange expectedRange = new RefreshRateRange(refreshRate, refreshRate);
assertThat(vote.refreshRateRange).isEqualTo(expectedRange);
}
+ private void assertVoteForRefreshRateRange(
+ Vote vote, float refreshRateLow, float refreshRateHigh) {
+ assertThat(vote).isNotNull();
+ final RefreshRateRange expectedRange =
+ new RefreshRateRange(refreshRateLow, refreshRateHigh);
+ assertThat(vote.refreshRateRange).isEqualTo(expectedRange);
+ }
+
public static class FakeDeviceConfig extends FakeDeviceConfigInterface {
@Override
public String getProperty(String namespace, String name) {
@@ -1748,6 +1795,11 @@
return false;
}
+ @Override
+ public IThermalService getThermalService() {
+ return null;
+ }
+
void notifyPeakRefreshRateChanged() {
if (mPeakRefreshRateObserver != null) {
mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/,
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java
new file mode 100644
index 0000000..f3070b6
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_ON;
+import static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_STANDBY;
+import static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
+
+import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
+import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2;
+import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_3;
+import static com.android.server.hdmi.DeviceSelectActionFromPlayback.STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_ROUTING_CHANGE;
+import static com.android.server.hdmi.DeviceSelectActionFromPlayback.STATE_WAIT_FOR_DEVICE_POWER_ON;
+import static com.android.server.hdmi.DeviceSelectActionFromPlayback.STATE_WAIT_FOR_REPORT_POWER_STATUS;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.os.Looper;
+import android.os.test.TestLooper;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.hdmi.HdmiCecFeatureAction.ActionTimer;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+@SmallTest
+@RunWith(JUnit4.class)
+public class DeviceSelectActionFromPlaybackTest {
+
+ private static final int PORT_1 = 1;
+ private static final int PORT_2 = 1;
+ private static final int PORT_3 = 1;
+ private static final int PHYSICAL_ADDRESS_PLAYBACK_1 = 0x1000;
+ private static final int PHYSICAL_ADDRESS_PLAYBACK_2 = 0x2000;
+ private static final int PHYSICAL_ADDRESS_PLAYBACK_3 = 0x3000;
+
+ private static final byte[] POWER_ON = new byte[] { POWER_STATUS_ON };
+ private static final byte[] POWER_STANDBY = new byte[] { POWER_STATUS_STANDBY };
+ private static final byte[] POWER_TRANSIENT_TO_ON = new byte[] { POWER_STATUS_TRANSIENT_TO_ON };
+ private static final HdmiCecMessage REPORT_POWER_STATUS_ON = new HdmiCecMessage(
+ ADDR_PLAYBACK_2, ADDR_PLAYBACK_1, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
+ private static final HdmiCecMessage REPORT_POWER_STATUS_STANDBY = new HdmiCecMessage(
+ ADDR_PLAYBACK_2, ADDR_PLAYBACK_1, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_STANDBY);
+ private static final HdmiCecMessage REPORT_POWER_STATUS_TRANSIENT_TO_ON = new HdmiCecMessage(
+ ADDR_PLAYBACK_2, ADDR_PLAYBACK_1, Constants.MESSAGE_REPORT_POWER_STATUS,
+ POWER_TRANSIENT_TO_ON);
+ private static final HdmiCecMessage SET_STREAM_PATH = HdmiCecMessageBuilder.buildSetStreamPath(
+ ADDR_PLAYBACK_1, PHYSICAL_ADDRESS_PLAYBACK_2);
+ private static final HdmiCecMessage ROUTING_CHANGE = HdmiCecMessageBuilder.buildRoutingChange(
+ ADDR_PLAYBACK_1, PHYSICAL_ADDRESS_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_2);
+ private static final HdmiCecMessage ACTIVE_SOURCE = HdmiCecMessageBuilder.buildActiveSource(
+ ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2);
+ private static final HdmiDeviceInfo INFO_PLAYBACK_1 = new HdmiDeviceInfo(
+ ADDR_PLAYBACK_1, PHYSICAL_ADDRESS_PLAYBACK_1, PORT_1, HdmiDeviceInfo.DEVICE_PLAYBACK,
+ 0x1234, "Playback 1",
+ HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+ private static final HdmiDeviceInfo INFO_PLAYBACK_2 = new HdmiDeviceInfo(
+ ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, PORT_2, HdmiDeviceInfo.DEVICE_PLAYBACK,
+ 0x1234, "Playback 2",
+ HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+ private static final HdmiDeviceInfo INFO_PLAYBACK_3 = new HdmiDeviceInfo(
+ ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3, PORT_3, HdmiDeviceInfo.DEVICE_PLAYBACK,
+ 0x1234, "Playback 3",
+ HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+
+ private HdmiCecController mHdmiCecController;
+ private HdmiCecLocalDevicePlayback mHdmiCecLocalDevicePlayback;
+ private HdmiCecNetwork mHdmiCecNetwork;
+ private HdmiControlService mHdmiControlService;
+ private HdmiMhlControllerStub mHdmiMhlControllerStub;
+
+ private FakeNativeWrapper mNativeWrapper;
+ private FakePowerManagerWrapper mPowerManager;
+ private Looper mMyLooper;
+ private TestLooper mTestLooper = new TestLooper();
+ private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ Context context = InstrumentationRegistry.getTargetContext();
+ mMyLooper = mTestLooper.getLooper();
+
+ mHdmiControlService =
+ new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+ Collections.emptyList()) {
+ @Override
+ boolean isControlEnabled() {
+ return true;
+ }
+
+ @Override
+ protected void writeStringSystemProperty(String key, String value) {
+ // do nothing
+ }
+
+ @Override
+ boolean isPowerStandbyOrTransient() {
+ return false;
+ }
+ };
+
+
+ mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(mHdmiControlService);
+ mHdmiCecLocalDevicePlayback.init();
+ mHdmiControlService.setIoLooper(mMyLooper);
+ mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
+ mNativeWrapper = new FakeNativeWrapper();
+ mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+ mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
+ mHdmiControlService.setCecController(mHdmiCecController);
+ mHdmiMhlControllerStub = HdmiMhlControllerStub.create(mHdmiControlService);
+ mHdmiControlService.setHdmiMhlController(mHdmiMhlControllerStub);
+ mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+ mHdmiControlService.setCecController(mHdmiCecController);
+ mHdmiControlService.setHdmiMhlController(mHdmiMhlControllerStub);
+ mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+ mHdmiCecNetwork = new HdmiCecNetwork(mHdmiControlService,
+ mHdmiCecController, mHdmiMhlControllerStub);
+ mHdmiControlService.setHdmiCecNetwork(mHdmiCecNetwork);
+
+ mLocalDevices.add(mHdmiCecLocalDevicePlayback);
+ mHdmiControlService.initService();
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mNativeWrapper.setPhysicalAddress(0x0000);
+ mPowerManager = new FakePowerManagerWrapper(context);
+ mHdmiControlService.setPowerManager(mPowerManager);
+ mTestLooper.dispatchAll();
+ mNativeWrapper.clearResultMessages();
+
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_PLAYBACK_1);
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_PLAYBACK_2);
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_PLAYBACK_3);
+ }
+
+ private static class TestActionTimer implements ActionTimer {
+ private int mState;
+
+ @Override
+ public void sendTimerMessage(int state, long delayMillis) {
+ mState = state;
+ }
+
+ @Override
+ public void clearTimerMessage() {
+ }
+
+ private int getState() {
+ return mState;
+ }
+ }
+
+ private static class TestCallback extends IHdmiControlCallback.Stub {
+ private final ArrayList<Integer> mCallbackResult = new ArrayList<Integer>();
+
+ @Override
+ public void onComplete(int result) {
+ mCallbackResult.add(result);
+ }
+
+ private int getResult() {
+ assertThat(mCallbackResult.size()).isEqualTo(1);
+ return mCallbackResult.get(0);
+ }
+ }
+
+ private DeviceSelectActionFromPlayback createDeviceSelectActionFromPlayback(
+ TestActionTimer actionTimer,
+ TestCallback callback,
+ boolean isCec20) {
+ HdmiDeviceInfo hdmiDeviceInfo =
+ mHdmiControlService.getHdmiCecNetwork().getCecDeviceInfo(ADDR_PLAYBACK_2);
+ DeviceSelectActionFromPlayback action = new DeviceSelectActionFromPlayback(
+ mHdmiCecLocalDevicePlayback,
+ hdmiDeviceInfo, callback, isCec20);
+ action.setActionTimer(actionTimer);
+ return action;
+ }
+
+ @Test
+ public void testDeviceSelect_DeviceInPowerOnStatus_Cec14b() {
+ // TV was watching playback3 device connected at port 3, and wants to select
+ // playback2.
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ DeviceSelectActionFromPlayback action = createDeviceSelectActionFromPlayback(actionTimer,
+ callback, /*isCec20=*/false);
+ mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
+ "testDeviceSelectFromPlayback");
+ action.start();
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
+ action.processCommand(REPORT_POWER_STATUS_ON);
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ action.processCommand(ACTIVE_SOURCE);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+
+ @Test
+ public void testDeviceSelect_DeviceInStandbyStatus_Cec14b() {
+ mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
+ "testDeviceSelectFromPlayback");
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ DeviceSelectActionFromPlayback action = createDeviceSelectActionFromPlayback(actionTimer,
+ callback, /*isCec20=*/false);
+ action.start();
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
+ action.processCommand(REPORT_POWER_STATUS_STANDBY);
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON);
+ action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON);
+ action.processCommand(REPORT_POWER_STATUS_ON);
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ action.processCommand(ACTIVE_SOURCE);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+
+ @Test
+ public void testDeviceSelect_DeviceInStandbyStatusWithSomeTimeouts_Cec14b() {
+ mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
+ "testDeviceSelectFromPlayback");
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ DeviceSelectActionFromPlayback action = createDeviceSelectActionFromPlayback(actionTimer,
+ callback, /*isCec20=*/false);
+ action.start();
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
+ action.processCommand(REPORT_POWER_STATUS_STANDBY);
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON);
+ action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON);
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
+ action.processCommand(REPORT_POWER_STATUS_TRANSIENT_TO_ON);
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON);
+ action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON);
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
+ action.processCommand(REPORT_POWER_STATUS_ON);
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ action.processCommand(ACTIVE_SOURCE);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+
+ @Test
+ public void testDeviceSelect_DeviceInStandbyAfterTimeoutForReportPowerStatus_Cec14b() {
+ mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
+ "testDeviceSelectFromPlayback");
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ DeviceSelectActionFromPlayback action = createDeviceSelectActionFromPlayback(actionTimer,
+ callback, /*isCec20=*/false);
+ action.start();
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
+ action.processCommand(REPORT_POWER_STATUS_STANDBY);
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON);
+ action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON);
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
+ action.processCommand(REPORT_POWER_STATUS_TRANSIENT_TO_ON);
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON);
+ action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON);
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
+ action.handleTimerEvent(STATE_WAIT_FOR_REPORT_POWER_STATUS);
+ // Give up getting power status, and just send <Routing Change>
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ action.processCommand(ACTIVE_SOURCE);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+
+ @Test
+ public void testDeviceSelect_ReachSetStreamPath_Cec14b() {
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ DeviceSelectActionFromPlayback action = createDeviceSelectActionFromPlayback(actionTimer,
+ callback, /*isCec20=*/false);
+ mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
+ "testDeviceSelectFromPlayback");
+ action.start();
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
+ action.processCommand(REPORT_POWER_STATUS_ON);
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(
+ STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_ROUTING_CHANGE);
+ action.handleTimerEvent(STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_ROUTING_CHANGE);
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
+ action.processCommand(ACTIVE_SOURCE);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+
+ @Test
+ public void testDeviceSelect_ReachSetStreamPathDeviceInPowerOnStatus_Cec20() {
+ mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_PLAYBACK_2,
+ HdmiControlManager.POWER_STATUS_ON);
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ DeviceSelectActionFromPlayback action = createDeviceSelectActionFromPlayback(actionTimer,
+ callback, /*isCec20=*/true);
+ mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
+ "testDeviceSelectFromPlayback");
+ action.start();
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(
+ STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_ROUTING_CHANGE);
+ action.handleTimerEvent(STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_ROUTING_CHANGE);
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
+ action.processCommand(ACTIVE_SOURCE);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+
+ @Test
+ public void testDeviceSelect_DeviceInPowerOnStatus_Cec20() {
+ mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_PLAYBACK_2,
+ HdmiControlManager.POWER_STATUS_ON);
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ DeviceSelectActionFromPlayback action = createDeviceSelectActionFromPlayback(actionTimer,
+ callback, /*isCec20=*/true);
+ mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
+ "testDeviceSelectFromPlayback");
+ action.start();
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ action.processCommand(ACTIVE_SOURCE);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+
+ @Test
+ public void testDeviceSelect_DeviceInPowerUnknownStatus_Cec20() {
+ mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_PLAYBACK_2,
+ HdmiControlManager.POWER_STATUS_UNKNOWN);
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ DeviceSelectActionFromPlayback action = createDeviceSelectActionFromPlayback(actionTimer,
+ callback, /*isCec20=*/true);
+ mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
+ "testDeviceSelectFromPlayback");
+ action.start();
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
+ action.processCommand(REPORT_POWER_STATUS_ON);
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ action.processCommand(ACTIVE_SOURCE);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+
+ @Test
+ public void testDeviceSelect_DeviceInStandbyStatus_Cec20() {
+ mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_PLAYBACK_2,
+ HdmiControlManager.POWER_STATUS_STANDBY);
+ mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
+ "testDeviceSelectFromPlayback");
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ DeviceSelectActionFromPlayback action = createDeviceSelectActionFromPlayback(actionTimer,
+ callback, /*isCec20=*/true);
+ action.start();
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
+ action.processCommand(REPORT_POWER_STATUS_STANDBY);
+ mTestLooper.dispatchAll();
+ mNativeWrapper.clearResultMessages();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON);
+ action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON);
+ action.processCommand(REPORT_POWER_STATUS_ON);
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ action.processCommand(ACTIVE_SOURCE);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
index b903b16..f30e97a 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
@@ -42,7 +42,7 @@
import android.os.RemoteException;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
-import android.stats.hdmi.nano.HdmiStatsEnums;
+import android.stats.hdmi.HdmiStatsEnums;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -53,6 +53,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
import java.util.ArrayList;
import java.util.Collections;
@@ -134,6 +135,8 @@
mHdmiControlServiceSpy.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
mTestLooper.dispatchAll();
+
+ Mockito.reset(mHdmiCecAtomWriterSpy);
}
@Test
@@ -149,6 +152,7 @@
mPhysicalAddress);
mHdmiCecController.sendCommand(message);
+ mTestLooper.dispatchAll();
verify(mHdmiCecAtomWriterSpy, times(1)).messageReported(
eq(message),
@@ -193,4 +197,109 @@
eq(callerUid),
anyInt());
}
+
+ @Test
+ public void testMessageReported_writesAtom_userControlPressed() {
+ HdmiCecMessage message = HdmiCecMessageBuilder.buildUserControlPressed(
+ Constants.ADDR_TV,
+ Constants.ADDR_PLAYBACK_1,
+ HdmiCecKeycode.CEC_KEYCODE_MUTE
+ );
+
+ mHdmiCecAtomWriterSpy.messageReported(
+ message,
+ HdmiStatsEnums.INCOMING,
+ 1234);
+
+ verify(mHdmiCecAtomWriterSpy, times(1))
+ .writeHdmiCecMessageReportedAtom(
+ 1234,
+ HdmiStatsEnums.INCOMING,
+ Constants.ADDR_TV,
+ Constants.ADDR_PLAYBACK_1,
+ Constants.MESSAGE_USER_CONTROL_PRESSED,
+ HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN,
+ HdmiStatsEnums.VOLUME_MUTE,
+ HdmiCecAtomWriter.FEATURE_ABORT_OPCODE_UNKNOWN,
+ HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN);
+ }
+
+ @Test
+ public void testMessageReported_writesAtom_userControlPressed_noParams() {
+ HdmiCecMessage message = new HdmiCecMessage(
+ Constants.ADDR_TV,
+ Constants.ADDR_PLAYBACK_1,
+ Constants.MESSAGE_USER_CONTROL_PRESSED,
+ new byte[0]);
+
+ mHdmiCecAtomWriterSpy.messageReported(
+ message,
+ HdmiStatsEnums.INCOMING,
+ 1234);
+
+ verify(mHdmiCecAtomWriterSpy, times(1))
+ .writeHdmiCecMessageReportedAtom(
+ 1234,
+ HdmiStatsEnums.INCOMING,
+ Constants.ADDR_TV,
+ Constants.ADDR_PLAYBACK_1,
+ Constants.MESSAGE_USER_CONTROL_PRESSED,
+ HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN,
+ HdmiStatsEnums.USER_CONTROL_PRESSED_COMMAND_UNKNOWN,
+ HdmiCecAtomWriter.FEATURE_ABORT_OPCODE_UNKNOWN,
+ HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN);
+ }
+
+ @Test
+ public void testMessageReported_writesAtom_featureAbort() {
+ HdmiCecMessage message = HdmiCecMessageBuilder.buildFeatureAbortCommand(
+ Constants.ADDR_TV,
+ Constants.ADDR_PLAYBACK_1,
+ Constants.MESSAGE_RECORD_ON,
+ Constants.ABORT_UNRECOGNIZED_OPCODE
+ );
+
+ mHdmiCecAtomWriterSpy.messageReported(
+ message,
+ HdmiStatsEnums.INCOMING,
+ 1234);
+
+ verify(mHdmiCecAtomWriterSpy, times(1))
+ .writeHdmiCecMessageReportedAtom(
+ 1234,
+ HdmiStatsEnums.INCOMING,
+ Constants.ADDR_TV,
+ Constants.ADDR_PLAYBACK_1,
+ Constants.MESSAGE_FEATURE_ABORT,
+ HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN,
+ HdmiStatsEnums.USER_CONTROL_PRESSED_COMMAND_UNKNOWN,
+ Constants.MESSAGE_RECORD_ON,
+ HdmiStatsEnums.UNRECOGNIZED_OPCODE);
+ }
+
+ @Test
+ public void testMessageReported_writesAtom_featureAbort_noParams() {
+ HdmiCecMessage message = new HdmiCecMessage(
+ Constants.ADDR_TV,
+ Constants.ADDR_PLAYBACK_1,
+ Constants.MESSAGE_FEATURE_ABORT,
+ new byte[0]);
+
+ mHdmiCecAtomWriterSpy.messageReported(
+ message,
+ HdmiStatsEnums.INCOMING,
+ 1234);
+
+ verify(mHdmiCecAtomWriterSpy, times(1))
+ .writeHdmiCecMessageReportedAtom(
+ 1234,
+ HdmiStatsEnums.INCOMING,
+ Constants.ADDR_TV,
+ Constants.ADDR_PLAYBACK_1,
+ Constants.MESSAGE_FEATURE_ABORT,
+ HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN,
+ HdmiStatsEnums.USER_CONTROL_PRESSED_COMMAND_UNKNOWN,
+ HdmiCecAtomWriter.FEATURE_ABORT_OPCODE_UNKNOWN,
+ HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index f241fe1..dbed445 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -151,7 +151,7 @@
String[] partitions = { "system", "vendor", "odm", "oem", "product", "system_ext" };
String[] appdir = { "app", "priv-app" };
for (int i = 0; i < partitions.length; i++) {
- final PackageManagerService.ScanPartition scanPartition =
+ final ScanPartition scanPartition =
PackageManagerService.SYSTEM_PARTITIONS.get(i);
for (int j = 0; j < appdir.length; j++) {
File path = new File(String.format("%s/%s/A.apk", partitions[i], appdir[j]));
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 6c6cfd4..77b9fd3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -87,6 +87,8 @@
PlatformCompat mMockCompatibility;
@Mock
PackageManagerService.Injector mMockInjector;
+ @Mock
+ PackageManagerService mMockPackageManager;
@Before
public void setupInjector() {
@@ -432,7 +434,7 @@
final ParsingPackage basicPackage = createBasicPackage(DUMMY_PACKAGE_NAME)
.addUsesPermission(new ParsedUsesPermission(Manifest.permission.FACTORY_TEST, 0));
- final ScanResult scanResult = PackageManagerService.scanPackageOnlyLI(
+ final ScanResult scanResult = mMockPackageManager.scanPackageOnlyLI(
createBasicScanRequestBuilder(basicPackage).build(),
mMockInjector,
true /*isUnderFactoryTest*/,
@@ -480,7 +482,7 @@
private ScanResult executeScan(
ScanRequest scanRequest) throws PackageManagerException {
- ScanResult result = PackageManagerService.scanPackageOnlyLI(
+ ScanResult result = mMockPackageManager.scanPackageOnlyLI(
scanRequest,
mMockInjector,
false /*isUnderFactoryTest*/,
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
index 16f72f7..e021d2a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
@@ -416,8 +416,11 @@
result.getErrorMessage(), result.getException());
}
final ApkLite baseApk = result.getResult();
- final PackageLite pkgLite = new PackageLite(null, baseApk.getPath(), baseApk, null,
- null, null, null, null, null, baseApk.getTargetSdkVersion());
+ final PackageLite pkgLite = new PackageLite(null, baseApk.getPath(), baseApk,
+ null /* splitNames */, null /* isFeatureSplits */, null /* usesSplitNames */,
+ null /* configForSplit */, null /* splitApkPaths */,
+ null /* splitRevisionCodes */, baseApk.getTargetSdkVersion(),
+ null /* requiredSplitTypes */, null /* splitTypes */);
Assert.assertEquals(dm.length(), DexMetadataHelper.getPackageDexMetadataSize(pkgLite));
}
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
index 1947481..3f8cf9c 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
@@ -76,7 +76,7 @@
public static Iterable<Object[]> data() {
List<Object[]> result = new LinkedList<>();
- for (String version : new String[]{"V2_0", "V2_1", "V2_2", "V2_3", "V2_4",}) {
+ for (String version : new String[]{"V2_0", "V2_1", "V2_2", "V2_3",}) {
for (boolean concurrentCapture : new boolean[]{false, true}) {
result.add(new Object[]{version, concurrentCapture});
}
@@ -113,9 +113,7 @@
|| descriptor.equals("android.hardware.soundtrigger@2.2::ISoundTriggerHw")
&& mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw
|| descriptor.equals("android.hardware.soundtrigger@2.3::ISoundTriggerHw")
- && mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw
- || descriptor.equals("android.hardware.soundtrigger@2.4::ISoundTriggerHw")
- && mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ && mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
return mHalDriver;
}
return null;
@@ -269,44 +267,9 @@
return handle;
}
- private int loadGenericModel_2_4(ISoundTriggerHal.ModelCallback canonicalCallback)
- throws Exception {
- final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
- (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
-
- final int handle = 29;
- ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> modelCaptor =
- ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class);
- ArgumentCaptor<android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback> callbackCaptor =
- ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback.class);
-
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_4.ISoundTriggerHw.loadSoundModel_2_4Callback
- resultCallback = invocation.getArgument(2);
-
- // This is the return of this method.
- resultCallback.onValues(0, handle);
- return null;
- }).when(driver_2_4).loadSoundModel_2_4(any(), any(), any());
-
- assertEquals(handle,
- mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback));
-
- verify(driver_2_4).loadSoundModel_2_4(modelCaptor.capture(), callbackCaptor.capture(),
- any());
-
- TestUtil.validateGenericSoundModel_2_1(modelCaptor.getValue());
- validateCallback_2_4(callbackCaptor.getValue(), canonicalCallback);
- return handle;
- }
-
private int loadGenericModel(ISoundTriggerHal.ModelCallback canonicalCallback)
throws Exception {
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
- return loadGenericModel_2_4(canonicalCallback);
- } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
return loadGenericModel_2_1(canonicalCallback);
} else {
return loadGenericModel_2_0(canonicalCallback);
@@ -322,8 +285,6 @@
@Test
public void testMaxModels() throws Exception {
- assumeFalse(mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw);
-
// Register global callback.
ISoundTriggerHal.GlobalCallback globalCallback = mock(
ISoundTriggerHal.GlobalCallback.class);
@@ -358,37 +319,6 @@
verify(globalCallback).onResourcesAvailable();
}
- private void testLoadGenericModelBusy_2_4() throws Exception {
- final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
- (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
-
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_4.ISoundTriggerHw.loadSoundModel_2_4Callback
- resultCallback = invocation.getArgument(2);
-
- // This is the return of this method.
- resultCallback.onValues(-OsConstants.EBUSY, 0);
- return null;
- }).when(driver_2_4).loadSoundModel_2_4(any(), any(), any());
-
- ISoundTriggerHal.ModelCallback canonicalCallback = mock(
- ISoundTriggerHal.ModelCallback.class);
- try {
- mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback);
- fail("Expected an exception");
- } catch (RecoverableException e) {
- assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
- }
- verify(driver_2_4).loadSoundModel_2_4(any(), any(), any());
- }
-
- @Test
- public void testLoadGenericModelBusy() throws Exception {
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
- testLoadGenericModelBusy_2_4();
- }
- }
-
private int loadPhraseModel_2_0(ISoundTriggerHal.ModelCallback canonicalCallback)
throws Exception {
final int handle = 29;
@@ -452,43 +382,8 @@
return handle;
}
- private int loadPhraseModel_2_4(ISoundTriggerHal.ModelCallback canonicalCallback)
- throws Exception {
- final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
- (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
-
- final int handle = 29;
- ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel>
- modelCaptor = ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class);
- ArgumentCaptor<android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback> callbackCaptor =
- ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback.class);
-
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_4.ISoundTriggerHw.loadPhraseSoundModel_2_4Callback
- resultCallback = invocation.getArgument(2);
-
- // This is the return of this method.
- resultCallback.onValues(0, handle);
- return null;
- }).when(driver_2_4).loadPhraseSoundModel_2_4(any(), any(), any());
-
- assertEquals(handle, mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(),
- canonicalCallback));
-
- verify(driver_2_4).loadPhraseSoundModel_2_4(modelCaptor.capture(), callbackCaptor.capture(),
- any());
-
- TestUtil.validatePhraseSoundModel_2_1(modelCaptor.getValue());
- validateCallback_2_4(callbackCaptor.getValue(), canonicalCallback);
- return handle;
- }
-
public int loadPhraseModel(ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception {
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
- return loadPhraseModel_2_4(canonicalCallback);
- } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
return loadPhraseModel_2_1(canonicalCallback);
} else {
return loadPhraseModel_2_0(canonicalCallback);
@@ -502,37 +397,6 @@
loadPhraseModel(canonicalCallback);
}
- private void testLoadPhraseModelBusy_2_4() throws Exception {
- final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
- (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
-
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_4.ISoundTriggerHw.loadPhraseSoundModel_2_4Callback
- resultCallback = invocation.getArgument(2);
-
- // This is the return of this method.
- resultCallback.onValues(-OsConstants.EBUSY, 0);
- return null;
- }).when(driver_2_4).loadPhraseSoundModel_2_4(any(), any(), any());
-
- ISoundTriggerHal.ModelCallback canonicalCallback = mock(
- ISoundTriggerHal.ModelCallback.class);
- try {
- mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(), canonicalCallback);
- fail("Expected an exception");
- } catch (RecoverableException e) {
- assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
- }
- verify(driver_2_4).loadPhraseSoundModel_2_4(any(), any(), any());
- }
-
- @Test
- public void testLoadPhraseModelBusy() throws Exception {
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
- testLoadPhraseModelBusy_2_4();
- }
- }
-
@Test
public void testUnloadModel() throws Exception {
mCanonical.unloadSoundModel(14);
@@ -596,25 +460,9 @@
TestUtil.validateRecognitionConfig_2_3(configCaptor.getValue(), 808, 909);
}
- private void startRecognition_2_4(int handle) throws Exception {
- final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
- (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
- ArgumentCaptor<android.hardware.soundtrigger.V2_3.RecognitionConfig> configCaptor =
- ArgumentCaptor.forClass(android.hardware.soundtrigger.V2_3.RecognitionConfig.class);
-
- when(driver_2_4.startRecognition_2_4(eq(handle), any())).thenReturn(0);
-
- RecognitionConfig config = TestUtil.createRecognitionConfig();
- mCanonical.startRecognition(handle, 21, 22, config);
- verify(driver_2_4).startRecognition_2_4(eq(handle), configCaptor.capture());
- TestUtil.validateRecognitionConfig_2_3(configCaptor.getValue(), 21, 22);
- }
-
private void startRecognition(int handle, ISoundTriggerHal.ModelCallback canonicalCallback)
throws Exception {
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
- startRecognition_2_4(handle);
- } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
startRecognition_2_3(handle);
} else if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
startRecognition_2_1(handle, canonicalCallback);
@@ -634,41 +482,9 @@
startRecognition(handle, canonicalCallback);
}
- private void testStartRecognitionBusy_2_4() throws Exception {
- final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
- (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
-
- final int handle = 68;
- when(driver_2_4.startRecognition_2_4(eq(handle), any())).thenReturn(-OsConstants.EBUSY);
-
- RecognitionConfig config = TestUtil.createRecognitionConfig();
- try {
- mCanonical.startRecognition(handle, 34, 35, config);
- fail("Expected an exception");
- } catch (RecoverableException e) {
- assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
- }
- verify(driver_2_4).startRecognition_2_4(eq(handle), any());
- }
-
- @Test
- public void testStartRecognitionBusy() throws Exception {
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
- testStartRecognitionBusy_2_4();
- }
- }
-
- @Test
- public void testNoRegisterCaptureStateListener() {
- assumeTrue(mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw
- || mSupportConcurrentCapture);
- verify(mCaptureStateNotifier, never()).registerListener(any());
- }
-
@Test
public void testConcurrentCaptureAbort() throws Exception {
- assumeFalse(mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw
- || mSupportConcurrentCapture);
+ assumeFalse(mSupportConcurrentCapture);
verify(mCaptureStateNotifier, atLeast(1)).registerListener(any());
// Register global callback.
@@ -707,8 +523,7 @@
@Test
public void testConcurrentCaptureReject() throws Exception {
- assumeFalse(mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw
- || mSupportConcurrentCapture);
+ assumeFalse(mSupportConcurrentCapture);
verify(mCaptureStateNotifier, atLeast(1)).registerListener(any());
// Register global callback.
@@ -858,28 +673,9 @@
// We just care that it doesn't throw.
}
- private void testGlobalCallback_2_4() throws Exception {
- android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
- (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
-
- ISoundTriggerHal.GlobalCallback canonicalCallback = mock(
- ISoundTriggerHal.GlobalCallback.class);
- mCanonical.registerCallback(canonicalCallback);
-
- ArgumentCaptor<android.hardware.soundtrigger.V2_4.ISoundTriggerHwGlobalCallback>
- callbackCaptor = ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_4.ISoundTriggerHwGlobalCallback.class);
- verify(driver_2_4).registerGlobalCallback(callbackCaptor.capture());
- validateGlobalCallback_2_4(callbackCaptor.getValue(), canonicalCallback);
- }
-
@Test
public void testGlobalCallback() throws Exception {
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
- testGlobalCallback_2_4();
- } else {
- testGlobalCallback_2_0();
- }
+ testGlobalCallback_2_0();
}
@Test
@@ -908,14 +704,6 @@
verify(mHalDriver).interfaceDescriptor();
}
- private void validateGlobalCallback_2_4(
- android.hardware.soundtrigger.V2_4.ISoundTriggerHwGlobalCallback hwCallback,
- ISoundTriggerHal.GlobalCallback canonicalCallback) throws Exception {
- hwCallback.onResourcesAvailable();
- mCanonical.flushCallbacks();
- verify(canonicalCallback).onResourcesAvailable();
- }
-
private void validateCallback_2_0(
android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback hwCallback,
ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception {
@@ -985,48 +773,6 @@
clearInvocations(canonicalCallback);
}
- private void validateCallback_2_4(
- android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback hwCallback,
- ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception {
- {
- final int handle = 85;
- final int status =
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.ABORT;
- ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
- RecognitionEvent.class);
-
- hwCallback.recognitionCallback_2_1(TestUtil.createRecognitionEvent_2_1(handle, status),
- 99);
- mCanonical.flushCallbacks();
- verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture());
- TestUtil.validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.ABORTED);
- }
-
- {
- final int handle = 92;
- final int status =
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS;
- ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
- PhraseRecognitionEvent.class);
-
- hwCallback.phraseRecognitionCallback_2_1(
- TestUtil.createPhraseRecognitionEvent_2_1(handle, status), 99);
- mCanonical.flushCallbacks();
- verify(canonicalCallback).phraseRecognitionCallback(eq(handle), eventCaptor.capture());
- TestUtil.validatePhraseRecognitionEvent(eventCaptor.getValue(),
- RecognitionStatus.SUCCESS);
- }
-
- {
- final int handle = 23;
- hwCallback.modelUnloaded(handle);
- mCanonical.flushCallbacks();
- verify(canonicalCallback).modelUnloaded(handle);
- }
- verifyNoMoreInteractions(canonicalCallback);
- clearInvocations(canonicalCallback);
- }
-
public static class CaptureStateNotifier implements ICaptureStateNotifier {
private final List<Listener> mListeners = new LinkedList<>();
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
index 6ed9dde..39815b7 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
@@ -22,11 +22,12 @@
import android.annotation.NonNull;
import android.hardware.soundtrigger.V2_1.ISoundTriggerHw;
-import android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback;
+import android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback;
import android.media.AudioFormat;
import android.media.MediaFormat;
import android.media.audio.common.AudioChannelLayout;
import android.media.audio.common.AudioConfig;
+import android.media.audio.common.AudioConfigBase;
import android.media.audio.common.AudioFormatDescription;
import android.media.audio.common.AudioFormatType;
import android.media.soundtrigger.AudioCapabilities;
@@ -340,10 +341,11 @@
event.capturePreambleMs = 345;
event.triggerInData = true;
event.audioConfig = new AudioConfig();
- event.audioConfig.sampleRateHz = 456;
- event.audioConfig.channelMask = AudioChannelLayout.layoutMask(
+ event.audioConfig.base = new AudioConfigBase();
+ event.audioConfig.base.sampleRate = 456;
+ event.audioConfig.base.channelMask = AudioChannelLayout.layoutMask(
AudioChannelLayout.LAYOUT_MONO);
- event.audioConfig.format = createAudioFormatMp3();
+ event.audioConfig.base.format = createAudioFormatMp3();
//event.audioConfig.offloadInfo is irrelevant.
event.data = new byte[]{31, 32, 33};
return event;
@@ -367,10 +369,10 @@
assertEquals(234, event.captureDelayMs);
assertEquals(345, event.capturePreambleMs);
assertTrue(event.triggerInData);
- assertEquals(456, event.audioConfig.sampleRateHz);
+ assertEquals(456, event.audioConfig.base.sampleRate);
assertEquals(AudioChannelLayout.layoutMask(AudioChannelLayout.LAYOUT_MONO),
- event.audioConfig.channelMask);
- assertEquals(createAudioFormatMp3(), event.audioConfig.format);
+ event.audioConfig.base.channelMask);
+ assertEquals(createAudioFormatMp3(), event.audioConfig.base.format);
assertArrayEquals(new byte[]{31, 32, 33}, event.data);
}
diff --git a/services/tests/servicestests/src/com/android/server/tare/AgentTest.java b/services/tests/servicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/tare/AgentTest.java
rename to services/tests/servicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java
index 546b84a..b1b53fc 100644
--- a/services/tests/servicestests/src/com/android/server/tare/AgentTest.java
+++ b/services/tests/servicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java
@@ -40,10 +40,10 @@
import java.util.List;
-/** Tests various aspects of the Agent. */
+/** Tests the TrendCalculator in the Agent. */
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class AgentTest {
+public class AgentTrendCalculatorTest {
private MockEconomicPolicy mEconomicPolicy;
@@ -98,7 +98,7 @@
}
@Test
- public void testTrendCalculator_NoOngoingEvents() {
+ public void testNoOngoingEvents() {
TrendCalculator trendCalculator = new TrendCalculator();
mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT, 20);
@@ -128,7 +128,7 @@
}
@Test
- public void testTrendCalculator_NoAffordabilityNotes() {
+ public void testNoAffordabilityNotes() {
TrendCalculator trendCalculator = new TrendCalculator();
OngoingEvent[] events = new OngoingEvent[]{
@@ -165,7 +165,7 @@
}
@Test
- public void testTrendCalculator_NoTrendToThreshold() {
+ public void testNoTrendToThreshold() {
TrendCalculator trendCalculator = new TrendCalculator();
mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING, 10);
@@ -213,7 +213,7 @@
}
@Test
- public void testTrendCalculator_SimpleTrendToThreshold() {
+ public void testSimpleTrendToThreshold() {
TrendCalculator trendCalculator = new TrendCalculator();
mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
@@ -257,7 +257,7 @@
}
@Test
- public void testTrendCalculator_SelectCorrectThreshold() {
+ public void testSelectCorrectThreshold() {
TrendCalculator trendCalculator = new TrendCalculator();
mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START, 15);
@@ -307,7 +307,7 @@
}
@Test
- public void testTrendCalculator_TrendsToBothThresholds() {
+ public void testTrendsToBothThresholds() {
TrendCalculator trendCalculator = new TrendCalculator();
mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
mEconomicPolicy.mEventCosts.put(AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK, 50);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
index 75f8a44..378304d 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -52,6 +52,7 @@
private boolean mIsAvailable = true;
private long mLatency;
+ private int mOffCount;
private int mCapabilities;
private int[] mSupportedEffects;
@@ -93,6 +94,7 @@
@Override
public void off() {
+ mOffCount++;
}
@Override
@@ -308,6 +310,11 @@
return mExternalControlStates;
}
+ /** Returns the number of times the vibrator was turned off. */
+ public int getOffCount() {
+ return mOffCount;
+ }
+
/**
* Return the {@link PrebakedSegment} effect enabled with given id, or {@code null} if
* missing or disabled.
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
index a732bd1..9fb8b38 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
@@ -259,6 +259,21 @@
}
@Test
+ public void reset_turnsOffVibratorAndDisablesExternalControl() {
+ mockVibratorCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+ when(mNativeWrapperMock.on(anyLong(), anyLong())).thenAnswer(args -> args.getArgument(0));
+ VibratorController controller = createController();
+
+ controller.on(100, 1);
+ assertTrue(controller.isVibrating());
+
+ controller.reset();
+ assertFalse(controller.isVibrating());
+ verify(mNativeWrapperMock).setExternalControl(eq(false));
+ verify(mNativeWrapperMock).off();
+ }
+
+ @Test
public void registerVibratorStateListener_callbacksAreTriggered() throws Exception {
when(mNativeWrapperMock.on(anyLong(), anyLong())).thenAnswer(args -> args.getArgument(0));
VibratorController controller = createController();
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 8f9eb22..f9e63d1 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -35,7 +35,6 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
@@ -234,6 +233,19 @@
}
@Test
+ public void createService_resetsVibrators() {
+ mockVibrators(1, 2);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+ mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+
+ createService();
+ assertEquals(1, mVibratorProviders.get(1).getOffCount());
+ assertEquals(1, mVibratorProviders.get(2).getOffCount());
+ assertEquals(Arrays.asList(false), mVibratorProviders.get(1).getExternalControlStates());
+ assertEquals(Arrays.asList(false), mVibratorProviders.get(2).getExternalControlStates());
+ }
+
+ @Test
public void createService_doNotCrashIfUsedBeforeSystemReady() {
mockVibrators(1, 2);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL);
@@ -992,7 +1004,7 @@
mExternalVibratorService.onExternalVibrationStop(externalVibration);
assertEquals(IExternalVibratorService.SCALE_NONE, scale);
- assertEquals(Arrays.asList(true, false),
+ assertEquals(Arrays.asList(false, true, false),
mVibratorProviders.get(1).getExternalControlStates());
}
@@ -1016,9 +1028,10 @@
assertEquals(IExternalVibratorService.SCALE_NONE, firstScale);
assertEquals(IExternalVibratorService.SCALE_NONE, secondScale);
verify(firstController).mute();
- verifyNoMoreInteractions(secondController);
+ verify(secondController, never()).mute();
// Set external control called only once.
- assertEquals(Arrays.asList(true), mVibratorProviders.get(1).getExternalControlStates());
+ assertEquals(Arrays.asList(false, true),
+ mVibratorProviders.get(1).getExternalControlStates());
}
@Test
@@ -1040,7 +1053,8 @@
// Vibration is cancelled.
assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
- assertEquals(Arrays.asList(true), mVibratorProviders.get(1).getExternalControlStates());
+ assertEquals(Arrays.asList(false, true),
+ mVibratorProviders.get(1).getExternalControlStates());
}
private VibrationEffectSegment expectedPrebaked(int effectId) {
diff --git a/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java b/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java
index a67f645..1612321 100644
--- a/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java
+++ b/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java
@@ -18,13 +18,14 @@
import android.annotation.NonNull;
import android.provider.DeviceConfig;
+import android.provider.DeviceConfigInterface;
import android.util.ArrayMap;
import android.util.Pair;
import com.android.internal.util.Preconditions;
-import com.android.server.utils.DeviceConfigInterface;
import java.lang.reflect.Constructor;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
@@ -33,10 +34,17 @@
public class FakeDeviceConfigInterface implements DeviceConfigInterface {
+ private static final String COMPOSITE_DELIMITER = "/";
private Map<String, String> mProperties = new HashMap<>();
private ArrayMap<DeviceConfig.OnPropertiesChangedListener, Pair<String, Executor>> mListeners =
new ArrayMap<>();
+ private static String createCompositeName(@NonNull String namespace, @NonNull String name) {
+ Preconditions.checkNotNull(namespace);
+ Preconditions.checkNotNull(name);
+ return namespace + COMPOSITE_DELIMITER + name;
+ }
+
public void clearProperties() {
mProperties.clear();
}
@@ -91,6 +99,59 @@
}
@Override
+ public DeviceConfig.Properties getProperties(String namespace, String... names) {
+ if (!mProperties.keySet().contains(namespace)) {
+ return new DeviceConfig.Properties(namespace, null);
+ }
+ DeviceConfig.Properties.Builder propertiesBuilder = new DeviceConfig.Properties.Builder(
+ namespace);
+
+ for (String compositeName : mProperties.keySet()) {
+ if (compositeName.split(COMPOSITE_DELIMITER).length != 2) {
+ continue;
+ }
+
+ String existingPropertyNamespace = compositeName.split(COMPOSITE_DELIMITER)[0];
+ String existingPropertyName = compositeName.split(COMPOSITE_DELIMITER)[1];
+
+ if ((names.length == 0 && existingPropertyNamespace.equals(namespace)) || Arrays.asList(
+ names).contains(compositeName)) {
+ propertiesBuilder.setString(existingPropertyName, mProperties.get(compositeName));
+ }
+ }
+
+ return propertiesBuilder.build();
+ }
+
+ @Override
+ public boolean setProperty(String namespace, String name, String value, boolean makeDefault) {
+ putPropertyAndNotify(namespace, name, value);
+ return true;
+ }
+
+ @Override
+ public boolean setProperties(DeviceConfig.Properties properties)
+ throws DeviceConfig.BadConfigException {
+ for (String property : properties.getKeyset()) {
+ String compositeName = createCompositeName(properties.getNamespace(), property);
+ putPropertyAndNotify(properties.getNamespace(), compositeName,
+ properties.getString(property, ""));
+ }
+ return true;
+ }
+
+ @Override
+ public boolean deleteProperty(String namespace, String name) {
+ mProperties.remove(createCompositeName(namespace, name));
+ return true;
+ }
+
+ @Override
+ public void resetToDefaults(int resetMode, String namespace) {
+ clearProperties();
+ }
+
+ @Override
public String getString(String namespace, String name, String defaultValue) {
String value = getProperty(namespace, name);
return value != null ? value : defaultValue;
@@ -166,10 +227,4 @@
DeviceConfig.OnPropertiesChangedListener listener) {
mListeners.remove(listener);
}
-
- private static String createCompositeName(@NonNull String namespace, @NonNull String name) {
- Preconditions.checkNotNull(namespace);
- Preconditions.checkNotNull(name);
- return namespace + "/" + name;
- }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 71c05b5..ea46eab 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -32,6 +32,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
@@ -72,6 +73,7 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Slog;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
@@ -1182,6 +1184,7 @@
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
mService.buzzBeepBlinkLocked(r);
+ verifyDelayedVibrate(mService.getVibratorHelper().createFallbackVibration(false));
// quiet update should stop making noise
mService.buzzBeepBlinkLocked(s);
@@ -1564,6 +1567,32 @@
}
@Test
+ public void testRingtoneInsistentBeep_canUpdate() throws Exception {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Uri.fromParts("a", "b", "c"),
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ ringtoneChannel.enableVibration(true);
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+ mService.addNotification(ringtoneNotification);
+ assertFalse(mService.shouldMuteNotificationLocked(ringtoneNotification));
+ mService.buzzBeepBlinkLocked(ringtoneNotification);
+ verifyBeepLooped();
+ verifyDelayedVibrateLooped();
+ Mockito.reset(mVibrator);
+ Mockito.reset(mRingtonePlayer);
+
+ assertFalse(mService.shouldMuteNotificationLocked(ringtoneNotification));
+ mService.buzzBeepBlinkLocked(ringtoneNotification);
+
+ // beep wasn't reset
+ verifyNeverBeep();
+ verifyNeverVibrate();
+ verify(mRingtonePlayer, never()).stopAsync();
+ verify(mVibrator, never()).cancel();
+ }
+
+ @Test
public void testCannotInterruptRingtoneInsistentBuzz() {
NotificationChannel ringtoneChannel =
new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 7bbf3e6..f660af0 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -516,7 +516,7 @@
when(mAssistants.isAdjustmentAllowed(anyString())).thenReturn(true);
- mWorkerHandler = mService.new WorkerHandler(mTestableLooper.getLooper());
+ mWorkerHandler = spy(mService.new WorkerHandler(mTestableLooper.getLooper()));
mService.init(mWorkerHandler, mRankingHandler, mPackageManager, mPackageManagerClient,
mockLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr,
mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager, mGroupHelper, mAm, mAtm,
@@ -2703,6 +2703,42 @@
}
@Test
+ public void testCrossUserSnooze() {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 10);
+ mService.addNotification(r);
+ NotificationRecord r2 = generateNotificationRecord(mTestNotificationChannel, 0);
+ mService.addNotification(r2);
+
+ mListener = mock(ManagedServices.ManagedServiceInfo.class);
+ mListener.component = new ComponentName(PKG, PKG);
+ when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
+ when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+
+ mService.snoozeNotificationInt(r.getKey(), 1000, null, mListener);
+
+ verify(mWorkerHandler, never()).post(
+ any(NotificationManagerService.SnoozeNotificationRunnable.class));
+ }
+
+ @Test
+ public void testSameUserSnooze() {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 10);
+ mService.addNotification(r);
+ NotificationRecord r2 = generateNotificationRecord(mTestNotificationChannel, 0);
+ mService.addNotification(r2);
+
+ mListener = mock(ManagedServices.ManagedServiceInfo.class);
+ mListener.component = new ComponentName(PKG, PKG);
+ when(mListener.enabledAndUserMatches(anyInt())).thenReturn(true);
+ when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+
+ mService.snoozeNotificationInt(r2.getKey(), 1000, null, mListener);
+
+ verify(mWorkerHandler).post(
+ any(NotificationManagerService.SnoozeNotificationRunnable.class));
+ }
+
+ @Test
public void testSnoozeRunnable_reSnoozeASingleSnoozedNotification() throws Exception {
final NotificationRecord notification = generateNotificationRecord(
mTestNotificationChannel, 1, null, true);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index bf0ed71..66d1577 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -91,6 +91,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
import android.service.notification.ConversationChannelWrapper;
@@ -376,27 +377,19 @@
when(mPm.getPackageUidAsUser(eq(packageName), anyInt())).thenReturn(uid);
}
- private static NotificationChannel createNotificationChannel(String id, String name,
- int importance) {
- NotificationChannel channel = new NotificationChannel(id, name, importance);
- channel.setSound(SOUND_URI, Notification.AUDIO_ATTRIBUTES_DEFAULT);
- return channel;
- }
-
@Test
public void testWriteXml_onlyBackupsTargetUser() throws Exception {
// Setup package notifications.
String package0 = "test.package.user0";
int uid0 = 1001;
setUpPackageWithUid(package0, uid0);
- NotificationChannel channel0 = createNotificationChannel("id0", "name0", IMPORTANCE_HIGH);
+ NotificationChannel channel0 = new NotificationChannel("id0", "name0", IMPORTANCE_HIGH);
assertTrue(mHelper.createNotificationChannel(package0, uid0, channel0, true, false));
String package10 = "test.package.user10";
int uid10 = 1001001;
setUpPackageWithUid(package10, uid10);
- NotificationChannel channel10 = createNotificationChannel("id10", "name10",
- IMPORTANCE_HIGH);
+ NotificationChannel channel10 = new NotificationChannel("id10", "name10", IMPORTANCE_HIGH);
assertTrue(mHelper.createNotificationChannel(package10, uid10, channel10, true, false));
ByteArrayOutputStream baos = writeXmlAndPurge(package10, uid10, true, 10);
@@ -421,7 +414,7 @@
String package0 = "test.package.user0";
int uid0 = 1001;
setUpPackageWithUid(package0, uid0);
- NotificationChannel channel0 = createNotificationChannel("id0", "name0", IMPORTANCE_HIGH);
+ NotificationChannel channel0 = new NotificationChannel("id0", "name0", IMPORTANCE_HIGH);
assertTrue(mHelper.createNotificationChannel(package0, uid0, channel0, true, false));
ByteArrayOutputStream baos = writeXmlAndPurge(package0, uid0, true, 0);
@@ -514,8 +507,9 @@
NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
NotificationChannel channel1 =
- createNotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
- NotificationChannel channel2 = createNotificationChannel("id2", "name2", IMPORTANCE_LOW);
+ new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+ NotificationChannel channel2 =
+ new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
channel2.setDescription("descriptions for all");
channel2.setSound(SOUND_URI, mAudioAttributes);
channel2.enableLights(true);
@@ -524,7 +518,7 @@
channel2.enableVibration(false);
channel2.setGroup(ncg.getId());
channel2.setLightColor(Color.BLUE);
- NotificationChannel channel3 = createNotificationChannel("id3", "NAM3", IMPORTANCE_HIGH);
+ NotificationChannel channel3 = new NotificationChannel("id3", "NAM3", IMPORTANCE_HIGH);
channel3.enableVibration(true);
mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
@@ -631,8 +625,7 @@
}
@Test
- public void testRestoreXml_withNonExistentCanonicalizedSoundUri_ignoreChannel()
- throws Exception {
+ public void testRestoreXml_withNonExistentCanonicalizedSoundUri() throws Exception {
Thread.sleep(3000);
doReturn(null)
.when(mTestIContentProvider).canonicalize(any(), eq(CANONICAL_SOUND_URI));
@@ -650,7 +643,7 @@
NotificationChannel actualChannel = mHelper.getNotificationChannel(
PKG_N_MR1, UID_N_MR1, channel.getId(), false);
- assertNull(actualChannel);
+ assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
}
@@ -659,8 +652,7 @@
* handle its restore properly.
*/
@Test
- public void testRestoreXml_withUncanonicalizedNonLocalSoundUri_ignoreChannel()
- throws Exception {
+ public void testRestoreXml_withUncanonicalizedNonLocalSoundUri() throws Exception {
// Not a local uncanonicalized uri, simulating that it fails to exist locally
doReturn(null)
.when(mTestIContentProvider).canonicalize(any(), eq(SOUND_URI));
@@ -679,7 +671,7 @@
backupWithUncanonicalizedSoundUri.getBytes(), true, UserHandle.USER_SYSTEM);
NotificationChannel actualChannel = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, id, false);
- assertNull(actualChannel);
+ assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
}
@Test
@@ -703,11 +695,11 @@
NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
NotificationChannel channel1 =
- createNotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+ new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
NotificationChannel channel2 =
- createNotificationChannel("id2", "name2", IMPORTANCE_HIGH);
+ new NotificationChannel("id2", "name2", IMPORTANCE_HIGH);
NotificationChannel channel3 =
- createNotificationChannel("id3", "name3", IMPORTANCE_LOW);
+ new NotificationChannel("id3", "name3", IMPORTANCE_LOW);
channel3.setGroup(ncg.getId());
mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
@@ -3062,7 +3054,7 @@
@Test
public void testChannelXml_backupDefaultApp() throws Exception {
NotificationChannel channel1 =
- createNotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+ new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
mHelper.createNotificationChannel(PKG_O, UID_O, channel1, true, false);
@@ -3343,7 +3335,7 @@
mAppOpsManager, mStatsEventBuilderFactory);
mHelper.createNotificationChannel(
- PKG_P, UID_P, createNotificationChannel("id", "id", 2), true, false);
+ PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false);
assertTrue(mHelper.deleteNotificationChannel(PKG_P, UID_P, "id"));
assertFalse(mHelper.deleteNotificationChannel(PKG_P, UID_P, "id"));
}
@@ -3354,7 +3346,7 @@
mAppOpsManager, mStatsEventBuilderFactory);
mHelper.createNotificationChannel(
- PKG_P, UID_P, createNotificationChannel("id", "id", 2), true, false);
+ PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false);
mHelper.deleteNotificationChannel(PKG_P, UID_P, "id");
NotificationChannel nc1 = mHelper.getNotificationChannel(PKG_P, UID_P, "id", true);
assertTrue(DateUtils.isToday(nc1.getDeletedTimeMs()));
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 8ca14bc..b95d56b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -173,7 +173,10 @@
@Override
public void onFixedRotationFinished(int displayId) {}
};
- mAtm.mWindowManager.registerDisplayWindowListener(listener);
+ int[] displayIds = mAtm.mWindowManager.registerDisplayWindowListener(listener);
+ for (int i = 0; i < displayIds.length; i++) {
+ added.add(displayIds[i]);
+ }
// Check that existing displays call added
assertEquals(mRootWindowContainer.getChildCount(), added.size());
assertEquals(0, changed.size());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
index 26a6882..a34586b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
@@ -30,6 +30,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -40,8 +41,10 @@
import android.app.WaitResult;
import android.content.ComponentName;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.ConditionVariable;
+import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.view.Display;
@@ -190,6 +193,24 @@
verify(taskChangeNotifier, never()).notifyActivityDismissingDockedRootTask();
}
+ /** Ensures that the calling package name passed to client complies with package visibility. */
+ @Test
+ public void testFilteredReferred() {
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setLaunchedFromPackage("other.package").setCreateTask(true).build();
+ assertNotNull(activity.launchedFromPackage);
+ try {
+ mSupervisor.realStartActivityLocked(activity, activity.app, false /* andResume */,
+ false /* checkConfig */);
+ } catch (RemoteException ignored) {
+ }
+ verify(activity).getFilteredReferrer(eq(activity.launchedFromPackage));
+
+ activity.deliverNewIntentLocked(ActivityBuilder.DEFAULT_FAKE_UID,
+ new Intent(), null /* intentGrants */, "other.package2");
+ verify(activity).getFilteredReferrer(eq("other.package2"));
+ }
+
/**
* Ensures that notify focus task changes.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 11e3fd4..53bae41 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -26,15 +26,19 @@
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.verify;
import android.os.Binder;
import android.os.IBinder;
@@ -47,8 +51,9 @@
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
import android.view.WindowManager;
+import android.window.ITaskFragmentOrganizer;
+import android.window.TaskFragmentOrganizer;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import org.junit.Before;
@@ -94,11 +99,10 @@
assertEquals(WindowManager.TRANSIT_OLD_UNSET,
AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
- null, null, false));
+ mDisplayContent.mChangingContainers, null, null, false));
}
@Test
- @FlakyTest(bugId = 131005232)
public void testTranslucentOpen() {
final ActivityRecord behind = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
@@ -112,12 +116,11 @@
assertEquals(WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN,
AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
- mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
- null, null, false));
+ mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+ mDisplayContent.mChangingContainers, null, null, false));
}
@Test
- @FlakyTest(bugId = 131005232)
public void testTranslucentClose() {
final ActivityRecord behind = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
@@ -129,11 +132,10 @@
assertEquals(WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE,
AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
- null, null, false));
+ mDisplayContent.mChangingContainers, null, null, false));
}
@Test
- @FlakyTest(bugId = 131005232)
public void testChangeIsNotOverwritten() {
final ActivityRecord behind = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
@@ -144,14 +146,14 @@
mDisplayContent.prepareAppTransition(TRANSIT_CHANGE);
mDisplayContent.mOpeningApps.add(behind);
mDisplayContent.mOpeningApps.add(translucentOpening);
+ mDisplayContent.mChangingContainers.add(translucentOpening.getTask());
assertEquals(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE,
AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
- null, null, false));
+ mDisplayContent.mChangingContainers, null, null, false));
}
@Test
- @FlakyTest(bugId = 131005232)
public void testTransitWithinTask() {
final ActivityRecord opening = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
@@ -198,7 +200,7 @@
assertEquals(WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN,
AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
- appWindowClosing, null, false));
+ mDisplayContent.mChangingContainers, appWindowClosing, null, false));
}
@Test
@@ -229,7 +231,7 @@
assertEquals(WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN,
AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
- appWindowClosing, null, false));
+ mDisplayContent.mChangingContainers, appWindowClosing, null, false));
}
@Test
@@ -741,4 +743,38 @@
mAppTransitionController.getRemoteAnimationOverride(
activity, TRANSIT_OLD_ACTIVITY_OPEN, new ArraySet<Integer>()));
}
+
+ @Test
+ public void testGetRemoteAnimationOverrideTaskFragmentOrganizer() {
+ // TaskFragmentOrganizer registers remote animation.
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final ITaskFragmentOrganizer iOrganizer =
+ ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
+ final RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
+ final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
+ new TestRemoteAnimationRunner(), 10, 1);
+ definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, adapter);
+ mAtm.mTaskFragmentOrganizerController.registerOrganizer(iOrganizer);
+ mAtm.mTaskFragmentOrganizerController.registerRemoteAnimations(iOrganizer, definition);
+
+ // Create a TaskFragment with embedded activity.
+ final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+ .setParentTask(createTask(mDisplayContent))
+ .createActivityCount(1)
+ .setOrganizer(organizer)
+ .build();
+ final ActivityRecord activity = taskFragment.getTopMostActivity();
+ activity.allDrawn = true;
+ spyOn(mDisplayContent.mAppTransition);
+
+ // Prepare a transition for TaskFragment.
+ mDisplayContent.mAppTransition.prepareAppTransition(TRANSIT_CHANGE, 0);
+ mDisplayContent.mOpeningApps.add(activity);
+ mDisplayContent.mChangingContainers.add(taskFragment);
+ mDisplayContent.mAppTransitionController.handleAppTransitionReady();
+
+ // Check if the transition has been overridden.
+ verify(mDisplayContent.mAppTransition)
+ .overridePendingAppTransitionRemote(adapter, false /* sync */);
+ }
}
\ No newline at end of file
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 67aac13..a0a3ce7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -18,11 +18,14 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_UNSET;
@@ -32,6 +35,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -39,6 +43,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
@@ -85,8 +90,8 @@
assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
AppTransitionController.getTransitCompatType(mDc.mAppTransition,
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
- null /* wallpaperTarget */, null /* oldWallpaper */,
- false /*skipAppTransitionAnimation*/));
+ mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+ null /* oldWallpaper */, false /*skipAppTransitionAnimation*/));
}
@Test
@@ -100,8 +105,8 @@
assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
AppTransitionController.getTransitCompatType(mDc.mAppTransition,
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
- null /* wallpaperTarget */, null /* oldWallpaper */,
- false /*skipAppTransitionAnimation*/));
+ mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+ null /* oldWallpaper */, false /*skipAppTransitionAnimation*/));
}
@Test
@@ -115,8 +120,8 @@
assertEquals(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE,
AppTransitionController.getTransitCompatType(mDc.mAppTransition,
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
- null /* wallpaperTarget */, null /* oldWallpaper */,
- false /*skipAppTransitionAnimation*/));
+ mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+ null /* oldWallpaper */, false /*skipAppTransitionAnimation*/));
}
@Test
@@ -130,8 +135,8 @@
assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
AppTransitionController.getTransitCompatType(mDc.mAppTransition,
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
- null /* wallpaperTarget */, null /* oldWallpaper */,
- false /*skipAppTransitionAnimation*/));
+ mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+ null /* oldWallpaper */, false /*skipAppTransitionAnimation*/));
}
@Test
@@ -145,8 +150,44 @@
assertEquals(TRANSIT_OLD_UNSET,
AppTransitionController.getTransitCompatType(mDc.mAppTransition,
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
- null /* wallpaperTarget */, null /* oldWallpaper */,
- true /*skipAppTransitionAnimation*/));
+ mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+ null /* oldWallpaper */, true /*skipAppTransitionAnimation*/));
+ }
+
+ @Test
+ public void testTaskChangeWindowingMode() {
+ final ActivityRecord activity = createActivityRecord(mDc);
+
+ mDc.prepareAppTransition(TRANSIT_OPEN);
+ mDc.prepareAppTransition(TRANSIT_CHANGE);
+ mDc.mOpeningApps.add(activity); // Make sure TRANSIT_CHANGE has the priority
+ mDc.mChangingContainers.add(activity.getTask());
+
+ assertEquals(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE,
+ AppTransitionController.getTransitCompatType(mDc.mAppTransition,
+ mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+ mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+ null /* oldWallpaper */, false /*skipAppTransitionAnimation*/));
+ }
+
+ @Test
+ public void testTaskFragmentChange() {
+ final ActivityRecord activity = createActivityRecord(mDc);
+ final TaskFragment taskFragment = new TaskFragment(mAtm, new Binder(),
+ true /* createdByOrganizer */, true /* isEmbedded */);
+ activity.getTask().addChild(taskFragment, POSITION_TOP);
+ activity.reparent(taskFragment, POSITION_TOP);
+
+ mDc.prepareAppTransition(TRANSIT_OPEN);
+ mDc.prepareAppTransition(TRANSIT_CHANGE);
+ mDc.mOpeningApps.add(activity); // Make sure TRANSIT_CHANGE has the priority
+ mDc.mChangingContainers.add(taskFragment);
+
+ assertEquals(TRANSIT_OLD_TASK_FRAGMENT_CHANGE,
+ AppTransitionController.getTransitCompatType(mDc.mAppTransition,
+ mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+ mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+ null /* oldWallpaper */, false /*skipAppTransitionAnimation*/));
}
@Test
@@ -159,9 +200,9 @@
mDisplayContent.mOpeningApps.add(activity);
assertEquals(TRANSIT_OLD_TASK_FRAGMENT_OPEN,
AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
- mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
- null /* wallpaperTarget */, null /* oldWallpaper */,
- false /* skipAppTransitionAnimation */));
+ mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+ mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+ null /* oldWallpaper */, false /* skipAppTransitionAnimation */));
}
@Test
@@ -175,8 +216,8 @@
assertEquals(TRANSIT_OLD_TASK_FRAGMENT_OPEN,
AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
- null /* wallpaperTarget */, null /* oldWallpaper */,
- false /* skipAppTransitionAnimation */));
+ mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+ null /* oldWallpaper */, false /* skipAppTransitionAnimation */));
}
@Test
@@ -190,8 +231,8 @@
assertEquals(TRANSIT_OLD_TASK_FRAGMENT_CLOSE,
AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
- null /* wallpaperTarget */, null /* oldWallpaper */,
- false /* skipAppTransitionAnimation */));
+ mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+ null /* oldWallpaper */, false /* skipAppTransitionAnimation */));
}
@Test
@@ -205,8 +246,8 @@
assertEquals(TRANSIT_OLD_TASK_FRAGMENT_CLOSE,
AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
- null /* wallpaperTarget */, null /* oldWallpaper */,
- false /* skipAppTransitionAnimation */));
+ mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+ null /* oldWallpaper */, false /* skipAppTransitionAnimation */));
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 5c79f5c..98c1eabe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -21,6 +21,7 @@
import static com.android.server.wm.testing.Assert.assertThrows;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@@ -36,6 +37,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.view.RemoteAnimationDefinition;
import android.view.SurfaceControl;
import android.window.ITaskFragmentOrganizer;
import android.window.TaskFragmentCreationParams;
@@ -70,6 +72,7 @@
private IBinder mFragmentToken;
private WindowContainerTransaction mTransaction;
private WindowContainerToken mFragmentWindowToken;
+ private RemoteAnimationDefinition mDefinition;
@Before
public void setup() {
@@ -83,6 +86,7 @@
new TaskFragment(mAtm, mFragmentToken, true /* createdByOrganizer */);
mTransaction = new WindowContainerTransaction();
mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken();
+ mDefinition = new RemoteAnimationDefinition();
spyOn(mController);
spyOn(mOrganizer);
@@ -208,6 +212,18 @@
}
@Test
+ public void testRegisterRemoteAnimations() {
+ mController.registerOrganizer(mIOrganizer);
+ mController.registerRemoteAnimations(mIOrganizer, mDefinition);
+
+ assertEquals(mDefinition, mController.getRemoteAnimationDefinition(mIOrganizer));
+
+ mController.unregisterRemoteAnimations(mIOrganizer);
+
+ assertNull(mController.getRemoteAnimationDefinition(mIOrganizer));
+ }
+
+ @Test
public void testWindowContainerTransaction_setTaskFragmentOrganizer() {
mOrganizer.applyTransaction(mTransaction);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index e7ef6ae..454ecd7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -67,6 +67,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.IApplicationThread;
@@ -100,6 +101,7 @@
import android.view.WindowManager.DisplayImePolicy;
import android.window.ITransitionPlayer;
import android.window.StartingWindowInfo;
+import android.window.TaskFragmentOrganizer;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
@@ -878,6 +880,7 @@
private int mConfigChanges;
private int mLaunchedFromPid;
private int mLaunchedFromUid;
+ private String mLaunchedFromPackage;
private WindowProcessController mWpc;
private Bundle mIntentExtras;
private boolean mOnTop = false;
@@ -988,6 +991,11 @@
return this;
}
+ ActivityBuilder setLaunchedFromPackage(String packageName) {
+ mLaunchedFromPackage = packageName;
+ return this;
+ }
+
ActivityBuilder setUseProcess(WindowProcessController wpc) {
mWpc = wpc;
return this;
@@ -1077,6 +1085,7 @@
final ActivityRecord activity = new ActivityRecord.Builder(mService)
.setLaunchedFromPid(mLaunchedFromPid)
.setLaunchedFromUid(mLaunchedFromUid)
+ .setLaunchedFromPackage(mLaunchedFromPackage)
.setIntent(intent)
.setActivityInfo(aInfo)
.setActivityOptions(options)
@@ -1128,6 +1137,8 @@
private boolean mCreateParentTask;
private boolean mCreateEmbeddedTask;
private int mCreateActivityCount = 0;
+ @Nullable
+ private TaskFragmentOrganizer mOrganizer;
TaskFragmentBuilder(ActivityTaskManagerService service) {
mAtm = service;
@@ -1154,11 +1165,16 @@
return this;
}
+ TaskFragmentBuilder setOrganizer(@Nullable TaskFragmentOrganizer organizer) {
+ mOrganizer = organizer;
+ return this;
+ }
+
TaskFragment build() {
SystemServicesTestRule.checkHoldsLock(mAtm.mGlobalLock);
final TaskFragment taskFragment = new TaskFragment(mAtm, null /* fragmentToken */,
- false /* createdByOrganizer */);
+ mOrganizer != null);
if (mParentTask == null && mCreateParentTask) {
mParentTask = new TaskBuilder(mAtm.mTaskSupervisor).build();
}
@@ -1176,6 +1192,10 @@
taskFragment.addChild(activity);
mCreateActivityCount--;
}
+ if (mOrganizer != null) {
+ taskFragment.setTaskFragmentOrganizer(
+ mOrganizer.getOrganizerToken(), 10000 /* pid */);
+ }
return taskFragment;
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index d048f1842..589f913 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -24,6 +24,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.policy.WindowManagerPolicy.TRANSIT_EXIT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -44,6 +45,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mockito;
import java.util.function.BiFunction;
@@ -126,7 +128,7 @@
final WindowState window1 = createWindow(null, TYPE_TOAST, token, "window1");
final WindowState window2 = createWindow(null, TYPE_TOAST, token, "window2");
- mDisplayContent.removeWindowToken(token.token);
+ mDisplayContent.removeWindowToken(token.token, true /* animateExit */);
// Verify that the token is no longer mapped on the display
assertNull(mDisplayContent.getWindowToken(token.token));
// Verify that the token is still attached to its parent
@@ -261,4 +263,29 @@
assertNotNull(app.getFrozenInsetsState());
assertNull(mDisplayContent.mInputMethodWindow.getFrozenInsetsState());
}
+
+ @Test
+ public void testRemoveWindowToken_noAnimateExitWhenSet() {
+ final TestWindowToken token = createTestWindowToken(0, mDisplayContent);
+ final WindowState win = createWindow(null, TYPE_APPLICATION, token, "win");
+ makeWindowVisible(win);
+ assertTrue(win.isOnScreen());
+ spyOn(win);
+ spyOn(win.mWinAnimator);
+ spyOn(win.mToken);
+
+ // Invoking removeWindowToken with setting no window exit animation and not remove window
+ // immediately. verify the window will hide without applying exit animation.
+ mWm.removeWindowToken(win.mToken.token, false /* removeWindows */, false /* animateExit */,
+ mDisplayContent.mDisplayId);
+ verify(win).onSetAppExiting(Mockito.eq(false) /* animateExit */);
+ verify(win).hide(false /* doAnimation */, false /* requestAnim */);
+ assertFalse(win.isOnScreen());
+ verify(win.mWinAnimator, Mockito.never()).applyAnimationLocked(TRANSIT_EXIT, false);
+ assertTrue(win.mToken.hasChild());
+
+ // Even though the window is being removed afterwards, it won't apply exit animation.
+ win.removeIfPossible();
+ verify(win.mWinAnimator, Mockito.never()).applyAnimationLocked(TRANSIT_EXIT, false);
+ }
}
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index 02bd001..bc0a146 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -23,6 +23,8 @@
import android.os.Bundle;
import android.util.ArrayMap;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -115,6 +117,7 @@
public static final int SDK_VERSION_R = 30;
// A Map allows us to track each Call by its Telecom-specified call ID
+ @GuardedBy("mLock")
private final Map<String, Call> mCallByTelecomCallId = new ArrayMap<>();
// A List allows us to keep the Calls in a stable iteration order so that casually developed
@@ -154,7 +157,7 @@
return;
}
- Call call = mCallByTelecomCallId.get(parcelableCall.getId());
+ Call call = getCallById(parcelableCall.getId());
if (call == null) {
call = new Call(this, parcelableCall.getId(), mInCallAdapter,
parcelableCall.getState(), mCallingPackage, mTargetSdkVersion);
@@ -191,14 +194,14 @@
if (mTargetSdkVersion < SDK_VERSION_R
&& parcelableCall.getState() == Call.STATE_AUDIO_PROCESSING) {
Log.i(this, "removing audio processing call during update for sdk compatibility");
- Call call = mCallByTelecomCallId.get(parcelableCall.getId());
+ Call call = getCallById(parcelableCall.getId());
if (call != null) {
internalRemoveCall(call);
}
return;
}
- Call call = mCallByTelecomCallId.get(parcelableCall.getId());
+ Call call = getCallById(parcelableCall.getId());
if (call != null) {
checkCallTree(parcelableCall);
call.internalUpdate(parcelableCall, mCallByTelecomCallId);
@@ -215,8 +218,14 @@
}
}
+ Call getCallById(String callId) {
+ synchronized (mLock) {
+ return mCallByTelecomCallId.get(callId);
+ }
+ }
+
final void internalSetPostDialWait(String telecomId, String remaining) {
- Call call = mCallByTelecomCallId.get(telecomId);
+ Call call = getCallById(telecomId);
if (call != null) {
call.internalSetPostDialWait(remaining);
}
@@ -230,7 +239,7 @@
}
final Call internalGetCallByTelecomId(String telecomId) {
- return mCallByTelecomCallId.get(telecomId);
+ return getCallById(telecomId);
}
final void internalBringToForeground(boolean showDialpad) {
@@ -249,35 +258,35 @@
}
final void internalOnConnectionEvent(String telecomId, String event, Bundle extras) {
- Call call = mCallByTelecomCallId.get(telecomId);
+ Call call = getCallById(telecomId);
if (call != null) {
call.internalOnConnectionEvent(event, extras);
}
}
final void internalOnRttUpgradeRequest(String callId, int requestId) {
- Call call = mCallByTelecomCallId.get(callId);
+ Call call = getCallById(callId);
if (call != null) {
call.internalOnRttUpgradeRequest(requestId);
}
}
final void internalOnRttInitiationFailure(String callId, int reason) {
- Call call = mCallByTelecomCallId.get(callId);
+ Call call = getCallById(callId);
if (call != null) {
call.internalOnRttInitiationFailure(reason);
}
}
final void internalOnHandoverFailed(String callId, int error) {
- Call call = mCallByTelecomCallId.get(callId);
+ Call call = getCallById(callId);
if (call != null) {
call.internalOnHandoverFailed(error);
}
}
final void internalOnHandoverComplete(String callId) {
- Call call = mCallByTelecomCallId.get(callId);
+ Call call = getCallById(callId);
if (call != null) {
call.internalOnHandoverComplete();
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 114c90b..3b44a34 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1847,8 +1847,9 @@
* @param subscriptionType the {@link #SUBSCRIPTION_TYPE}
* @hide
*/
- public void addSubscriptionInfoRecord(String uniqueId, String displayName, int slotIndex,
- int subscriptionType) {
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public void addSubscriptionInfoRecord(@NonNull String uniqueId, @Nullable String displayName,
+ int slotIndex, int subscriptionType) {
if (VDBG) {
logd("[addSubscriptionInfoRecord]+ uniqueId:" + uniqueId
+ ", displayName:" + displayName + ", slotIndex:" + slotIndex
@@ -1883,7 +1884,8 @@
* @param subscriptionType the {@link #SUBSCRIPTION_TYPE}
* @hide
*/
- public void removeSubscriptionInfoRecord(String uniqueId, int subscriptionType) {
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public void removeSubscriptionInfoRecord(@NonNull String uniqueId, int subscriptionType) {
if (VDBG) {
logd("[removeSubscriptionInfoRecord]+ uniqueId:" + uniqueId
+ ", subscriptionType: " + subscriptionType);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 412c02c..39cb04a 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3483,7 +3483,8 @@
*/
private int getLogicalSlotIndex(int physicalSlotIndex) {
UiccSlotInfo[] slotInfos = getUiccSlotsInfo();
- if (slotInfos != null && physicalSlotIndex >= 0 && physicalSlotIndex < slotInfos.length) {
+ if (slotInfos != null && physicalSlotIndex >= 0 && physicalSlotIndex < slotInfos.length
+ && slotInfos[physicalSlotIndex] != null) {
return slotInfos[physicalSlotIndex].getLogicalSlotIdx();
}
diff --git a/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl
index 1a1c7f8..43273a4 100755
--- a/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl
+++ b/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl
@@ -25,6 +25,7 @@
{
void onError(int errorCode, String message);
+ @SuppressWarnings(value={"untyped-collection"})
void onAvailableSaisUpdated(in List currentSai, in List availableSais);
void onServiceInterfaceAvailable(String interfaceName, int index);
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
index 44cc24a..6e139ac 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
@@ -29,9 +29,11 @@
void stopGroupCall(int subId, long tmgi);
+ @SuppressWarnings(value={"untyped-collection"})
void updateGroupCall(int subscriptionId, long tmgi, in List saiList,
in List frequencyList);
+ @SuppressWarnings(value={"untyped-collection"})
int startGroupCall(int subscriptionId, long tmgi, in List saiList,
in List frequencyList, IGroupCallCallback callback);
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 4fc7776..4f3b4cb 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1975,6 +1975,7 @@
/**
* Return the emergency number list from all the active subscriptions.
*/
+ @SuppressWarnings(value={"untyped-collection"})
Map getEmergencyNumberList(String callingPackage, String callingFeatureId);
/**
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 08c9e5d..2cc1943 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -36,30 +36,35 @@
}
}
+/**
+ * If [allStates] is true, checks if the stack space of all displays is fully covered
+ * by any visible layer, during the whole transitions
+ *
+ * Otherwise, checks if the stack space of all displays is fully covered
+ * by any visible layer, at the start and end of the transition
+ *
+ * @param allStates if all states should be checked, othersie, just initial and final
+ */
@JvmOverloads
-fun FlickerTestParameter.entireScreenCovered(
- beginRotation: Int,
- endRotation: Int = beginRotation,
- allStates: Boolean = true
-) {
- val startingBounds = WindowUtils.getDisplayBounds(beginRotation)
- val endingBounds = WindowUtils.getDisplayBounds(endRotation)
+fun FlickerTestParameter.entireScreenCovered(allStates: Boolean = true) {
if (allStates) {
assertLayers {
- if (startingBounds == endingBounds) {
- this.coversAtLeast(startingBounds)
- } else {
- this.coversAtLeast(startingBounds)
- .then()
- .coversAtLeast(endingBounds)
+ this.invoke("entireScreenCovered") { entry ->
+ entry.entry.displays.forEach { display ->
+ entry.visibleRegion().coversAtLeast(display.layerStackSpace)
+ }
}
}
} else {
assertLayersStart {
- this.visibleRegion().coversAtLeast(startingBounds)
+ this.entry.displays.forEach { display ->
+ this.visibleRegion().coversAtLeast(display.layerStackSpace)
+ }
}
assertLayersEnd {
- this.visibleRegion().coversAtLeast(endingBounds)
+ this.entry.displays.forEach { display ->
+ this.visibleRegion().coversAtLeast(display.layerStackSpace)
+ }
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index 90c851d..22bf57a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -23,7 +23,7 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import org.junit.FixMethodOrder
import org.junit.Test
@@ -39,7 +39,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
+@Group4
class CloseAppBackButtonTest(testSpec: FlickerTestParameter) : CloseAppTransition(testSpec) {
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index e8391ed..a1c0e01 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -22,7 +22,7 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import org.junit.FixMethodOrder
import org.junit.Test
@@ -38,7 +38,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
+@Group4
class CloseAppHomeButtonTest(testSpec: FlickerTestParameter) : CloseAppTransition(testSpec) {
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index f9e6bab..3c610af 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -123,9 +123,7 @@
@Presubmit
@Test
- open fun entireScreenCovered() {
- testSpec.entireScreenCovered(testSpec.config.startRotation, Surface.ROTATION_0)
- }
+ open fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index 384d8e8..d17e77d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -119,7 +119,7 @@
@Presubmit
@Test
- fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
+ fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index ade215b..6f0f55a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -116,8 +116,7 @@
@Presubmit
@Test
- fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
- Surface.ROTATION_0)
+ fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index cdfcff3..6cdf32c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -115,7 +115,7 @@
@Presubmit
@Test
- fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
+ fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 05fc267..8aaf925 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -125,8 +125,7 @@
@Presubmit
@Test
- fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
- Surface.ROTATION_0)
+ fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index f35a180..665204b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -108,7 +108,7 @@
@Presubmit
@Test
- fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
+ fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index 3bcf793..fe1b1cd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -143,8 +143,7 @@
@Presubmit
@Test
// During testing the launcher is always in portrait mode
- fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
- testSpec.config.endRotation)
+ fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
index f9dd88e..1c32e32 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
@@ -28,7 +28,7 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group2
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
@@ -52,7 +52,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group2
+@Group4
@Presubmit
class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
index a21eae8..3e35e21 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
@@ -27,8 +27,7 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.repetitions
-import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.TwoActivitiesAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
@@ -46,7 +45,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
+@Group4
class ActivitiesTransitionTest(val testSpec: FlickerTestParameter) {
val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp: TwoActivitiesAppHelper = TwoActivitiesAppHelper(instrumentation)
@@ -90,9 +89,7 @@
@Presubmit
@Test
- fun entireScreenCovered() {
- testSpec.entireScreenCovered(testSpec.config.startRotation)
- }
+ fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index e6dc852..be919cd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -16,6 +16,8 @@
package com.android.server.wm.flicker.launch
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -33,8 +35,21 @@
import org.junit.runners.Parameterized
/**
- * Test cold launch app from launcher.
+ * Test cold launching an app from launcher
+ *
* To run this test: `atest FlickerTests:OpenAppColdTest`
+ *
+ * Actions:
+ * Make sure no apps are running on the device
+ * Launch an app [testApp] and wait animation to complete
+ *
+ * Notes:
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [OpenAppTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@@ -42,6 +57,9 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
class OpenAppColdTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
+ /**
+ * Defines the transition used to run the test
+ */
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
super.transition(this, it)
@@ -62,25 +80,46 @@
}
}
+ /** {@inheritDoc} */
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() {
super.navBarLayerRotatesAndScales()
}
- @FlakyTest(bugId = 192721431)
+ /** {@inheritDoc} */
+ @Postsubmit
@Test
- override fun appLayerReplacesLauncher() {
- super.appLayerReplacesLauncher()
- }
+ override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
- @FlakyTest(bugId = 192721431)
+ /** {@inheritDoc} */
+ @Postsubmit
@Test
- override fun appWindowReplacesLauncherAsTopWindow() {
+ override fun appWindowReplacesLauncherAsTopWindow() =
super.appWindowReplacesLauncherAsTopWindow()
- }
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun launcherWindowBecomesInvisible() = super.launcherWindowBecomesInvisible()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 7833e2f..3678f33 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.launch
+import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -33,8 +34,23 @@
import org.junit.runners.Parameterized
/**
- * Launch an app from the recents app view (the overview)
+ * Test launching an app from the recents app view (the overview)
+ *
* To run this test: `atest FlickerTests:OpenAppFromOverviewTest`
+ *
+ * Actions:
+ * Launch [testApp]
+ * Press recents
+ * Relaunch an app [testApp] by selecting it in the overview screen, and wait animation to
+ * complete (only this action is traced)
+ *
+ * Notes:
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [OpenAppTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@@ -42,6 +58,9 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
class OpenAppFromOverviewTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
+ /**
+ * Defines the transition used to run the test
+ */
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
super.transition(this, it)
@@ -63,13 +82,38 @@
}
}
+ /** {@inheritDoc} */
@FlakyTest
@Test
- override fun navBarLayerRotatesAndScales() {
- super.navBarLayerRotatesAndScales()
- }
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun launcherWindowBecomesInvisible() = super.launcherWindowBecomesInvisible()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index 0adcca2..b717612 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.launch
+import android.content.ComponentName
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
@@ -29,6 +30,7 @@
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.google.common.truth.Truth
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -36,8 +38,21 @@
import org.junit.runners.Parameterized
/**
- * Launch an app while the phone is locked
+ * Test launching an app while the device is locked
+ *
* To run this test: `atest FlickerTests:OpenAppNonResizeableTest`
+ *
+ * Actions:
+ * Lock the device.
+ * Launch an app on top of the lock screen [testApp] and wait animation to complete
+ *
+ * Notes:
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [OpenAppTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@@ -46,14 +61,20 @@
@Group1
class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
override val testApp = NonResizeableAppHelper(instrumentation)
+ private val colorFadComponent = ComponentName("", "ColorFade BLAST#")
+ /**
+ * Defines the transition used to run the test
+ */
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = {
- super.transition(this, it)
+ get() = { args ->
+ super.transition(this, args)
setup {
eachRun {
device.sleep()
- wmHelper.waitForAppTransitionIdle()
+ wmHelper.waitFor("noAppWindowsOnTop") {
+ it.wmState.topVisibleAppWindow.isEmpty()
+ }
}
}
teardown {
@@ -67,17 +88,29 @@
}
}
+ /**
+ * Checks that the nav bar layer starts visible, becomes invisible during unlocking animation
+ * and becomes visible at the end
+ */
@Presubmit
@Test
- override fun navBarLayerIsVisible() {
- testSpec.assertLayersEnd {
- isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ fun navBarLayerVisibilityChanges() {
+ testSpec.assertLayers {
+ this.isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ .then()
+ .isInvisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ .then()
+ .isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
}
}
+ /**
+ * Checks that the app layer doesn't exist at the start of the transition, that it is
+ * created (invisible) and becomes visible during the transition
+ */
@Presubmit
@Test
- fun nonResizableAppLayerBecomesVisible() {
+ fun appLayerBecomesVisible() {
testSpec.assertLayers {
this.notContains(testApp.component)
.then()
@@ -87,9 +120,16 @@
}
}
+ /**
+ * Checks that the app window doesn't exist at the start of the transition, that it is
+ * created (invisible - optional) and becomes visible during the transition
+ *
+ * The `isAppWindowInvisible` step is optional because we log once per frame, upon logging,
+ * the window may be visible or not depending on what was processed until that moment.
+ */
@Presubmit
@Test
- fun nonResizableAppWindowBecomesVisible() {
+ fun appWindowBecomesVisible() {
testSpec.assertWm {
this.notContains(testApp.component)
.then()
@@ -100,62 +140,95 @@
}
}
+ /**
+ * Checks if [testApp] is visible at the end of the transition
+ */
@Presubmit
@Test
- fun nonResizableAppWindowBecomesVisibleAtEnd() {
+ fun appWindowBecomesVisibleAtEnd() {
testSpec.assertWmEnd {
this.isVisible(testApp.component)
}
}
- @FlakyTest
- @Test
- override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
-
+ /**
+ * Checks that the nav bar starts the transition visible, then becomes invisible during
+ * then unlocking animation and becomes visible at the end of the transition
+ */
@Postsubmit
@Test
- override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+ fun navBarWindowsVisibilityChanges() {
+ testSpec.assertWm {
+ this.isAboveAppWindowVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ .then()
+ .isNonAppWindowInvisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ .then()
+ .isAboveAppWindowVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ }
+ }
- @Postsubmit
- @Test
- override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible()
-
- @Postsubmit
- @Test
- override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
-
- @Postsubmit
- @Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
-
+ /** {@inheritDoc} */
@FlakyTest
@Test
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ /** {@inheritDoc} */
@FlakyTest
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
super.visibleLayersShownMoreThanOneConsecutiveEntry()
- @FlakyTest
+ /** {@inheritDoc} */
+ @Postsubmit
@Test
override fun entireScreenCovered() = super.entireScreenCovered()
+ /**
+ * Checks that the focus changes from the launcher to [testApp]
+ */
@FlakyTest
@Test
override fun focusChanges() = super.focusChanges()
- @FlakyTest
+ /**
+ * Checks that the screen is locked at the start of the transition ([colorFadComponent])
+ * layer is visible
+ */
+ @Postsubmit
@Test
- override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
+ fun screenLockedStart() {
+ testSpec.assertLayersStart {
+ isVisible(colorFadComponent)
+ }
+ }
- @FlakyTest
+ /**
+ * This test checks if the launcher is visible at the start and the app at the end,
+ * it cannot use the regular assertion (check over time), because on lock screen neither
+ * the app not the launcher are visible, and there is no top visible window.
+ */
+ @Postsubmit
@Test
- override fun appWindowReplacesLauncherAsTopWindow() =
- super.appWindowReplacesLauncherAsTopWindow()
+ override fun appWindowReplacesLauncherAsTopWindow() {
+ testSpec.assertWm {
+ this.invoke("noAppWindowsOnTop") {
+ Truth.assertWithMessage("Should not have any app window on top " +
+ "when the screen is locked")
+ .that(it.wmState.topVisibleAppWindow)
+ .isEmpty()
+ }.then()
+ .isAppWindowOnTop(testApp.component)
+ }
+ }
companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index 1435120..14d17f8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -40,12 +40,16 @@
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SNAPSHOT_COMPONENT
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SPLASH_SCREEN_COMPONENT
import org.junit.Test
abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
+ /**
+ * Defines the transition used to run the test
+ */
protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
withTestName { testSpec.name }
repeat { testSpec.config.repetitions }
@@ -62,6 +66,10 @@
}
}
+ /**
+ * Entry point for the test runner. It will use this method to initialize and cache
+ * flicker executions
+ */
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
@@ -69,42 +77,60 @@
}
}
- @Presubmit
- @Test
+ /**
+ * Checks that the navigation bar window is visible during the whole transition
+ */
open fun navBarWindowIsVisible() {
testSpec.navBarWindowIsVisible()
}
- @Presubmit
- @Test
+ /**
+ * Checks that the navigation bar layer is visible during the whole transition
+ */
open fun navBarLayerIsVisible() {
testSpec.navBarLayerIsVisible()
}
+ /**
+ * Checks the position of the navigation bar at the start and end of the transition
+ */
@Presubmit
@Test
open fun navBarLayerRotatesAndScales() {
testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation)
}
+ /**
+ * Checks that the status bar window is visible during the whole transition
+ */
@Presubmit
@Test
open fun statusBarWindowIsVisible() {
testSpec.statusBarWindowIsVisible()
}
+ /**
+ * Checks that the status bar layer is visible during the whole transition
+ */
@Presubmit
@Test
open fun statusBarLayerIsVisible() {
testSpec.statusBarLayerIsVisible()
}
+ /**
+ * Checks the position of the status bar at the start and end of the transition
+ */
@Presubmit
@Test
open fun statusBarLayerRotatesScales() {
testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation)
}
+ /**
+ * Checks that all windows that are visible on the trace, are visible for at least 2
+ * consecutive entries.
+ */
@Presubmit
@Test
open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
@@ -113,6 +139,10 @@
}
}
+ /**
+ * Checks that all layers that are visible on the trace, are visible for at least 2
+ * consecutive entries.
+ */
@Presubmit
@Test
open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
@@ -121,13 +151,16 @@
}
}
+ /**
+ * Checks that all parts of the screen are covered during the transition
+ */
@Presubmit
@Test
- // During testing the launcher is always in portrait mode
- open fun entireScreenCovered() {
- testSpec.entireScreenCovered(Surface.ROTATION_0, testSpec.config.endRotation)
- }
+ open fun entireScreenCovered() = testSpec.entireScreenCovered()
+ /**
+ * Checks that the focus changes from the launcher to [testApp]
+ */
@Presubmit
@Test
open fun focusChanges() {
@@ -136,12 +169,19 @@
}
}
- @Presubmit
- @Test
+ /**
+ * Checks that [LAUNCHER_COMPONENT] layer is visible at the start of the transition, and
+ * is replaced by [testApp], which remains visible until the end
+ */
open fun appLayerReplacesLauncher() {
testSpec.replacesLayer(LAUNCHER_COMPONENT, testApp.component)
}
+ /**
+ * Checks that [LAUNCHER_COMPONENT] window is visible at the start of the transition, and
+ * is replaced by a snapshot or splash screen (optional), and finally, is replaced by
+ * [testApp], which remains visible until the end
+ */
@Presubmit
@Test
open fun appWindowReplacesLauncherAsTopWindow() {
@@ -150,12 +190,16 @@
.then()
.isAppWindowOnTop(SNAPSHOT_COMPONENT, isOptional = true)
.then()
+ .isAppWindowOnTop(SPLASH_SCREEN_COMPONENT, isOptional = true)
+ .then()
.isAppWindowOnTop(testApp.component)
}
}
- @Presubmit
- @Test
+ /**
+ * Checks that [LAUNCHER_COMPONENT] window is visible at the start, and
+ * becomes invisible during the transition
+ */
open fun launcherWindowBecomesInvisible() {
testSpec.assertWm {
this.isAppWindowVisible(LAUNCHER_COMPONENT)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index b509c61..5edee0c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.launch
+import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -32,8 +33,22 @@
import org.junit.runners.Parameterized
/**
- * Test warm launch app.
+ * Test warm launching an app from launcher
+ *
* To run this test: `atest FlickerTests:OpenAppWarmTest`
+ *
+ * Actions:
+ * Launch [testApp]
+ * Press home
+ * Relaunch an app [testApp] and wait animation to complete (only this action is traced)
+ *
+ * Notes:
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [OpenAppTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@@ -41,6 +56,9 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
class OpenAppWarmTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
+ /**
+ * Defines the transition used to run the test
+ */
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
super.transition(this, it)
@@ -65,11 +83,38 @@
}
}
+ /** {@inheritDoc} */
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun launcherWindowBecomesInvisible() = super.launcherWindowBecomesInvisible()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
+
companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
new file mode 100644
index 0000000..035aac1
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.quickswitch
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.RequiresDevice
+import android.view.Surface
+import android.view.WindowManagerPolicyConstants
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.isRotated
+import com.android.server.wm.flicker.navBarLayerIsVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SNAPSHOT_COMPONENT
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test quick switching back to previous app from last opened app
+ *
+ * To run this test: `atest FlickerTests:QuickSwitchBetweenTwoAppsBackTest`
+ *
+ * Actions:
+ * Launch an app [testApp1]
+ * Launch another app [testApp2]
+ * Swipe right from the bottom of the screen to quick switch back to the first app [testApp1]
+ *
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParameter) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+
+ private val testApp1 = SimpleAppHelper(instrumentation)
+ private val testApp2 = NonResizeableAppHelper(instrumentation)
+
+ private val startDisplayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ setup {
+ eachRun {
+ testApp1.launchViaIntent(wmHelper)
+ wmHelper.waitForFullScreenApp(testApp1.component)
+
+ testApp2.launchViaIntent(wmHelper)
+ wmHelper.waitForFullScreenApp(testApp2.component)
+ }
+ }
+ transitions {
+ // Swipe right from bottom to quick switch back
+ // NOTE: We don't perform an edge-to-edge swipe but instead only swipe in the middle
+ // as to not accidentally trigger a swipe back or forward action which would result
+ // in the same behavior but not testing quick swap.
+ device.swipe(
+ startDisplayBounds.bounds.right / 3,
+ startDisplayBounds.bounds.bottom,
+ 2 * startDisplayBounds.bounds.right / 3,
+ startDisplayBounds.bounds.bottom,
+ if (testSpec.config.startRotation.isRotated()) 75 else 30
+ )
+
+ wmHelper.waitForFullScreenApp(testApp1.component)
+ wmHelper.waitForAppTransitionIdle()
+ }
+
+ teardown {
+ test {
+ testApp1.exit()
+ testApp2.exit()
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks that the transition starts with [testApp2]'s windows filling/covering exactly the
+ * entirety of the display.
+ */
+ @Postsubmit
+ @Test
+ fun startsWithApp2WindowsCoverFullScreen() {
+ testSpec.assertWmStart {
+ this.frameRegion(testApp2.component).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that the transition starts with [testApp2]'s layers filling/covering exactly the
+ * entirety of the display.
+ */
+ @Postsubmit
+ @Test
+ fun startsWithApp2LayersCoverFullScreen() {
+ testSpec.assertLayersStart {
+ this.visibleRegion(testApp2.component).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that the transition starts with [testApp2] being the top window.
+ */
+ @Postsubmit
+ @Test
+ fun startsWithApp2WindowBeingOnTop() {
+ testSpec.assertWmStart {
+ this.isAppWindowOnTop(testApp2.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp1] windows fill the entire screen (i.e. is "fullscreen") at the end of the
+ * transition once we have fully quick switched from [testApp2] back to the [testApp1].
+ */
+ @Postsubmit
+ @Test
+ fun endsWithApp1WindowsCoveringFullScreen() {
+ testSpec.assertWmEnd {
+ this.frameRegion(testApp1.component).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that [testApp1] layers fill the entire screen (i.e. is "fullscreen") at the end of the
+ * transition once we have fully quick switched from [testApp2] back to the [testApp1].
+ */
+ @Postsubmit
+ @Test
+ fun endsWithApp1LayersCoveringFullScreen() {
+ testSpec.assertLayersEnd {
+ this.visibleRegion(testApp1.component).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that [testApp1] is the top window at the end of the transition once we have fully quick
+ * switched from [testApp2] back to the [testApp1].
+ */
+ @Postsubmit
+ @Test
+ fun endsWithApp1BeingOnTop() {
+ testSpec.assertWmEnd {
+ this.isAppWindowOnTop(testApp1.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp1]'s window starts off invisible and becomes visible at some point before
+ * the end of the transition and then stays visible until the end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun app1WindowBecomesAndStaysVisible() {
+ testSpec.assertWm {
+ this.isAppWindowInvisible(testApp1.component)
+ .then()
+ .isAppWindowVisible(SNAPSHOT_COMPONENT, isOptional = true)
+ .then()
+ .isAppWindowVisible(testApp1.component, ignoreActivity = true)
+ }
+ }
+
+ /**
+ * Checks that [testApp1]'s layer starts off invisible and becomes visible at some point before
+ * the end of the transition and then stays visible until the end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun app1LayerBecomesAndStaysVisible() {
+ testSpec.assertLayers {
+ this.isInvisible(testApp1.component)
+ .then()
+ .isVisible(testApp1.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp2]'s window starts off visible and becomes invisible at some point before
+ * the end of the transition and then stays invisible until the end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun app2WindowBecomesAndStaysInvisible() {
+ testSpec.assertWm {
+ this.isAppWindowVisible(testApp2.component, ignoreActivity = true)
+ .then()
+ .isAppWindowInvisible(testApp2.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp2]'s layer starts off visible and becomes invisible at some point before
+ * the end of the transition and then stays invisible until the end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun app2LayerBecomesAndStaysInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(testApp2.component)
+ .then()
+ .isInvisible(testApp2.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp2]'s window is visible at least until [testApp1]'s window is visible.
+ * Ensures that at any point, either [testApp1] or [testApp2]'s windows are at least partially
+ * visible.
+ */
+ @Postsubmit
+ @Test
+ fun app1WindowIsVisibleOnceApp2WindowIsInvisible() {
+ testSpec.assertWm {
+ this.isAppWindowVisible(testApp2.component)
+ .then()
+ // TODO: Do we actually want to test this? Seems too implementation specific...
+ .isAppWindowVisible(LAUNCHER_COMPONENT, isOptional = true)
+ .then()
+ .isAppWindowVisible(SNAPSHOT_COMPONENT, isOptional = true)
+ .then()
+ .isAppWindowVisible(testApp1.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp2]'s layer is visible at least until [testApp1]'s window is visible.
+ * Ensures that at any point, either [testApp1] or [testApp2]'s windows are at least partially
+ * visible.
+ */
+ @Postsubmit
+ @Test
+ fun app1LayerIsVisibleOnceApp2LayerIsInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(testApp2.component)
+ .then()
+ .isVisible(LAUNCHER_COMPONENT, isOptional = true)
+ .then()
+ .isVisible(SNAPSHOT_COMPONENT, isOptional = true)
+ .then()
+ .isVisible(testApp1.component)
+ }
+ }
+
+ /**
+ * Checks that the navbar window is visible throughout the entire transition.
+ */
+ @Postsubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsVisible()
+
+ /**
+ * Checks that the navbar layer is visible throughout the entire transition.
+ */
+ @Postsubmit
+ @Test
+ fun navBarLayerAlwaysIsVisible() = testSpec.navBarLayerIsVisible()
+
+ /**
+ * Checks that the navbar is always in the right position and covers the expected region.
+ *
+ * NOTE: This doesn't check that the navbar is visible or not.
+ */
+ @Postsubmit
+ @Test
+ fun navbarIsAlwaysInRightPosition() =
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
+
+ /**
+ * Checks that the status bar window is visible throughout the entire transition.
+ */
+ @Postsubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsVisible()
+
+ /**
+ * Checks that the status bar layer is visible throughout the entire transition.
+ */
+ @Postsubmit
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsVisible()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(
+ repetitions = 5,
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ ),
+ supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90)
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
new file mode 100644
index 0000000..b975360
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.quickswitch
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.RequiresDevice
+import android.view.Surface
+import android.view.WindowManagerPolicyConstants
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.isRotated
+import com.android.server.wm.flicker.navBarLayerIsVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SNAPSHOT_COMPONENT
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test quick switching back to previous app from last opened app
+ *
+ * To run this test: `atest FlickerTests:QuickSwitchBetweenTwoAppsBackTest`
+ *
+ * Actions:
+ * Launch an app [testApp1]
+ * Launch another app [testApp2]
+ * Swipe right from the bottom of the screen to quick switch back to the first app [testApp1]
+ * Swipe left from the bottom of the screen to quick switch forward to the second app [testApp2]
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestParameter) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+
+ private val testApp1 = SimpleAppHelper(instrumentation)
+ private val testApp2 = NonResizeableAppHelper(instrumentation)
+
+ private val startDisplayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ withTestName { testSpec.name }
+ repeat { testSpec.config.repetitions }
+ setup {
+ eachRun {
+ testApp1.launchViaIntent(wmHelper)
+ wmHelper.waitForFullScreenApp(testApp1.component)
+
+ testApp2.launchViaIntent(wmHelper)
+ wmHelper.waitForFullScreenApp(testApp2.component)
+
+ // Swipe right from bottom to quick switch back
+ // NOTE: We don't perform an edge-to-edge swipe but instead only swipe in the middle
+ // as to not accidentally trigger a swipe back or forward action which would result
+ // in the same behavior but not testing quick swap.
+ device.swipe(
+ startDisplayBounds.bounds.right / 3,
+ startDisplayBounds.bounds.bottom,
+ 2 * startDisplayBounds.bounds.right / 3,
+ startDisplayBounds.bounds.bottom,
+ if (testSpec.config.startRotation.isRotated()) 75 else 30
+ )
+
+ wmHelper.waitForFullScreenApp(testApp1.component)
+ wmHelper.waitForAppTransitionIdle()
+ }
+ }
+ transitions {
+ // Swipe left from bottom to quick switch forward
+ // NOTE: We don't perform an edge-to-edge swipe but instead only swipe in the middle
+ // as to not accidentally trigger a swipe back or forward action which would result
+ // in the same behavior but not testing quick swap.
+ device.swipe(
+ 2 * startDisplayBounds.bounds.right / 3,
+ startDisplayBounds.bounds.bottom,
+ startDisplayBounds.bounds.right / 3,
+ startDisplayBounds.bounds.bottom,
+ if (testSpec.config.startRotation.isRotated()) 75 else 30
+ )
+
+ wmHelper.waitForFullScreenApp(testApp2.component)
+ wmHelper.waitForAppTransitionIdle()
+ }
+
+ teardown {
+ test {
+ testApp1.exit()
+ testApp2.exit()
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks that the transition starts with [testApp1]'s windows filling/covering exactly the
+ * entirety of the display.
+ */
+ @Postsubmit
+ @Test
+ fun startsWithApp1WindowsCoverFullScreen() {
+ testSpec.assertWmStart {
+ this.frameRegion(testApp1.component).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that the transition starts with [testApp1]'s layers filling/covering exactly the
+ * entirety of the display.
+ */
+ @Postsubmit
+ @Test
+ fun startsWithApp1LayersCoverFullScreen() {
+ testSpec.assertLayersStart {
+ this.visibleRegion(testApp1.component).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that the transition starts with [testApp1] being the top window.
+ */
+ @Postsubmit
+ @Test
+ fun startsWithApp1WindowBeingOnTop() {
+ testSpec.assertWmStart {
+ this.isAppWindowOnTop(testApp1.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp2] windows fill the entire screen (i.e. is "fullscreen") at the end of the
+ * transition once we have fully quick switched from [testApp1] back to the [testApp2].
+ */
+ @Postsubmit
+ @Test
+ fun endsWithApp2WindowsCoveringFullScreen() {
+ testSpec.assertWmEnd {
+ this.frameRegion(testApp2.component).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that [testApp2] layers fill the entire screen (i.e. is "fullscreen") at the end of the
+ * transition once we have fully quick switched from [testApp1] back to the [testApp2].
+ */
+ @Postsubmit
+ @Test
+ fun endsWithApp2LayersCoveringFullScreen() {
+ testSpec.assertLayersEnd {
+ this.visibleRegion(testApp2.component).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that [testApp2] is the top window at the end of the transition once we have fully quick
+ * switched from [testApp1] back to the [testApp2].
+ */
+ @Postsubmit
+ @Test
+ fun endsWithApp2BeingOnTop() {
+ testSpec.assertWmEnd {
+ this.isAppWindowOnTop(testApp2.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp2]'s window starts off invisible and becomes visible at some point before
+ * the end of the transition and then stays visible until the end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun app2WindowBecomesAndStaysVisible() {
+ testSpec.assertWm {
+ this.isAppWindowInvisible(testApp2.component)
+ .then()
+ .isAppWindowVisible(SNAPSHOT_COMPONENT, isOptional = true)
+ .then()
+ .isAppWindowVisible(testApp2.component, ignoreActivity = true)
+ }
+ }
+
+ /**
+ * Checks that [testApp2]'s layer starts off invisible and becomes visible at some point before
+ * the end of the transition and then stays visible until the end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun app2LayerBecomesAndStaysVisible() {
+ testSpec.assertLayers {
+ this.isInvisible(testApp2.component)
+ .then()
+ .isVisible(testApp2.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp1]'s window starts off visible and becomes invisible at some point before
+ * the end of the transition and then stays invisible until the end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun app1WindowBecomesAndStaysInvisible() {
+ testSpec.assertWm {
+ this.isAppWindowVisible(testApp1.component, ignoreActivity = true)
+ .then()
+ .isAppWindowInvisible(testApp1.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp1]'s layer starts off visible and becomes invisible at some point before
+ * the end of the transition and then stays invisible until the end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun app1LayerBecomesAndStaysInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(testApp1.component)
+ .then()
+ .isInvisible(testApp1.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp1]'s window is visible at least until [testApp2]'s window is visible.
+ * Ensures that at any point, either [testApp2] or [testApp1]'s windows are at least partially
+ * visible.
+ */
+ @Postsubmit
+ @Test
+ fun app2WindowIsVisibleOnceApp1WindowIsInvisible() {
+ testSpec.assertWm {
+ this.isAppWindowVisible(testApp1.component)
+ .then()
+ .isAppWindowVisible(LAUNCHER_COMPONENT, isOptional = true)
+ .then()
+ .isAppWindowVisible(SNAPSHOT_COMPONENT, isOptional = true)
+ .then()
+ .isAppWindowVisible(testApp2.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp1]'s layer is visible at least until [testApp2]'s window is visible.
+ * Ensures that at any point, either [testApp2] or [testApp1]'s windows are at least partially
+ * visible.
+ */
+ @Postsubmit
+ @Test
+ fun app2LayerIsVisibleOnceApp1LayerIsInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(testApp1.component)
+ .then()
+ .isVisible(LAUNCHER_COMPONENT, isOptional = true)
+ .then()
+ .isVisible(SNAPSHOT_COMPONENT, isOptional = true)
+ .then()
+ .isVisible(testApp2.component)
+ }
+ }
+
+ /**
+ * Checks that the navbar window is visible throughout the entire transition.
+ */
+ @Postsubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsVisible()
+
+ /**
+ * Checks that the navbar layer is visible throughout the entire transition.
+ */
+ @Postsubmit
+ @Test
+ fun navBarLayerAlwaysIsVisible() = testSpec.navBarLayerIsVisible()
+
+ /**
+ * Checks that the navbar is always in the right position and covers the expected region.
+ *
+ * NOTE: This doesn't check that the navbar is visible or not.
+ */
+ @Postsubmit
+ @Test
+ fun navbarIsAlwaysInRightPosition() =
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
+
+ /**
+ * Checks that the status bar window is visible throughout the entire transition.
+ */
+ @Postsubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsVisible()
+
+ /**
+ * Checks that the status bar layer is visible throughout the entire transition.
+ */
+ @Postsubmit
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsVisible()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(
+ repetitions = 5,
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ ),
+ supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90)
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
index 3c94be0..b010d4c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.quickswitch
import android.app.Instrumentation
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
import android.view.Surface
import android.view.WindowManagerPolicyConstants
@@ -27,7 +27,7 @@
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.LAUNCHER_COMPONENT
-import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.SimpleAppHelper
@@ -60,7 +60,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
+@Group4
class QuickSwitchFromLauncherTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp = SimpleAppHelper(instrumentation)
@@ -106,7 +106,7 @@
* Checks that [testApp] windows fill the entire screen (i.e. is "fullscreen") at the end of the
* transition once we have fully quick switched from the launcher back to the [testApp].
*/
- @Postsubmit
+ @Presubmit
@Test
fun endsWithAppWindowsCoveringFullScreen() {
testSpec.assertWmEnd {
@@ -118,7 +118,7 @@
* Checks that [testApp] layers fill the entire screen (i.e. is "fullscreen") at the end of the
* transition once we have fully quick switched from the launcher back to the [testApp].
*/
- @Postsubmit
+ @Presubmit
@Test
fun endsWithAppLayersCoveringFullScreen() {
testSpec.assertLayersEnd {
@@ -130,7 +130,7 @@
* Checks that [testApp] is the top window at the end of the transition once we have fully quick
* switched from the launcher back to the [testApp].
*/
- @Postsubmit
+ @Presubmit
@Test
fun endsWithAppBeingOnTop() {
testSpec.assertWmEnd {
@@ -141,7 +141,7 @@
/**
* Checks that the transition starts with the home activity being tagged as visible.
*/
- @Postsubmit
+ @Presubmit
@Test
fun startsWithHomeActivityFlaggedVisible() {
testSpec.assertWmStart {
@@ -153,7 +153,7 @@
* Checks that the transition starts with the launcher windows filling/covering exactly the
* entirety of the display.
*/
- @Postsubmit
+ @Presubmit
@Test
fun startsWithLauncherWindowsCoverFullScreen() {
testSpec.assertWmStart {
@@ -165,7 +165,7 @@
* Checks that the transition starts with the launcher layers filling/covering exactly the
* entirety of the display.
*/
- @Postsubmit
+ @Presubmit
@Test
fun startsWithLauncherLayersCoverFullScreen() {
testSpec.assertLayersStart {
@@ -176,7 +176,7 @@
/**
* Checks that the transition starts with the launcher being the top window.
*/
- @Postsubmit
+ @Presubmit
@Test
fun startsWithLauncherBeingOnTop() {
testSpec.assertWmStart {
@@ -188,7 +188,7 @@
* Checks that the transition ends with the home activity being flagged as not visible. By this
* point we should have quick switched away from the launcher back to the [testApp].
*/
- @Postsubmit
+ @Presubmit
@Test
fun endsWithHomeActivityFlaggedInvisible() {
testSpec.assertWmEnd {
@@ -197,13 +197,10 @@
}
/**
- * Checks that the [testApp] window starts off invisible and once it becomes visible, stays
- * visible until the end of the transition.
- *
- * NOTE: It does not ensure that the app becomes visible at some point ([testApp] can stay
- * invisible throughout and pass this check), that is what [endsWithAppBeingVisible] is for.
+ * Checks that [testApp]'s window starts off invisible and becomes visible at some point before
+ * the end of the transition and then stays visible until the end of the transition.
*/
- @Postsubmit
+ @Presubmit
@Test
fun appWindowBecomesAndStaysVisible() {
testSpec.assertWm {
@@ -214,13 +211,10 @@
}
/**
- * Checks that the [testApp] layer starts off invisible and once it becomes visible, stays
- * visible until the end of the transition.
- *
- * NOTE: It does not ensure that the app becomes visible at some point ([testApp] can stay
- * invisible throughout and pass this check).
+ * Checks that [testApp]'s layer starts off invisible and becomes visible at some point before
+ * the end of the transition and then stays visible until the end of the transition.
*/
- @Postsubmit
+ @Presubmit
@Test
fun appLayerBecomesAndStaysVisible() {
testSpec.assertLayers {
@@ -231,13 +225,10 @@
}
/**
- * Checks that the launcher window starts off visible and once it becomes invisible, stays
- * invisible until the end of the transition.
- *
- * NOTE: It does not ensure that the launcher becomes invisible at some point, (the launcher can
- * stay invisible throughout and pass this check).
+ * Checks that the launcher window starts off visible and becomes invisible at some point before
+ * the end of the transition and then stays invisible until the end of the transition.
*/
- @Postsubmit
+ @Presubmit
@Test
fun launcherWindowBecomesAndStaysInvisible() {
testSpec.assertWm {
@@ -248,13 +239,10 @@
}
/**
- * Checks that the launcher layer starts off visible and once it becomes invisible, stays
- * invisible until the end of the transition.
- *
- * NOTE: It does not ensure that the launcher becomes invisible at some point, (the launcher can
- * stay invisible throughout and pass this check).
+ * Checks that the launcher layer starts off visible and becomes invisible at some point before
+ * the end of the transition and then stays invisible until the end of the transition.
*/
- @Postsubmit
+ @Presubmit
@Test
fun launcherLayerBecomesAndStaysInvisible() {
testSpec.assertLayers {
@@ -268,7 +256,7 @@
* Checks that the launcher window is visible at least until the app window is visible. Ensures
* that at any point, either the launcher or [testApp] windows are at least partially visible.
*/
- @Postsubmit
+ @Presubmit
@Test
fun appWindowIsVisibleOnceLauncherWindowIsInvisible() {
testSpec.assertWm {
@@ -284,7 +272,7 @@
* Checks that the launcher layer is visible at least until the app layer is visible. Ensures
* that at any point, either the launcher or [testApp] layers are at least partially visible.
*/
- @Postsubmit
+ @Presubmit
@Test
fun appLayerIsVisibleOnceLauncherLayerIsInvisible() {
testSpec.assertLayers {
@@ -299,14 +287,14 @@
/**
* Checks that the navbar window is visible throughout the entire transition.
*/
- @Postsubmit
+ @Presubmit
@Test
fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsVisible()
/**
* Checks that the navbar layer is visible throughout the entire transition.
*/
- @Postsubmit
+ @Presubmit
@Test
fun navBarLayerAlwaysIsVisible() = testSpec.navBarLayerIsVisible()
@@ -315,7 +303,7 @@
*
* NOTE: This doesn't check that the navbar is visible or not.
*/
- @Postsubmit
+ @Presubmit
@Test
fun navbarIsAlwaysInRightPosition() =
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
@@ -323,23 +311,23 @@
/**
* Checks that the status bar window is visible throughout the entire transition.
*/
- @Postsubmit
+ @Presubmit
@Test
fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsVisible()
/**
* Checks that the status bar layer is visible throughout the entire transition.
*/
- @Postsubmit
+ @Presubmit
@Test
fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsVisible()
/**
* Checks that the screen is always fully covered by visible layers throughout the transition.
*/
- @Postsubmit
+ @Presubmit
@Test
- fun screenIsAlwaysFilled() = testSpec.entireScreenCovered(testSpec.config.startRotation)
+ fun screenIsAlwaysFilled() = testSpec.entireScreenCovered()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index d57c6698..33ddd00 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -103,6 +103,11 @@
super.navBarLayerRotatesAndScales()
}
+ @FlakyTest
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index 2b0b3c2..612ff9d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -25,7 +25,6 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
@@ -39,8 +38,6 @@
protected abstract val testApp: StandardAppHelper
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- protected val startingPos get() = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
- protected val endingPos get() = WindowUtils.getDisplayBounds(testSpec.config.endRotation)
protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
setup {
@@ -107,10 +104,7 @@
@Presubmit
@Test
- open fun entireScreenCovered() {
- testSpec.entireScreenCovered(testSpec.config.startRotation,
- testSpec.config.endRotation, allStates = false)
- }
+ open fun entireScreenCovered() = testSpec.entireScreenCovered()
@Presubmit
@Test
@@ -124,7 +118,9 @@
@Test
open fun appLayerRotates_StartingPos() {
testSpec.assertLayersStart {
- this.visibleRegion(testApp.component).coversExactly(startingPos)
+ this.entry.displays.map { display ->
+ this.visibleRegion(testApp.component).coversExactly(display.layerStackSpace)
+ }
}
}
@@ -132,7 +128,9 @@
@Test
open fun appLayerRotates_EndingPos() {
testSpec.assertLayersEnd {
- this.visibleRegion(testApp.component).coversExactly(endingPos)
+ this.entry.displays.map { display ->
+ this.visibleRegion(testApp.component).coversExactly(display.layerStackSpace)
+ }
}
}
}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index 264c5d8..48efe73 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -102,9 +102,11 @@
@Test
fun appLayerRotates() {
testSpec.assertLayers {
- this.coversExactly(startingPos, testApp.component)
- .then()
- .coversExactly(endingPos, testApp.component)
+ this.invoke("entireScreenCovered") { entry ->
+ entry.entry.displays.map { display ->
+ entry.visibleRegion(testApp.component).coversExactly(display.layerStackSpace)
+ }
+ }
}
}
diff --git a/tools/aapt/Android.bp b/tools/aapt/Android.bp
index a19d183..01eb4f6 100644
--- a/tools/aapt/Android.bp
+++ b/tools/aapt/Android.bp
@@ -50,7 +50,6 @@
"libbase",
"libz",
],
- group_static_libs: true,
cflags: [
"-Wall",
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 12dc156..740b44e 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -72,7 +72,6 @@
"libidmap2_policies",
],
stl: "libc++_static",
- group_static_libs: true,
}
// ==========================================================
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 1bb0696..63b2fcd 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -163,6 +163,31 @@
return true;
}
+static bool AutoGenerateIsSplitRequired(xml::Element* el, SourcePathDiagnostics* diag) {
+ constexpr const char* kRequiredSplitTypes = "requiredSplitTypes";
+ constexpr const char* kIsSplitRequired = "isSplitRequired";
+
+ xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, kRequiredSplitTypes);
+ if (attr != nullptr) {
+ // Now inject the android:isSplitRequired="true" attribute.
+ xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, kIsSplitRequired);
+ if (attr != nullptr) {
+ if (!ResourceUtils::ParseBool(attr->value).value_or(false)) {
+ // The isFeatureSplit attribute is false, which conflicts with the use
+ // of "featureSplit".
+ diag->Error(DiagMessage(el->line_number)
+ << "attribute 'requiredSplitTypes' used in <manifest> but "
+ "'android:isSplitRequired' is not 'true'");
+ return false;
+ }
+ // The attribute is already there and set to true, nothing to do.
+ } else {
+ el->attributes.push_back(xml::Attribute{xml::kSchemaAndroid, kIsSplitRequired, "true"});
+ }
+ }
+ return true;
+}
+
static bool VerifyManifest(xml::Element* el, xml::XmlActionExecutorPolicy policy,
SourcePathDiagnostics* diag) {
xml::Attribute* attr = el->FindAttribute({}, "package");
@@ -329,6 +354,7 @@
// Manifest actions.
xml::XmlNodeAction& manifest_action = (*executor)["manifest"];
manifest_action.Action(AutoGenerateIsFeatureSplit);
+ manifest_action.Action(AutoGenerateIsSplitRequired);
manifest_action.Action(VerifyManifest);
manifest_action.Action(FixCoreAppAttribute);
manifest_action.Action([&](xml::Element* el) -> bool {
diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh
index 3cdb27c..ac23c3d 100755
--- a/tools/aosp/aosp_sha.sh
+++ b/tools/aosp/aosp_sha.sh
@@ -35,6 +35,7 @@
echo
echo "If your change contains no confidential details (such as security fixes), please"
echo "upload and merge this change at https://android-review.googlesource.com/."
+ echo "Else add a tag 'Ignore-AOSP-First:' with the reason to bypass AOSP."
echo
exit 1
fi
diff --git a/tools/lint/OWNERS b/tools/lint/OWNERS
new file mode 100644
index 0000000..7c04519
--- /dev/null
+++ b/tools/lint/OWNERS
@@ -0,0 +1,5 @@
+brufino@google.com
+jsharkey@google.com
+
+per-file *CallingSettingsNonUserGetterMethods* = file:/packages/SettingsProvider/OWNERS
+
diff --git a/tools/split-select/Android.bp b/tools/split-select/Android.bp
index c12fc6a..5402657 100644
--- a/tools/split-select/Android.bp
+++ b/tools/split-select/Android.bp
@@ -48,7 +48,6 @@
"libbase",
"libz",
],
- group_static_libs: true,
target: {
windows: {