Merge "Promote or demote stable and flaky flicker tests"
diff --git a/apex/appsearch/service/Android.bp b/apex/appsearch/service/Android.bp
index 5ab7ff9..b101895 100644
--- a/apex/appsearch/service/Android.bp
+++ b/apex/appsearch/service/Android.bp
@@ -51,6 +51,7 @@
],
libs: [
"framework-appsearch.impl",
+ "framework-statsd.stubs.module_lib",
"unsupportedappusage", // TODO(b/181887768) should be removed
],
defaults: ["framework-system-server-module-defaults"],
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchConfig.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchConfig.java
index 689aa1f..c44fd40 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchConfig.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchConfig.java
@@ -64,6 +64,12 @@
static final int DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES = 512 * 1024; // 512KiB
@VisibleForTesting
static final int DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_COUNT = 20_000;
+ @VisibleForTesting
+ static final int DEFAULT_BYTES_OPTIMIZE_THRESHOLD = 1 * 1024 * 1024; // 1 MiB
+ @VisibleForTesting
+ static final int DEFAULT_TIME_OPTIMIZE_THRESHOLD_MILLIS = Integer.MAX_VALUE;
+ @VisibleForTesting
+ static final int DEFAULT_DOC_COUNT_OPTIMIZE_THRESHOLD = 10_000;
/*
* Keys for ALL the flags stored in DeviceConfig.
@@ -79,6 +85,9 @@
"limit_config_max_document_size_bytes";
public static final String KEY_LIMIT_CONFIG_MAX_DOCUMENT_COUNT =
"limit_config_max_document_docunt";
+ public static final String KEY_BYTES_OPTIMIZE_THRESHOLD = "bytes_optimize_threshold";
+ public static final String KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS = "time_optimize_threshold";
+ public static final String KEY_DOC_COUNT_OPTIMIZE_THRESHOLD = "doc_count_optimize_threshold";
// Array contains all the corresponding keys for the cached values.
private static final String[] KEYS_TO_ALL_CACHED_VALUES = {
@@ -88,6 +97,9 @@
KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS,
KEY_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES,
KEY_LIMIT_CONFIG_MAX_DOCUMENT_COUNT,
+ KEY_BYTES_OPTIMIZE_THRESHOLD,
+ KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS,
+ KEY_DOC_COUNT_OPTIMIZE_THRESHOLD
};
// Lock needed for all the operations in this class.
@@ -251,6 +263,48 @@
}
}
+ /**
+ * Returns the cached optimize byte size threshold.
+ *
+ * An AppSearch Optimize job will be triggered if the bytes size of garbage resource exceeds
+ * this threshold.
+ */
+ int getCachedBytesOptimizeThreshold() {
+ synchronized (mLock) {
+ throwIfClosedLocked();
+ return mBundleLocked.getInt(KEY_BYTES_OPTIMIZE_THRESHOLD,
+ DEFAULT_BYTES_OPTIMIZE_THRESHOLD);
+ }
+ }
+
+ /**
+ * Returns the cached optimize time interval threshold.
+ *
+ * An AppSearch Optimize job will be triggered if the time since last optimize job exceeds
+ * this threshold.
+ */
+ int getCachedTimeOptimizeThresholdMs() {
+ synchronized (mLock) {
+ throwIfClosedLocked();
+ return mBundleLocked.getInt(KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS,
+ DEFAULT_TIME_OPTIMIZE_THRESHOLD_MILLIS);
+ }
+ }
+
+ /**
+ * Returns the cached optimize document count threshold threshold.
+ *
+ * An AppSearch Optimize job will be triggered if the number of document of garbage resource
+ * exceeds this threshold.
+ */
+ int getCachedDocCountOptimizeThreshold() {
+ synchronized (mLock) {
+ throwIfClosedLocked();
+ return mBundleLocked.getInt(KEY_DOC_COUNT_OPTIMIZE_THRESHOLD,
+ DEFAULT_DOC_COUNT_OPTIMIZE_THRESHOLD);
+ }
+ }
+
@GuardedBy("mLock")
private void throwIfClosedLocked() {
if (mIsClosedLocked) {
@@ -307,6 +361,24 @@
properties.getInt(key, DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_COUNT));
}
break;
+ case KEY_BYTES_OPTIMIZE_THRESHOLD:
+ synchronized (mLock) {
+ mBundleLocked.putInt(key, properties.getInt(key,
+ DEFAULT_BYTES_OPTIMIZE_THRESHOLD));
+ }
+ break;
+ case KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS:
+ synchronized (mLock) {
+ mBundleLocked.putInt(key, properties.getInt(key,
+ DEFAULT_TIME_OPTIMIZE_THRESHOLD_MILLIS));
+ }
+ break;
+ case KEY_DOC_COUNT_OPTIMIZE_THRESHOLD:
+ synchronized (mLock) {
+ mBundleLocked.putInt(key, properties.getInt(key,
+ DEFAULT_DOC_COUNT_OPTIMIZE_THRESHOLD));
+ }
+ break;
default:
break;
}
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 ec37c3f..b52a503 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -61,6 +61,7 @@
import com.android.server.SystemService;
import com.android.server.appsearch.external.localstorage.stats.CallStats;
import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore;
+import com.android.server.appsearch.stats.StatsCollector;
import com.android.server.appsearch.util.PackageUtil;
import com.android.server.usage.StorageStatsManagerLocal;
import com.android.server.usage.StorageStatsManagerLocal.StorageStatsAugmenter;
@@ -123,6 +124,13 @@
.registerStorageStatsAugmenter(new AppSearchStorageStatsAugmenter(), TAG);
}
+ @Override
+ public void onBootPhase(/* @BootPhase */ int phase) {
+ if (phase == PHASE_BOOT_COMPLETED) {
+ StatsCollector.getInstance(mContext, EXECUTOR);
+ }
+ }
+
private void registerReceivers() {
mContext.registerReceiverForAllUsers(
new UserActionReceiver(),
@@ -364,6 +372,12 @@
++operationSuccessCount;
invokeCallbackOnResult(callback,
AppSearchResult.newSuccessfulResult(setSchemaResponse.getBundle()));
+
+ // setSchema will sync the schemas in the request to AppSearch, any existing
+ // schemas which is not included in the request will be delete if we force
+ // override incompatible schemas. And all documents of these types will be
+ // deleted as well. We should checkForOptimize for these deletion.
+ checkForOptimize(instance);
} catch (Throwable t) {
++operationFailureCount;
statusCode = throwableToFailedResult(t).getResultCode();
@@ -505,6 +519,10 @@
// Now that the batch has been written. Persist the newly written data.
instance.getAppSearchImpl().persistToDisk(PersistType.Code.LITE);
invokeCallbackOnResult(callback, resultBuilder.build());
+
+ // The existing documents with same ID will be deleted, so there may be some
+ // resources that could be released after optimize().
+ checkForOptimize(instance, /*mutateBatchSize=*/ documentBundles.size());
} catch (Throwable t) {
++operationFailureCount;
statusCode = throwableToFailedResult(t).getResultCode();
@@ -1023,6 +1041,8 @@
// Now that the batch has been written. Persist the newly written data.
instance.getAppSearchImpl().persistToDisk(PersistType.Code.LITE);
invokeCallbackOnResult(callback, resultBuilder.build());
+
+ checkForOptimize(instance, ids.size());
} catch (Throwable t) {
++operationFailureCount;
statusCode = throwableToFailedResult(t).getResultCode();
@@ -1092,6 +1112,8 @@
instance.getAppSearchImpl().persistToDisk(PersistType.Code.LITE);
++operationSuccessCount;
invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
+
+ checkForOptimize(instance);
} catch (Throwable t) {
++operationFailureCount;
statusCode = throwableToFailedResult(t).getResultCode();
@@ -1472,4 +1494,24 @@
}
}
}
+
+ private void checkForOptimize(AppSearchUserInstance instance, int mutateBatchSize) {
+ EXECUTOR.execute(() -> {
+ try {
+ instance.getAppSearchImpl().checkForOptimize(mutateBatchSize);
+ } catch (AppSearchException e) {
+ Log.w(TAG, "Error occurred when check for optimize", e);
+ }
+ });
+ }
+
+ private void checkForOptimize(AppSearchUserInstance instance) {
+ EXECUTOR.execute(() -> {
+ try {
+ instance.getAppSearchImpl().checkForOptimize();
+ } catch (AppSearchException e) {
+ Log.w(TAG, "Error occurred when check for optimize", e);
+ }
+ });
+ }
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java
index d0d2e89..529f2b0 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java
@@ -27,12 +27,13 @@
import com.android.internal.annotations.GuardedBy;
import com.android.server.appsearch.external.localstorage.AppSearchImpl;
-import com.android.server.appsearch.external.localstorage.FrameworkOptimizeStrategy;
import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
import com.android.server.appsearch.stats.PlatformLogger;
import com.android.server.appsearch.visibilitystore.VisibilityStoreImpl;
import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -158,6 +159,18 @@
}
}
+ /**
+ * Returns the list of all {@link UserHandle}s.
+ *
+ * <p>It can return an empty list if there is no {@link AppSearchUserInstance} created yet.
+ */
+ @NonNull
+ public List<UserHandle> getAllUserHandles() {
+ synchronized (mInstancesLocked) {
+ return new ArrayList<>(mInstancesLocked.keySet());
+ }
+ }
+
@NonNull
private AppSearchUserInstance createUserInstance(
@NonNull Context userContext,
@@ -177,7 +190,7 @@
icingDir,
new FrameworkLimitConfig(config),
initStatsBuilder,
- new FrameworkOptimizeStrategy());
+ new FrameworkOptimizeStrategy(config));
long prepareVisibilityStoreLatencyStartMillis = SystemClock.elapsedRealtime();
VisibilityStoreImpl visibilityStore =
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/FrameworkOptimizeStrategy.java b/apex/appsearch/service/java/com/android/server/appsearch/FrameworkOptimizeStrategy.java
new file mode 100644
index 0000000..d934449
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/FrameworkOptimizeStrategy.java
@@ -0,0 +1,48 @@
+/*
+ * 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.appsearch;
+
+import android.annotation.NonNull;
+
+import com.android.server.appsearch.external.localstorage.AppSearchImpl;
+import com.android.server.appsearch.external.localstorage.OptimizeStrategy;
+
+import com.google.android.icing.proto.GetOptimizeInfoResultProto;
+
+import java.util.Objects;
+
+/**
+ * An implementation of {@link OptimizeStrategy} will determine when to trigger {@link
+ * AppSearchImpl#optimize()} in Jetpack environment.
+ *
+ * @hide
+ */
+public class FrameworkOptimizeStrategy implements OptimizeStrategy {
+ private final AppSearchConfig mAppSearchConfig;
+ FrameworkOptimizeStrategy(@NonNull AppSearchConfig config) {
+ mAppSearchConfig = Objects.requireNonNull(config);
+ }
+
+ @Override
+ public boolean shouldOptimize(@NonNull GetOptimizeInfoResultProto optimizeInfo) {
+ return optimizeInfo.getOptimizableDocs()
+ >= mAppSearchConfig.getCachedDocCountOptimizeThreshold()
+ || optimizeInfo.getEstimatedOptimizableBytes()
+ >= mAppSearchConfig.getCachedBytesOptimizeThreshold()
+ || optimizeInfo.getTimeSinceLastOptimizeMs()
+ >= mAppSearchConfig.getCachedTimeOptimizeThresholdMs();
+ }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/FrameworkOptimizeStrategy.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/FrameworkOptimizeStrategy.java
deleted file mode 100644
index 8ec30e1..0000000
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/FrameworkOptimizeStrategy.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.appsearch.external.localstorage;
-
-import android.annotation.NonNull;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import com.google.android.icing.proto.GetOptimizeInfoResultProto;
-
-/**
- * An implementation of {@link OptimizeStrategy} will determine when to trigger {@link
- * AppSearchImpl#optimize()} in Jetpack environment.
- *
- * @hide
- */
-public class FrameworkOptimizeStrategy implements OptimizeStrategy {
-
- @VisibleForTesting static final int DOC_COUNT_OPTIMIZE_THRESHOLD = 100_000;
- @VisibleForTesting static final int BYTES_OPTIMIZE_THRESHOLD = 1 * 1024 * 1024 * 1024; // 1GB
-
- @VisibleForTesting
- static final long TIME_OPTIMIZE_THRESHOLD_MILLIS = 7 * 24 * 60 * 60 * 1000; // 1 week
-
- @Override
- public boolean shouldOptimize(@NonNull GetOptimizeInfoResultProto optimizeInfo) {
- return optimizeInfo.getOptimizableDocs() >= DOC_COUNT_OPTIMIZE_THRESHOLD
- || optimizeInfo.getEstimatedOptimizableBytes() >= BYTES_OPTIMIZE_THRESHOLD
- || optimizeInfo.getTimeSinceLastOptimizeMs() >= TIME_OPTIMIZE_THRESHOLD_MILLIS;
- }
-}
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 5371478..2cbce10 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
@@ -45,7 +45,7 @@
import java.util.Random;
/**
- * Logger Implementation to log to statsd.
+ * Logger Implementation for pushed atoms.
*
* <p>This class is thread-safe.
*
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/stats/StatsCollector.java b/apex/appsearch/service/java/com/android/server/appsearch/stats/StatsCollector.java
new file mode 100644
index 0000000..dd56739
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/stats/StatsCollector.java
@@ -0,0 +1,203 @@
+/*
+ * 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.appsearch.stats;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.StatsManager;
+import android.content.Context;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.StatsEvent;
+
+import com.android.server.appsearch.AppSearchUserInstance;
+import com.android.server.appsearch.AppSearchUserInstanceManager;
+
+import com.google.android.icing.proto.DocumentStorageInfoProto;
+import com.google.android.icing.proto.IndexStorageInfoProto;
+import com.google.android.icing.proto.SchemaStoreStorageInfoProto;
+import com.google.android.icing.proto.StorageInfoProto;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Implements statsd pullers for AppSearch.
+ *
+ * <p>This class registers pullers to statsd, which will be called once a day to obtain AppSearch
+ * statistics that cannot be sent to statsd in real time by {@link PlatformLogger}.
+ *
+ * @hide
+ */
+public final class StatsCollector implements StatsManager.StatsPullAtomCallback {
+ private static final String TAG = "AppSearchStatsCollector";
+
+ private static volatile StatsCollector sStatsCollector;
+ private final StatsManager mStatsManager;
+
+ /**
+ * Gets an instance of {@link StatsCollector} to be used.
+ *
+ * <p>If no instance has been initialized yet, a new one will be created. Otherwise, the
+ * existing instance will be returned.
+ */
+ @NonNull
+ public static StatsCollector getInstance(@NonNull Context context,
+ @NonNull Executor executor) {
+ Objects.requireNonNull(context);
+ Objects.requireNonNull(executor);
+ if (sStatsCollector == null) {
+ synchronized (StatsCollector.class) {
+ if (sStatsCollector == null) {
+ sStatsCollector = new StatsCollector(context, executor);
+ }
+ }
+ }
+ return sStatsCollector;
+ }
+
+ private StatsCollector(@NonNull Context context, @NonNull Executor executor) {
+ mStatsManager = context.getSystemService(StatsManager.class);
+ if (mStatsManager != null) {
+ registerAtom(AppSearchStatsLog.APP_SEARCH_STORAGE_INFO, /*policy=*/ null, executor);
+ Log.d(TAG, "atoms registered");
+ } else {
+ Log.e(TAG, "could not get StatsManager, atoms not registered");
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return {@link StatsManager#PULL_SUCCESS} with list of atoms (potentially empty) if pull
+ * succeeded, {@link StatsManager#PULL_SKIP} if pull was too frequent or atom ID is
+ * unexpected.
+ */
+ @Override
+ public int onPullAtom(int atomTag, @NonNull List<StatsEvent> data) {
+ Objects.requireNonNull(data);
+ switch (atomTag) {
+ case AppSearchStatsLog.APP_SEARCH_STORAGE_INFO:
+ return pullAppSearchStorageInfo(data);
+ default:
+ Log.e(TAG, "unexpected atom ID " + atomTag);
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private static int pullAppSearchStorageInfo(@NonNull List<StatsEvent> data) {
+ AppSearchUserInstanceManager userInstanceManager =
+ AppSearchUserInstanceManager.getInstance();
+ List<UserHandle> userHandles = userInstanceManager.getAllUserHandles();
+ for (int i = 0; i < userHandles.size(); i++) {
+ UserHandle userHandle = userHandles.get(i);
+ try {
+ AppSearchUserInstance userInstance = userInstanceManager.getUserInstance(
+ userHandle);
+ StorageInfoProto storageInfoProto =
+ userInstance.getAppSearchImpl().getRawStorageInfoProto();
+ data.add(buildStatsEvent(userHandle.getIdentifier(), storageInfoProto));
+ } catch (Throwable t) {
+ Log.e(TAG,
+ "Failed to pull the storage info for user " + userHandle.toString(),
+ t);
+ }
+ }
+
+ // Skip the report if there is no data.
+ if (data.isEmpty()) {
+ return StatsManager.PULL_SKIP;
+ }
+
+ return StatsManager.PULL_SUCCESS;
+ }
+
+ /**
+ * Registers and configures the callback for the pulled atom.
+ *
+ * @param atomId The id of the atom
+ * @param policy Optional metadata specifying the timeout, cool down time etc. statsD would
+ * use default values if it is null
+ * @param executor The executor in which to run the callback
+ */
+ private void registerAtom(int atomId, @Nullable StatsManager.PullAtomMetadata policy,
+ @NonNull Executor executor) {
+ mStatsManager.setPullAtomCallback(atomId, policy, executor, /*callback=*/this);
+ }
+
+ private static StatsEvent buildStatsEvent(@UserIdInt int userId,
+ @NonNull StorageInfoProto storageInfoProto) {
+ return AppSearchStatsLog.buildStatsEvent(
+ AppSearchStatsLog.APP_SEARCH_STORAGE_INFO,
+ userId,
+ storageInfoProto.getTotalStorageSize(),
+ getDocumentStorageInfoBytes(storageInfoProto.getDocumentStorageInfo()),
+ getSchemaStoreStorageInfoBytes(storageInfoProto.getSchemaStoreStorageInfo()),
+ getIndexStorageInfoBytes(storageInfoProto.getIndexStorageInfo()));
+ }
+
+ private static byte[] getDocumentStorageInfoBytes(
+ @NonNull DocumentStorageInfoProto proto) {
+ // Make sure we only log the fields defined in the atom in case new fields are added in
+ // IcingLib
+ DocumentStorageInfoProto.Builder builder = DocumentStorageInfoProto.newBuilder();
+ builder.setNumAliveDocuments(proto.getNumAliveDocuments())
+ .setNumDeletedDocuments(proto.getNumDeletedDocuments())
+ .setNumExpiredDocuments(proto.getNumExpiredDocuments())
+ .setDocumentStoreSize(proto.getDocumentStoreSize())
+ .setDocumentLogSize(proto.getDocumentLogSize())
+ .setKeyMapperSize(proto.getKeyMapperSize())
+ .setDocumentIdMapperSize(proto.getDocumentIdMapperSize())
+ .setScoreCacheSize(proto.getScoreCacheSize())
+ .setFilterCacheSize(proto.getFilterCacheSize())
+ .setCorpusMapperSize(proto.getCorpusMapperSize())
+ .setCorpusScoreCacheSize(proto.getCorpusScoreCacheSize())
+ .setNamespaceIdMapperSize(proto.getNamespaceIdMapperSize())
+ .setNumNamespaces(proto.getNumNamespaces());
+ return builder.build().toByteArray();
+ }
+
+ private static byte[] getSchemaStoreStorageInfoBytes(
+ @NonNull SchemaStoreStorageInfoProto proto) {
+ // Make sure we only log the fields defined in the atom in case new fields are added in
+ // IcingLib
+ SchemaStoreStorageInfoProto.Builder builder = SchemaStoreStorageInfoProto.newBuilder();
+ builder.setSchemaStoreSize(proto.getSchemaStoreSize())
+ .setNumSchemaTypes(proto.getNumSchemaTypes())
+ .setNumTotalSections(proto.getNumTotalSections())
+ .setNumSchemaTypesSectionsExhausted(proto.getNumSchemaTypesSectionsExhausted());
+ return builder.build().toByteArray();
+ }
+
+ private static byte[] getIndexStorageInfoBytes(
+ @NonNull IndexStorageInfoProto proto) {
+ // Make sure we only log the fields defined in the atom in case new fields are added in
+ // IcingLib
+ IndexStorageInfoProto.Builder builder = IndexStorageInfoProto.newBuilder();
+ builder.setIndexSize(proto.getIndexSize())
+ .setLiteIndexLexiconSize(proto.getLiteIndexLexiconSize())
+ .setLiteIndexHitBufferSize(proto.getLiteIndexHitBufferSize())
+ .setMainIndexLexiconSize(proto.getMainIndexLexiconSize())
+ .setMainIndexStorageSize(proto.getMainIndexStorageSize())
+ .setMainIndexBlockSize(proto.getMainIndexBlockSize())
+ .setNumBlocks(proto.getNumBlocks())
+ .setMinFreeFraction(proto.getMinFreeFraction());
+ return builder.build().toByteArray();
+ }
+}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 9dea181..6518c18 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2085,7 +2085,7 @@
public final class PermissionManager {
method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData();
method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData(boolean);
- method public void registerAttributionSource(@NonNull android.content.AttributionSource);
+ method @NonNull public android.content.AttributionSource registerAttributionSource(@NonNull android.content.AttributionSource);
}
}
@@ -2184,7 +2184,6 @@
field @Deprecated public static final String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages";
field public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
field public static final String IMMERSIVE_MODE_CONFIRMATIONS = "immersive_mode_confirmations";
- field public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
field public static final String NOTIFICATION_BADGING = "notification_badging";
field public static final String NOTIFICATION_BUBBLES = "notification_bubbles";
field public static final String POWER_MENU_LOCKED_SHOW_CONTENT = "power_menu_locked_show_content";
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 6496303..24fb76a 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3149,7 +3149,8 @@
// If we want to access protected data on behalf of another app we need to
// tell the OS that we opt in to participate in the attribution chain.
if (nextAttributionSource != null) {
- getSystemService(PermissionManager.class).registerAttributionSource(attributionSource);
+ attributionSource = getSystemService(PermissionManager.class)
+ .registerAttributionSource(attributionSource);
}
return attributionSource;
}
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index c499f69..d63ce0f 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -88,6 +88,8 @@
public final class AttributionSource implements Parcelable {
private static final String DESCRIPTOR = "android.content.AttributionSource";
+ private static final Binder sDefaultToken = new Binder(DESCRIPTOR);
+
private final @NonNull AttributionSourceState mAttributionSourceState;
private @Nullable AttributionSource mNextCached;
@@ -97,7 +99,7 @@
@TestApi
public AttributionSource(int uid, @Nullable String packageName,
@Nullable String attributionTag) {
- this(uid, packageName, attributionTag, new Binder(DESCRIPTOR));
+ this(uid, packageName, attributionTag, sDefaultToken);
}
/** @hide */
@@ -132,7 +134,7 @@
AttributionSource(int uid, @Nullable String packageName, @Nullable String attributionTag,
@Nullable String[] renouncedPermissions, @Nullable AttributionSource next) {
- this(uid, packageName, attributionTag, new Binder(DESCRIPTOR), renouncedPermissions, next);
+ this(uid, packageName, attributionTag, sDefaultToken, renouncedPermissions, next);
}
AttributionSource(int uid, @Nullable String packageName, @Nullable String attributionTag,
@@ -170,6 +172,12 @@
}
/** @hide */
+ public AttributionSource withToken(@NonNull Binder token) {
+ return new AttributionSource(getUid(), getPackageName(), getAttributionTag(),
+ token, mAttributionSourceState.renouncedPermissions, getNext());
+ }
+
+ /** @hide */
public @NonNull AttributionSourceState asState() {
return mAttributionSourceState;
}
@@ -543,7 +551,9 @@
if ((mBuilderFieldsSet & 0x10) == 0) {
mAttributionSourceState.next = null;
}
- mAttributionSourceState.token = new Binder(DESCRIPTOR);
+
+ mAttributionSourceState.token = sDefaultToken;
+
if (mAttributionSourceState.next == null) {
// The NDK aidl backend doesn't support null parcelable arrays.
mAttributionSourceState.next = new AttributionSourceState[0];
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 09fe102..6cbe107 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -1869,40 +1869,28 @@
@FastNative
private static native void nativeUpdate(long dst, long src);
- @FastNative
- private static native void nativeWriteToParcel(Parcel dest, long ptr);
- @FastNative
- private static native void nativeReadFromParcel(Parcel source, long ptr);
- @FastNative
- private static native void nativeSwap(long ptr, long otherPtr)
+ private static synchronized native void nativeWriteToParcel(Parcel dest, long ptr);
+ private static synchronized native void nativeReadFromParcel(Parcel source, long ptr);
+ private static synchronized native void nativeSwap(long ptr, long otherPtr)
throws NullPointerException;
@FastNative
- private static native void nativeClose(long ptr);
- @FastNative
- private static native boolean nativeIsEmpty(long ptr);
- @FastNative
- private static native int nativeGetEntryCount(long ptr);
- @FastNative
- private static native long nativeGetBufferSize(long ptr);
- @FastNative
private static native void nativeSetVendorId(long ptr, long vendorId);
+ private static synchronized native void nativeClose(long ptr);
+ private static synchronized native boolean nativeIsEmpty(long ptr);
+ private static synchronized native int nativeGetEntryCount(long ptr);
+ private static synchronized native long nativeGetBufferSize(long ptr);
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @FastNative
- private static native byte[] nativeReadValues(int tag, long ptr);
- @FastNative
- private static native void nativeWriteValues(int tag, byte[] src, long ptr);
+ private static synchronized native byte[] nativeReadValues(int tag, long ptr);
+ private static synchronized native void nativeWriteValues(int tag, byte[] src, long ptr);
private static synchronized native void nativeDump(long ptr) throws IOException; // dump to LOGD
- @FastNative
- private static native ArrayList nativeGetAllVendorKeys(long ptr, Class keyClass);
+ private static synchronized native ArrayList nativeGetAllVendorKeys(long ptr, Class keyClass);
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @FastNative
- private static native int nativeGetTagFromKeyLocal(long ptr, String keyName)
+ private static synchronized native int nativeGetTagFromKeyLocal(long ptr, String keyName)
throws IllegalArgumentException;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @FastNative
- private static native int nativeGetTypeFromTagLocal(long ptr, int tag)
+ private static synchronized native int nativeGetTypeFromTagLocal(long ptr, int tag)
throws IllegalArgumentException;
@FastNative
private static native int nativeGetTagFromKey(String keyName, long vendorId)
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 4ef0e6e..a52ede8 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -44,6 +44,7 @@
import android.content.pm.PermissionInfo;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.media.AudioManager;
+import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -1163,18 +1164,24 @@
* that doesn't participate in an attribution chain.
*
* @param source The attribution source to register.
+ * @return The registered new attribution source.
*
* @see #isRegisteredAttributionSource(AttributionSource)
*
* @hide
*/
@TestApi
- public void registerAttributionSource(@NonNull AttributionSource source) {
+ public @NonNull AttributionSource registerAttributionSource(@NonNull AttributionSource source) {
+ // We use a shared static token for sources that are not registered since the token's
+ // only used for process death detection. If we are about to use the source for security
+ // enforcement we need to replace the binder with a unique one.
+ final AttributionSource registeredSource = source.withToken(new Binder());
try {
- mPermissionManager.registerAttributionSource(source);
+ mPermissionManager.registerAttributionSource(registeredSource);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
+ return registeredSource;
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9a6f9fc..d2588a3 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8749,8 +8749,6 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @TestApi
- @Readable
public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
/**
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 145607a..6f915c9 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -128,6 +128,10 @@
*/
@Appearance int getSystemBarsAppearance();
+ default boolean isSystemBarsAppearanceControlled() {
+ return false;
+ }
+
/**
* @see WindowInsetsController#setSystemBarsBehavior
*/
@@ -138,6 +142,10 @@
*/
@Behavior int getSystemBarsBehavior();
+ default boolean isSystemBarsBehaviorControlled() {
+ return false;
+ }
+
/**
* Releases a surface and ensure that this is done after {@link #applySurfaceParams} has
* finished applying params.
@@ -1520,6 +1528,10 @@
@Override
public @Appearance int getSystemBarsAppearance() {
+ if (!mHost.isSystemBarsAppearanceControlled()) {
+ // We only return the requested appearance, not the implied one.
+ return 0;
+ }
return mHost.getSystemBarsAppearance();
}
@@ -1544,6 +1556,10 @@
@Override
public @Behavior int getSystemBarsBehavior() {
+ if (!mHost.isSystemBarsBehaviorControlled()) {
+ // We only return the requested behavior, not the implied one.
+ return 0;
+ }
return mHost.getSystemBarsBehavior();
}
diff --git a/core/java/android/view/ViewRootInsetsControllerHost.java b/core/java/android/view/ViewRootInsetsControllerHost.java
index 27821fd..ce882da 100644
--- a/core/java/android/view/ViewRootInsetsControllerHost.java
+++ b/core/java/android/view/ViewRootInsetsControllerHost.java
@@ -180,14 +180,15 @@
@Override
public int getSystemBarsAppearance() {
- if ((mViewRoot.mWindowAttributes.privateFlags & PRIVATE_FLAG_APPEARANCE_CONTROLLED) == 0) {
- // We only return the requested appearance, not the implied one.
- return 0;
- }
return mViewRoot.mWindowAttributes.insetsFlags.appearance;
}
@Override
+ public boolean isSystemBarsAppearanceControlled() {
+ return (mViewRoot.mWindowAttributes.privateFlags & PRIVATE_FLAG_APPEARANCE_CONTROLLED) != 0;
+ }
+
+ @Override
public void setSystemBarsBehavior(int behavior) {
mViewRoot.mWindowAttributes.privateFlags |= PRIVATE_FLAG_BEHAVIOR_CONTROLLED;
if (mViewRoot.mWindowAttributes.insetsFlags.behavior != behavior) {
@@ -199,14 +200,15 @@
@Override
public int getSystemBarsBehavior() {
- if ((mViewRoot.mWindowAttributes.privateFlags & PRIVATE_FLAG_BEHAVIOR_CONTROLLED) == 0) {
- // We only return the requested behavior, not the implied one.
- return 0;
- }
return mViewRoot.mWindowAttributes.insetsFlags.behavior;
}
@Override
+ public boolean isSystemBarsBehaviorControlled() {
+ return (mViewRoot.mWindowAttributes.privateFlags & PRIVATE_FLAG_BEHAVIOR_CONTROLLED) != 0;
+ }
+
+ @Override
public void releaseSurfaceControlFromRt(SurfaceControl surfaceControl) {
// At the time we receive new leashes (e.g. InsetsSourceConsumer is processing
diff --git a/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java b/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java
index 848a5ba..d14adf6 100644
--- a/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java
+++ b/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java
@@ -16,11 +16,6 @@
package com.android.internal.view;
-import static com.android.internal.view.ScrollCaptureViewSupport.computeScrollAmount;
-import static com.android.internal.view.ScrollCaptureViewSupport.findScrollingReferenceView;
-import static com.android.internal.view.ScrollCaptureViewSupport.transformFromContainerToRequest;
-import static com.android.internal.view.ScrollCaptureViewSupport.transformFromRequestToContainer;
-
import android.annotation.NonNull;
import android.graphics.Rect;
import android.util.Log;
@@ -43,6 +38,7 @@
*/
public class RecyclerViewCaptureHelper implements ScrollCaptureViewHelper<ViewGroup> {
private static final String TAG = "RVCaptureHelper";
+
private int mScrollDelta;
private boolean mScrollBarWasEnabled;
private int mOverScrollMode;
@@ -66,7 +62,6 @@
result.scrollDelta = mScrollDelta;
result.availableArea = new Rect(); // empty
- Log.d(TAG, "current scrollDelta: " + mScrollDelta);
if (!recyclerView.isVisibleToUser() || recyclerView.getChildCount() == 0) {
Log.w(TAG, "recyclerView is empty or not visible, cannot continue");
return result; // result.availableArea == empty Rect
@@ -76,22 +71,18 @@
Rect requestedContainerBounds = new Rect(requestRect);
requestedContainerBounds.offset(0, -mScrollDelta);
requestedContainerBounds.offset(scrollBounds.left, scrollBounds.top);
-
// requestedContainerBounds is now in recyclerview-local coordinates
- Log.d(TAG, "requestedContainerBounds: " + requestedContainerBounds);
// Save a copy for later
View anchor = findChildNearestTarget(recyclerView, requestedContainerBounds);
if (anchor == null) {
- Log.d(TAG, "Failed to locate anchor view");
- return result; // result.availableArea == null
+ Log.w(TAG, "Failed to locate anchor view");
+ return result; // result.availableArea == empty rect
}
- Log.d(TAG, "Anchor view:" + anchor);
Rect requestedContentBounds = new Rect(requestedContainerBounds);
recyclerView.offsetRectIntoDescendantCoords(anchor, requestedContentBounds);
- Log.d(TAG, "requestedContentBounds = " + requestedContentBounds);
int prevAnchorTop = anchor.getTop();
// Note: requestChildRectangleOnScreen may modify rectangle, must pass pass in a copy here
Rect input = new Rect(requestedContentBounds);
@@ -101,34 +92,27 @@
if (remainingHeight > 0) {
input.inset(0, -remainingHeight / 2);
}
- Log.d(TAG, "input (post center adjustment) = " + input);
if (recyclerView.requestChildRectangleOnScreen(anchor, input, true)) {
int scrolled = prevAnchorTop - anchor.getTop(); // inverse of movement
- Log.d(TAG, "RecyclerView scrolled by " + scrolled + " px");
mScrollDelta += scrolled; // view.top-- is equivalent to parent.scrollY++
result.scrollDelta = mScrollDelta;
- Log.d(TAG, "requestedContentBounds, (post-request-rect) = " + requestedContentBounds);
}
requestedContainerBounds.set(requestedContentBounds);
recyclerView.offsetDescendantRectToMyCoords(anchor, requestedContainerBounds);
- Log.d(TAG, "requestedContainerBounds, (post-scroll): " + requestedContainerBounds);
Rect recyclerLocalVisible = new Rect(scrollBounds);
recyclerView.getLocalVisibleRect(recyclerLocalVisible);
- Log.d(TAG, "recyclerLocalVisible: " + recyclerLocalVisible);
if (!requestedContainerBounds.intersect(recyclerLocalVisible)) {
// Requested area is still not visible
- Log.d(TAG, "requested bounds not visible!");
return result;
}
Rect available = new Rect(requestedContainerBounds);
available.offset(-scrollBounds.left, -scrollBounds.top);
available.offset(0, mScrollDelta);
result.availableArea = available;
- Log.d(TAG, "availableArea: " + result.availableArea);
return result;
}
@@ -154,22 +138,17 @@
Rect parentLocalVis = new Rect();
parent.getLocalVisibleRect(parentLocalVis);
- Log.d(TAG, "findChildNearestTarget: parentVis=" + parentLocalVis
- + " targetRect=" + targetRect);
Rect frame = new Rect();
for (int i = 0; i < parent.getChildCount(); i++) {
final View child = parent.getChildAt(i);
child.getHitRect(frame);
- Log.d(TAG, "child #" + i + " hitRect=" + frame);
if (child.getVisibility() != View.VISIBLE) {
- Log.d(TAG, "child #" + i + " is not visible");
continue;
}
int centerDistance = Math.abs(targetRect.centerY() - frame.centerY());
- Log.d(TAG, "child #" + i + " : center to center: " + centerDistance + "px");
if (centerDistance < minCenterDistance) {
// closer to center
diff --git a/core/java/com/android/internal/view/ScrollCaptureInternal.java b/core/java/com/android/internal/view/ScrollCaptureInternal.java
index ffee16a..e3a9fda 100644
--- a/core/java/com/android/internal/view/ScrollCaptureInternal.java
+++ b/core/java/com/android/internal/view/ScrollCaptureInternal.java
@@ -34,7 +34,7 @@
private static final String TAG = "ScrollCaptureInternal";
// Log found scrolling views
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
// Log all investigated views, as well as heuristic checks
private static final boolean DEBUG_VERBOSE = false;
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 96d5a92..9802f8d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4739,9 +4739,15 @@
MediaSessionService. -->
<string name="config_customMediaSessionPolicyProvider"></string>
+ <!-- The min scale for the wallpaper when it's zoomed out -->
+ <item name="config_wallpaperMinScale" format="float" type="dimen">1</item>
+
<!-- The max scale for the wallpaper when it's zoomed in -->
<item name="config_wallpaperMaxScale" format="float" type="dimen">1.10</item>
+ <!-- If true, the wallpaper will scale regardless of the value of shouldZoomOutWallpaper() -->
+ <bool name="config_alwaysScaleWallpaper">false</bool>
+
<!-- Package name that will receive an explicit manifest broadcast for
android.os.action.POWER_SAVE_MODE_CHANGED. -->
<string name="config_powerSaveModeChangedListenerPackage" translatable="false"></string>
@@ -4760,6 +4766,16 @@
<!-- pdp data reject retry delay in ms -->
<integer name="config_pdp_reject_retry_delay_ms">-1</integer>
+ <!-- Duration in milliseconds for device to vibrate on mash press on power
+ button. -->
+ <integer name="config_mashPressVibrateTimeOnPowerButton">0</integer>
+
+ <!-- Control the behavior when the user presses the power button 5 times.
+ 0 - Nothing
+ 1 - Launch panic button gesture
+ -->
+ <integer name="config_mashPressOnPowerBehavior">0</integer>
+
<!-- Whether or not to enable the binder heavy hitter watcher by default -->
<bool name="config_defaultBinderHeavyHitterWatcherEnabled">false</bool>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index c208350..e1035f3 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -314,6 +314,7 @@
<permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/>
<permission name="android.permission.INSTALL_LOCATION_PROVIDER"/>
<permission name="android.permission.INSTALL_PACKAGES"/>
+ <permission name="android.permission.INSTALL_PACKAGE_UPDATES"/>
<!-- Needed for test only -->
<permission name="android.permission.ACCESS_MTP"/>
<!-- Needed for test only -->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 54b81ad..2038cff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -140,8 +140,6 @@
private final ContentObserver mActivatedObserver;
private final ContentObserver mEnabledObserver;
- private final ContentObserver mTimeoutObserver;
- private final ContentObserver mTaskChangeExitObserver;
private final ContentObserver mSwipeToNotificationEnabledObserver;
private final ContentObserver mShortcutEnabledObserver;
@@ -225,7 +223,7 @@
OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor);
OneHandedState transitionState = new OneHandedState();
OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context,
- displayLayout, windowManager, settingsUtil, mainExecutor);
+ windowManager);
OneHandedAnimationController animationController =
new OneHandedAnimationController(context);
OneHandedTouchHandler touchHandler = new OneHandedTouchHandler(timeoutHandler,
@@ -292,8 +290,6 @@
mActivatedObserver = getObserver(this::onActivatedActionChanged);
mEnabledObserver = getObserver(this::onEnabledSettingChanged);
- mTimeoutObserver = getObserver(this::onTimeoutSettingChanged);
- mTaskChangeExitObserver = getObserver(this::onTaskChangeExitSettingChanged);
mSwipeToNotificationEnabledObserver =
getObserver(this::onSwipeToNotificationEnabledChanged);
mShortcutEnabledObserver = getObserver(this::onShortcutEnabledChanged);
@@ -440,10 +436,6 @@
mContext.getContentResolver(), mActivatedObserver, newUserId);
mOneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_ENABLED,
mContext.getContentResolver(), mEnabledObserver, newUserId);
- mOneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
- mContext.getContentResolver(), mTimeoutObserver, newUserId);
- mOneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.TAPS_APP_TO_EXIT,
- mContext.getContentResolver(), mTaskChangeExitObserver, newUserId);
mOneHandedSettingsUtil.registerSettingsKeyObserver(
Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
mContext.getContentResolver(), mSwipeToNotificationEnabledObserver, newUserId);
@@ -456,10 +448,6 @@
mOneHandedSettingsUtil.unregisterSettingsKeyObserver(mContext.getContentResolver(),
mEnabledObserver);
mOneHandedSettingsUtil.unregisterSettingsKeyObserver(mContext.getContentResolver(),
- mTimeoutObserver);
- mOneHandedSettingsUtil.unregisterSettingsKeyObserver(mContext.getContentResolver(),
- mTaskChangeExitObserver);
- mOneHandedSettingsUtil.unregisterSettingsKeyObserver(mContext.getContentResolver(),
mSwipeToNotificationEnabledObserver);
mOneHandedSettingsUtil.unregisterSettingsKeyObserver(mContext.getContentResolver(),
mShortcutEnabledObserver);
@@ -474,6 +462,7 @@
.getSettingsTapsAppToExit(mContext.getContentResolver(), mUserId));
setSwipeToNotificationEnabled(mOneHandedSettingsUtil
.getSettingsSwipeToNotificationEnabled(mContext.getContentResolver(), mUserId));
+ onShortcutEnabledChanged();
}
private void updateDisplayLayout(int displayId) {
@@ -547,46 +536,6 @@
}
@VisibleForTesting
- void onTimeoutSettingChanged() {
- final int newTimeout = mOneHandedSettingsUtil.getSettingsOneHandedModeTimeout(
- mContext.getContentResolver(), mUserId);
- int metricsId = OneHandedUiEventLogger.OneHandedSettingsTogglesEvent.INVALID.getId();
- switch (newTimeout) {
- case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER:
- metricsId = OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_NEVER;
- break;
- case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS:
- metricsId = OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_4;
- break;
- case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS:
- metricsId = OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_8;
- break;
- case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_LONG_IN_SECONDS:
- metricsId = OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_12;
- break;
- default:
- // do nothing
- break;
- }
- mOneHandedUiEventLogger.writeEvent(metricsId);
-
- if (mTimeoutHandler != null) {
- mTimeoutHandler.setTimeout(newTimeout);
- }
- }
-
- @VisibleForTesting
- void onTaskChangeExitSettingChanged() {
- final boolean enabled = mOneHandedSettingsUtil.getSettingsTapsAppToExit(
- mContext.getContentResolver(), mUserId);
- mOneHandedUiEventLogger.writeEvent(enabled
- ? OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_ON
- : OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_OFF);
-
- setTaskChangeToExit(enabled);
- }
-
- @VisibleForTesting
void onSwipeToNotificationEnabledChanged() {
final boolean enabled =
mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
@@ -601,6 +550,10 @@
void onShortcutEnabledChanged() {
mIsShortcutEnabled = mOneHandedSettingsUtil.getShortcutEnabled(
mContext.getContentResolver(), mUserId);
+
+ mOneHandedUiEventLogger.writeEvent(mIsShortcutEnabled
+ ? OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_SHORTCUT_ENABLED_ON
+ : OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_SHORTCUT_ENABLED_OFF);
}
private void setupTimeoutListener() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
index 6cee404..0f6c4b0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
@@ -16,7 +16,7 @@
package com.android.wm.shell.onehanded;
-import static android.os.UserHandle.myUserId;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING;
@@ -24,7 +24,6 @@
import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE;
import android.annotation.Nullable;
-import android.content.ContentResolver;
import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.Rect;
@@ -41,7 +40,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.ShellExecutor;
import java.io.PrintWriter;
@@ -56,57 +54,44 @@
private static final String TAG = "OneHandedTutorialHandler";
private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE =
"persist.debug.one_handed_offset_percentage";
- private static final int MAX_TUTORIAL_SHOW_COUNT = 2;
private final float mTutorialHeightRatio;
private final WindowManager mWindowManager;
- private final OneHandedSettingsUtil mSettingsUtil;
- private final ShellExecutor mShellExecutor;
- private boolean mCanShow;
+ private boolean mIsShowing;
private @OneHandedState.State int mCurrentState;
- private int mShownCounts;
private int mTutorialAreaHeight;
private Context mContext;
- private ContentResolver mContentResolver;
private Rect mDisplayBounds;
private @Nullable View mTutorialView;
private @Nullable ViewGroup mTargetViewContainer;
- private final OneHandedAnimationCallback mAnimationCallback = new OneHandedAnimationCallback() {
- @Override
- public void onAnimationUpdate(float xPos, float yPos) {
- if (!canShowTutorial()) {
- return;
- }
- mTargetViewContainer.setTransitionGroup(true);
- mTargetViewContainer.setTranslationY(yPos - mTargetViewContainer.getHeight());
- }
- };
+ private final OneHandedAnimationCallback mAnimationCallback;
- public OneHandedTutorialHandler(Context context, DisplayLayout displayLayout,
- WindowManager windowManager, OneHandedSettingsUtil settingsUtil,
- ShellExecutor mainExecutor) {
+ public OneHandedTutorialHandler(Context context, WindowManager windowManager) {
mContext = context;
- mContentResolver = context.getContentResolver();
mWindowManager = windowManager;
- mSettingsUtil = settingsUtil;
- mShellExecutor = mainExecutor;
final float offsetPercentageConfig = context.getResources().getFraction(
R.fraction.config_one_handed_offset, 1, 1);
final int sysPropPercentageConfig = SystemProperties.getInt(
ONE_HANDED_MODE_OFFSET_PERCENTAGE, Math.round(offsetPercentageConfig * 100.0f));
mTutorialHeightRatio = sysPropPercentageConfig / 100.0f;
- mShownCounts = mSettingsUtil.getTutorialShownCounts(mContentResolver, myUserId());
+ mAnimationCallback = new OneHandedAnimationCallback() {
+ @Override
+ public void onAnimationUpdate(float xPos, float yPos) {
+ if (!isShowing()) {
+ return;
+ }
+ mTargetViewContainer.setTransitionGroup(true);
+ mTargetViewContainer.setTranslationY(yPos - mTargetViewContainer.getHeight());
+ }
+ };
}
@Override
public void onStateChanged(int newState) {
mCurrentState = newState;
- if (!canShowTutorial()) {
- return;
- }
switch (newState) {
case STATE_ENTERING:
createViewAndAttachToWindow(mContext);
@@ -139,7 +124,7 @@
@VisibleForTesting
void createViewAndAttachToWindow(Context context) {
- if (!canShowTutorial()) {
+ if (isShowing()) {
return;
}
mTutorialView = LayoutInflater.from(context).inflate(R.layout.one_handed_tutorial, null);
@@ -150,15 +135,6 @@
attachTargetToWindow();
}
- @VisibleForTesting
- boolean setTutorialShownCountIncrement() {
- if (!canShowTutorial()) {
- return false;
- }
- mShownCounts += 1;
- return mSettingsUtil.setTutorialShownCounts(mContentResolver, mShownCounts, myUserId());
- }
-
/**
* Adds the tutorial target view to the WindowManager and update its layout.
*/
@@ -166,6 +142,7 @@
if (!mTargetViewContainer.isAttachedToWindow()) {
try {
mWindowManager.addView(mTargetViewContainer, getTutorialTargetLayoutParams());
+ mIsShowing = true;
} catch (IllegalStateException e) {
// This shouldn't happen, but if the target is already added, just update its
// layout params.
@@ -179,14 +156,12 @@
void removeTutorialFromWindowManager(boolean increment) {
if (mTargetViewContainer != null && mTargetViewContainer.isAttachedToWindow()) {
mWindowManager.removeViewImmediate(mTargetViewContainer);
- if (increment) {
- setTutorialShownCountIncrement();
- }
+ mIsShowing = false;
}
}
@Nullable OneHandedAnimationCallback getAnimationCallback() {
- return canShowTutorial() ? mAnimationCallback : null /* Disabled */;
+ return isShowing() ? mAnimationCallback : null /* Disabled */;
}
/**
@@ -200,6 +175,7 @@
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
lp.gravity = Gravity.TOP | Gravity.LEFT;
+ lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setFitInsetsTypes(0 /* types */);
lp.setTitle("one-handed-tutorial-overlay");
@@ -207,17 +183,14 @@
}
@VisibleForTesting
- boolean canShowTutorial() {
- return mCanShow = mShownCounts < MAX_TUTORIAL_SHOW_COUNT;
+ boolean isShowing() {
+ return mIsShowing;
}
/**
* onConfigurationChanged events for updating tutorial text.
*/
public void onConfigurationChanged() {
- if (!canShowTutorial()) {
- return;
- }
removeTutorialFromWindowManager(false /* increment */);
if (mCurrentState == STATE_ENTERING || mCurrentState == STATE_ACTIVE) {
createViewAndAttachToWindow(mContext);
@@ -227,14 +200,12 @@
void dump(@NonNull PrintWriter pw) {
final String innerPrefix = " ";
pw.println(TAG);
- pw.print(innerPrefix + "mCanShow=");
- pw.println(mCanShow);
+ pw.print(innerPrefix + "mIsShowing=");
+ pw.println(mIsShowing);
pw.print(innerPrefix + "mCurrentState=");
pw.println(mCurrentState);
pw.print(innerPrefix + "mDisplayBounds=");
pw.println(mDisplayBounds);
- pw.print(innerPrefix + "mShownCounts=");
- pw.println(mShownCounts);
pw.print(innerPrefix + "mTutorialAreaHeight=");
pw.println(mTutorialAreaHeight);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedUiEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedUiEventLogger.java
index 4e610fa..1cf4080 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedUiEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedUiEventLogger.java
@@ -52,6 +52,8 @@
public static final int EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_12 = 17;
public static final int EVENT_ONE_HANDED_SETTINGS_SHOW_NOTIFICATION_ENABLED_ON = 18;
public static final int EVENT_ONE_HANDED_SETTINGS_SHOW_NOTIFICATION_ENABLED_OFF = 19;
+ public static final int EVENT_ONE_HANDED_SETTINGS_SHORTCUT_ENABLED_ON = 20;
+ public static final int EVENT_ONE_HANDED_SETTINGS_SHORTCUT_ENABLED_OFF = 21;
private static final String[] EVENT_TAGS = {
"one_handed_trigger_gesture_in",
@@ -73,7 +75,9 @@
"one_handed_settings_timeout_seconds_8",
"one_handed_settings_timeout_seconds_12",
"one_handed_settings_show_notification_enabled_on",
- "one_handed_settings_show_notification_enabled_off"
+ "one_handed_settings_show_notification_enabled_off",
+ "one_handed_settings_shortcut_enabled_on",
+ "one_handed_settings_shortcut_enabled_off"
};
public OneHandedUiEventLogger(UiEventLogger uiEventLogger) {
@@ -162,7 +166,13 @@
ONE_HANDED_SETTINGS_TOGGLES_SHOW_NOTIFICATION_ENABLED_ON(847),
@UiEvent(doc = "One-Handed mode show notification toggle off")
- ONE_HANDED_SETTINGS_TOGGLES_SHOW_NOTIFICATION_ENABLED_OFF(848);
+ ONE_HANDED_SETTINGS_TOGGLES_SHOW_NOTIFICATION_ENABLED_OFF(848),
+
+ @UiEvent(doc = "One-Handed mode shortcut toggle on")
+ ONE_HANDED_SETTINGS_TOGGLES_SHORTCUT_ENABLED_ON(870),
+
+ @UiEvent(doc = "One-Handed mode shortcut toggle off")
+ ONE_HANDED_SETTINGS_TOGGLES_SHORTCUT_ENABLED_OFF(871);
private final int mId;
@@ -265,6 +275,14 @@
mUiEventLogger.log(OneHandedSettingsTogglesEvent
.ONE_HANDED_SETTINGS_TOGGLES_SHOW_NOTIFICATION_ENABLED_OFF);
break;
+ case EVENT_ONE_HANDED_SETTINGS_SHORTCUT_ENABLED_ON:
+ mUiEventLogger.log(OneHandedSettingsTogglesEvent
+ .ONE_HANDED_SETTINGS_TOGGLES_SHORTCUT_ENABLED_ON);
+ break;
+ case EVENT_ONE_HANDED_SETTINGS_SHORTCUT_ENABLED_OFF:
+ mUiEventLogger.log(OneHandedSettingsTogglesEvent
+ .ONE_HANDED_SETTINGS_TOGGLES_SHORTCUT_ENABLED_OFF);
+ break;
default:
// Do nothing
break;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index 9ec7d30..b224ae6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -238,13 +238,6 @@
}
@Test
- public void testSettingsObserverUpdateTimeout() {
- mSpiedOneHandedController.onTimeoutSettingChanged();
-
- verify(mSpiedTimeoutHandler, atLeastOnce()).setTimeout(anyInt());
- }
-
- @Test
public void testSettingsObserverUpdateSwipeToNotification() {
mSpiedOneHandedController.onSwipeToNotificationEnabledChanged();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
index 1bc2a08..25bdb8e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
@@ -19,8 +19,6 @@
import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE;
-import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
@@ -68,25 +66,18 @@
mDisplayLayout = new DisplayLayout(mContext, mDisplay);
mSpiedTransitionState = spy(new OneHandedState());
mSpiedTutorialHandler = spy(
- new OneHandedTutorialHandler(mContext, mDisplayLayout, mMockWindowManager,
- mMockSettingsUtil, mMockShellMainExecutor));
+ new OneHandedTutorialHandler(mContext, mMockWindowManager));
mTimeoutHandler = new OneHandedTimeoutHandler(mMockShellMainExecutor);
}
@Test
- public void testDefaultZeroShownCounts_canShowTutorial() {
- assertThat(mSpiedTutorialHandler.canShowTutorial()).isTrue();
- verify(mMockShellMainExecutor, never()).execute(any());
- }
-
- @Test
public void testDefaultZeroShownCounts_doNotAttachWindow() {
verify(mMockShellMainExecutor, never()).execute(any());
}
@Test
public void testOnStateChangedEntering_createViewAndAttachToWindow() {
- when(mSpiedTutorialHandler.canShowTutorial()).thenReturn(true);
+ when(mSpiedTutorialHandler.isShowing()).thenReturn(true);
try {
mSpiedTutorialHandler.onStateChanged(STATE_ENTERING);
} catch (ClassCastException e) {
@@ -98,7 +89,7 @@
@Test
public void testOnStateChangedNone_removeViewAndAttachToWindow() {
- when(mSpiedTutorialHandler.canShowTutorial()).thenReturn(true);
+ when(mSpiedTutorialHandler.isShowing()).thenReturn(true);
try {
mSpiedTutorialHandler.onStateChanged(STATE_NONE);
} catch (ClassCastException e) {
@@ -110,19 +101,19 @@
@Test
public void testOnStateChangedNone_shouldNotAttachWindow() {
- when(mSpiedTutorialHandler.canShowTutorial()).thenReturn(true);
+ when(mSpiedTutorialHandler.isShowing()).thenReturn(true);
try {
mSpiedTutorialHandler.onStateChanged(STATE_NONE);
} catch (ClassCastException e) {
// no-op, just assert setTutorialShownCountIncrement() never be called
}
- verify(mSpiedTutorialHandler, never()).setTutorialShownCountIncrement();
+ verify(mSpiedTutorialHandler, never()).createViewAndAttachToWindow(any());
}
@Test
public void testOnConfigurationChanged_shouldUpdateViewContent() {
- when(mSpiedTutorialHandler.canShowTutorial()).thenReturn(true);
+ when(mSpiedTutorialHandler.isShowing()).thenReturn(true);
try {
mSpiedTutorialHandler.onStateChanged(STATE_ENTERING);
} catch (ClassCastException e) {
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 0a232d6..2c299fa 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -47,6 +47,7 @@
"-DATRACE_TAG=ATRACE_TAG_VIEW",
"-DLOG_TAG=\"OpenGLRenderer\"",
"-Wall",
+ "-Wthread-safety",
"-Wno-unused-parameter",
"-Wunreachable-code",
"-Werror",
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index dd977c3..34e5577 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -99,7 +99,7 @@
mFrameIntervalLegacy = frameIntervalNanos;
}
-void JankTracker::calculateLegacyJank(FrameInfo& frame) {
+void JankTracker::calculateLegacyJank(FrameInfo& frame) REQUIRES(mDataMutex) {
// Fast-path for jank-free frames
int64_t totalDuration = frame.duration(sFrameStart, FrameInfoIndex::SwapBuffersCompleted);
if (mDequeueTimeForgivenessLegacy && frame[FrameInfoIndex::DequeueBufferDuration] > 500_us) {
@@ -257,7 +257,7 @@
}
}
-void JankTracker::recomputeThresholds(int64_t frameBudget) {
+void JankTracker::recomputeThresholds(int64_t frameBudget) REQUIRES(mDataMutex) {
if (mThresholdsFrameBudget == frameBudget) {
return;
}
@@ -308,7 +308,7 @@
dprintf(fd, "\n---PROFILEDATA---\n\n");
}
-void JankTracker::reset() {
+void JankTracker::reset() REQUIRES(mDataMutex) {
mFrames.clear();
mData->reset();
(*mGlobalData)->reset();
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index 0d2574c..bdb784d 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -62,7 +62,7 @@
// Calculates the 'legacy' jank information, i.e. with outdated refresh rate information and
// without GPU completion or deadlined information.
void calculateLegacyJank(FrameInfo& frame);
- void dumpStats(int fd) { dumpData(fd, &mDescription, mData.get()); }
+ void dumpStats(int fd) NO_THREAD_SAFETY_ANALYSIS { dumpData(fd, &mDescription, mData.get()); }
void dumpFrames(int fd);
void reset();
diff --git a/libs/hwui/ProfileDataContainer.cpp b/libs/hwui/ProfileDataContainer.cpp
index 41afc0e..dd78847 100644
--- a/libs/hwui/ProfileDataContainer.cpp
+++ b/libs/hwui/ProfileDataContainer.cpp
@@ -27,7 +27,7 @@
namespace android {
namespace uirenderer {
-void ProfileDataContainer::freeData() {
+void ProfileDataContainer::freeData() REQUIRES(mJankDataMutex) {
if (mIsMapped) {
munmap(mData, sizeof(ProfileData));
} else {
diff --git a/libs/hwui/ProfileDataContainer.h b/libs/hwui/ProfileDataContainer.h
index a61b8dc..7d1b46c 100644
--- a/libs/hwui/ProfileDataContainer.h
+++ b/libs/hwui/ProfileDataContainer.h
@@ -37,8 +37,9 @@
void rotateStorage();
void switchStorageToAshmem(int ashmemfd);
- ProfileData* get() { return mData; }
- ProfileData* operator->() { return mData; }
+ ProfileData* get() NO_THREAD_SAFETY_ANALYSIS { return mData; }
+
+ ProfileData* operator->() NO_THREAD_SAFETY_ANALYSIS { return mData; }
std::mutex& getDataMutex() { return mJankDataMutex; }
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 76c4a03..7b962cc 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -187,7 +187,7 @@
void SkiaRecordingCanvas::FilterForImage(SkPaint& paint) {
// kClear blend mode is drawn as kDstOut on HW for compatibility with Android O and
// older.
- if (sApiLevel <= 27 && paint.getBlendMode() == SkBlendMode::kClear) {
+ if (sApiLevel <= 27 && paint.asBlendMode() == SkBlendMode::kClear) {
paint.setBlendMode(SkBlendMode::kDstOut);
}
}
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 4dcd5af..025be7b 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -614,6 +614,7 @@
mCurrentFrameInfo->markFrameCompleted();
mCurrentFrameInfo->set(FrameInfoIndex::GpuCompleted)
= mCurrentFrameInfo->get(FrameInfoIndex::FrameCompleted);
+ std::scoped_lock lock(mFrameMetricsReporterMutex);
mJankTracker.finishFrame(*mCurrentFrameInfo, mFrameMetricsReporter);
}
}
@@ -638,9 +639,12 @@
}
void CanvasContext::reportMetricsWithPresentTime() {
- if (mFrameMetricsReporter == nullptr) {
- return;
- }
+ { // acquire lock
+ std::scoped_lock lock(mFrameMetricsReporterMutex);
+ if (mFrameMetricsReporter == nullptr) {
+ return;
+ }
+ } // release lock
if (mNativeSurface == nullptr) {
return;
}
@@ -666,7 +670,22 @@
nullptr /*outReleaseTime*/);
forthBehind->set(FrameInfoIndex::DisplayPresentTime) = presentTime;
- mFrameMetricsReporter->reportFrameMetrics(forthBehind->data(), true /*hasPresentTime*/);
+ { // acquire lock
+ std::scoped_lock lock(mFrameMetricsReporterMutex);
+ if (mFrameMetricsReporter != nullptr) {
+ mFrameMetricsReporter->reportFrameMetrics(forthBehind->data(), true /*hasPresentTime*/);
+ }
+ } // release lock
+}
+
+FrameInfo* CanvasContext::getFrameInfoFromLast4(uint64_t frameNumber) {
+ std::scoped_lock lock(mLast4FrameInfosMutex);
+ for (size_t i = 0; i < mLast4FrameInfos.size(); i++) {
+ if (mLast4FrameInfos[i].second == frameNumber) {
+ return mLast4FrameInfos[i].first;
+ }
+ }
+ return nullptr;
}
void CanvasContext::onSurfaceStatsAvailable(void* context, ASurfaceControl* control,
@@ -680,22 +699,13 @@
nsecs_t gpuCompleteTime = functions.getAcquireTimeFunc(stats);
uint64_t frameNumber = functions.getFrameNumberFunc(stats);
- FrameInfo* frameInfo = nullptr;
- {
- std::lock_guard(instance->mLast4FrameInfosMutex);
- for (size_t i = 0; i < instance->mLast4FrameInfos.size(); i++) {
- if (instance->mLast4FrameInfos[i].second == frameNumber) {
- frameInfo = instance->mLast4FrameInfos[i].first;
- break;
- }
- }
- }
+ FrameInfo* frameInfo = instance->getFrameInfoFromLast4(frameNumber);
if (frameInfo != nullptr) {
frameInfo->set(FrameInfoIndex::FrameCompleted) = std::max(gpuCompleteTime,
frameInfo->get(FrameInfoIndex::SwapBuffersCompleted));
frameInfo->set(FrameInfoIndex::GpuCompleted) = gpuCompleteTime;
- std::lock_guard(instance->mFrameMetricsReporterMutex);
+ std::scoped_lock lock(instance->mFrameMetricsReporterMutex);
instance->mJankTracker.finishFrame(*frameInfo, instance->mFrameMetricsReporter);
}
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 85af3e4..6dbfcc3 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -160,6 +160,7 @@
void setContentDrawBounds(const Rect& bounds) { mContentDrawBounds = bounds; }
void addFrameMetricsObserver(FrameMetricsObserver* observer) {
+ std::scoped_lock lock(mFrameMetricsReporterMutex);
if (mFrameMetricsReporter.get() == nullptr) {
mFrameMetricsReporter.reset(new FrameMetricsReporter());
}
@@ -168,10 +169,10 @@
}
void removeFrameMetricsObserver(FrameMetricsObserver* observer) {
+ std::scoped_lock lock(mFrameMetricsReporterMutex);
if (mFrameMetricsReporter.get() != nullptr) {
mFrameMetricsReporter->removeObserver(observer);
if (!mFrameMetricsReporter->hasObservers()) {
- std::lock_guard lock(mFrameMetricsReporterMutex);
mFrameMetricsReporter.reset(nullptr);
}
}
@@ -245,6 +246,8 @@
*/
void reportMetricsWithPresentTime();
+ FrameInfo* getFrameInfoFromLast4(uint64_t frameNumber);
+
// The same type as Frame.mWidth and Frame.mHeight
int32_t mLastFrameWidth = 0;
int32_t mLastFrameHeight = 0;
@@ -305,7 +308,8 @@
std::string mName;
JankTracker mJankTracker;
FrameInfoVisualizer mProfiler;
- std::unique_ptr<FrameMetricsReporter> mFrameMetricsReporter;
+ std::unique_ptr<FrameMetricsReporter> mFrameMetricsReporter
+ GUARDED_BY(mFrameMetricsReporterMutex);
std::mutex mFrameMetricsReporterMutex;
std::set<RenderNode*> mPrefetchedLayers;
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index a8f2d9a..f0fb068 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -48,7 +48,7 @@
}
// Only let simple srcOver / src blending modes declare opaque, since behavior is clear.
- SkBlendMode mode = paint->getBlendMode();
+ const auto mode = paint->asBlendMode();
return mode == SkBlendMode::kSrcOver || mode == SkBlendMode::kSrc;
}
@@ -59,7 +59,7 @@
}
static inline SkBlendMode getBlendModeDirect(const SkPaint* paint) {
- return paint ? paint->getBlendMode() : SkBlendMode::kSrcOver;
+ return paint ? paint->getBlendMode_or(SkBlendMode::kSrcOver) : SkBlendMode::kSrcOver;
}
static inline int getAlphaDirect(const SkPaint* paint) {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index e40bd57..7c82a9b 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -73,6 +73,7 @@
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -103,7 +104,7 @@
private static final AudioVolumeGroupChangeHandler sAudioAudioVolumeGroupChangedHandler =
new AudioVolumeGroupChangeHandler();
- private static Context sContext;
+ private static WeakReference<Context> sContext;
/**
* Broadcast intent, a hint for applications that audio is about to become
@@ -812,7 +813,7 @@
} else {
mOriginalContext = context;
}
- sContext = context;
+ sContext = new WeakReference<>(context);
}
@UnsupportedAppUsage
@@ -7273,23 +7274,27 @@
*/
public static boolean hasHapticChannels(@Nullable Context context, @NonNull Uri uri) {
Objects.requireNonNull(uri);
+
if (context != null) {
return hasHapticChannelsImpl(context, uri);
- } else if (sContext != null) {
+ }
+
+ Context cachedContext = sContext.get();
+ if (cachedContext != null) {
if (DEBUG) {
Log.d(TAG, "Try to use static context to query if having haptic channels");
}
- return hasHapticChannelsImpl(sContext, uri);
- } else {
- // Try with audio service context, this may fail to get correct result.
- if (DEBUG) {
- Log.d(TAG, "Try to use audio service context to query if having haptic channels");
- }
- try {
- return getService().hasHapticChannels(uri);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return hasHapticChannelsImpl(cachedContext, uri);
+ }
+
+ // Try with audio service context, this may fail to get correct result.
+ if (DEBUG) {
+ Log.d(TAG, "Try to use audio service context to query if having haptic channels");
+ }
+ try {
+ return getService().hasHapticChannels(uri);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 657c9ef..3cf9b03 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -339,6 +339,12 @@
if (pC2Buffer != NULL) {
pC2Buffer->unregisterOnDestroyNotify(&DestroyCallback, this);
}
+
+ if (mLinearBlockObj != NULL) {
+ env->DeleteWeakGlobalRef(mLinearBlockObj);
+ mLinearBlockObj = NULL;
+ }
+
mFilterClient = NULL;
}
@@ -2450,7 +2456,10 @@
if (old != NULL) {
old->decStrong(thiz);
}
- env->SetLongField(thiz, gFields.tunerContext, (jlong)tuner.get());
+
+ if (tuner != NULL) {
+ env->SetLongField(thiz, gFields.tunerContext, (jlong)tuner.get());
+ }
return old;
}
@@ -4042,6 +4051,7 @@
static jint android_media_tv_Tuner_close_tuner(JNIEnv* env, jobject thiz) {
sp<JTuner> tuner = getTuner(env, thiz);
+ setTuner(env, thiz, NULL);
return (jint) tuner->close();
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index bc1d420..1581e24 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -161,6 +161,7 @@
<uses-permission android:name="android.permission.READ_INPUT_STATE" />
<uses-permission android:name="android.permission.SET_ORIENTATION" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
+ <uses-permission android:name="android.permission.INSTALL_PACKAGE_UPDATES" />
<uses-permission android:name="com.android.permission.USE_INSTALLER_V2" />
<uses-permission android:name="android.permission.INSTALL_TEST_ONLY_PACKAGE" />
<uses-permission android:name="com.android.permission.USE_SYSTEM_DATA_LOADERS" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3dd2f241..07ae535 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2956,6 +2956,8 @@
<string name="new_notification_image_content_description"><xliff:g id="name" example="Anna">%1$s</xliff:g> sent an image</string>
<!-- Content description text on the Conversation widget when a person has a new status posted [CHAR LIMIT=150] -->
<string name="new_status_content_description"><xliff:g id="name" example="Anna">%1$s</xliff:g> has a status update: <xliff:g id="status" example="Listening to music">%2$s</xliff:g></string>
+ <!-- Content description text on the Conversation widget when a person is available, meaning online on an application [CHAR LIMIT=150] -->
+ <string name="person_available">Available</string>
<!-- Title to display in a notification when ACTION_BATTERY_CHANGED.EXTRA_PRESENT field is false
[CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 37b0625..71e2bb6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -48,10 +48,7 @@
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
-import android.view.Display;
import android.view.MotionEvent;
-import android.view.OrientationEventListener;
-import android.view.Surface;
import android.view.WindowManager;
import com.android.internal.R;
@@ -72,6 +69,8 @@
import javax.inject.Inject;
import javax.inject.Provider;
+import kotlin.Unit;
+
/**
* Receives messages sent from {@link com.android.server.biometrics.BiometricService} and shows the
* appropriate biometric UI (e.g. BiometricDialogView).
@@ -107,7 +106,8 @@
TaskStackListener mTaskStackListener;
@VisibleForTesting
IBiometricSysuiReceiver mReceiver;
- @NonNull private final BiometricOrientationEventListener mOrientationListener;
+ @VisibleForTesting
+ @NonNull final BiometricOrientationEventListener mOrientationListener;
@Nullable private final List<FaceSensorPropertiesInternal> mFaceProps;
@Nullable private List<FingerprintSensorPropertiesInternal> mFpProps;
@Nullable private List<FingerprintSensorPropertiesInternal> mUdfpsProps;
@@ -120,46 +120,6 @@
}
}
- private class BiometricOrientationEventListener extends OrientationEventListener {
- @Surface.Rotation private int mLastRotation = ORIENTATION_UNKNOWN;
-
- BiometricOrientationEventListener(Context context) {
- super(context);
-
- final Display display = context.getDisplay();
- if (display != null) {
- mLastRotation = display.getRotation();
- }
- }
-
- @Override
- public void onOrientationChanged(int orientation) {
- if (orientation == ORIENTATION_UNKNOWN) {
- return;
- }
-
- final Display display = mContext.getDisplay();
- if (display == null) {
- return;
- }
-
- final int rotation = display.getRotation();
- if (mLastRotation != rotation) {
- mLastRotation = rotation;
-
- if (mCurrentDialog != null) {
- mCurrentDialog.onOrientationChanged();
- }
- if (mUdfpsController != null) {
- mUdfpsController.onOrientationChanged();
- }
- if (mSidefpsController != null) {
- mSidefpsController.onOrientationChanged();
- }
- }
- }
- }
-
@NonNull
private final IFingerprintAuthenticatorsRegisteredCallback
mFingerprintAuthenticatorsRegisteredCallback =
@@ -204,6 +164,7 @@
Log.w(TAG, "ACTION_CLOSE_SYSTEM_DIALOGS received");
mCurrentDialog.dismissWithoutCallback(true /* animate */);
mCurrentDialog = null;
+ mOrientationListener.disable();
try {
if (mReceiver != null) {
@@ -232,6 +193,7 @@
Log.w(TAG, "Evicting client due to: " + topPackage);
mCurrentDialog.dismissWithoutCallback(true /* animate */);
mCurrentDialog = null;
+ mOrientationListener.disable();
if (mReceiver != null) {
mReceiver.onDialogDismissed(
@@ -470,8 +432,10 @@
mUdfpsControllerFactory = udfpsControllerFactory;
mSidefpsControllerFactory = sidefpsControllerFactory;
mWindowManager = windowManager;
- mOrientationListener = new BiometricOrientationEventListener(context);
- mOrientationListener.enable();
+ mOrientationListener = new BiometricOrientationEventListener(context, () -> {
+ onOrientationChanged();
+ return Unit.INSTANCE;
+ });
mFaceProps = mFaceManager != null ? mFaceManager.getSensorPropertiesInternal() : null;
@@ -666,6 +630,7 @@
// BiometricService will have already sent the callback to the client in this case.
// This avoids a round trip to SystemUI. So, just dismiss the dialog and we're done.
mCurrentDialog = null;
+ mOrientationListener.disable();
}
/**
@@ -748,6 +713,7 @@
mReceiver = (IBiometricSysuiReceiver) args.arg2;
mCurrentDialog = newDialog;
mCurrentDialog.show(mWindowManager, savedState);
+ mOrientationListener.enable();
}
private void onDialogDismissed(@DismissedReason int reason) {
@@ -757,6 +723,7 @@
}
mReceiver = null;
mCurrentDialog = null;
+ mOrientationListener.disable();
}
@Override
@@ -769,6 +736,7 @@
mCurrentDialog.onSaveState(savedState);
mCurrentDialog.dismissWithoutCallback(false /* animate */);
mCurrentDialog = null;
+ mOrientationListener.disable();
// Only show the dialog if necessary. If it was animating out, the dialog is supposed
// to send its pending callback immediately.
@@ -789,6 +757,12 @@
}
}
+ private void onOrientationChanged() {
+ if (mCurrentDialog != null) {
+ mCurrentDialog.onOrientationChanged();
+ }
+ }
+
protected AuthDialog buildDialog(PromptInfo promptInfo, boolean requireConfirmation,
int userId, int[] sensorIds, boolean credentialAllowed, String opPackageName,
boolean skipIntro, long operationId,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt
new file mode 100644
index 0000000..08ea857
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.biometrics
+
+import android.content.Context
+import android.view.OrientationEventListener
+
+/**
+ * An [OrientationEventListener] that invokes the [onOrientationChanged] callback whenever
+ * the orientation of the device has changed in order to keep overlays for biometric sensors
+ * aligned with the device's screen.
+ */
+class BiometricOrientationEventListener(
+ private val context: Context,
+ private val onOrientationChanged: () -> Unit
+) : OrientationEventListener(context) {
+
+ /** If actively listening (not available in base class). */
+ var enabled: Boolean = false
+ private set
+
+ private var lastRotation = context.display?.rotation ?: ORIENTATION_UNKNOWN
+
+ override fun onOrientationChanged(orientation: Int) {
+ if (orientation == ORIENTATION_UNKNOWN) {
+ return
+ }
+
+ val rotation = context.display?.rotation ?: return
+ if (lastRotation != rotation) {
+ lastRotation = rotation
+
+ onOrientationChanged()
+ }
+ }
+
+ override fun enable() {
+ enabled = true
+ super.enable()
+ }
+
+ override fun disable() {
+ enabled = false
+ super.disable()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java
index 436e1e4..a51c2b8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java
@@ -41,6 +41,8 @@
import javax.inject.Inject;
+import kotlin.Unit;
+
/**
* Shows and hides the side fingerprint sensor (side-fps) overlay and handles side fps touch events.
*/
@@ -52,6 +54,8 @@
private final FingerprintManager mFingerprintManager;
private final WindowManager mWindowManager;
private final DelayableExecutor mFgExecutor;
+ @VisibleForTesting @NonNull final BiometricOrientationEventListener mOrientationListener;
+
// TODO: update mDisplayHeight and mDisplayWidth for multi-display devices
private final int mDisplayHeight;
private final int mDisplayWidth;
@@ -95,6 +99,10 @@
mFingerprintManager = checkNotNull(fingerprintManager);
mWindowManager = windowManager;
mFgExecutor = fgExecutor;
+ mOrientationListener = new BiometricOrientationEventListener(context, () -> {
+ onOrientationChanged();
+ return Unit.INSTANCE;
+ });
mSensorProps = findFirstSidefps();
checkArgument(mSensorProps != null);
@@ -119,14 +127,15 @@
mFingerprintManager.setSidefpsController(mSidefpsControllerImpl);
}
- void show() {
+ private void show() {
mView = (SidefpsView) mInflater.inflate(R.layout.sidefps_view, null, false);
mView.setSensorProperties(mSensorProps);
mWindowManager.addView(mView, computeLayoutParams());
+ mOrientationListener.enable();
}
- void hide() {
+ private void hide() {
if (mView != null) {
mWindowManager.removeView(mView);
mView.setOnTouchListener(null);
@@ -135,13 +144,16 @@
} else {
Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden");
}
+
+ mOrientationListener.disable();
}
- void onOrientationChanged() {
+ private void onOrientationChanged() {
// If mView is null or if view is hidden, then return.
if (mView == null || !mIsVisible) {
return;
}
+
// If the overlay needs to be displayed with a new configuration, destroy the current
// overlay, and re-create and show the overlay with the updated LayoutParams.
hide();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 710aca0..f9103b5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -79,6 +79,8 @@
import javax.inject.Inject;
+import kotlin.Unit;
+
/**
* Shows and hides the under-display fingerprint sensor (UDFPS) overlay, handles UDFPS touch events,
* and coordinates triggering of the high-brightness mode (HBM).
@@ -118,6 +120,7 @@
@NonNull private final AccessibilityManager mAccessibilityManager;
@NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@Nullable private final UdfpsHbmProvider mHbmProvider;
+ @VisibleForTesting @NonNull final BiometricOrientationEventListener mOrientationListener;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
@@ -511,6 +514,10 @@
mHbmProvider = hbmProvider.orElse(null);
screenLifecycle.addObserver(mScreenObserver);
mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
+ mOrientationListener = new BiometricOrientationEventListener(context, () -> {
+ onOrientationChanged();
+ return Unit.INSTANCE;
+ });
mSensorProps = findFirstUdfps();
// At least one UDFPS sensor exists
@@ -650,7 +657,7 @@
return mCoreLayoutParams;
}
- void onOrientationChanged() {
+ private void onOrientationChanged() {
// When the configuration changes it's almost always necessary to destroy and re-create
// the overlay's window to pass it the new LayoutParams.
// Hiding the overlay will destroy its window. It's safe to hide the overlay regardless
@@ -668,6 +675,7 @@
if (mView == null) {
try {
Log.v(TAG, "showUdfpsOverlay | adding window reason=" + reason);
+
mView = (UdfpsView) mInflater.inflate(R.layout.udfps_view, null, false);
mOnFingerDown = false;
mView.setSensorProperties(mSensorProps);
@@ -675,6 +683,7 @@
UdfpsAnimationViewController animation = inflateUdfpsAnimation(reason);
animation.init();
mView.setAnimationViewController(animation);
+ mOrientationListener.enable();
// This view overlaps the sensor area, so prevent it from being selectable
// during a11y.
@@ -768,6 +777,8 @@
} else {
Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden");
}
+
+ mOrientationListener.disable();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
index ec96af3..e82f648 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -114,12 +114,10 @@
@Override
void onIlluminationStarting() {
- setVisibility(View.INVISIBLE);
}
@Override
void onIlluminationStopped() {
- setVisibility(View.VISIBLE);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index 7ab4b6f..335ebca 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -501,6 +501,8 @@
views.setViewVisibility(R.id.availability, View.VISIBLE);
startPadding = mContext.getResources().getDimensionPixelSize(
R.dimen.availability_dot_shown_padding);
+ views.setContentDescription(R.id.availability,
+ mContext.getString(R.string.person_available));
} else {
views.setViewVisibility(R.id.availability, View.GONE);
startPadding = mContext.getResources().getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 4b13015..04f089d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -150,11 +150,7 @@
}
List<CastDevice> activeDevices = getActiveDevices();
- // We want to pop up the media route selection dialog if we either have no active devices
- // (neither routes nor projection), or if we have an active route. In other cases, we assume
- // that a projection is active. This is messy, but this tile never correctly handled the
- // case where multiple devices were active :-/.
- if (activeDevices.isEmpty() || (activeDevices.get(0).tag instanceof RouteInfo)) {
+ if (willPopDetail()) {
mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
showDetail(true);
});
@@ -163,6 +159,15 @@
}
}
+ // We want to pop up the media route selection dialog if we either have no active devices
+ // (neither routes nor projection), or if we have an active route. In other cases, we assume
+ // that a projection is active. This is messy, but this tile never correctly handled the
+ // case where multiple devices were active :-/.
+ private boolean willPopDetail() {
+ List<CastDevice> activeDevices = getActiveDevices();
+ return activeDevices.isEmpty() || (activeDevices.get(0).tag instanceof RouteInfo);
+ }
+
private List<CastDevice> getActiveDevices() {
ArrayList<CastDevice> activeDevices = new ArrayList<>();
for (CastDevice device : mController.getCastDevices()) {
@@ -234,10 +239,12 @@
state.contentDescription = state.contentDescription + ","
+ mContext.getString(R.string.accessibility_quick_settings_open_details);
state.expandedAccessibilityClassName = Button.class.getName();
+ state.forceExpandIcon = willPopDetail();
} else {
state.state = Tile.STATE_UNAVAILABLE;
String noWifi = mContext.getString(R.string.quick_settings_cast_no_wifi);
state.secondaryLabel = noWifi;
+ state.forceExpandIcon = false;
}
state.stateDescription = state.stateDescription + ", " + state.secondaryLabel;
mDetailAdapter.updateItems(devices);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index 82b6c0c..ab81ac1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -153,9 +153,25 @@
});
}
+ @Nullable
+ private CharSequence getServiceLabelSafe() {
+ try {
+ return mController.getWalletClient().getServiceLabel();
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Failed to get the service label safely, recreating wallet client", e);
+ mController.reCreateWalletClient();
+ try {
+ return mController.getWalletClient().getServiceLabel();
+ } catch (RuntimeException e2) {
+ Log.e(TAG, "The QAW service label is broken.", e2);
+ return null;
+ }
+ }
+ }
+
@Override
protected void handleUpdateState(State state, Object arg) {
- CharSequence label = mController.getWalletClient().getServiceLabel();
+ CharSequence label = getServiceLabelSafe();
state.label = label == null ? mLabel : label;
state.contentDescription = state.label;
Drawable tileIcon = mController.getWalletClient().getTileIcon();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
index 51cc32a..356f67e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
@@ -24,7 +24,6 @@
import android.graphics.RenderNode;
import android.graphics.drawable.Drawable;
import android.os.Handler;
-import android.util.Log;
import androidx.annotation.UiThread;
@@ -140,7 +139,6 @@
* getHeight()).
*/
Bitmap toBitmap(Rect bounds) {
- Log.d(TAG, "exporting with bounds: " + bounds);
if (mTiles.isEmpty()) {
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index af0141c..3b03909 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -63,7 +63,7 @@
* and bottom before saving/sharing/editing.
*/
public class LongScreenshotActivity extends Activity {
- private static final String TAG = "LongScreenshotActivity";
+ private static final String TAG = LogConfig.logTag(LongScreenshotActivity.class);
public static final String EXTRA_CAPTURE_RESPONSE = "capture-response";
private static final String KEY_SAVED_IMAGE_PATH = "saved-image-path";
@@ -113,7 +113,6 @@
@Override
public void onCreate(Bundle savedInstanceState) {
- Log.d(TAG, "onCreate(savedInstanceState = " + savedInstanceState + ")");
super.onCreate(savedInstanceState);
setContentView(R.layout.long_screenshot);
@@ -161,9 +160,13 @@
@Override
public void onStart() {
- Log.d(TAG, "onStart");
super.onStart();
+ if (mPreview.getDrawable() != null) {
+ // We already have an image, so no need to try to load again.
+ return;
+ }
+
if (mCacheLoadFuture != null) {
Log.d(TAG, "mCacheLoadFuture != null");
final ListenableFuture<ImageLoader.Result> future = mCacheLoadFuture;
@@ -194,7 +197,7 @@
}
private void onLongScreenshotReceived(LongScreenshot longScreenshot) {
- Log.d(TAG, "onLongScreenshotReceived(longScreenshot=" + longScreenshot + ")");
+ Log.i(TAG, "Completed: " + longScreenshot);
mLongScreenshot = longScreenshot;
Drawable drawable = mLongScreenshot.getDrawable();
mPreview.setImageDrawable(drawable);
@@ -249,7 +252,6 @@
}
private void onCachedImageLoaded(ImageLoader.Result imageResult) {
- Log.d(TAG, "onCachedImageLoaded(imageResult=" + imageResult + ")");
BitmapDrawable drawable = new BitmapDrawable(getResources(), imageResult.bitmap);
mPreview.setImageDrawable(drawable);
mPreview.setAlpha(1f);
@@ -274,7 +276,6 @@
@Override
protected void onSaveInstanceState(Bundle outState) {
- Log.d(TAG, "onSaveInstanceState");
super.onSaveInstanceState(outState);
if (mSavedImagePath != null) {
outState.putString(KEY_SAVED_IMAGE_PATH, mSavedImagePath.getPath());
@@ -282,14 +283,7 @@
}
@Override
- protected void onPause() {
- Log.d(TAG, "onPause");
- super.onPause();
- }
-
- @Override
protected void onStop() {
- Log.d(TAG, "onStop finishing=" + isFinishing());
super.onStop();
if (mTransitionStarted) {
finish();
@@ -311,19 +305,12 @@
mCacheSaveFuture.cancel(true);
}
if (mSavedImagePath != null) {
- Log.d(TAG, "Deleting " + mSavedImagePath);
//noinspection ResultOfMethodCallIgnored
mSavedImagePath.delete();
mSavedImagePath = null;
}
}
- @Override
- protected void onDestroy() {
- Log.d(TAG, "onDestroy");
- super.onDestroy();
- }
-
private void setButtonsEnabled(boolean enabled) {
mSave.setEnabled(enabled);
mEdit.setEnabled(enabled);
@@ -392,7 +379,6 @@
}
private void startExport(PendingAction action) {
- Log.d(TAG, "startExport(action = " + action + ")");
Drawable drawable = mPreview.getDrawable();
if (drawable == null) {
Log.e(TAG, "No drawable, skipping export!");
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index 4c1f6a1..6dc6874 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -43,7 +43,7 @@
* Interaction controller between the UI and ScrollCaptureClient.
*/
public class ScrollCaptureController {
- private static final String TAG = "ScrollCaptureController";
+ private static final String TAG = LogConfig.logTag(ScrollCaptureController.class);
private static final float MAX_PAGES_DEFAULT = 3f;
private static final String SETTING_KEY_MAX_PAGES = "screenshot.scroll_max_pages";
@@ -90,7 +90,9 @@
/** Releases image resources from the screenshot. */
public void release() {
- Log.d(TAG, "LongScreenshot :: release()");
+ if (LogConfig.DEBUG_SCROLL) {
+ Log.d(TAG, "LongScreenshot :: release()");
+ }
mImageTileSet.clear();
mSession.release();
}
@@ -153,15 +155,11 @@
* @return a future ImageTile set containing the result
*/
ListenableFuture<LongScreenshot> run(ScrollCaptureResponse response) {
- Log.d(TAG, "run: " + response);
return CallbackToFutureAdapter.getFuture(completer -> {
- Log.d(TAG, "getFuture(ImageTileSet) ");
mCaptureCompleter = completer;
mBgExecutor.execute(() -> {
- Log.d(TAG, "bgExecutor.execute");
float maxPages = Settings.Secure.getFloat(mContext.getContentResolver(),
SETTING_KEY_MAX_PAGES, MAX_PAGES_DEFAULT);
- Log.d(TAG, "client start, maxPages=" + maxPages);
mSessionFuture = mClient.start(response, maxPages);
mSessionFuture.addListener(this::onStartComplete, mContext.getMainExecutor());
});
@@ -172,21 +170,27 @@
private void onStartComplete() {
try {
mSession = mSessionFuture.get();
- Log.d(TAG, "got session " + mSession);
+ if (LogConfig.DEBUG_SCROLL) {
+ Log.d(TAG, "got session " + mSession);
+ }
requestNextTile(0);
} catch (InterruptedException | ExecutionException e) {
// Failure to start, propagate to caller
- Log.d(TAG, "session start failed!");
+ Log.e(TAG, "session start failed!");
mCaptureCompleter.setException(e);
}
}
private void requestNextTile(int topPx) {
- Log.d(TAG, "requestNextTile: " + topPx);
+ if (LogConfig.DEBUG_SCROLL) {
+ Log.d(TAG, "requestNextTile: " + topPx);
+ }
mTileFuture = mSession.requestTile(topPx);
mTileFuture.addListener(() -> {
try {
- Log.d(TAG, "onCaptureResult");
+ if (LogConfig.DEBUG_SCROLL) {
+ Log.d(TAG, "onCaptureResult");
+ }
onCaptureResult(mTileFuture.get());
} catch (InterruptedException | ExecutionException e) {
Log.e(TAG, "requestTile failed!", e);
@@ -196,14 +200,18 @@
}
private void onCaptureResult(CaptureResult result) {
- Log.d(TAG, "onCaptureResult: " + result + " scrolling " + (mScrollingUp ? "UP" : "DOWN")
- + " finish on boundary: " + mFinishOnBoundary);
+ if (LogConfig.DEBUG_SCROLL) {
+ Log.d(TAG, "onCaptureResult: " + result + " scrolling " + (mScrollingUp ? "UP" : "DOWN")
+ + " finish on boundary: " + mFinishOnBoundary);
+ }
boolean emptyResult = result.captured.height() == 0;
if (emptyResult) {
// Potentially reached a vertical boundary. Extend in the other direction.
if (mFinishOnBoundary) {
- Log.d(TAG, "Empty: finished!");
+ if (LogConfig.DEBUG_SCROLL) {
+ Log.d(TAG, "Empty: finished!");
+ }
finishCapture();
return;
} else {
@@ -212,13 +220,17 @@
mImageTileSet.clear();
mFinishOnBoundary = true;
mScrollingUp = !mScrollingUp;
- Log.d(TAG, "Empty: cleared, switch direction to finish");
+ if (LogConfig.DEBUG_SCROLL) {
+ Log.d(TAG, "Empty: cleared, switch direction to finish");
+ }
}
} else {
// Got a non-empty result, but may already have enough bitmap data now
int expectedTiles = mImageTileSet.size() + 1;
if (expectedTiles >= mSession.getMaxTiles()) {
- Log.d(TAG, "Hit max tiles: finished");
+ if (LogConfig.DEBUG_SCROLL) {
+ Log.d(TAG, "Hit max tiles: finished");
+ }
// If we ever hit the max tiles, we've got enough bitmap data to finish
// (even if we weren't sure we'd finish on this pass).
finishCapture();
@@ -229,7 +241,9 @@
// by IDEAL_PORTION_ABOVE.
if (mImageTileSet.getHeight() + result.captured.height()
>= mSession.getTargetHeight() * IDEAL_PORTION_ABOVE) {
- Log.d(TAG, "Hit ideal portion above: clear and switch direction");
+ if (LogConfig.DEBUG_SCROLL) {
+ Log.d(TAG, "Hit ideal portion above: clear and switch direction");
+ }
// We got enough above the start point, now see how far down it can go.
mImageTileSet.clear();
mScrollingUp = false;
@@ -241,20 +255,25 @@
if (!emptyResult) {
mImageTileSet.addTile(new ImageTile(result.image, result.captured));
}
-
- Log.d(TAG, "bounds: " + mImageTileSet.getLeft() + "," + mImageTileSet.getTop()
- + " - " + mImageTileSet.getRight() + "," + mImageTileSet.getBottom()
- + " (" + mImageTileSet.getWidth() + "x" + mImageTileSet.getHeight() + ")");
+ if (LogConfig.DEBUG_SCROLL) {
+ Log.d(TAG, "bounds: " + mImageTileSet.getLeft() + "," + mImageTileSet.getTop()
+ + " - " + mImageTileSet.getRight() + "," + mImageTileSet.getBottom()
+ + " (" + mImageTileSet.getWidth() + "x" + mImageTileSet.getHeight() + ")");
+ }
Rect gapBounds = mImageTileSet.getGaps();
if (!gapBounds.isEmpty()) {
- Log.d(TAG, "Found gaps in tileset: " + gapBounds + ", requesting " + gapBounds.top);
+ if (LogConfig.DEBUG_SCROLL) {
+ Log.d(TAG, "Found gaps in tileset: " + gapBounds + ", requesting " + gapBounds.top);
+ }
requestNextTile(gapBounds.top);
return;
}
if (mImageTileSet.getHeight() >= mSession.getTargetHeight()) {
- Log.d(TAG, "Target height reached.");
+ if (LogConfig.DEBUG_SCROLL) {
+ Log.d(TAG, "Target height reached.");
+ }
finishCapture();
return;
}
@@ -275,10 +294,14 @@
}
private void finishCapture() {
- Log.d(TAG, "finishCapture()");
+ if (LogConfig.DEBUG_SCROLL) {
+ Log.d(TAG, "finishCapture()");
+ }
mEndFuture = mSession.end();
mEndFuture.addListener(() -> {
- Log.d(TAG, "endCapture completed");
+ if (LogConfig.DEBUG_SCROLL) {
+ Log.d(TAG, "endCapture completed");
+ }
// Provide result to caller and complete the top-level future
// Caller is responsible for releasing this resource (ImageReader/HardwareBuffers)
mCaptureCompleter.set(new LongScreenshot(mSession, mImageTileSet));
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index 2477534..ef18df5 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -38,6 +38,10 @@
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
+import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION
+import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__ENABLE
+import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__CANCEL
+import com.android.internal.util.FrameworkStatsLog.write
/**
* Dialog to be shown on top of apps that are attempting to use a sensor (e.g. microphone) which is
@@ -185,16 +189,25 @@
keyguardDismissUtil.executeWhenUnlocked({
bgHandler.postDelayed({
disableSensorPrivacy()
+ write(PRIVACY_TOGGLE_DIALOG_INTERACTION,
+ PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__ENABLE,
+ sensorUsePackageName)
}, UNLOCK_DELAY_MILLIS)
false
}, false, true)
} else {
disableSensorPrivacy()
+ write(PRIVACY_TOGGLE_DIALOG_INTERACTION,
+ PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__ENABLE,
+ sensorUsePackageName)
}
}
BUTTON_NEGATIVE -> {
unsuppressImmediately = false
+ write(PRIVACY_TOGGLE_DIALOG_INTERACTION,
+ PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__CANCEL,
+ sensorUsePackageName)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index f22c139..4bd2716 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -412,6 +412,7 @@
private boolean mBackwardScrollable;
private NotificationShelf mShelf;
private int mMaxDisplayedNotifications = -1;
+ private float mKeyguardBottomPadding = -1;
private int mStatusBarHeight;
private int mMinInteractionHeight;
private final Rect mClipRect = new Rect();
@@ -736,6 +737,16 @@
mDebugPaint.setColor(Color.YELLOW);
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
+ y = (int) mMaxLayoutHeight;
+ mDebugPaint.setColor(Color.MAGENTA);
+ canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
+
+ if (mKeyguardBottomPadding >= 0) {
+ y = getHeight() - (int) mKeyguardBottomPadding;
+ mDebugPaint.setColor(Color.GRAY);
+ canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
+ }
+
y = getHeight() - getEmptyBottomMargin();
mDebugPaint.setColor(Color.GREEN);
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
@@ -4771,6 +4782,16 @@
}
}
+ /**
+ * This is used for debugging only; it will be used to draw the otherwise invisible line which
+ * NotificationPanelViewController treats as the bottom when calculating how many notifications
+ * appear on the keyguard.
+ * Setting a negative number will disable rendering this line.
+ */
+ public void setKeyguardBottomPadding(float keyguardBottomPadding) {
+ mKeyguardBottomPadding = keyguardBottomPadding;
+ }
+
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
mShouldShowShelfOnly = shouldShowShelfOnly;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index a92682a..a47afb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -1204,6 +1204,16 @@
mNotificationListContainer.setMaxDisplayedNotifications(maxNotifications);
}
+ /**
+ * This is used for debugging only; it will be used to draw the otherwise invisible line which
+ * NotificationPanelViewController treats as the bottom when calculating how many notifications
+ * appear on the keyguard.
+ * Setting a negative number will disable rendering this line.
+ */
+ public void setKeyguardBottomPadding(float keyguardBottomPadding) {
+ mView.setKeyguardBottomPadding(keyguardBottomPadding);
+ }
+
public RemoteInputController.Delegate createDelegate() {
return new RemoteInputController.Delegate() {
public void setRemoteInputActive(NotificationEntry entry,
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 920e2a5..591d46b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -332,6 +332,8 @@
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final TapAgainViewController mTapAgainViewController;
private boolean mShouldUseSplitNotificationShade;
+ // The bottom padding reserved for elements of the keyguard measuring notifications
+ private float mKeyguardNotificationBottomPadding;
// Current max allowed keyguard notifications determined by measuring the panel
private int mMaxAllowedKeyguardNotifications;
@@ -1189,9 +1191,12 @@
if (mKeyguardShowing && !mKeyguardBypassController.getBypassEnabled()) {
mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(
mMaxAllowedKeyguardNotifications);
+ mNotificationStackScrollLayoutController.setKeyguardBottomPadding(
+ mKeyguardNotificationBottomPadding);
} else {
// no max when not on the keyguard
mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(-1);
+ mNotificationStackScrollLayoutController.setKeyguardBottomPadding(-1f);
}
}
@@ -1393,6 +1398,7 @@
float bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
bottomPadding = Math.max(lockIconPadding, bottomPadding);
+ mKeyguardNotificationBottomPadding = bottomPadding;
float availableSpace =
mNotificationStackScrollLayoutController.getHeight()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index 1f1817c..5e70d0d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -21,7 +21,7 @@
import android.database.DataSetObserver;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
-import android.os.UserManager;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@@ -76,7 +76,6 @@
private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
private final KeyguardUserDetailAdapter mUserDetailAdapter;
private NotificationPanelViewController mNotificationPanelViewController;
- private UserManager mUserManager;
UserSwitcherController.UserRecord mCurrentUser;
// State info for the user switch and keyguard
@@ -115,7 +114,6 @@
UserAvatarView view,
Context context,
@Main Resources resources,
- UserManager userManager,
ScreenLifecycle screenLifecycle,
UserSwitcherController userSwitcherController,
KeyguardStateController keyguardStateController,
@@ -129,7 +127,6 @@
if (DEBUG) Log.d(TAG, "New KeyguardQsUserSwitchController");
mContext = context;
mResources = resources;
- mUserManager = userManager;
mScreenLifecycle = screenLifecycle;
mUserSwitcherController = userSwitcherController;
mKeyguardStateController = keyguardStateController;
@@ -227,47 +224,39 @@
return;
}
- if (mCurrentUser == null) {
- mView.setVisibility(View.GONE);
- return;
- }
-
- mView.setVisibility(View.VISIBLE);
-
- String currentUserName = mCurrentUser.info.name;
String contentDescription = null;
-
- if (!TextUtils.isEmpty(currentUserName)) {
+ if (mCurrentUser != null && mCurrentUser.info != null && !TextUtils.isEmpty(
+ mCurrentUser.info.name)) {
+ // If we know the current user's name, have TalkBack to announce "Signed in as [user
+ // name]" when the icon is selected
+ contentDescription = mContext.getString(R.string.accessibility_quick_settings_user,
+ mCurrentUser.info.name);
+ } else {
+ // As a fallback, have TalkBack announce "Switch user"
contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_user,
- currentUserName);
+ R.string.accessibility_multi_user_switch_switcher);
}
if (!TextUtils.equals(mView.getContentDescription(), contentDescription)) {
mView.setContentDescription(contentDescription);
}
- mView.setDrawableWithBadge(getCurrentUserIcon().mutate(), mCurrentUser.resolveId());
+ int userId = mCurrentUser != null ? mCurrentUser.resolveId() : UserHandle.USER_NULL;
+ mView.setDrawableWithBadge(getCurrentUserIcon().mutate(), userId);
}
Drawable getCurrentUserIcon() {
Drawable drawable;
- if (mCurrentUser.picture == null) {
- if (mCurrentUser.isCurrent && mCurrentUser.isGuest) {
+ if (mCurrentUser == null || mCurrentUser.picture == null) {
+ if (mCurrentUser != null && mCurrentUser.isGuest) {
drawable = mContext.getDrawable(R.drawable.ic_avatar_guest_user);
} else {
- drawable = mAdapter.getIconDrawable(mContext, mCurrentUser);
+ drawable = mContext.getDrawable(R.drawable.ic_avatar_user);
}
- int iconColorRes;
- if (mCurrentUser.isSwitchToEnabled) {
- iconColorRes = R.color.kg_user_switcher_avatar_icon_color;
- } else {
- iconColorRes = R.color.kg_user_switcher_restricted_avatar_icon_color;
- }
+ int iconColorRes = R.color.kg_user_switcher_avatar_icon_color;
drawable.setTint(mResources.getColor(iconColorRes, mContext.getTheme()));
} else {
- int avatarSize =
- (int) mResources.getDimension(R.dimen.kg_framed_avatar_size);
+ int avatarSize = (int) mResources.getDimension(R.dimen.kg_framed_avatar_size);
drawable = new CircleFramedDrawable(mCurrentUser.picture, avatarSize);
}
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 9434c5d..acbbde5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -707,7 +707,7 @@
cb.setIsAirplaneMode(new IconState(mAirplaneMode,
TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext));
cb.setNoSims(mHasNoSubs, mSimDetected);
- if (mProviderModelBehavior) {
+ if (mProviderModelSetting) {
cb.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition, mNoNetworksAvailable);
}
mWifiSignalController.notifyListeners(cb);
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 bfcd131..e94f836 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -20,7 +20,9 @@
import static android.hardware.biometrics.BiometricManager.BIOMETRIC_MULTI_SENSOR_FACE_THEN_FINGERPRINT;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -55,12 +57,13 @@
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
import android.os.Bundle;
import android.os.RemoteException;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableLooper.RunWithLooper;
import android.view.WindowManager;
+import androidx.test.filters.SmallTest;
+
import com.android.internal.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.CommandQueue;
@@ -539,6 +542,17 @@
verify(mUdfpsController).onAodInterrupt(eq(pos), eq(pos), eq(majorMinor), eq(majorMinor));
}
+ @Test
+ public void testSubscribesToOrientationChangesWhenShowingDialog() {
+ assertFalse(mAuthController.mOrientationListener.getEnabled());
+
+ showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */);
+ assertTrue(mAuthController.mOrientationListener.getEnabled());
+
+ mAuthController.hideAuthenticationDialog();
+ assertFalse(mAuthController.mOrientationListener.getEnabled());
+ }
+
// Helpers
private void showDialog(int[] sensorIds, boolean credentialAllowed) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
new file mode 100644
index 0000000..7019a4b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
@@ -0,0 +1,117 @@
+/*
+ * 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.biometrics
+
+import android.hardware.biometrics.SensorProperties
+import android.hardware.display.DisplayManagerGlobal
+import android.hardware.fingerprint.FingerprintManager
+import android.hardware.fingerprint.FingerprintSensorProperties
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
+import android.hardware.fingerprint.ISidefpsController
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.Display
+import android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS
+import android.view.DisplayInfo
+import android.view.LayoutInflater
+import android.view.WindowManager
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+
+private const val DISPLAY_ID = 2
+private const val SENSOR_ID = 1
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class SidefpsControllerTest : SysuiTestCase() {
+
+ @JvmField @Rule
+ var rule = MockitoJUnit.rule()
+
+ @Mock
+ lateinit var layoutInflater: LayoutInflater
+ @Mock
+ lateinit var fingerprintManager: FingerprintManager
+ @Mock
+ lateinit var windowManager: WindowManager
+ @Mock
+ lateinit var sidefpsView: SidefpsView
+
+ private val executor = FakeExecutor(FakeSystemClock())
+ private lateinit var overlayController: ISidefpsController
+ private lateinit var sideFpsController: SidefpsController
+
+ @Before
+ fun setup() {
+ `when`(layoutInflater.inflate(R.layout.sidefps_view, null, false)).thenReturn(sidefpsView)
+ `when`(fingerprintManager.sensorPropertiesInternal).thenReturn(
+ listOf(
+ FingerprintSensorPropertiesInternal(
+ SENSOR_ID,
+ SensorProperties.STRENGTH_STRONG,
+ 5 /* maxEnrollmentsPerUser */,
+ listOf() /* componentInfo */,
+ FingerprintSensorProperties.TYPE_POWER_BUTTON,
+ true /* resetLockoutRequiresHardwareAuthToken */
+ )
+ )
+ )
+ `when`(windowManager.defaultDisplay).thenReturn(
+ Display(
+ DisplayManagerGlobal.getInstance(),
+ DISPLAY_ID,
+ DisplayInfo(),
+ DEFAULT_DISPLAY_ADJUSTMENTS
+ )
+ )
+
+ sideFpsController = SidefpsController(
+ mContext, layoutInflater, fingerprintManager, windowManager, executor
+ )
+
+ overlayController = ArgumentCaptor.forClass(ISidefpsController::class.java).apply {
+ verify(fingerprintManager).setSidefpsController(capture())
+ }.value
+ }
+
+ @Test
+ fun testSubscribesToOrientationChangesWhenShowingOverlay() {
+ assertThat(sideFpsController.mOrientationListener.enabled).isFalse()
+
+ overlayController.show()
+ executor.runAllReady()
+ assertThat(sideFpsController.mOrientationListener.enabled).isTrue()
+
+ overlayController.hide()
+ executor.runAllReady()
+ assertThat(sideFpsController.mOrientationListener.enabled).isFalse()
+ }
+}
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 25722e1..d8d3676 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -17,6 +17,8 @@
package com.android.systemui.biometrics;
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.anyFloat;
@@ -236,6 +238,22 @@
}
@Test
+ public void testSubscribesToOrientationChangesWhenShowingOverlay() throws Exception {
+ assertFalse(mUdfpsController.mOrientationListener.getEnabled());
+
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
+ IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ mFgExecutor.runAllReady();
+
+ assertTrue(mUdfpsController.mOrientationListener.getEnabled());
+
+ mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
+ mFgExecutor.runAllReady();
+
+ assertFalse(mUdfpsController.mOrientationListener.getEnabled());
+ }
+
+ @Test
public void fingerDown() throws RemoteException {
// Configure UdfpsView to accept the ACTION_DOWN event
when(mUdfpsView.isIlluminationRequested()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
index c818da8..b4b4597 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
@@ -289,6 +289,8 @@
assertEquals(View.GONE, result.findViewById(R.id.last_interaction).getVisibility());
// Has availability.
assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
+ assertEquals(result.findViewById(R.id.availability).getContentDescription(),
+ mContext.getString(R.string.person_available));
// Has person icon.
assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility());
// No status.
@@ -334,6 +336,8 @@
assertEquals(View.GONE, largeResult.findViewById(R.id.last_interaction).getVisibility());
// Has availability.
assertEquals(View.VISIBLE, largeResult.findViewById(R.id.availability).getVisibility());
+ assertEquals(largeResult.findViewById(R.id.availability).getContentDescription(),
+ mContext.getString(R.string.person_available));
// Shows person icon.
assertEquals(View.VISIBLE, largeResult.findViewById(R.id.person_icon).getVisibility());
// No status.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index d44a526..e939411 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -17,6 +17,7 @@
import static junit.framework.Assert.assertTrue;
import static junit.framework.TestCase.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
@@ -327,4 +328,77 @@
assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
assertTrue(mCastTile.getState().secondaryLabel.toString().startsWith(connected.name));
}
+
+ @Test
+ public void testExpandView_wifiNotConnected() {
+ mCastTile.refreshState();
+ mTestableLooper.processAllMessages();
+
+ assertFalse(mCastTile.getState().forceExpandIcon);
+ }
+
+ @Test
+ public void testExpandView_wifiEnabledNotCasting() {
+ enableWifiAndProcessMessages();
+
+ assertTrue(mCastTile.getState().forceExpandIcon);
+ }
+
+ @Test
+ public void testExpandView_casting_projection() {
+ CastController.CastDevice device = new CastController.CastDevice();
+ device.state = CastController.CastDevice.STATE_CONNECTED;
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(device);
+ when(mController.getCastDevices()).thenReturn(devices);
+
+ enableWifiAndProcessMessages();
+
+ assertFalse(mCastTile.getState().forceExpandIcon);
+ }
+
+ @Test
+ public void testExpandView_connecting_projection() {
+ CastController.CastDevice connecting = new CastController.CastDevice();
+ connecting.state = CastDevice.STATE_CONNECTING;
+ connecting.name = "Test Casting Device";
+
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(connecting);
+ when(mController.getCastDevices()).thenReturn(devices);
+
+ enableWifiAndProcessMessages();
+
+ assertFalse(mCastTile.getState().forceExpandIcon);
+ }
+
+ @Test
+ public void testExpandView_casting_mediaRoute() {
+ CastController.CastDevice device = new CastController.CastDevice();
+ device.state = CastDevice.STATE_CONNECTED;
+ device.tag = mock(MediaRouter.RouteInfo.class);
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(device);
+ when(mController.getCastDevices()).thenReturn(devices);
+
+ enableWifiAndProcessMessages();
+
+ assertTrue(mCastTile.getState().forceExpandIcon);
+ }
+
+ @Test
+ public void testExpandView_connecting_mediaRoute() {
+ CastController.CastDevice connecting = new CastController.CastDevice();
+ connecting.state = CastDevice.STATE_CONNECTING;
+ connecting.tag = mock(RouteInfo.class);
+ connecting.name = "Test Casting Device";
+
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(connecting);
+ when(mController.getCastDevices()).thenReturn(devices);
+
+ enableWifiAndProcessMessages();
+
+ assertTrue(mCastTile.getState().forceExpandIcon);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index a70c2be..17797b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -30,6 +30,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -256,6 +257,19 @@
}
@Test
+ public void testGetServiceLabelUnsafe_recreateWalletClient() {
+ doAnswer(invocation -> {
+ throw new Exception("Bad service label.");
+ }).when(mQuickAccessWalletClient).getServiceLabel();
+
+ QSTile.State state = new QSTile.State();
+
+ mTile.handleUpdateState(state, null);
+
+ verify(mController).reCreateWalletClient();
+ }
+
+ @Test
public void testHandleUpdateState_updateLabelAndIcon() {
QSTile.State state = new QSTile.State();
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index 50b27a0..d04698c 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -519,15 +519,6 @@
// user has completed setup.
return intercept && isUserSetupComplete();
}
-
- public boolean isCameraDoubleTapPowerEnabled() {
- return mCameraDoubleTapPowerEnabled;
- }
-
- public boolean isEmergencyGestureEnabled() {
- return mEmergencyGestureEnabled;
- }
-
/**
* @return true if camera was launched, false otherwise.
*/
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 7789d60..33bc212 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1090,8 +1090,7 @@
*/
private void finishOrPause(@NonNull IBinder clientId, boolean triggerCallbackIfNeeded,
boolean isPausing) {
- int indexOfToken = mInProgressEvents != null
- ? mInProgressEvents.indexOfKey(clientId) : -1;
+ int indexOfToken = isRunning() ? mInProgressEvents.indexOfKey(clientId) : -1;
if (indexOfToken < 0) {
finishPossiblyPaused(clientId, isPausing);
return;
@@ -1145,7 +1144,7 @@
// Finish or pause (no-op) an already paused op
private void finishPossiblyPaused(@NonNull IBinder clientId, boolean isPausing) {
- if (mPausedInProgressEvents == null) {
+ if (!isPaused()) {
Slog.wtf(TAG, "No ops running or paused");
return;
}
@@ -1186,7 +1185,7 @@
* Pause all currently started ops. This will create a HistoricalRegistry
*/
public void pause() {
- if (mInProgressEvents == null) {
+ if (!isRunning()) {
return;
}
@@ -1211,7 +1210,7 @@
* times, but keep all other values the same
*/
public void resume() {
- if (mPausedInProgressEvents == null) {
+ if (!isPaused()) {
return;
}
@@ -1245,7 +1244,7 @@
*/
void onClientDeath(@NonNull IBinder clientId) {
synchronized (AppOpsService.this) {
- if (mInProgressEvents == null && mPausedInProgressEvents == null) {
+ if (!isPaused() && !isRunning()) {
return;
}
@@ -1266,7 +1265,7 @@
* @param newState The new state
*/
public void onUidStateChanged(@AppOpsManager.UidState int newState) {
- if (mInProgressEvents == null && mPausedInProgressEvents == null) {
+ if (!isPaused() && !isRunning()) {
return;
}
@@ -1350,12 +1349,15 @@
* @param opToAdd The op to add
*/
public void add(@NonNull AttributedOp opToAdd) {
- if (opToAdd.mInProgressEvents != null) {
- Slog.w(TAG, "Ignoring " + opToAdd.mInProgressEvents.size() + " running app-ops");
+ if (opToAdd.isRunning() || opToAdd.isPaused()) {
+ ArrayMap<IBinder, InProgressStartOpEvent> ignoredEvents = opToAdd.isRunning()
+ ? opToAdd.mInProgressEvents : opToAdd.mPausedInProgressEvents;
+ Slog.w(TAG, "Ignoring " + ignoredEvents.size() + " app-ops, running: "
+ + opToAdd.isRunning());
- int numInProgressEvents = opToAdd.mInProgressEvents.size();
+ int numInProgressEvents = ignoredEvents.size();
for (int i = 0; i < numInProgressEvents; i++) {
- InProgressStartOpEvent event = opToAdd.mInProgressEvents.valueAt(i);
+ InProgressStartOpEvent event = ignoredEvents.valueAt(i);
event.finish();
mInProgressStartOpEventPool.release(event);
@@ -1367,11 +1369,11 @@
}
public boolean isRunning() {
- return mInProgressEvents != null;
+ return mInProgressEvents != null && !mInProgressEvents.isEmpty();
}
public boolean isPaused() {
- return mPausedInProgressEvents != null;
+ return mPausedInProgressEvents != null && !mPausedInProgressEvents.isEmpty();
}
boolean hasAnyTime() {
@@ -1401,7 +1403,7 @@
LongSparseArray<NoteOpEvent> accessEvents = deepClone(mAccessEvents);
// Add in progress events as access events
- if (mInProgressEvents != null) {
+ if (isRunning()) {
long now = SystemClock.elapsedRealtime();
int numInProgressEvents = mInProgressEvents.size();
@@ -2041,9 +2043,12 @@
attributionNum++) {
AttributedOp attributedOp = op.mAttributions.valueAt(attributionNum);
- while (attributedOp.mInProgressEvents != null) {
+ while (attributedOp.isRunning()) {
attributedOp.finished(attributedOp.mInProgressEvents.keyAt(0));
}
+ while (attributedOp.isPaused()) {
+ attributedOp.finished(attributedOp.mPausedInProgressEvents.keyAt(0));
+ }
}
}
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 1acbde9..565c9ae 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -24,8 +24,7 @@
import static android.os.PowerWhitelistManager.REASON_VPN;
import static android.os.UserHandle.PER_USER_RANGE;
-import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkNotNull;
+import static java.util.Objects.requireNonNull;
import android.Manifest;
import android.annotation.NonNull;
@@ -1098,13 +1097,14 @@
return Process.myUid();
}
PackageManager pm = mContext.getPackageManager();
- return Binder.withCleanCallingIdentity(() -> {
- try {
- return pm.getPackageUidAsUser(app, userId);
- } catch (NameNotFoundException e) {
- return -1;
- }
- });
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return pm.getPackageUidAsUser(app, userId);
+ } catch (NameNotFoundException e) {
+ return -1;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
private boolean doesPackageTargetAtLeastQ(String packageName) {
@@ -1280,15 +1280,16 @@
// We are user controlled, not driven by NetworkRequest.
}
};
- Binder.withCleanCallingIdentity(() -> {
- try {
- mNetworkAgent.register();
- } catch (final Exception e) {
- // If register() throws, don't keep an unregistered agent.
- mNetworkAgent = null;
- throw e;
- }
- });
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mNetworkAgent.register();
+ } catch (final Exception e) {
+ // If register() throws, don't keep an unregistered agent.
+ mNetworkAgent = null;
+ throw e;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
mNetworkAgent.setUnderlyingNetworks((mConfig.underlyingNetworks != null)
? Arrays.asList(mConfig.underlyingNetworks) : null);
updateState(DetailedState.CONNECTED, "agentConnect");
@@ -2026,13 +2027,16 @@
}
private void enforceNotRestrictedUser() {
- Binder.withCleanCallingIdentity(() -> {
+ final long token = Binder.clearCallingIdentity();
+ try {
final UserInfo user = mUserManager.getUserInfo(mUserId);
if (user.isRestricted()) {
throw new SecurityException("Restricted users cannot configure VPNs");
}
- });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
/**
@@ -2825,8 +2829,10 @@
LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd, VpnProfile profile) {
super(TAG);
- checkArgument(racoon != null || mtpd != null, "Arguments to racoon and mtpd "
- + "must not both be null");
+ if (racoon == null && mtpd == null) {
+ throw new IllegalArgumentException(
+ "Arguments to racoon and mtpd must not both be null");
+ }
mConfig = config;
mDaemons = new String[] {"racoon", "mtpd"};
// TODO: clear arguments from memory once launched
@@ -3151,8 +3157,8 @@
*/
public synchronized boolean provisionVpnProfile(
@NonNull String packageName, @NonNull VpnProfile profile) {
- checkNotNull(packageName, "No package name provided");
- checkNotNull(profile, "No profile provided");
+ requireNonNull(packageName, "No package name provided");
+ requireNonNull(profile, "No profile provided");
verifyCallingUidAndPackage(packageName);
enforceNotRestrictedUser();
@@ -3169,12 +3175,12 @@
}
// Permissions checked during startVpnProfile()
- Binder.withCleanCallingIdentity(
- () -> {
- getVpnProfileStore().put(
- getProfileNameForPackage(packageName),
- encodedProfile);
- });
+ final long token = Binder.clearCallingIdentity();
+ try {
+ getVpnProfileStore().put(getProfileNameForPackage(packageName), encodedProfile);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
// TODO: if package has CONTROL_VPN, grant the ACTIVATE_PLATFORM_VPN appop.
// This mirrors the prepareAndAuthorize that is used by VpnService.
@@ -3194,26 +3200,28 @@
*/
public synchronized void deleteVpnProfile(
@NonNull String packageName) {
- checkNotNull(packageName, "No package name provided");
+ requireNonNull(packageName, "No package name provided");
verifyCallingUidAndPackage(packageName);
enforceNotRestrictedUser();
- Binder.withCleanCallingIdentity(
- () -> {
- // If this profile is providing the current VPN, turn it off, disabling
- // always-on as well if enabled.
- if (isCurrentIkev2VpnLocked(packageName)) {
- if (mAlwaysOn) {
- // Will transitively call prepareInternal(VpnConfig.LEGACY_VPN).
- setAlwaysOnPackage(null, false, null);
- } else {
- prepareInternal(VpnConfig.LEGACY_VPN);
- }
- }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // If this profile is providing the current VPN, turn it off, disabling
+ // always-on as well if enabled.
+ if (isCurrentIkev2VpnLocked(packageName)) {
+ if (mAlwaysOn) {
+ // Will transitively call prepareInternal(VpnConfig.LEGACY_VPN).
+ setAlwaysOnPackage(null, false, null);
+ } else {
+ prepareInternal(VpnConfig.LEGACY_VPN);
+ }
+ }
- getVpnProfileStore().remove(getProfileNameForPackage(packageName));
- });
+ getVpnProfileStore().remove(getProfileNameForPackage(packageName));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
/**
@@ -3247,7 +3255,7 @@
*/
public synchronized void startVpnProfile(
@NonNull String packageName) {
- checkNotNull(packageName, "No package name provided");
+ requireNonNull(packageName, "No package name provided");
enforceNotRestrictedUser();
@@ -3256,15 +3264,17 @@
throw new SecurityException("User consent not granted for package " + packageName);
}
- Binder.withCleanCallingIdentity(
- () -> {
- final VpnProfile profile = getVpnProfilePrivileged(packageName);
- if (profile == null) {
- throw new IllegalArgumentException("No profile found for " + packageName);
- }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final VpnProfile profile = getVpnProfilePrivileged(packageName);
+ if (profile == null) {
+ throw new IllegalArgumentException("No profile found for " + packageName);
+ }
- startVpnProfilePrivileged(profile, packageName);
- });
+ startVpnProfilePrivileged(profile, packageName);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
private synchronized void startVpnProfilePrivileged(
@@ -3325,7 +3335,7 @@
* @param packageName the package name of the app provisioning this profile
*/
public synchronized void stopVpnProfile(@NonNull String packageName) {
- checkNotNull(packageName, "No package name provided");
+ requireNonNull(packageName, "No package name provided");
enforceNotRestrictedUser();
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index a4a5f96..cb2cd14 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -778,7 +778,6 @@
mScreenBrightnessThresholds.getBrighteningThreshold(newScreenAutoBrightness));
mScreenDarkeningThreshold = clampScreenBrightness(
mScreenBrightnessThresholds.getDarkeningThreshold(newScreenAutoBrightness));
- mHbmController.onAutoBrightnessChanged(mScreenAutoBrightness);
if (sendUpdate) {
mCallbacks.updateBrightness();
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 9d8ca9a..7d06d6e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -705,10 +705,8 @@
final BrightnessPair brightnessPair =
index < 0 ? null : mDisplayBrightnesses.valueAt(index);
if (index < 0 || (mDisplayStates.valueAt(index) == state
- && BrightnessSynchronizer.floatEquals(
- brightnessPair.brightness, brightnessState)
- && BrightnessSynchronizer.floatEquals(
- brightnessPair.sdrBrightness, sdrBrightnessState))) {
+ && brightnessPair.brightness == brightnessState
+ && brightnessPair.sdrBrightness == sdrBrightnessState)) {
return; // Display no longer exists or no change.
}
@@ -1281,6 +1279,11 @@
sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
scheduleTraversalLocked(false);
mPersistentDataStore.saveIfNeeded();
+
+ DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+ if (dpc != null) {
+ dpc.onDisplayChanged();
+ }
}
private void handleLogicalDisplayFrameRateOverridesChangedLocked(
@@ -1312,11 +1315,6 @@
if (work != null) {
mHandler.post(work);
}
- final int displayId = display.getDisplayIdLocked();
- DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
- if (dpc != null) {
- dpc.onDisplayChanged();
- }
handleLogicalDisplayChangedLocked(display);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 20d364e..b6d65197 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -511,7 +511,7 @@
mSkipScreenOnBrightnessRamp = resources.getBoolean(
com.android.internal.R.bool.config_skipScreenOnBrightnessRamp);
- mHbmController = createHbmController();
+ mHbmController = createHbmControllerLocked();
// Seed the cached brightness
saveBrightnessInfo(getScreenBrightnessSetting());
@@ -717,6 +717,7 @@
final String uniqueId = device.getUniqueId();
final DisplayDeviceConfig config = device.getDisplayDeviceConfig();
final IBinder token = device.getDisplayTokenLocked();
+ final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
mHandler.post(() -> {
if (mDisplayDevice == device) {
return;
@@ -724,7 +725,7 @@
mDisplayDevice = device;
mUniqueDisplayId = uniqueId;
mDisplayDeviceConfig = config;
- loadFromDisplayDeviceConfig(token);
+ loadFromDisplayDeviceConfig(token, info);
});
}
@@ -765,7 +766,7 @@
}
}
- private void loadFromDisplayDeviceConfig(IBinder token) {
+ private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info) {
// All properties that depend on the associated DisplayDevice and the DDC must be
// updated here.
loadAmbientLightSensor();
@@ -774,7 +775,8 @@
loadNitsRange(mContext.getResources());
setUpAutoBrightness(mContext.getResources(), mHandler);
reloadReduceBrightColours();
- mHbmController.resetHbmData(token, mDisplayDeviceConfig.getHighBrightnessModeData());
+ mHbmController.resetHbmData(info.width, info.height, token,
+ mDisplayDeviceConfig.getHighBrightnessModeData(), mBrightnessSetting);
}
private void sendUpdatePowerState() {
@@ -1343,15 +1345,15 @@
if (mHbmController.getHighBrightnessMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
&& ((mBrightnessReason.modifier & BrightnessReason.MODIFIER_DIMMED) == 0
|| (mBrightnessReason.modifier & BrightnessReason.MODIFIER_LOW_POWER) == 0)) {
+ // We want to scale HDR brightness level with the SDR level
animateValue = mHbmController.getHdrBrightnessValue();
}
final float currentBrightness = mPowerState.getScreenBrightness();
final float currentSdrBrightness = mPowerState.getSdrScreenBrightness();
if (isValidBrightnessValue(animateValue)
- && (!BrightnessSynchronizer.floatEquals(animateValue, currentBrightness)
- || !BrightnessSynchronizer.floatEquals(
- sdrAnimateValue, currentSdrBrightness))) {
+ && (animateValue != currentBrightness
+ || sdrAnimateValue != currentSdrBrightness)) {
if (initialRampSkip || hasBrightnessBuckets
|| wasOrWillBeInVr || !isDisplayContentVisible || brightnessIsTemporary) {
animateScreenBrightness(animateValue, sdrAnimateValue,
@@ -1514,21 +1516,22 @@
}
}
- private HighBrightnessModeController createHbmController() {
- final DisplayDeviceConfig ddConfig =
- mLogicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig();
+ private HighBrightnessModeController createHbmControllerLocked() {
+ final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
+ final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
final IBinder displayToken =
mLogicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayTokenLocked();
final DisplayDeviceConfig.HighBrightnessModeData hbmData =
ddConfig != null ? ddConfig.getHighBrightnessModeData() : null;
- return new HighBrightnessModeController(mHandler, displayToken, PowerManager.BRIGHTNESS_MIN,
- PowerManager.BRIGHTNESS_MAX, hbmData,
+ final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+ return new HighBrightnessModeController(mHandler, info.width, info.height, displayToken,
+ PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, hbmData,
() -> {
sendUpdatePowerStateLocked();
mHandler.post(mOnBrightnessChangeRunnable);
// TODO(b/192258832): Switch the HBMChangeCallback to a listener pattern.
mAutomaticBrightnessController.update();
- }, mContext);
+ }, mContext, mBrightnessSetting);
}
private void blockScreenOn() {
@@ -1680,11 +1683,10 @@
mHbmController.getCurrentBrightnessMin(), mHbmController.getCurrentBrightnessMax());
}
- // Checks whether the brightness is within the valid brightness range, not including the off or
- // invalid states.
- private boolean isValidBrightnessValue(float brightnessState) {
- return brightnessState >= PowerManager.BRIGHTNESS_MIN
- && brightnessState <= PowerManager.BRIGHTNESS_MAX;
+ // Checks whether the brightness is within the valid brightness range, not including off.
+ private boolean isValidBrightnessValue(float brightness) {
+ return brightness >= PowerManager.BRIGHTNESS_MIN
+ && brightness <= PowerManager.BRIGHTNESS_MAX;
}
private void animateScreenBrightness(float target, float sdrTarget, float rate) {
@@ -2014,6 +2016,9 @@
}
private void putScreenBrightnessSetting(float brightnessValue, boolean updateCurrent) {
+ if (!isValidBrightnessValue(brightnessValue)) {
+ return;
+ }
if (updateCurrent) {
setCurrentScreenBrightness(brightnessValue);
}
@@ -2060,8 +2065,7 @@
|| mPendingScreenBrightnessSetting < 0.0f)) {
return brightnessSplineChanged;
}
- if (BrightnessSynchronizer.floatEquals(
- mCurrentScreenBrightnessSetting, mPendingScreenBrightnessSetting)) {
+ if (mCurrentScreenBrightnessSetting == mPendingScreenBrightnessSetting) {
mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
return brightnessSplineChanged;
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index b58dd38..6af1923 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -26,8 +26,6 @@
import android.view.Choreographer;
import android.view.Display;
-import com.android.internal.display.BrightnessSynchronizer;
-
import java.io.PrintWriter;
/**
@@ -166,10 +164,11 @@
/**
* Sets the display's SDR brightness.
*
- * @param brightness The brightness, ranges from 0.0f (minimum / off) to 1.0f (brightest).
+ * @param brightness The brightness, ranges from 0.0f (minimum) to 1.0f (brightest), or is -1f
+ * (off).
*/
public void setSdrScreenBrightness(float brightness) {
- if (!BrightnessSynchronizer.floatEquals(mSdrScreenBrightness, brightness)) {
+ if (mSdrScreenBrightness != brightness) {
if (DEBUG) {
Slog.d(TAG, "setSdrScreenBrightness: brightness=" + brightness);
}
@@ -192,10 +191,11 @@
/**
* Sets the display brightness.
*
- * @param brightness The brightness, ranges from 0.0f (minimum / off) to 1.0f (brightest).
+ * @param brightness The brightness, ranges from 0.0f (minimum) to 1.0f (brightest), or is -1f
+ * (off).
*/
public void setScreenBrightness(float brightness) {
- if (!BrightnessSynchronizer.floatEquals(mScreenBrightness, brightness)) {
+ if (mScreenBrightness != brightness) {
if (DEBUG) {
Slog.d(TAG, "setScreenBrightness: brightness=" + brightness);
}
@@ -432,10 +432,8 @@
public boolean setState(int state, float brightnessState, float sdrBrightnessState) {
synchronized (mLock) {
boolean stateChanged = state != mPendingState;
- boolean backlightChanged =
- !BrightnessSynchronizer.floatEquals(brightnessState, mPendingBacklight)
- || !BrightnessSynchronizer.floatEquals(
- sdrBrightnessState, mPendingSdrBacklight);
+ boolean backlightChanged = brightnessState != mPendingBacklight
+ || sdrBrightnessState != mPendingSdrBacklight;
if (stateChanged || backlightChanged) {
if (DEBUG) {
Slog.d(TAG, "Requesting new screen state: state="
@@ -486,10 +484,8 @@
stateChanged = (state != mActualState);
brightnessState = mPendingBacklight;
sdrBrightnessState = mPendingSdrBacklight;
- backlightChanged =
- !BrightnessSynchronizer.floatEquals(brightnessState, mActualBacklight)
- || !BrightnessSynchronizer.floatEquals(
- sdrBrightnessState, mActualSdrBacklight);
+ backlightChanged = brightnessState != mActualBacklight
+ || sdrBrightnessState != mActualSdrBacklight;
if (!stateChanged) {
// State changed applied, notify outer class.
postScreenUpdateThreadSafe();
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index d629422..e0b4d47 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -31,11 +31,13 @@
import android.os.Temperature;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.MathUtils;
import android.util.Slog;
import android.util.TimeUtils;
import android.view.SurfaceControlHdrLayerInfoListener;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.BrightnessSetting.BrightnessSettingListener;
import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData;
import com.android.server.display.DisplayManagerService.Clock;
@@ -56,6 +58,8 @@
private static final boolean DEBUG = false;
+ private static final float HDR_PERCENT_OF_SCREEN_REQUIRED = 0.50f;
+
private final float mBrightnessMin;
private final float mBrightnessMax;
private final Handler mHandler;
@@ -66,6 +70,7 @@
private final Context mContext;
private final SettingsObserver mSettingsObserver;
private final Injector mInjector;
+ private final BrightnessSettingListener mBrightnessSettingListener = this::onBrightnessChanged;
private SurfaceControlHdrLayerInfoListener mHdrListener;
private HighBrightnessModeData mHbmData;
@@ -74,11 +79,15 @@
private boolean mIsInAllowedAmbientRange = false;
private boolean mIsTimeAvailable = false;
private boolean mIsAutoBrightnessEnabled = false;
- private float mAutoBrightness;
+ private float mBrightness;
private int mHbmMode = BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
private boolean mIsHdrLayerPresent = false;
private boolean mIsThermalStatusWithinLimit = true;
private boolean mIsBlockedByLowPowerMode = false;
+ private int mWidth;
+ private int mHeight;
+ private BrightnessSetting mBrightnessSetting;
+ private float mAmbientLux;
/**
* If HBM is currently running, this is the start time for the current HBM session.
@@ -92,30 +101,32 @@
*/
private LinkedList<HbmEvent> mEvents = new LinkedList<>();
- HighBrightnessModeController(Handler handler, IBinder displayToken, float brightnessMin,
- float brightnessMax, HighBrightnessModeData hbmData, Runnable hbmChangeCallback,
- Context context) {
- this(new Injector(), handler, displayToken, brightnessMin, brightnessMax,
- hbmData, hbmChangeCallback, context);
+ HighBrightnessModeController(Handler handler, int width, int height, IBinder displayToken,
+ float brightnessMin, float brightnessMax, HighBrightnessModeData hbmData,
+ Runnable hbmChangeCallback, Context context, BrightnessSetting brightnessSetting) {
+ this(new Injector(), handler, width, height, displayToken, brightnessMin, brightnessMax,
+ hbmData, hbmChangeCallback, context, brightnessSetting);
}
@VisibleForTesting
- HighBrightnessModeController(Injector injector, Handler handler, IBinder displayToken,
- float brightnessMin, float brightnessMax, HighBrightnessModeData hbmData,
- Runnable hbmChangeCallback, Context context) {
+ HighBrightnessModeController(Injector injector, Handler handler, int width, int height,
+ IBinder displayToken, float brightnessMin, float brightnessMax,
+ HighBrightnessModeData hbmData, Runnable hbmChangeCallback,
+ Context context, BrightnessSetting brightnessSetting) {
mInjector = injector;
+ mContext = context;
mClock = injector.getClock();
mHandler = handler;
mBrightnessMin = brightnessMin;
mBrightnessMax = brightnessMax;
+ mBrightness = brightnessSetting.getBrightness();
mHbmChangeCallback = hbmChangeCallback;
- mContext = context;
- mAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
- mRecalcRunnable = this::recalculateTimeAllowance;
- mHdrListener = new HdrListener();
mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
- resetHbmData(displayToken, hbmData);
+ mRecalcRunnable = this::recalculateTimeAllowance;
+ mHdrListener = new HdrListener();
+
+ resetHbmData(width, height, displayToken, hbmData, brightnessSetting);
}
void setAutoBrightnessEnabled(boolean isEnabled) {
@@ -123,7 +134,7 @@
return;
}
if (DEBUG) {
- Slog.d(TAG, "setAutoBrightness( " + isEnabled + " )");
+ Slog.d(TAG, "setAutoBrightnessEnabled( " + isEnabled + " )");
}
mIsAutoBrightnessEnabled = isEnabled;
mIsInAllowedAmbientRange = false; // reset when auto-brightness switches
@@ -147,11 +158,22 @@
}
}
+ float getNormalBrightnessMax() {
+ return deviceSupportsHbm() ? mHbmData.transitionPoint : mBrightnessMax;
+ }
+
float getHdrBrightnessValue() {
- return mBrightnessMax;
+ // For HDR brightness, we take the current brightness and scale it to the max. The reason
+ // we do this is because we want brightness to go to HBM max when it would normally go
+ // to normal max, meaning it should not wait to go to 10000 lux (or whatever the transition
+ // point happens to be) in order to go full HDR. Likewise, HDR on manual brightness should
+ // automatically scale the brightness without forcing the user to adjust to higher values.
+ return MathUtils.map(getCurrentBrightnessMin(), getCurrentBrightnessMax(),
+ mBrightnessMin, mBrightnessMax, mBrightness);
}
void onAmbientLuxChange(float ambientLux) {
+ mAmbientLux = ambientLux;
if (!deviceSupportsHbm() || !mIsAutoBrightnessEnabled) {
return;
}
@@ -163,17 +185,17 @@
}
}
- void onAutoBrightnessChanged(float autoBrightness) {
+ @VisibleForTesting
+ void onBrightnessChanged(float brightness) {
if (!deviceSupportsHbm()) {
return;
}
- final float oldAutoBrightness = mAutoBrightness;
- mAutoBrightness = autoBrightness;
+ mBrightness = brightness;
// If we are starting or ending a high brightness mode session, store the current
// session in mRunningStartTimeMillis, or the old one in mEvents.
final boolean wasHbmDrainingAvailableTime = mRunningStartTimeMillis != -1;
- final boolean shouldHbmDrainAvailableTime = mAutoBrightness > mHbmData.transitionPoint
+ final boolean shouldHbmDrainAvailableTime = mBrightness > mHbmData.transitionPoint
&& !mIsHdrLayerPresent;
if (wasHbmDrainingAvailableTime != shouldHbmDrainAvailableTime) {
final long currentTime = mClock.uptimeMillis();
@@ -202,8 +224,12 @@
mSettingsObserver.stopObserving();
}
- void resetHbmData(IBinder displayToken, HighBrightnessModeData hbmData) {
+ void resetHbmData(int width, int height, IBinder displayToken, HighBrightnessModeData hbmData,
+ BrightnessSetting brightnessSetting) {
+ mWidth = width;
+ mHeight = height;
mHbmData = hbmData;
+ resetBrightnessSetting(brightnessSetting);
unregisterHdrListener();
mSkinThermalStatusObserver.stopObserving();
mSettingsObserver.stopObserving();
@@ -227,21 +253,23 @@
private void dumpLocal(PrintWriter pw) {
pw.println("HighBrightnessModeController:");
+ pw.println(" mBrightness=" + mBrightness);
pw.println(" mCurrentMin=" + getCurrentBrightnessMin());
pw.println(" mCurrentMax=" + getCurrentBrightnessMax());
pw.println(" mHbmMode=" + BrightnessInfo.hbmToString(mHbmMode));
- pw.println(" remainingTime=" + calculateRemainingTime(mClock.uptimeMillis()));
pw.println(" mHbmData=" + mHbmData);
+ pw.println(" mAmbientLux=" + mAmbientLux);
pw.println(" mIsInAllowedAmbientRange=" + mIsInAllowedAmbientRange);
- pw.println(" mIsTimeAvailable= " + mIsTimeAvailable);
pw.println(" mIsAutoBrightnessEnabled=" + mIsAutoBrightnessEnabled);
- pw.println(" mAutoBrightness=" + mAutoBrightness);
pw.println(" mIsHdrLayerPresent=" + mIsHdrLayerPresent);
pw.println(" mBrightnessMin=" + mBrightnessMin);
pw.println(" mBrightnessMax=" + mBrightnessMax);
+ pw.println(" remainingTime=" + calculateRemainingTime(mClock.uptimeMillis()));
+ pw.println(" mIsTimeAvailable= " + mIsTimeAvailable);
pw.println(" mRunningStartTimeMillis=" + TimeUtils.formatUptime(mRunningStartTimeMillis));
pw.println(" mIsThermalStatusWithinLimit=" + mIsThermalStatusWithinLimit);
pw.println(" mIsBlockedByLowPowerMode=" + mIsBlockedByLowPowerMode);
+ pw.println(" width*height=" + mWidth + "*" + mHeight);
pw.println(" mEvents=");
final long currentTime = mClock.uptimeMillis();
long lastStartTime = currentTime;
@@ -268,9 +296,26 @@
return event.startTimeMillis;
}
+ private void resetBrightnessSetting(BrightnessSetting brightnessSetting) {
+ if (mBrightnessSetting != null) {
+ mBrightnessSetting.unregisterListener(mBrightnessSettingListener);
+ }
+ mBrightnessSetting = brightnessSetting;
+ if (mBrightnessSetting != null) {
+ mBrightnessSetting.registerListener(mBrightnessSettingListener);
+ }
+ }
+
private boolean isCurrentlyAllowed() {
- return mIsHdrLayerPresent
- || (mIsAutoBrightnessEnabled && mIsTimeAvailable && mIsInAllowedAmbientRange
+ // Returns true if HBM is allowed (above the ambient lux threshold) and there's still
+ // time within the current window for additional HBM usage. We return false if there is an
+ // HDR layer because we don't want the brightness MAX to change for HDR, which has its
+ // brightness scaled in a different way than sunlight HBM that doesn't require changing
+ // the MAX. HDR also needs to work under manual brightness which never adjusts the
+ // brightness maximum; so we implement HDR-HBM in a way that doesn't adjust the max.
+ // See {@link #getHdrBrightnessValue}.
+ return !mIsHdrLayerPresent
+ && (mIsAutoBrightnessEnabled && mIsTimeAvailable && mIsInAllowedAmbientRange
&& mIsThermalStatusWithinLimit && !mIsBlockedByLowPowerMode);
}
@@ -334,13 +379,13 @@
// or if brightness is already in the high range, if there is any time left at all.
final boolean isAllowedWithoutRestrictions = remainingTime >= mHbmData.timeMinMillis;
final boolean isOnlyAllowedToStayOn = !isAllowedWithoutRestrictions
- && remainingTime > 0 && mAutoBrightness > mHbmData.transitionPoint;
+ && remainingTime > 0 && mBrightness > mHbmData.transitionPoint;
mIsTimeAvailable = isAllowedWithoutRestrictions || isOnlyAllowedToStayOn;
// Calculate the time at which we want to recalculate mIsTimeAvailable in case a lux or
// brightness change doesn't happen before then.
long nextTimeout = -1;
- if (mAutoBrightness > mHbmData.transitionPoint) {
+ if (mBrightness > mHbmData.transitionPoint) {
// if we're in high-lux now, timeout when we run out of allowed time.
nextTimeout = currentTime + remainingTime;
} else if (!mIsTimeAvailable && mEvents.size() > 0) {
@@ -370,7 +415,7 @@
+ ", mIsInAllowedAmbientRange: " + mIsInAllowedAmbientRange
+ ", mIsThermalStatusWithinLimit: " + mIsThermalStatusWithinLimit
+ ", mIsBlockedByLowPowerMode: " + mIsBlockedByLowPowerMode
- + ", brightness: " + mAutoBrightness
+ + ", mBrightness: " + mBrightness
+ ", RunningStartTimeMillis: " + mRunningStartTimeMillis
+ ", nextTimeout: " + (nextTimeout != -1 ? (nextTimeout - currentTime) : -1)
+ ", events: " + mEvents);
@@ -447,11 +492,12 @@
public void onHdrInfoChanged(IBinder displayToken, int numberOfHdrLayers,
int maxW, int maxH, int flags) {
mHandler.post(() -> {
- mIsHdrLayerPresent = numberOfHdrLayers > 0;
- // Calling the auto-brightness update so that we can recalculate
- // auto-brightness with HDR in mind. When HDR layers are present,
- // we don't limit auto-brightness' HBM time limits.
- onAutoBrightnessChanged(mAutoBrightness);
+ mIsHdrLayerPresent = numberOfHdrLayers > 0
+ && (float) (maxW * maxH)
+ >= ((float) (mWidth * mHeight) * HDR_PERCENT_OF_SCREEN_REQUIRED);
+ // Calling the brightness update so that we can recalculate
+ // brightness with HDR in mind.
+ onBrightnessChanged(mBrightness);
});
}
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 2f17481..f953cc8 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -648,12 +648,11 @@
public Runnable requestDisplayStateLocked(final int state, final float brightnessState,
final float sdrBrightnessState) {
// Assume that the brightness is off if the display is being turned off.
- assert state != Display.STATE_OFF || BrightnessSynchronizer.floatEquals(
- brightnessState, PowerManager.BRIGHTNESS_OFF_FLOAT);
+ assert state != Display.STATE_OFF
+ || brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT;
final boolean stateChanged = (mState != state);
- final boolean brightnessChanged =
- !(BrightnessSynchronizer.floatEquals(mBrightnessState, brightnessState)
- && BrightnessSynchronizer.floatEquals(mSdrBrightnessState, sdrBrightnessState));
+ final boolean brightnessChanged = mBrightnessState != brightnessState
+ || mSdrBrightnessState != sdrBrightnessState;
if (stateChanged || brightnessChanged) {
final long physicalDisplayId = mPhysicalDisplayId;
final IBinder token = getDisplayTokenLocked();
@@ -807,8 +806,7 @@
}
private float brightnessToBacklight(float brightness) {
- if (BrightnessSynchronizer.floatEquals(
- brightness, PowerManager.BRIGHTNESS_OFF_FLOAT)) {
+ if (brightness == PowerManager.BRIGHTNESS_OFF_FLOAT) {
return PowerManager.BRIGHTNESS_OFF_FLOAT;
} else {
return getDisplayDeviceConfig().getBacklightFromBrightness(brightness);
diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java
index 20feafa..ed3b15f 100644
--- a/services/core/java/com/android/server/display/RampAnimator.java
+++ b/services/core/java/com/android/server/display/RampAnimator.java
@@ -20,8 +20,6 @@
import android.util.FloatProperty;
import android.view.Choreographer;
-import com.android.internal.display.BrightnessSynchronizer;
-
/**
* A custom animator that progressively updates a property value at
* a given variable rate until it reaches a particular target value.
@@ -157,10 +155,10 @@
}
final float oldCurrentValue = mCurrentValue;
mCurrentValue = mAnimatedValue;
- if (!BrightnessSynchronizer.floatEquals(oldCurrentValue, mCurrentValue)) {
+ if (oldCurrentValue != mCurrentValue) {
mProperty.setValue(mObject, mCurrentValue);
}
- if (!BrightnessSynchronizer.floatEquals(mTargetValue, mCurrentValue)) {
+ if (mTargetValue != mCurrentValue) {
postAnimationCallback();
} else {
mAnimating = false;
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 8787649..206682e 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -1343,8 +1343,11 @@
// it is handled (otherwise the wake lock would be leaked).
mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
boolean success = mHandler.post(() -> {
- runnable.run();
- mWakeLock.release();
+ try {
+ runnable.run();
+ } finally {
+ mWakeLock.release();
+ }
});
if (!success) {
mWakeLock.release();
diff --git a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
index 981e759..2519bbf 100644
--- a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
+++ b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
@@ -195,7 +195,7 @@
}
private String getSessionIdInternal(int userId) {
- byte[] byteId = new byte[16]; // 128 bits
+ byte[] byteId = new byte[12]; // 96 bits (128 bits when expanded to Base64 string)
mSecureRandom.nextBytes(byteId);
String id = Base64.encodeToString(
byteId, Base64.NO_PADDING | Base64.NO_WRAP | Base64.URL_SAFE);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2634f6c..74ef4ca 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6196,8 +6196,10 @@
// Fix the notification as best we can.
try {
fixNotification(notification, pkg, tag, id, userId);
-
} catch (Exception e) {
+ if (notification.isForegroundService()) {
+ throw new SecurityException("Invalid FGS notification", e);
+ }
Slog.e(TAG, "Cannot fix notification", e);
return;
}
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index de45466..e28540c 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledAfter;
import android.content.pm.ApplicationInfo;
import android.content.pm.Signature;
@@ -79,13 +80,13 @@
/**
* Allows opt-in to the latest targetSdkVersion enforced changes without changing target SDK.
- * Turning this change off for an app targeting the latest SDK or higher is a no-op.
+ * Turning this change on for an app targeting the latest SDK or higher is a no-op.
*
* <p>Has no effect for apps using shared user id.
*
* TODO(b/143539591): Update description with relevant SELINUX changes this opts in to.
*/
- @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.R)
+ @Disabled
@ChangeId
static final long SELINUX_LATEST_CHANGES = 143539591L;
@@ -364,7 +365,8 @@
}
final ApplicationInfo appInfo = pkg.toAppInfoWithoutState();
if (compatibility.isChangeEnabledInternal(SELINUX_LATEST_CHANGES, appInfo)) {
- return Math.max(android.os.Build.VERSION_CODES.S, pkg.getTargetSdkVersion());
+ return Math.max(
+ android.os.Build.VERSION_CODES.CUR_DEVELOPMENT, pkg.getTargetSdkVersion());
} else if (compatibility.isChangeEnabledInternal(SELINUX_R_CHANGES, appInfo)) {
return Math.max(android.os.Build.VERSION_CODES.R, pkg.getTargetSdkVersion());
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index e6adeb3..fb4d96e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -904,7 +904,9 @@
}
} else {
// handled by single key or another power key policy.
- mSingleKeyGestureDetector.reset();
+ if (!mSingleKeyGestureDetector.isKeyIntercepted(KEYCODE_POWER)) {
+ mSingleKeyGestureDetector.reset();
+ }
}
finishPowerKeyPress();
@@ -918,7 +920,6 @@
}
private void powerPress(long eventTime, int count, boolean beganFromNonInteractive) {
- mCameraGestureTriggered = false;
if (mDefaultDisplayPolicy.isScreenOnEarly() && !mDefaultDisplayPolicy.isScreenOnFully()) {
Slog.i(TAG, "Suppressed redundant power key press while "
+ "already in the process of turning the screen on.");
@@ -1068,24 +1069,18 @@
}
private int getMaxMultiPressPowerCount() {
- // GestureLauncherService could handle power multi tap gesture.
- if (mGestureLauncherService != null
- && mGestureLauncherService.isEmergencyGestureEnabled()) {
- return 5; // EMERGENCY_GESTURE_POWER_TAP_COUNT_THRESHOLD
- }
-
+ // The actual max power button press count is 5
+ // (EMERGENCY_GESTURE_POWER_TAP_COUNT_THRESHOLD), which is coming from
+ // GestureLauncherService.
+ // To speed up the handling of single-press of power button inside SingleKeyGestureDetector,
+ // however, we limit the max count to the number of button presses actually handled by the
+ // SingleKeyGestureDetector.
if (mTriplePressOnPowerBehavior != MULTI_PRESS_POWER_NOTHING) {
return 3;
}
if (mDoublePressOnPowerBehavior != MULTI_PRESS_POWER_NOTHING) {
return 2;
}
-
- if (mGestureLauncherService != null
- && mGestureLauncherService.isCameraDoubleTapPowerEnabled()) {
- return 2; // CAMERA_POWER_TAP_COUNT_THRESHOLD
- }
-
return 1;
}
@@ -1972,7 +1967,6 @@
void onPress(long downTime) {
powerPress(downTime, 1 /*count*/,
mSingleKeyGestureDetector.beganFromNonInteractive());
- finishPowerKeyPress();
}
@Override
@@ -1995,7 +1989,6 @@
@Override
void onMultiPress(long downTime, int count) {
powerPress(downTime, count, mSingleKeyGestureDetector.beganFromNonInteractive());
- finishPowerKeyPress();
}
}
@@ -3849,17 +3842,17 @@
if (mGestureLauncherService == null) {
return false;
}
-
+ mCameraGestureTriggered = false;
final MutableBoolean outLaunched = new MutableBoolean(false);
- final boolean gesturedServiceIntercepted = mGestureLauncherService.interceptPowerKeyDown(
- event, interactive, outLaunched);
- if (outLaunched.value) {
- mCameraGestureTriggered = true;
+ mGestureLauncherService.interceptPowerKeyDown(event, interactive, outLaunched);
+ if (!outLaunched.value) {
+ return false;
}
- if (outLaunched.value && mRequestedOrSleepingDefaultDisplay) {
+ mCameraGestureTriggered = true;
+ if (mRequestedOrSleepingDefaultDisplay) {
mCameraGestureTriggeredDuringGoingToSleep = true;
}
- return gesturedServiceIntercepted;
+ return true;
}
/**
@@ -4232,7 +4225,8 @@
mDefaultDisplayRotation.updateOrientationListener();
if (mKeyguardDelegate != null) {
- mKeyguardDelegate.onFinishedGoingToSleep(pmSleepReason, mCameraGestureTriggered);
+ mKeyguardDelegate.onFinishedGoingToSleep(pmSleepReason,
+ mCameraGestureTriggeredDuringGoingToSleep);
}
if (mDisplayFoldController != null) {
mDisplayFoldController.finishedGoingToSleep();
@@ -4428,6 +4422,8 @@
// Called on the DisplayManager's DisplayPowerController thread.
@Override
public void screenTurnedOn(int displayId) {
+ if (DEBUG_WAKEUP) Slog.i(TAG, "Display " + displayId + " turned on...");
+
if (displayId != DEFAULT_DISPLAY) {
return;
}
diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
index 3f4d920..1ef2bf9 100644
--- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
+++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
@@ -272,8 +272,10 @@
if (DEBUG) {
Log.i(TAG, "press key " + KeyEvent.keyCodeToString(event.getKeyCode()));
}
- mActiveRule.onPress(downTime);
- reset();
+ Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, mActiveRule.mKeyCode,
+ 1, downTime);
+ msg.setAsynchronous(true);
+ mHandler.sendMessage(msg);
return true;
}
@@ -316,10 +318,7 @@
}
boolean isKeyIntercepted(int keyCode) {
- if (mActiveRule != null && mActiveRule.shouldInterceptKey(keyCode)) {
- return mHandledByLongPress;
- }
- return false;
+ return mActiveRule != null && mActiveRule.shouldInterceptKey(keyCode);
}
boolean beganFromNonInteractive() {
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index fb8498e..2a47512 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -1301,7 +1301,8 @@
}
/** Implementation of {@link IExternalVibratorService} to be triggered on external control. */
- private final class ExternalVibratorService extends IExternalVibratorService.Stub {
+ @VisibleForTesting
+ final class ExternalVibratorService extends IExternalVibratorService.Stub {
ExternalVibrationDeathRecipient mCurrentExternalDeathRecipient;
@Override
@@ -1332,6 +1333,7 @@
return vibHolder.scale;
}
+ ExternalVibrationHolder cancelingExternalVibration = null;
VibrationThread cancelingVibration = null;
int scale;
synchronized (mLock) {
@@ -1350,16 +1352,18 @@
cancelingVibration = mCurrentVibration;
}
} else {
+ // 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.
+ //
+ // 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.
+ mCurrentExternalVibration.externalVibration.mute();
endVibrationLocked(mCurrentExternalVibration, Vibration.Status.CANCELLED);
+ cancelingExternalVibration = mCurrentExternalVibration;
}
- // At this point we either have an externally controlled vibration playing, or
- // no vibration playing. Since the interface defines that only one externally
- // controlled vibration can play at a time, by returning something other than
- // SCALE_MUTE from this function we can be assured that if we are currently
- // playing vibration, it 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.
mCurrentExternalVibration = new ExternalVibrationHolder(vib);
mCurrentExternalDeathRecipient = new ExternalVibrationDeathRecipient();
vib.linkToDeath(mCurrentExternalDeathRecipient);
@@ -1376,10 +1380,14 @@
+ "external control", e);
}
}
- if (DEBUG) {
- Slog.d(TAG, "Vibrator going under external control.");
+ if (cancelingExternalVibration == null) {
+ // We only need to set external control if it was not already set by another
+ // external vibration.
+ if (DEBUG) {
+ Slog.d(TAG, "Vibrator going under external control.");
+ }
+ setExternalControl(true);
}
- setExternalControl(true);
if (DEBUG) {
Slog.e(TAG, "Playing external vibration: " + vib);
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 50c1c56c..a6ea585 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -551,6 +551,7 @@
if (maxFd > enableThreshold) {
// Do a manual GC to clean up fds that are hanging around as garbage.
System.gc();
+ System.runFinalization();
maxFd = getMaxFd();
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java
index ffb1dd9..8336663 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java
@@ -54,6 +54,12 @@
AppSearchConfig.DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES);
assertThat(appSearchConfig.getCachedLimitConfigMaxDocumentCount()).isEqualTo(
AppSearchConfig.DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_COUNT);
+ assertThat(appSearchConfig.getCachedBytesOptimizeThreshold()).isEqualTo(
+ AppSearchConfig.DEFAULT_BYTES_OPTIMIZE_THRESHOLD);
+ assertThat(appSearchConfig.getCachedTimeOptimizeThresholdMs()).isEqualTo(
+ AppSearchConfig.DEFAULT_TIME_OPTIMIZE_THRESHOLD_MILLIS);
+ assertThat(appSearchConfig.getCachedDocCountOptimizeThreshold()).isEqualTo(
+ AppSearchConfig.DEFAULT_DOC_COUNT_OPTIMIZE_THRESHOLD);
}
@Test
@@ -163,10 +169,8 @@
/**
* Tests if we fall back to {@link AppSearchConfig#DEFAULT_SAMPLING_INTERVAL} if both default
- * sampling
- * interval and custom value are not set in DeviceConfig, and there is some other sampling
- * interval
- * set.
+ * sampling interval and custom value are not set in DeviceConfig, and there is some other
+ * sampling interval set.
*/
@Test
public void testFallbackToDefaultSamplingValue_useHardCodedDefault() {
@@ -269,7 +273,7 @@
}
@Test
- public void testCustomizedValue() {
+ public void testCustomizedValue_maxDocument() {
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
AppSearchConfig.KEY_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES,
Integer.toString(2001),
@@ -285,6 +289,64 @@
}
@Test
+ public void testCustomizedValue_optimizeThreshold() {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_BYTES_OPTIMIZE_THRESHOLD,
+ Integer.toString(147147),
+ false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS,
+ Integer.toString(258258),
+ false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_DOC_COUNT_OPTIMIZE_THRESHOLD,
+ Integer.toString(369369),
+ false);
+
+ AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
+
+ assertThat(appSearchConfig.getCachedBytesOptimizeThreshold()).isEqualTo(147147);
+ assertThat(appSearchConfig.getCachedTimeOptimizeThresholdMs()).isEqualTo(258258);
+ assertThat(appSearchConfig.getCachedDocCountOptimizeThreshold()).isEqualTo(369369);
+ }
+
+ @Test
+ public void testCustomizedValueOverride_optimizeThreshold() {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_BYTES_OPTIMIZE_THRESHOLD,
+ Integer.toString(147147),
+ false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS,
+ Integer.toString(258258),
+ false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_DOC_COUNT_OPTIMIZE_THRESHOLD,
+ Integer.toString(369369),
+ false);
+
+ AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
+
+ // Override
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_BYTES_OPTIMIZE_THRESHOLD,
+ Integer.toString(741741),
+ false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS,
+ Integer.toString(852852),
+ false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_DOC_COUNT_OPTIMIZE_THRESHOLD,
+ Integer.toString(963963),
+ false);
+
+ assertThat(appSearchConfig.getCachedBytesOptimizeThreshold()).isEqualTo(741741);
+ assertThat(appSearchConfig.getCachedTimeOptimizeThresholdMs()).isEqualTo(852852);
+ assertThat(appSearchConfig.getCachedDocCountOptimizeThreshold()).isEqualTo(963963);
+ }
+
+ @Test
public void testNotUsable_afterClose() {
AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
@@ -302,5 +364,14 @@
Assert.assertThrows("Trying to use a closed AppSearchConfig instance.",
IllegalStateException.class,
() -> appSearchConfig.getCachedSamplingIntervalForPutDocumentStats());
+ Assert.assertThrows("Trying to use a closed AppSearchConfig instance.",
+ IllegalStateException.class,
+ () -> appSearchConfig.getCachedBytesOptimizeThreshold());
+ Assert.assertThrows("Trying to use a closed AppSearchConfig instance.",
+ IllegalStateException.class,
+ () -> appSearchConfig.getCachedTimeOptimizeThresholdMs());
+ Assert.assertThrows("Trying to use a closed AppSearchConfig instance.",
+ IllegalStateException.class,
+ () -> appSearchConfig.getCachedDocCountOptimizeThreshold());
}
}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 4abc7ee..d1afa1a 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -39,6 +39,7 @@
"services.usage",
"services.uwb",
"guava",
+ "guava-android-testlib",
"androidx.test.core",
"androidx.test.ext.truth",
"androidx.test.runner",
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/FrameworkOptimizeStrategyTest.java b/services/tests/servicestests/src/com/android/server/appsearch/FrameworkOptimizeStrategyTest.java
similarity index 74%
rename from services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/FrameworkOptimizeStrategyTest.java
rename to services/tests/servicestests/src/com/android/server/appsearch/FrameworkOptimizeStrategyTest.java
index de71d21..8389c85 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/FrameworkOptimizeStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/FrameworkOptimizeStrategyTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 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.
@@ -13,12 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.server.appsearch;
-package com.android.server.appsearch.external.localstorage;
-
-import static com.android.server.appsearch.external.localstorage.FrameworkOptimizeStrategy.BYTES_OPTIMIZE_THRESHOLD;
-import static com.android.server.appsearch.external.localstorage.FrameworkOptimizeStrategy.DOC_COUNT_OPTIMIZE_THRESHOLD;
-import static com.android.server.appsearch.external.localstorage.FrameworkOptimizeStrategy.TIME_OPTIMIZE_THRESHOLD_MILLIS;
+import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import static com.google.common.truth.Truth.assertThat;
@@ -28,26 +25,17 @@
import org.junit.Test;
public class FrameworkOptimizeStrategyTest {
- FrameworkOptimizeStrategy mFrameworkOptimizeStrategy = new FrameworkOptimizeStrategy();
-
- @Test
- public void testShouldOptimize_docCountThreshold() {
- GetOptimizeInfoResultProto optimizeInfo =
- GetOptimizeInfoResultProto.newBuilder()
- .setTimeSinceLastOptimizeMs(0)
- .setEstimatedOptimizableBytes(BYTES_OPTIMIZE_THRESHOLD)
- .setOptimizableDocs(0)
- .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.OK).build())
- .build();
- assertThat(mFrameworkOptimizeStrategy.shouldOptimize(optimizeInfo)).isTrue();
- }
+ AppSearchConfig mAppSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
+ FrameworkOptimizeStrategy mFrameworkOptimizeStrategy =
+ new FrameworkOptimizeStrategy(mAppSearchConfig);
@Test
public void testShouldOptimize_byteThreshold() {
GetOptimizeInfoResultProto optimizeInfo =
GetOptimizeInfoResultProto.newBuilder()
- .setTimeSinceLastOptimizeMs(TIME_OPTIMIZE_THRESHOLD_MILLIS)
- .setEstimatedOptimizableBytes(0)
+ .setTimeSinceLastOptimizeMs(0)
+ .setEstimatedOptimizableBytes(
+ mAppSearchConfig.getCachedBytesOptimizeThreshold())
.setOptimizableDocs(0)
.setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.OK).build())
.build();
@@ -58,9 +46,23 @@
public void testShouldNotOptimize_timeThreshold() {
GetOptimizeInfoResultProto optimizeInfo =
GetOptimizeInfoResultProto.newBuilder()
+ .setTimeSinceLastOptimizeMs(
+ mAppSearchConfig.getCachedTimeOptimizeThresholdMs())
+ .setEstimatedOptimizableBytes(0)
+ .setOptimizableDocs(0)
+ .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.OK).build())
+ .build();
+ assertThat(mFrameworkOptimizeStrategy.shouldOptimize(optimizeInfo)).isTrue();
+ }
+
+ @Test
+ public void testShouldOptimize_docCountThreshold() {
+ GetOptimizeInfoResultProto optimizeInfo =
+ GetOptimizeInfoResultProto.newBuilder()
.setTimeSinceLastOptimizeMs(0)
.setEstimatedOptimizableBytes(0)
- .setOptimizableDocs(DOC_COUNT_OPTIMIZE_THRESHOLD)
+ .setOptimizableDocs(
+ mAppSearchConfig.getCachedDocCountOptimizeThreshold())
.setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.OK).build())
.build();
assertThat(mFrameworkOptimizeStrategy.shouldOptimize(optimizeInfo)).isTrue();
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index fbcf53d..7243947 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -74,6 +74,9 @@
private static final float DEFAULT_MIN = 0.01f;
private static final float DEFAULT_MAX = 0.80f;
+ private static final int DISPLAY_WIDTH = 900;
+ private static final int DISPLAY_HEIGHT = 1600;
+
private static final float EPSILON = 0.000001f;
private OffsettableClock mClock;
@@ -90,6 +93,8 @@
@Captor ArgumentCaptor<IThermalEventListener> mThermalEventListenerCaptor;
+ @Mock private BrightnessSetting mBrightnessSetting;
+
private static final HighBrightnessModeData DEFAULT_HBM_DATA =
new HighBrightnessModeData(MINIMUM_LUX, TRANSITION_POINT, TIME_WINDOW_MILLIS,
TIME_ALLOWED_IN_WINDOW_MILLIS, TIME_MINIMUM_AVAILABLE_TO_ENABLE_MILLIS,
@@ -98,6 +103,8 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mClock = new OffsettableClock.Stopped();
+ mTestLooper = new TestLooper(mClock::now);
mDisplayToken = null;
mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
@@ -114,8 +121,8 @@
public void testNoHbmData() {
initHandler(null);
final HighBrightnessModeController hbmc = new HighBrightnessModeController(
- mInjectorMock, mHandler, mDisplayToken, DEFAULT_MIN, DEFAULT_MAX, null,
- () -> {}, mContextSpy);
+ mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN,
+ DEFAULT_MAX, null, () -> {}, mContextSpy, mBrightnessSetting);
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
}
@@ -123,8 +130,8 @@
public void testNoHbmData_Enabled() {
initHandler(null);
final HighBrightnessModeController hbmc = new HighBrightnessModeController(
- mInjectorMock, mHandler, mDisplayToken, DEFAULT_MIN, DEFAULT_MAX, null,
- () -> {}, mContextSpy);
+ mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN,
+ DEFAULT_MAX, null, () -> {}, mContextSpy, mBrightnessSetting);
hbmc.setAutoBrightnessEnabled(true);
hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
@@ -180,7 +187,7 @@
hbmc.setAutoBrightnessEnabled(true);
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
- hbmc.onAutoBrightnessChanged(TRANSITION_POINT + 0.01f);
+ hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
// Verify we are in HBM
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
@@ -212,7 +219,7 @@
hbmc.setAutoBrightnessEnabled(true);
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
- hbmc.onAutoBrightnessChanged(TRANSITION_POINT + 0.01f);
+ hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
// Verify we are in HBM
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
@@ -237,18 +244,18 @@
hbmc.setAutoBrightnessEnabled(true);
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
- hbmc.onAutoBrightnessChanged(TRANSITION_POINT + 0.01f);
+ hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2);
// Verify we are in HBM
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
- hbmc.onAutoBrightnessChanged(TRANSITION_POINT - 0.01f);
+ hbmc.onBrightnessChanged(TRANSITION_POINT - 0.01f);
advanceTime(1);
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
- hbmc.onAutoBrightnessChanged(TRANSITION_POINT + 0.01f);
+ hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2);
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
@@ -267,13 +274,13 @@
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
// Go into HBM for half the allowed window
- hbmc.onAutoBrightnessChanged(TRANSITION_POINT + 0.01f);
+ hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2);
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
// Move lux below threshold (ending first event);
hbmc.onAmbientLuxChange(MINIMUM_LUX - 1);
- hbmc.onAutoBrightnessChanged(TRANSITION_POINT);
+ hbmc.onBrightnessChanged(TRANSITION_POINT);
assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF);
// Move up some amount of time so that there's still time in the window even after a
@@ -283,7 +290,7 @@
// Go into HBM for just under the second half of allowed window
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
- hbmc.onAutoBrightnessChanged(TRANSITION_POINT + 1);
+ hbmc.onBrightnessChanged(TRANSITION_POINT + 1);
advanceTime((TIME_ALLOWED_IN_WINDOW_MILLIS / 2) - 1);
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
@@ -355,8 +362,9 @@
// Creates instance with standard initialization values.
private HighBrightnessModeController createDefaultHbm(OffsettableClock clock) {
initHandler(clock);
- return new HighBrightnessModeController(mInjectorMock, mHandler, mDisplayToken, DEFAULT_MIN,
- DEFAULT_MAX, DEFAULT_HBM_DATA, () -> {}, mContextSpy);
+ return new HighBrightnessModeController(mInjectorMock, mHandler, DISPLAY_WIDTH,
+ DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN, DEFAULT_MAX, DEFAULT_HBM_DATA, () -> {},
+ mContextSpy, mBrightnessSetting);
}
private void initHandler(OffsettableClock clock) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index 6109c2f..6c2db36 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -46,6 +46,8 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.google.common.testing.EqualsTester;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -208,6 +210,24 @@
}
@Test
+ public void testEqualsActiveSource() {
+ int logicalAddress = 0;
+ int physicalAddress = 0x0000;
+ new EqualsTester()
+ .addEqualityGroup(
+ new HdmiCecLocalDevice.ActiveSource(logicalAddress, physicalAddress),
+ new HdmiCecLocalDevice.ActiveSource(logicalAddress, physicalAddress))
+ .addEqualityGroup(
+ new HdmiCecLocalDevice.ActiveSource(logicalAddress, physicalAddress + 1))
+ .addEqualityGroup(
+ new HdmiCecLocalDevice.ActiveSource(logicalAddress + 1, physicalAddress))
+ .addEqualityGroup(
+ new HdmiCecLocalDevice.ActiveSource(
+ logicalAddress + 1, physicalAddress + 1))
+ .testEquals();
+ }
+
+ @Test
public void dispatchMessage_logicalAddressDoesNotMatch() {
HdmiCecMessage msg =
new HdmiCecMessage(
diff --git a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
index cee4cda..901b200 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
@@ -44,7 +44,7 @@
public class SELinuxMMACTest {
private static final String PACKAGE_NAME = "my.package";
- private static final int LATEST_OPT_IN_VERSION = Build.VERSION_CODES.S;
+ private static final int LATEST_OPT_IN_VERSION = Build.VERSION_CODES.CUR_DEVELOPMENT;
private static final int R_OPT_IN_VERSION = Build.VERSION_CODES.R;
@Mock
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 0449e44..75f8a44 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -46,6 +46,7 @@
private final List<VibrationEffectSegment> mEffectSegments = new ArrayList<>();
private final List<Integer> mBraking = new ArrayList<>();
private final List<Float> mAmplitudes = new ArrayList<>();
+ private final List<Boolean> mExternalControlStates = new ArrayList<>();
private final Handler mHandler;
private final FakeNativeWrapper mNativeWrapper;
@@ -139,6 +140,7 @@
@Override
public void setExternalControl(boolean enabled) {
+ mExternalControlStates.add(enabled);
}
@Override
@@ -301,6 +303,11 @@
return new ArrayList<>(mEffectSegments);
}
+ /** Return list of states set for external control to the fake vibrator hardware. */
+ public List<Boolean> getExternalControlStates() {
+ return mExternalControlStates;
+ }
+
/**
* 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/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 77003b2..5a00e0d 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -35,6 +35,7 @@
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;
@@ -50,8 +51,11 @@
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.os.CombinedVibration;
+import android.os.ExternalVibration;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IExternalVibrationController;
+import android.os.IExternalVibratorService;
import android.os.IVibratorStateListener;
import android.os.Looper;
import android.os.PowerManager;
@@ -108,6 +112,8 @@
private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build();
private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder()
.setBatterySaverEnabled(true).build();
+ private static final AudioAttributes AUDIO_ATTRS =
+ new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_ALARM).build();
private static final VibrationAttributes ALARM_ATTRS =
new VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_ALARM).build();
private static final VibrationAttributes HAPTIC_FEEDBACK_ATTRS =
@@ -136,6 +142,7 @@
private TestLooper mTestLooper;
private FakeVibrator mVibrator;
private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
+ private VibratorManagerService.ExternalVibratorService mExternalVibratorService;
@Before
public void setUp() throws Exception {
@@ -208,6 +215,9 @@
@Override
void addService(String name, IBinder service) {
+ Object serviceInstance = service;
+ mExternalVibratorService =
+ (VibratorManagerService.ExternalVibratorService) serviceInstance;
}
});
}
@@ -967,6 +977,69 @@
assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
}
+ @Test
+ public void onExternalVibration_setsExternalControl() {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+ createSystemReadyService();
+
+ ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_ATTRS,
+ mock(IExternalVibrationController.class));
+ int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ mExternalVibratorService.onExternalVibrationStop(externalVibration);
+
+ assertEquals(IExternalVibratorService.SCALE_NONE, scale);
+ assertEquals(Arrays.asList(true, false),
+ mVibratorProviders.get(1).getExternalControlStates());
+ }
+
+ @Test
+ public void onExternalVibration_withOngoingExternalVibration_mutesPreviousVibration()
+ throws Exception {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+ createSystemReadyService();
+
+ IExternalVibrationController firstController = mock(IExternalVibrationController.class);
+ IExternalVibrationController secondController = mock(IExternalVibrationController.class);
+ ExternalVibration firstVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_ATTRS,
+ firstController);
+ int firstScale = mExternalVibratorService.onExternalVibrationStart(firstVibration);
+
+ ExternalVibration secondVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_ATTRS,
+ secondController);
+ int secondScale = mExternalVibratorService.onExternalVibrationStart(secondVibration);
+
+ assertEquals(IExternalVibratorService.SCALE_NONE, firstScale);
+ assertEquals(IExternalVibratorService.SCALE_NONE, secondScale);
+ verify(firstController).mute();
+ verifyNoMoreInteractions(secondController);
+ // Set external control called only once.
+ assertEquals(Arrays.asList(true), mVibratorProviders.get(1).getExternalControlStates());
+ }
+
+ @Test
+ public void onExternalVibration_withOngoingVibration_cancelsOngoingVibrationImmediately()
+ throws Exception {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL,
+ IVibrator.CAP_AMPLITUDE_CONTROL);
+ VibratorManagerService service = createSystemReadyService();
+
+ VibrationEffect effect = VibrationEffect.createOneShot(10 * TEST_TIMEOUT_MILLIS, 100);
+ vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS);
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_ATTRS,
+ mock(IExternalVibrationController.class));
+ int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ assertEquals(IExternalVibratorService.SCALE_NONE, scale);
+
+ // Vibration is cancelled.
+ assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+ assertEquals(Arrays.asList(true), mVibratorProviders.get(1).getExternalControlStates());
+ }
+
private VibrationEffectSegment expectedPrebaked(int effectId) {
return new PrebakedSegment(effectId, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
}