diff options
331 files changed, 10258 insertions, 3930 deletions
diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java index 8e0ea9888e4c..f6af09c853ca 100644 --- a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java +++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java @@ -30,6 +30,7 @@ import com.android.utils.blob.DummyBlobData; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -89,6 +90,7 @@ public class BlobStorePerfTests { runShellCommand("cmd jobscheduler run -f android 191934935"); } + @Ignore @Test public void testComputeDigest() throws Exception { mAtraceUtils.startTrace(ATRACE_CATEGORY_SYSTEM_SERVER); diff --git a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java index d339afac5c77..9c3bd812f298 100644 --- a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java +++ b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java @@ -129,7 +129,8 @@ public final class BlobHandle implements Parcelable { * @param label a label indicating what the blob is, that can be surfaced to the user. * @param expiryTimeMillis the time in secs after which the blob should be invalidated and not * allowed to be accessed by any other app, - * in {@link System#currentTimeMillis()} timebase. + * in {@link System#currentTimeMillis()} timebase or {@code 0} to + * indicate that there is no expiry time associated with this blob. * @param tag an opaque {@link String} associated with the blob. The length of the tag * cannot be more than 128 characters. * diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java index 821305e18240..f53f1f19aea7 100644 --- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java +++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java @@ -266,6 +266,9 @@ public class BlobStoreManager { * <p> This lease information is persisted and calling this more than once will result in * latest lease overriding any previous lease. * + * <p> When an app acquires a lease on a blob, the System will try to keep this + * blob around but note that it can still be deleted if it was requested by the user. + * * @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to * acquire a lease for. * @param descriptionResId the resource id for a short description string that can be surfaced @@ -307,7 +310,7 @@ public class BlobStoreManager { * Acquire a lease to the blob represented by {@code blobHandle}. This lease indicates to the * system that the caller wants the blob to be kept around. * - * <p> This is variant of {@link #acquireLease(BlobHandle, int, long)} taking a + * <p> This is a variant of {@link #acquireLease(BlobHandle, int, long)} taking a * {@link CharSequence} for {@code description}. It is highly recommended that callers only * use this when a valid resource ID for {@code description} could not be provided. Otherwise, * apps should prefer using {@link #acquireLease(BlobHandle, int)} which will allow @@ -319,6 +322,9 @@ public class BlobStoreManager { * <p> This lease information is persisted and calling this more than once will result in * latest lease overriding any previous lease. * + * <p> When an app acquires a lease on a blob, the System will try to keep this + * blob around but note that it can still be deleted if it was requested by the user. + * * @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to * acquire a lease for. * @param description a short description string that can be surfaced @@ -371,6 +377,9 @@ public class BlobStoreManager { * <p> This lease information is persisted and calling this more than once will result in * latest lease overriding any previous lease. * + * <p> When an app acquires a lease on a blob, the System will try to keep this + * blob around but note that it can still be deleted if it was requested by the user. + * * @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to * acquire a lease for. * @param descriptionResId the resource id for a short description string that can be surfaced @@ -395,7 +404,7 @@ public class BlobStoreManager { * Acquire a lease to the blob represented by {@code blobHandle}. This lease indicates to the * system that the caller wants the blob to be kept around. * - * <p> This is variant of {@link #acquireLease(BlobHandle, int)} taking a {@link CharSequence} + * <p> This is a variant of {@link #acquireLease(BlobHandle, int)} taking a {@link CharSequence} * for {@code description}. It is highly recommended that callers only use this when a valid * resource ID for {@code description} could not be provided. Otherwise, apps should prefer * using {@link #acquireLease(BlobHandle, int)} which will allow {@code description} to be @@ -412,6 +421,9 @@ public class BlobStoreManager { * <p> This lease information is persisted and calling this more than once will result in * latest lease overriding any previous lease. * + * <p> When an app acquires a lease on a blob, the System will try to keep this + * blob around but note that it can still be deleted if it was requested by the user. + * * @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to * acquire a lease for. * @param description a short description string that can be surfaced diff --git a/apex/extservices/testing/Android.bp b/apex/extservices/testing/Android.bp new file mode 100644 index 000000000000..88a47246c824 --- /dev/null +++ b/apex/extservices/testing/Android.bp @@ -0,0 +1,25 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +apex_test { + name: "test_com.android.extservices", + visibility: [ + "//system/apex/tests", + ], + defaults: ["com.android.extservices-defaults"], + manifest: "test_manifest.json", + file_contexts: ":com.android.extservices-file_contexts", + // Test APEX, should never be installed + installable: false, +} diff --git a/apex/extservices/testing/test_manifest.json b/apex/extservices/testing/test_manifest.json new file mode 100644 index 000000000000..23a50e37bdd3 --- /dev/null +++ b/apex/extservices/testing/test_manifest.json @@ -0,0 +1,4 @@ +{ + "name": "com.android.extservices", + "version": 2147483647 +} diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index 939164edd13e..b516279b58a5 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -473,13 +473,17 @@ public class JobSchedulerService extends com.android.server.SystemService */ public static class Constants { // Key names stored in the settings value. - private static final String KEY_MIN_IDLE_COUNT = "min_idle_count"; - private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count"; - private static final String KEY_MIN_BATTERY_NOT_LOW_COUNT = "min_battery_not_low_count"; - private static final String KEY_MIN_STORAGE_NOT_LOW_COUNT = "min_storage_not_low_count"; - private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count"; - private static final String KEY_MIN_CONTENT_COUNT = "min_content_count"; - private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count"; + // TODO(124466289): remove deprecated flags when we migrate to DeviceConfig + private static final String DEPRECATED_KEY_MIN_IDLE_COUNT = "min_idle_count"; + private static final String DEPRECATED_KEY_MIN_CHARGING_COUNT = "min_charging_count"; + private static final String DEPRECATED_KEY_MIN_BATTERY_NOT_LOW_COUNT = + "min_battery_not_low_count"; + private static final String DEPRECATED_KEY_MIN_STORAGE_NOT_LOW_COUNT = + "min_storage_not_low_count"; + private static final String DEPRECATED_KEY_MIN_CONNECTIVITY_COUNT = + "min_connectivity_count"; + private static final String DEPRECATED_KEY_MIN_CONTENT_COUNT = "min_content_count"; + private static final String DEPRECATED_KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count"; private static final String KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT = "min_ready_non_active_jobs_count"; private static final String KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = @@ -515,13 +519,6 @@ public class JobSchedulerService extends com.android.server.SystemService private static final String KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION = "aq_schedule_throw_exception"; - private static final int DEFAULT_MIN_IDLE_COUNT = 1; - private static final int DEFAULT_MIN_CHARGING_COUNT = 1; - private static final int DEFAULT_MIN_BATTERY_NOT_LOW_COUNT = 1; - private static final int DEFAULT_MIN_STORAGE_NOT_LOW_COUNT = 1; - private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1; - private static final int DEFAULT_MIN_CONTENT_COUNT = 1; - private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1; private static final int DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT = 5; private static final long DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = 31 * MINUTE_IN_MILLIS; private static final float DEFAULT_HEAVY_USE_FACTOR = .9f; @@ -536,44 +533,6 @@ public class JobSchedulerService extends com.android.server.SystemService private static final boolean DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION = true; /** - * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things - * early. - */ - int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT; - /** - * Minimum # of charging jobs that must be ready in order to force the JMS to schedule - * things early. - */ - int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT; - /** - * Minimum # of "battery not low" jobs that must be ready in order to force the JMS to - * schedule things early. - */ - int MIN_BATTERY_NOT_LOW_COUNT = DEFAULT_MIN_BATTERY_NOT_LOW_COUNT; - /** - * Minimum # of "storage not low" jobs that must be ready in order to force the JMS to - * schedule things early. - */ - int MIN_STORAGE_NOT_LOW_COUNT = DEFAULT_MIN_STORAGE_NOT_LOW_COUNT; - /** - * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule - * things early. 1 == Run connectivity jobs as soon as ready. - */ - int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT; - /** - * Minimum # of content trigger jobs that must be ready in order to force the JMS to - * schedule things early. - */ - int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT; - /** - * Minimum # of jobs (with no particular constraints) for which the JMS will be happy - * running some work early. This (and thus the other min counts) is now set to 1, to - * prevent any batching at this level. Since we now do batching through doze, that is - * a much better mechanism. - */ - int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT; - - /** * Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early. */ int MIN_READY_NON_ACTIVE_JOBS_COUNT = DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT; @@ -687,20 +646,6 @@ public class JobSchedulerService extends com.android.server.SystemService Slog.e(TAG, "Bad jobscheduler settings", e); } - MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT, - DEFAULT_MIN_IDLE_COUNT); - MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT, - DEFAULT_MIN_CHARGING_COUNT); - MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT, - DEFAULT_MIN_BATTERY_NOT_LOW_COUNT); - MIN_STORAGE_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_STORAGE_NOT_LOW_COUNT, - DEFAULT_MIN_STORAGE_NOT_LOW_COUNT); - MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT, - DEFAULT_MIN_CONNECTIVITY_COUNT); - MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT, - DEFAULT_MIN_CONTENT_COUNT); - MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT, - DEFAULT_MIN_READY_JOBS_COUNT); MIN_READY_NON_ACTIVE_JOBS_COUNT = mParser.getInt( KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT, DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT); @@ -749,13 +694,6 @@ public class JobSchedulerService extends com.android.server.SystemService void dump(IndentingPrintWriter pw) { pw.println("Settings:"); pw.increaseIndent(); - pw.printPair(KEY_MIN_IDLE_COUNT, MIN_IDLE_COUNT).println(); - pw.printPair(KEY_MIN_CHARGING_COUNT, MIN_CHARGING_COUNT).println(); - pw.printPair(KEY_MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT).println(); - pw.printPair(KEY_MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT).println(); - pw.printPair(KEY_MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT).println(); - pw.printPair(KEY_MIN_CONTENT_COUNT, MIN_CONTENT_COUNT).println(); - pw.printPair(KEY_MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT).println(); pw.printPair(KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT, MIN_READY_NON_ACTIVE_JOBS_COUNT).println(); pw.printPair(KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS, @@ -790,13 +728,6 @@ public class JobSchedulerService extends com.android.server.SystemService } void dump(ProtoOutputStream proto) { - proto.write(ConstantsProto.MIN_IDLE_COUNT, MIN_IDLE_COUNT); - proto.write(ConstantsProto.MIN_CHARGING_COUNT, MIN_CHARGING_COUNT); - proto.write(ConstantsProto.MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT); - proto.write(ConstantsProto.MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT); - proto.write(ConstantsProto.MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT); - proto.write(ConstantsProto.MIN_CONTENT_COUNT, MIN_CONTENT_COUNT); - proto.write(ConstantsProto.MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT); proto.write(ConstantsProto.MIN_READY_NON_ACTIVE_JOBS_COUNT, MIN_READY_NON_ACTIVE_JOBS_COUNT); proto.write(ConstantsProto.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS, @@ -2106,13 +2037,6 @@ public class JobSchedulerService extends com.android.server.SystemService * policies on when we want to execute jobs. */ final class MaybeReadyJobQueueFunctor implements Consumer<JobStatus> { - int chargingCount; - int batteryNotLowCount; - int storageNotLowCount; - int idleCount; - int backoffCount; - int connectivityCount; - int contentCount; int forceBatchedCount; int unbatchedCount; final List<JobStatus> runnableJobs = new ArrayList<>(); @@ -2135,13 +2059,25 @@ public class JobSchedulerService extends com.android.server.SystemService } } catch (RemoteException e) { } + + final boolean shouldForceBatchJob; // Restricted jobs must always be batched - if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX - || (mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT > 1 - && job.getEffectiveStandbyBucket() != ACTIVE_INDEX - && (job.getFirstForceBatchedTimeElapsed() == 0 - || sElapsedRealtimeClock.millis() - job.getFirstForceBatchedTimeElapsed() - < mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS))) { + if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX) { + shouldForceBatchJob = true; + } else if (job.getNumFailures() > 0) { + shouldForceBatchJob = false; + } else { + final long nowElapsed = sElapsedRealtimeClock.millis(); + final boolean batchDelayExpired = job.getFirstForceBatchedTimeElapsed() > 0 + && nowElapsed - job.getFirstForceBatchedTimeElapsed() + >= mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS; + shouldForceBatchJob = + mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT > 1 + && job.getEffectiveStandbyBucket() != ACTIVE_INDEX + && !batchDelayExpired; + } + + if (shouldForceBatchJob) { // Force batching non-ACTIVE jobs. Don't include them in the other counts. forceBatchedCount++; if (job.getFirstForceBatchedTimeElapsed() == 0) { @@ -2149,27 +2085,6 @@ public class JobSchedulerService extends com.android.server.SystemService } } else { unbatchedCount++; - if (job.getNumFailures() > 0) { - backoffCount++; - } - if (job.hasIdleConstraint()) { - idleCount++; - } - if (job.hasConnectivityConstraint()) { - connectivityCount++; - } - if (job.hasChargingConstraint()) { - chargingCount++; - } - if (job.hasBatteryNotLowConstraint()) { - batteryNotLowCount++; - } - if (job.hasStorageNotLowConstraint()) { - storageNotLowCount++; - } - if (job.hasContentTriggerConstraint()) { - contentCount++; - } } runnableJobs.add(job); } else { @@ -2178,16 +2093,8 @@ public class JobSchedulerService extends com.android.server.SystemService } public void postProcess() { - if (backoffCount > 0 || - idleCount >= mConstants.MIN_IDLE_COUNT || - connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT || - chargingCount >= mConstants.MIN_CHARGING_COUNT || - batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT || - storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT || - contentCount >= mConstants.MIN_CONTENT_COUNT || - forceBatchedCount >= mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT || - (unbatchedCount > 0 && (unbatchedCount + forceBatchedCount) - >= mConstants.MIN_READY_JOBS_COUNT)) { + if (unbatchedCount > 0 + || forceBatchedCount >= mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT) { if (DEBUG) { Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs."); } @@ -2208,13 +2115,6 @@ public class JobSchedulerService extends com.android.server.SystemService @VisibleForTesting void reset() { - chargingCount = 0; - idleCount = 0; - backoffCount = 0; - connectivityCount = 0; - batteryNotLowCount = 0; - storageNotLowCount = 0; - contentCount = 0; forceBatchedCount = 0; unbatchedCount = 0; runnableJobs.clear(); diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index b1b8fba78ab9..f1bfa0411978 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -136,25 +136,59 @@ public class AppStandbyController implements AppStandbyInternal { private static final long ONE_HOUR = ONE_MINUTE * 60; private static final long ONE_DAY = ONE_HOUR * 24; - static final long[] SCREEN_TIME_THRESHOLDS = { + /** + * The minimum amount of time the screen must have been on before an app can time out from its + * current bucket to the next bucket. + */ + private static final long[] SCREEN_TIME_THRESHOLDS = { 0, 0, - COMPRESS_TIME ? 120 * 1000 : 1 * ONE_HOUR, - COMPRESS_TIME ? 240 * 1000 : 2 * ONE_HOUR + COMPRESS_TIME ? 2 * ONE_MINUTE : 1 * ONE_HOUR, + COMPRESS_TIME ? 4 * ONE_MINUTE : 2 * ONE_HOUR, + COMPRESS_TIME ? 8 * ONE_MINUTE : 6 * ONE_HOUR }; - static final long[] ELAPSED_TIME_THRESHOLDS = { + /** The minimum allowed values for each index in {@link #SCREEN_TIME_THRESHOLDS}. */ + private static final long[] MINIMUM_SCREEN_TIME_THRESHOLDS = COMPRESS_TIME + ? new long[SCREEN_TIME_THRESHOLDS.length] + : new long[]{ + 0, + 0, + 0, + 30 * ONE_MINUTE, + ONE_HOUR + }; + + /** + * The minimum amount of elapsed time that must have passed before an app can time out from its + * current bucket to the next bucket. + */ + private static final long[] ELAPSED_TIME_THRESHOLDS = { 0, COMPRESS_TIME ? 1 * ONE_MINUTE : 12 * ONE_HOUR, COMPRESS_TIME ? 4 * ONE_MINUTE : 24 * ONE_HOUR, - COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR + COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR, + // TODO(149050681): increase timeout to 30+ days + COMPRESS_TIME ? 32 * ONE_MINUTE : 4 * ONE_DAY }; - static final int[] THRESHOLD_BUCKETS = { + /** The minimum allowed values for each index in {@link #ELAPSED_TIME_THRESHOLDS}. */ + private static final long[] MINIMUM_ELAPSED_TIME_THRESHOLDS = COMPRESS_TIME + ? new long[ELAPSED_TIME_THRESHOLDS.length] + : new long[]{ + 0, + ONE_HOUR, + ONE_HOUR, + 2 * ONE_HOUR, + 4 * ONE_DAY + }; + + private static final int[] THRESHOLD_BUCKETS = { STANDBY_BUCKET_ACTIVE, STANDBY_BUCKET_WORKING_SET, STANDBY_BUCKET_FREQUENT, - STANDBY_BUCKET_RARE + STANDBY_BUCKET_RARE, + STANDBY_BUCKET_RESTRICTED }; /** Default expiration time for bucket prediction. After this, use thresholds to downgrade. */ @@ -204,7 +238,15 @@ public class AppStandbyController implements AppStandbyInternal { static final int MSG_REPORT_EXEMPTED_SYNC_START = 13; long mCheckIdleIntervalMillis; + /** + * The minimum amount of time the screen must have been on before an app can time out from its + * current bucket to the next bucket. + */ long[] mAppStandbyScreenThresholds = SCREEN_TIME_THRESHOLDS; + /** + * The minimum amount of elapsed time that must have passed before an app can time out from its + * current bucket to the next bucket. + */ long[] mAppStandbyElapsedThresholds = ELAPSED_TIME_THRESHOLDS; /** Minimum time a strong usage event should keep the bucket elevated. */ long mStrongUsageTimeoutMillis; @@ -396,12 +438,9 @@ public class AppStandbyController implements AppStandbyInternal { } if (!packageName.equals(providerPkgName)) { synchronized (mAppIdleLock) { - AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId, - STANDBY_BUCKET_ACTIVE, REASON_SUB_USAGE_SYNC_ADAPTER, - 0, - elapsedRealtime + mSyncAdapterTimeoutMillis); - maybeInformListeners(packageName, userId, elapsedRealtime, - appUsage.currentBucket, appUsage.bucketingReason, false); + reportNoninteractiveUsageLocked(packageName, userId, STANDBY_BUCKET_ACTIVE, + REASON_SUB_USAGE_SYNC_ADAPTER, elapsedRealtime, + mSyncAdapterTimeoutMillis); } } } catch (PackageManager.NameNotFoundException e) { @@ -432,12 +471,8 @@ public class AppStandbyController implements AppStandbyInternal { final long elapsedRealtime = mInjector.elapsedRealtime(); synchronized (mAppIdleLock) { - AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId, - bucketToPromote, usageReason, - 0, - elapsedRealtime + durationMillis); - maybeInformListeners(packageName, userId, elapsedRealtime, - appUsage.currentBucket, appUsage.bucketingReason, false); + reportNoninteractiveUsageLocked(packageName, userId, bucketToPromote, + usageReason, elapsedRealtime, durationMillis); } } @@ -450,12 +485,9 @@ public class AppStandbyController implements AppStandbyInternal { mAppIdleHistory.getAppStandbyBucket(packageName, userId, elapsedRealtime); if (currentBucket == STANDBY_BUCKET_NEVER) { // Bring the app out of the never bucket - AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId, - STANDBY_BUCKET_WORKING_SET, REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED, - 0, - elapsedRealtime + mUnexemptedSyncScheduledTimeoutMillis); - maybeInformListeners(packageName, userId, elapsedRealtime, - appUsage.currentBucket, appUsage.bucketingReason, false); + reportNoninteractiveUsageLocked(packageName, userId, STANDBY_BUCKET_WORKING_SET, + REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED, elapsedRealtime, + mUnexemptedSyncScheduledTimeoutMillis); } } } @@ -466,15 +498,23 @@ public class AppStandbyController implements AppStandbyInternal { final long elapsedRealtime = mInjector.elapsedRealtime(); synchronized (mAppIdleLock) { - AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId, - STANDBY_BUCKET_ACTIVE, REASON_SUB_USAGE_EXEMPTED_SYNC_START, - 0, - elapsedRealtime + mExemptedSyncStartTimeoutMillis); - maybeInformListeners(packageName, userId, elapsedRealtime, - appUsage.currentBucket, appUsage.bucketingReason, false); + reportNoninteractiveUsageLocked(packageName, userId, STANDBY_BUCKET_ACTIVE, + REASON_SUB_USAGE_EXEMPTED_SYNC_START, elapsedRealtime, + mExemptedSyncStartTimeoutMillis); } } + private void reportNoninteractiveUsageLocked(String packageName, int userId, int bucket, + int subReason, long elapsedRealtime, long nextCheckDelay) { + final AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId, bucket, + subReason, 0, elapsedRealtime + nextCheckDelay); + mHandler.sendMessageDelayed( + mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, packageName), + nextCheckDelay); + maybeInformListeners(packageName, userId, elapsedRealtime, appUsage.currentBucket, + appUsage.bucketingReason, false); + } + @Override public void postCheckIdleStates(int userId) { mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0)); @@ -623,7 +663,7 @@ public class AppStandbyController implements AppStandbyInternal { if (app.lastRestrictAttemptElapsedTime > app.lastUsedByUserElapsedTime && elapsedTimeAdjusted - app.lastUsedByUserElapsedTime - >= mInjector.getRestrictedBucketDelayMs()) { + >= mInjector.getAutoRestrictedBucketDelayMs()) { newBucket = STANDBY_BUCKET_RESTRICTED; reason = app.lastRestrictReason; if (DEBUG) { @@ -699,73 +739,75 @@ public class AppStandbyController implements AppStandbyInternal { @Override public void reportEvent(UsageEvents.Event event, long elapsedRealtime, int userId) { if (!mAppIdleEnabled) return; - synchronized (mAppIdleLock) { - final String pkg = event.getPackageName(); - final int eventType = event.getEventType(); - // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back - // about apps that are on some kind of whitelist anyway. - final boolean previouslyIdle = mAppIdleHistory.isIdle( - pkg, userId, elapsedRealtime); - // Inform listeners if necessary - if ((eventType == UsageEvents.Event.ACTIVITY_RESUMED - || eventType == UsageEvents.Event.ACTIVITY_PAUSED - || eventType == UsageEvents.Event.SYSTEM_INTERACTION - || eventType == UsageEvents.Event.USER_INTERACTION - || eventType == UsageEvents.Event.NOTIFICATION_SEEN - || eventType == UsageEvents.Event.SLICE_PINNED - || eventType == UsageEvents.Event.SLICE_PINNED_PRIV - || eventType == UsageEvents.Event.FOREGROUND_SERVICE_START)) { - - final AppUsageHistory appHistory = mAppIdleHistory.getAppUsageHistory( - pkg, userId, elapsedRealtime); - final int prevBucket = appHistory.currentBucket; - final int prevBucketReason = appHistory.bucketingReason; - final long nextCheckTime; - final int subReason = usageEventToSubReason(eventType); - final int reason = REASON_MAIN_USAGE | subReason; - if (eventType == UsageEvents.Event.NOTIFICATION_SEEN - || eventType == UsageEvents.Event.SLICE_PINNED) { - // Mild usage elevates to WORKING_SET but doesn't change usage time. - mAppIdleHistory.reportUsage(appHistory, pkg, - STANDBY_BUCKET_WORKING_SET, subReason, - 0, elapsedRealtime + mNotificationSeenTimeoutMillis); - nextCheckTime = mNotificationSeenTimeoutMillis; - } else if (eventType == UsageEvents.Event.SYSTEM_INTERACTION) { - mAppIdleHistory.reportUsage(appHistory, pkg, - STANDBY_BUCKET_ACTIVE, subReason, - 0, elapsedRealtime + mSystemInteractionTimeoutMillis); - nextCheckTime = mSystemInteractionTimeoutMillis; - } else if (eventType == UsageEvents.Event.FOREGROUND_SERVICE_START) { - // Only elevate bucket if this is the first usage of the app - if (prevBucket != STANDBY_BUCKET_NEVER) return; - mAppIdleHistory.reportUsage(appHistory, pkg, - STANDBY_BUCKET_ACTIVE, subReason, - 0, elapsedRealtime + mInitialForegroundServiceStartTimeoutMillis); - nextCheckTime = mInitialForegroundServiceStartTimeoutMillis; - } else { - mAppIdleHistory.reportUsage(appHistory, pkg, - STANDBY_BUCKET_ACTIVE, subReason, - elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis); - nextCheckTime = mStrongUsageTimeoutMillis; - } - if (appHistory.currentBucket != prevBucket) { - mHandler.sendMessageDelayed( - mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, pkg), - nextCheckTime); - final boolean userStartedInteracting = - appHistory.currentBucket == STANDBY_BUCKET_ACTIVE - && (prevBucketReason & REASON_MAIN_MASK) != REASON_MAIN_USAGE; - maybeInformListeners(pkg, userId, elapsedRealtime, - appHistory.currentBucket, reason, userStartedInteracting); - } - - if (previouslyIdle) { - notifyBatteryStats(pkg, userId, false); - } + final int eventType = event.getEventType(); + if ((eventType == UsageEvents.Event.ACTIVITY_RESUMED + || eventType == UsageEvents.Event.ACTIVITY_PAUSED + || eventType == UsageEvents.Event.SYSTEM_INTERACTION + || eventType == UsageEvents.Event.USER_INTERACTION + || eventType == UsageEvents.Event.NOTIFICATION_SEEN + || eventType == UsageEvents.Event.SLICE_PINNED + || eventType == UsageEvents.Event.SLICE_PINNED_PRIV + || eventType == UsageEvents.Event.FOREGROUND_SERVICE_START)) { + synchronized (mAppIdleLock) { + reportEventLocked(event.getPackageName(), eventType, elapsedRealtime, userId); } } } + private void reportEventLocked(String pkg, int eventType, long elapsedRealtime, int userId) { + // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back + // about apps that are on some kind of whitelist anyway. + final boolean previouslyIdle = mAppIdleHistory.isIdle( + pkg, userId, elapsedRealtime); + + final AppUsageHistory appHistory = mAppIdleHistory.getAppUsageHistory( + pkg, userId, elapsedRealtime); + final int prevBucket = appHistory.currentBucket; + final int prevBucketReason = appHistory.bucketingReason; + final long nextCheckDelay; + final int subReason = usageEventToSubReason(eventType); + final int reason = REASON_MAIN_USAGE | subReason; + if (eventType == UsageEvents.Event.NOTIFICATION_SEEN + || eventType == UsageEvents.Event.SLICE_PINNED) { + // Mild usage elevates to WORKING_SET but doesn't change usage time. + mAppIdleHistory.reportUsage(appHistory, pkg, + STANDBY_BUCKET_WORKING_SET, subReason, + 0, elapsedRealtime + mNotificationSeenTimeoutMillis); + nextCheckDelay = mNotificationSeenTimeoutMillis; + } else if (eventType == UsageEvents.Event.SYSTEM_INTERACTION) { + mAppIdleHistory.reportUsage(appHistory, pkg, + STANDBY_BUCKET_ACTIVE, subReason, + 0, elapsedRealtime + mSystemInteractionTimeoutMillis); + nextCheckDelay = mSystemInteractionTimeoutMillis; + } else if (eventType == UsageEvents.Event.FOREGROUND_SERVICE_START) { + // Only elevate bucket if this is the first usage of the app + if (prevBucket != STANDBY_BUCKET_NEVER) return; + mAppIdleHistory.reportUsage(appHistory, pkg, + STANDBY_BUCKET_ACTIVE, subReason, + 0, elapsedRealtime + mInitialForegroundServiceStartTimeoutMillis); + nextCheckDelay = mInitialForegroundServiceStartTimeoutMillis; + } else { + mAppIdleHistory.reportUsage(appHistory, pkg, + STANDBY_BUCKET_ACTIVE, subReason, + elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis); + nextCheckDelay = mStrongUsageTimeoutMillis; + } + if (appHistory.currentBucket != prevBucket) { + mHandler.sendMessageDelayed( + mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, pkg), + nextCheckDelay); + final boolean userStartedInteracting = + appHistory.currentBucket == STANDBY_BUCKET_ACTIVE + && (prevBucketReason & REASON_MAIN_MASK) != REASON_MAIN_USAGE; + maybeInformListeners(pkg, userId, elapsedRealtime, + appHistory.currentBucket, reason, userStartedInteracting); + } + + if (previouslyIdle) { + notifyBatteryStats(pkg, userId, false); + } + } + private int usageEventToSubReason(int eventType) { switch (eventType) { case UsageEvents.Event.ACTIVITY_RESUMED: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND; @@ -1147,9 +1189,11 @@ public class AppStandbyController implements AppStandbyInternal { final boolean isForcedByUser = (reason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_USER; - // If the current bucket is RESTRICTED, only user force or usage should bring it out. + // If the current bucket is RESTRICTED, only user force or usage should bring it out, + // unless the app was put into the bucket due to timing out. if (app.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage(reason) - && !isForcedByUser) { + && !isForcedByUser + && (app.bucketingReason & REASON_MAIN_MASK) != REASON_MAIN_TIMEOUT) { return; } @@ -1175,7 +1219,7 @@ public class AppStandbyController implements AppStandbyInternal { } } else { final long timeUntilRestrictPossibleMs = app.lastUsedByUserElapsedTime - + mInjector.getRestrictedBucketDelayMs() - elapsedRealtime; + + mInjector.getAutoRestrictedBucketDelayMs() - elapsedRealtime; if (timeUntilRestrictPossibleMs > 0) { Slog.w(TAG, "Tried to restrict recently used app: " + packageName + " due to " + reason); @@ -1522,10 +1566,9 @@ public class AppStandbyController implements AppStandbyInternal { int mBootPhase; /** * The minimum amount of time required since the last user interaction before an app can be - * placed in the RESTRICTED bucket. + * automatically placed in the RESTRICTED bucket. */ - // TODO: make configurable via DeviceConfig - private long mRestrictedBucketDelayMs = ONE_DAY; + long mAutoRestrictedBucketDelayMs = ONE_DAY; Injector(Context context, Looper looper) { mContext = context; @@ -1554,7 +1597,7 @@ public class AppStandbyController implements AppStandbyInternal { final ActivityManager activityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); if (activityManager.isLowRamDevice() || ActivityManager.isSmallBatteryDevice()) { - mRestrictedBucketDelayMs = 12 * ONE_HOUR; + mAutoRestrictedBucketDelayMs = 12 * ONE_HOUR; } } mBootPhase = phase; @@ -1595,8 +1638,13 @@ public class AppStandbyController implements AppStandbyInternal { return Environment.getDataSystemDirectory(); } - long getRestrictedBucketDelayMs() { - return mRestrictedBucketDelayMs; + /** + * Return the minimum amount of time that must have passed since the last user usage before + * an app can be automatically put into the + * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket. + */ + long getAutoRestrictedBucketDelayMs() { + return mAutoRestrictedBucketDelayMs; } void noteEvent(int event, String packageName, int uid) throws RemoteException { @@ -1772,6 +1820,8 @@ public class AppStandbyController implements AppStandbyInternal { "system_interaction_duration"; private static final String KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION = "initial_foreground_service_start_duration"; + private static final String KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS = + "auto_restricted_bucket_delay_ms"; public static final long DEFAULT_STRONG_USAGE_TIMEOUT = 1 * ONE_HOUR; public static final long DEFAULT_NOTIFICATION_TIMEOUT = 12 * ONE_HOUR; public static final long DEFAULT_SYSTEM_UPDATE_TIMEOUT = 2 * ONE_HOUR; @@ -1782,6 +1832,7 @@ public class AppStandbyController implements AppStandbyInternal { public static final long DEFAULT_EXEMPTED_SYNC_START_TIMEOUT = 10 * ONE_MINUTE; public static final long DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT = 10 * ONE_MINUTE; public static final long DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT = 30 * ONE_MINUTE; + public static final long DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS = ONE_DAY; private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -1829,12 +1880,12 @@ public class AppStandbyController implements AppStandbyInternal { String screenThresholdsValue = mParser.getString(KEY_SCREEN_TIME_THRESHOLDS, null); mAppStandbyScreenThresholds = parseLongArray(screenThresholdsValue, - SCREEN_TIME_THRESHOLDS); + SCREEN_TIME_THRESHOLDS, MINIMUM_SCREEN_TIME_THRESHOLDS); String elapsedThresholdsValue = mParser.getString(KEY_ELAPSED_TIME_THRESHOLDS, null); mAppStandbyElapsedThresholds = parseLongArray(elapsedThresholdsValue, - ELAPSED_TIME_THRESHOLDS); + ELAPSED_TIME_THRESHOLDS, MINIMUM_ELAPSED_TIME_THRESHOLDS); mCheckIdleIntervalMillis = Math.min(mAppStandbyElapsedThresholds[1] / 4, COMPRESS_TIME ? ONE_MINUTE : 4 * 60 * ONE_MINUTE); // 4 hours mStrongUsageTimeoutMillis = mParser.getDurationMillis( @@ -1870,8 +1921,8 @@ public class AppStandbyController implements AppStandbyInternal { mUnexemptedSyncScheduledTimeoutMillis = mParser.getDurationMillis( KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION, - COMPRESS_TIME ? ONE_MINUTE - : DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT); // TODO + COMPRESS_TIME + ? ONE_MINUTE : DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT); mSystemInteractionTimeoutMillis = mParser.getDurationMillis( KEY_SYSTEM_INTERACTION_HOLD_DURATION, @@ -1881,6 +1932,12 @@ public class AppStandbyController implements AppStandbyInternal { KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION, COMPRESS_TIME ? ONE_MINUTE : DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT); + + mInjector.mAutoRestrictedBucketDelayMs = Math.max( + COMPRESS_TIME ? ONE_MINUTE : 2 * ONE_HOUR, + mParser.getDurationMillis(KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS, + COMPRESS_TIME + ? ONE_MINUTE : DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS)); } // Check if app_idle_enabled has changed. Do this after getting the rest of the settings @@ -1888,7 +1945,7 @@ public class AppStandbyController implements AppStandbyInternal { setAppIdleEnabled(mInjector.isAppIdleEnabled()); } - long[] parseLongArray(String values, long[] defaults) { + long[] parseLongArray(String values, long[] defaults, long[] minValues) { if (values == null) return defaults; if (values.isEmpty()) { // Reset to defaults @@ -1896,13 +1953,19 @@ public class AppStandbyController implements AppStandbyInternal { } else { String[] thresholds = values.split("/"); if (thresholds.length == THRESHOLD_BUCKETS.length) { + if (minValues.length != THRESHOLD_BUCKETS.length) { + Slog.wtf(TAG, "minValues array is the wrong size"); + // Use zeroes as the minimums. + minValues = new long[THRESHOLD_BUCKETS.length]; + } long[] array = new long[THRESHOLD_BUCKETS.length]; for (int i = 0; i < THRESHOLD_BUCKETS.length; i++) { try { if (thresholds[i].startsWith("P") || thresholds[i].startsWith("p")) { - array[i] = Duration.parse(thresholds[i]).toMillis(); + array[i] = Math.max(minValues[i], + Duration.parse(thresholds[i]).toMillis()); } else { - array[i] = Long.parseLong(thresholds[i]); + array[i] = Math.max(minValues[i], Long.parseLong(thresholds[i])); } } catch (NumberFormatException|DateTimeParseException e) { return defaults; diff --git a/apex/statsd/aidl/Android.bp b/apex/statsd/aidl/Android.bp index db5f439cd40e..7c93bc73e45d 100644 --- a/apex/statsd/aidl/Android.bp +++ b/apex/statsd/aidl/Android.bp @@ -14,7 +14,7 @@ // limitations under the License. // filegroup { - name: "statsd_java_aidl", + name: "framework-statsd-aidl-sources", srcs: ["**/*.aidl"], } diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp index 80def475efb9..63a853a6005c 100644 --- a/apex/statsd/framework/Android.bp +++ b/apex/statsd/framework/Android.bp @@ -31,35 +31,56 @@ filegroup { name: "framework-statsd-sources", srcs: [ "java/**/*.java", - ":statsd_java_aidl", + ":framework-statsd-aidl-sources", ":statslog-statsd-java-gen", ], } +java_defaults { + name: "framework-statsd-defaults", + + // TODO(b/146757305): Use "module_current" once it's ready. + sdk_version: "core_current", + + libs: [ + "framework-annotations-lib", + + // TODO(b/146757305): should be unnecessary once + // sdk_version="module_lib_current" or "module_current" + "android_module_lib_stubs_current", + ], +} + java_library { name: "framework-statsd", + defaults: [ + "framework-statsd-defaults", + ], installable: true, - // TODO(b/146209659): Use system_current instead. - sdk_version: "core_platform", + srcs: [ ":framework-statsd-sources", ], + + aidl: { + // TODO(b/146757305): should be unnecessary once + // sdk_version="module_lib_current" or "module_current" + include_dirs: [ + // To refer: + // android.app.PendintIntent + "frameworks/base/core/java", + ], + }, + permitted_packages: [ "android.app", "android.os", "android.util", ], - libs: [ - "framework-annotations-lib", - // TODO(b/146230220): Use android_module_lib_stubs_current instead. - //"android_module_lib_stubs_current", - "framework-all", - ], + hostdex: true, // for hiddenapi check visibility: [ "//frameworks/base/apex/statsd:__subpackages__", - //TODO(b/146167933) remove this - "//frameworks/opt/net/wifi/service", ], apex_available: [ "com.android.os.statsd", @@ -69,13 +90,14 @@ java_library { stubs_defaults { name: "framework-statsd-stubs-srcs-defaults", - srcs: [ ":framework-statsd-sources" ], + srcs: [ + ":framework-statsd-sources", + ], + libs: [ - // TODO(b/148218250): Change to android_system_stubs_current - "framework-all", "framework-annotations-lib", ], - sdk_version: "core_platform", + sdk_version: "system_current", } droidstubs { @@ -92,6 +114,7 @@ droidstubs { "framework-module-stubs-defaults-systemapi", "framework-statsd-stubs-srcs-defaults", ], + } droidstubs { @@ -112,24 +135,24 @@ droidstubs { java_library { name: "framework-statsd-stubs-publicapi", + defaults: [ + "framework-statsd-defaults", + ], srcs: [ ":framework-statsd-stubs-srcs-publicapi" ], - // TODO(b/148218250): Change to current - libs: [ "framework-all" ], - sdk_version: "core_platform", } java_library { name: "framework-statsd-stubs-systemapi", + defaults: [ + "framework-statsd-defaults", + ], srcs: [ ":framework-statsd-stubs-srcs-systemapi" ], - // TODO(b/148218250): Change to system_current - libs: [ "framework-all" ], - sdk_version: "core_platform", } java_library { name: "framework-statsd-stubs-module_libs_api", - srcs: [ ":framework-statsd-stubs-srcs-systemapi" ], - // TODO(b/148218250): Change to system_current - libs: [ "framework-all" ], - sdk_version: "core_platform", + defaults: [ + "framework-statsd-defaults", + ], + srcs: [ ":framework-statsd-stubs-srcs-module_libs_api" ], } diff --git a/apex/statsd/service/Android.bp b/apex/statsd/service/Android.bp index 0f8a151eed7c..0f325e399d0f 100644 --- a/apex/statsd/service/Android.bp +++ b/apex/statsd/service/Android.bp @@ -16,16 +16,21 @@ java_library { srcs: [ ":service-statsd-sources", ], - // TODO(b/146209659): Use system_current instead once framework-statsd compiles against - // system_current. + + // TODO(b/146757305) should be "module_current" since not allowed to use + // @CorePlatformApi's sdk_version: "core_platform", + libs: [ "framework-annotations-lib", - "framework-statsd", - // TODO(b/146758669): Remove this line after nullability annotations are system APIs. - "android_system_stubs_current", "services-stubs", + "framework-statsd", + + // TODO(b/146757305): should be unnecessary once + // sdk_version="module_lib_current" or "module_current" + "android_module_lib_stubs_current", ], + apex_available: [ "com.android.os.statsd", "test_com.android.os.statsd", diff --git a/api/current.txt b/api/current.txt index 475506a317bc..67e512a565ed 100644 --- a/api/current.txt +++ b/api/current.txt @@ -91,6 +91,7 @@ package android { field public static final String INTERACT_ACROSS_PROFILES = "android.permission.INTERACT_ACROSS_PROFILES"; field public static final String INTERNET = "android.permission.INTERNET"; field public static final String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES"; + field public static final String LOADER_USAGE_STATS = "android.permission.LOADER_USAGE_STATS"; field public static final String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE"; field public static final String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS"; field public static final String MANAGE_EXTERNAL_STORAGE = "android.permission.MANAGE_EXTERNAL_STORAGE"; @@ -23477,6 +23478,69 @@ package android.location { method public static boolean isPresent(); } + public final class GnssAntennaInfo implements android.os.Parcelable { + ctor public GnssAntennaInfo(double, @NonNull android.location.GnssAntennaInfo.PhaseCenterOffsetCoordinates, @Nullable android.location.GnssAntennaInfo.PhaseCenterVariationCorrections, @Nullable android.location.GnssAntennaInfo.SignalGainCorrections); + method public int describeContents(); + method public double getCarrierFrequencyMHz(); + method @NonNull public android.location.GnssAntennaInfo.PhaseCenterOffsetCoordinates getPhaseCenterOffsetCoordinates(); + method @Nullable public android.location.GnssAntennaInfo.PhaseCenterVariationCorrections getPhaseCenterVariationCorrections(); + method @Nullable public android.location.GnssAntennaInfo.SignalGainCorrections getSignalGainCorrections(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAntennaInfo> CREATOR; + } + + public abstract static class GnssAntennaInfo.Callback { + ctor public GnssAntennaInfo.Callback(); + method public void onGnssAntennaInfoReceived(@NonNull java.util.List<android.location.GnssAntennaInfo>); + method public void onStatusChanged(int); + field public static final int STATUS_LOCATION_DISABLED = 2; // 0x2 + field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0 + field public static final int STATUS_READY = 1; // 0x1 + } + + public static final class GnssAntennaInfo.PhaseCenterOffsetCoordinates implements android.os.Parcelable { + ctor public GnssAntennaInfo.PhaseCenterOffsetCoordinates(double, double, double, double, double, double); + method public int describeContents(); + method public double getXCoordMillimeters(); + method public double getXCoordUncertaintyMillimeters(); + method public double getYCoordMillimeters(); + method public double getYCoordUncertaintyMillimeters(); + method public double getZCoordMillimeters(); + method public double getZCoordUncertaintyMillimeters(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAntennaInfo.PhaseCenterOffsetCoordinates> CREATOR; + } + + public static final class GnssAntennaInfo.PhaseCenterVariationCorrections implements android.os.Parcelable { + ctor public GnssAntennaInfo.PhaseCenterVariationCorrections(@NonNull double[][], @NonNull double[][]); + method public int describeContents(); + method public double getDeltaPhi(); + method public double getDeltaTheta(); + method public int getNumColumns(); + method public int getNumRows(); + method public double getPhaseCenterVariationCorrectionMillimetersAt(int, int); + method public double getPhaseCenterVariationCorrectionUncertaintyMillimetersAt(int, int); + method @NonNull public double[][] getRawCorrectionUncertaintiesArray(); + method @NonNull public double[][] getRawCorrectionsArray(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAntennaInfo.PhaseCenterVariationCorrections> CREATOR; + } + + public static final class GnssAntennaInfo.SignalGainCorrections implements android.os.Parcelable { + ctor public GnssAntennaInfo.SignalGainCorrections(@NonNull double[][], @NonNull double[][]); + method public int describeContents(); + method public double getDeltaPhi(); + method public double getDeltaTheta(); + method public int getNumColumns(); + method public int getNumRows(); + method @NonNull public double[][] getRawCorrectionUncertaintiesArray(); + method @NonNull public double[][] getRawCorrectionsArray(); + method public double getSignalGainCorrectionDbiAt(int, int); + method public double getSignalGainCorrectionUncertaintyDbiAt(int, int); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAntennaInfo.SignalGainCorrections> CREATOR; + } + public final class GnssClock implements android.os.Parcelable { method public int describeContents(); method public double getBiasNanos(); @@ -23794,6 +23858,7 @@ package android.location { method @NonNull public java.util.List<java.lang.String> getProviders(@NonNull android.location.Criteria, boolean); method public boolean isLocationEnabled(); method public boolean isProviderEnabled(@NonNull String); + method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerAntennaInfoCallback(@NonNull java.util.concurrent.Executor, @NonNull android.location.GnssAntennaInfo.Callback); method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback); method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback, @Nullable android.os.Handler); method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback); @@ -23825,6 +23890,7 @@ package android.location { method public void setTestProviderEnabled(@NonNull String, boolean); method public void setTestProviderLocation(@NonNull String, @NonNull android.location.Location); method @Deprecated public void setTestProviderStatus(@NonNull String, int, @Nullable android.os.Bundle, long); + method public void unregisterAntennaInfoCallback(@NonNull android.location.GnssAntennaInfo.Callback); method public void unregisterGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback); method public void unregisterGnssNavigationMessageCallback(@NonNull android.location.GnssNavigationMessage.Callback); method public void unregisterGnssStatusCallback(@NonNull android.location.GnssStatus.Callback); @@ -40255,6 +40321,7 @@ package android.provider { field public static final String VOLUME_NAME = "volume_name"; field public static final String WIDTH = "width"; field public static final String WRITER = "writer"; + field public static final String XMP = "xmp"; field public static final String YEAR = "year"; } @@ -43305,6 +43372,7 @@ package android.service.controls { public abstract class ControlsProviderService extends android.app.Service { ctor public ControlsProviderService(); method public abstract void loadAvailableControls(@NonNull java.util.function.Consumer<java.util.List<android.service.controls.Control>>); + method public void loadSuggestedControls(int, @NonNull java.util.function.Consumer<java.util.List<android.service.controls.Control>>); method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent); method public abstract void performControlAction(@NonNull String, @NonNull android.service.controls.actions.ControlAction, @NonNull java.util.function.Consumer<java.lang.Integer>); method @NonNull public abstract java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherFor(@NonNull java.util.List<java.lang.String>); @@ -55142,7 +55210,7 @@ package android.view { } public interface WindowInsetsController { - method public void controlWindowInsetsAnimation(int, long, @Nullable android.view.animation.Interpolator, @NonNull android.view.WindowInsetsAnimationControlListener); + method @NonNull public android.os.CancellationSignal controlWindowInsetsAnimation(int, long, @Nullable android.view.animation.Interpolator, @NonNull android.view.WindowInsetsAnimationControlListener); method public int getSystemBarsAppearance(); method public int getSystemBarsBehavior(); method public void hide(int); diff --git a/api/system-current.txt b/api/system-current.txt index a722db95fdd6..e017b94ab29e 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -18,6 +18,7 @@ package android { field public static final String ACCESS_SHORTCUTS = "android.permission.ACCESS_SHORTCUTS"; field public static final String ACCESS_SURFACE_FLINGER = "android.permission.ACCESS_SURFACE_FLINGER"; field public static final String ACCESS_TV_TUNER = "android.permission.ACCESS_TV_TUNER"; + field public static final String ACCESS_VIBRATOR_STATE = "android.permission.ACCESS_VIBRATOR_STATE"; field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING"; field public static final String ADJUST_RUNTIME_PERMISSIONS_POLICY = "android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY"; field public static final String ALLOCATE_AGGRESSIVE = "android.permission.ALLOCATE_AGGRESSIVE"; @@ -391,6 +392,7 @@ package android.app { field public static final String OPSTR_INSTANT_APP_START_FOREGROUND = "android:instant_app_start_foreground"; field public static final String OPSTR_INTERACT_ACROSS_PROFILES = "android:interact_across_profiles"; field public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage"; + field public static final String OPSTR_LOADER_USAGE_STATS = "android:loader_usage_stats"; field public static final String OPSTR_MANAGE_EXTERNAL_STORAGE = "android:manage_external_storage"; field public static final String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels"; field public static final String OPSTR_MUTE_MICROPHONE = "android:mute_microphone"; @@ -1922,18 +1924,22 @@ package android.content.integrity { public abstract class IntegrityFormula { method @NonNull public static android.content.integrity.IntegrityFormula all(@NonNull android.content.integrity.IntegrityFormula...); method @NonNull public static android.content.integrity.IntegrityFormula any(@NonNull android.content.integrity.IntegrityFormula...); - method @NonNull public android.content.integrity.IntegrityFormula equalTo(@NonNull String); - method @NonNull public android.content.integrity.IntegrityFormula equalTo(boolean); - method @NonNull public android.content.integrity.IntegrityFormula equalTo(long); - method @NonNull public android.content.integrity.IntegrityFormula greaterThan(long); - method @NonNull public android.content.integrity.IntegrityFormula greaterThanOrEquals(long); method @NonNull public static android.content.integrity.IntegrityFormula not(@NonNull android.content.integrity.IntegrityFormula); - field @NonNull public static final android.content.integrity.IntegrityFormula APP_CERTIFICATE; - field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_CERTIFICATE; - field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_NAME; - field @NonNull public static final android.content.integrity.IntegrityFormula PACKAGE_NAME; - field @NonNull public static final android.content.integrity.IntegrityFormula PRE_INSTALLED; - field @NonNull public static final android.content.integrity.IntegrityFormula VERSION_CODE; + } + + public static final class IntegrityFormula.Application { + method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String); + method @NonNull public static android.content.integrity.IntegrityFormula isPreInstalled(); + method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String); + method @NonNull public static android.content.integrity.IntegrityFormula versionCodeEquals(@NonNull long); + method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThan(@NonNull long); + method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThanOrEqualTo(@NonNull long); + } + + public static final class IntegrityFormula.Installer { + method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String); + method @NonNull public static android.content.integrity.IntegrityFormula notAllowedByManifest(); + method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String); } public final class Rule implements android.os.Parcelable { @@ -2210,8 +2216,8 @@ package android.content.pm { field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock"; field public static final int FLAGS_PERMISSION_RESERVED_PERMISSIONCONTROLLER = -268435456; // 0xf0000000 field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000 - field public static final int FLAG_PERMISSION_DONT_AUTO_REVOKE = 131072; // 0x20000 - field public static final int FLAG_PERMISSION_DONT_AUTO_REVOKE_USER_SET = 262144; // 0x40000 + field public static final int FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED = 131072; // 0x20000 + field public static final int FLAG_PERMISSION_AUTO_REVOKE_USER_SET = 262144; // 0x40000 field public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 32; // 0x20 field public static final int FLAG_PERMISSION_GRANTED_BY_ROLE = 32768; // 0x8000 field public static final int FLAG_PERMISSION_ONE_TIME = 65536; // 0x10000 @@ -2295,7 +2301,7 @@ package android.content.pm { method public void onPermissionsChanged(int); } - @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME, android.content.pm.PackageManager.FLAG_PERMISSION_DONT_AUTO_REVOKE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags { + @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKE_USER_SET}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags { } public class PermissionGroupInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { @@ -3800,6 +3806,7 @@ package android.location { public final class GnssCapabilities { method public boolean hasGeofencing(); + method public boolean hasGnssAntennaInfo(); method public boolean hasLowPowerMode(); method public boolean hasMeasurementCorrections(); method public boolean hasMeasurementCorrectionsExcessPathLength(); @@ -3902,236 +3909,236 @@ package android.location { method @NonNull public android.location.GnssSingleSatCorrection.Builder setSatelliteId(@IntRange(from=0) int); } - public class GpsClock implements android.os.Parcelable { - method public int describeContents(); - method public double getBiasInNs(); - method public double getBiasUncertaintyInNs(); - method public double getDriftInNsPerSec(); - method public double getDriftUncertaintyInNsPerSec(); - method public long getFullBiasInNs(); - method public short getLeapSecond(); - method public long getTimeInNs(); - method public double getTimeUncertaintyInNs(); - method public byte getType(); - method public boolean hasBiasInNs(); - method public boolean hasBiasUncertaintyInNs(); - method public boolean hasDriftInNsPerSec(); - method public boolean hasDriftUncertaintyInNsPerSec(); - method public boolean hasFullBiasInNs(); - method public boolean hasLeapSecond(); - method public boolean hasTimeUncertaintyInNs(); - method public void reset(); - method public void resetBiasInNs(); - method public void resetBiasUncertaintyInNs(); - method public void resetDriftInNsPerSec(); - method public void resetDriftUncertaintyInNsPerSec(); - method public void resetFullBiasInNs(); - method public void resetLeapSecond(); - method public void resetTimeUncertaintyInNs(); - method public void set(android.location.GpsClock); - method public void setBiasInNs(double); - method public void setBiasUncertaintyInNs(double); - method public void setDriftInNsPerSec(double); - method public void setDriftUncertaintyInNsPerSec(double); - method public void setFullBiasInNs(long); - method public void setLeapSecond(short); - method public void setTimeInNs(long); - method public void setTimeUncertaintyInNs(double); - method public void setType(byte); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.location.GpsClock> CREATOR; - field public static final byte TYPE_GPS_TIME = 2; // 0x2 - field public static final byte TYPE_LOCAL_HW_TIME = 1; // 0x1 - field public static final byte TYPE_UNKNOWN = 0; // 0x0 - } - - public class GpsMeasurement implements android.os.Parcelable { - method public int describeContents(); - method public double getAccumulatedDeltaRangeInMeters(); - method public short getAccumulatedDeltaRangeState(); - method public double getAccumulatedDeltaRangeUncertaintyInMeters(); - method public double getAzimuthInDeg(); - method public double getAzimuthUncertaintyInDeg(); - method public int getBitNumber(); - method public long getCarrierCycles(); - method public float getCarrierFrequencyInHz(); - method public double getCarrierPhase(); - method public double getCarrierPhaseUncertainty(); - method public double getCn0InDbHz(); - method public double getCodePhaseInChips(); - method public double getCodePhaseUncertaintyInChips(); - method public double getDopplerShiftInHz(); - method public double getDopplerShiftUncertaintyInHz(); - method public double getElevationInDeg(); - method public double getElevationUncertaintyInDeg(); - method public byte getLossOfLock(); - method public byte getMultipathIndicator(); - method public byte getPrn(); - method public double getPseudorangeInMeters(); - method public double getPseudorangeRateInMetersPerSec(); - method public double getPseudorangeRateUncertaintyInMetersPerSec(); - method public double getPseudorangeUncertaintyInMeters(); - method public long getReceivedGpsTowInNs(); - method public long getReceivedGpsTowUncertaintyInNs(); - method public double getSnrInDb(); - method public short getState(); - method public short getTimeFromLastBitInMs(); - method public double getTimeOffsetInNs(); - method public boolean hasAzimuthInDeg(); - method public boolean hasAzimuthUncertaintyInDeg(); - method public boolean hasBitNumber(); - method public boolean hasCarrierCycles(); - method public boolean hasCarrierFrequencyInHz(); - method public boolean hasCarrierPhase(); - method public boolean hasCarrierPhaseUncertainty(); - method public boolean hasCodePhaseInChips(); - method public boolean hasCodePhaseUncertaintyInChips(); - method public boolean hasDopplerShiftInHz(); - method public boolean hasDopplerShiftUncertaintyInHz(); - method public boolean hasElevationInDeg(); - method public boolean hasElevationUncertaintyInDeg(); - method public boolean hasPseudorangeInMeters(); - method public boolean hasPseudorangeUncertaintyInMeters(); - method public boolean hasSnrInDb(); - method public boolean hasTimeFromLastBitInMs(); - method public boolean isPseudorangeRateCorrected(); - method public boolean isUsedInFix(); - method public void reset(); - method public void resetAzimuthInDeg(); - method public void resetAzimuthUncertaintyInDeg(); - method public void resetBitNumber(); - method public void resetCarrierCycles(); - method public void resetCarrierFrequencyInHz(); - method public void resetCarrierPhase(); - method public void resetCarrierPhaseUncertainty(); - method public void resetCodePhaseInChips(); - method public void resetCodePhaseUncertaintyInChips(); - method public void resetDopplerShiftInHz(); - method public void resetDopplerShiftUncertaintyInHz(); - method public void resetElevationInDeg(); - method public void resetElevationUncertaintyInDeg(); - method public void resetPseudorangeInMeters(); - method public void resetPseudorangeUncertaintyInMeters(); - method public void resetSnrInDb(); - method public void resetTimeFromLastBitInMs(); - method public void set(android.location.GpsMeasurement); - method public void setAccumulatedDeltaRangeInMeters(double); - method public void setAccumulatedDeltaRangeState(short); - method public void setAccumulatedDeltaRangeUncertaintyInMeters(double); - method public void setAzimuthInDeg(double); - method public void setAzimuthUncertaintyInDeg(double); - method public void setBitNumber(int); - method public void setCarrierCycles(long); - method public void setCarrierFrequencyInHz(float); - method public void setCarrierPhase(double); - method public void setCarrierPhaseUncertainty(double); - method public void setCn0InDbHz(double); - method public void setCodePhaseInChips(double); - method public void setCodePhaseUncertaintyInChips(double); - method public void setDopplerShiftInHz(double); - method public void setDopplerShiftUncertaintyInHz(double); - method public void setElevationInDeg(double); - method public void setElevationUncertaintyInDeg(double); - method public void setLossOfLock(byte); - method public void setMultipathIndicator(byte); - method public void setPrn(byte); - method public void setPseudorangeInMeters(double); - method public void setPseudorangeRateInMetersPerSec(double); - method public void setPseudorangeRateUncertaintyInMetersPerSec(double); - method public void setPseudorangeUncertaintyInMeters(double); - method public void setReceivedGpsTowInNs(long); - method public void setReceivedGpsTowUncertaintyInNs(long); - method public void setSnrInDb(double); - method public void setState(short); - method public void setTimeFromLastBitInMs(short); - method public void setTimeOffsetInNs(double); - method public void setUsedInFix(boolean); - method public void writeToParcel(android.os.Parcel, int); - field public static final short ADR_STATE_CYCLE_SLIP = 4; // 0x4 - field public static final short ADR_STATE_RESET = 2; // 0x2 - field public static final short ADR_STATE_UNKNOWN = 0; // 0x0 - field public static final short ADR_STATE_VALID = 1; // 0x1 - field @NonNull public static final android.os.Parcelable.Creator<android.location.GpsMeasurement> CREATOR; - field public static final byte LOSS_OF_LOCK_CYCLE_SLIP = 2; // 0x2 - field public static final byte LOSS_OF_LOCK_OK = 1; // 0x1 - field public static final byte LOSS_OF_LOCK_UNKNOWN = 0; // 0x0 - field public static final byte MULTIPATH_INDICATOR_DETECTED = 1; // 0x1 - field public static final byte MULTIPATH_INDICATOR_NOT_USED = 2; // 0x2 - field public static final byte MULTIPATH_INDICATOR_UNKNOWN = 0; // 0x0 - field public static final short STATE_BIT_SYNC = 2; // 0x2 - field public static final short STATE_CODE_LOCK = 1; // 0x1 - field public static final short STATE_MSEC_AMBIGUOUS = 16; // 0x10 - field public static final short STATE_SUBFRAME_SYNC = 4; // 0x4 - field public static final short STATE_TOW_DECODED = 8; // 0x8 - field public static final short STATE_UNKNOWN = 0; // 0x0 - } - - public class GpsMeasurementsEvent implements android.os.Parcelable { - ctor public GpsMeasurementsEvent(android.location.GpsClock, android.location.GpsMeasurement[]); - method public int describeContents(); - method @NonNull public android.location.GpsClock getClock(); - method @NonNull public java.util.Collection<android.location.GpsMeasurement> getMeasurements(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.location.GpsMeasurementsEvent> CREATOR; - field public static final int STATUS_GPS_LOCATION_DISABLED = 2; // 0x2 - field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0 - field public static final int STATUS_READY = 1; // 0x1 - } - - public static interface GpsMeasurementsEvent.Listener { - method public void onGpsMeasurementsReceived(android.location.GpsMeasurementsEvent); - method public void onStatusChanged(int); - } - - public class GpsNavigationMessage implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public byte[] getData(); - method public short getMessageId(); - method public byte getPrn(); - method public short getStatus(); - method public short getSubmessageId(); - method public byte getType(); - method public void reset(); - method public void set(android.location.GpsNavigationMessage); - method public void setData(byte[]); - method public void setMessageId(short); - method public void setPrn(byte); - method public void setStatus(short); - method public void setSubmessageId(short); - method public void setType(byte); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.location.GpsNavigationMessage> CREATOR; - field public static final short STATUS_PARITY_PASSED = 1; // 0x1 - field public static final short STATUS_PARITY_REBUILT = 2; // 0x2 - field public static final short STATUS_UNKNOWN = 0; // 0x0 - field public static final byte TYPE_CNAV2 = 4; // 0x4 - field public static final byte TYPE_L1CA = 1; // 0x1 - field public static final byte TYPE_L2CNAV = 2; // 0x2 - field public static final byte TYPE_L5CNAV = 3; // 0x3 - field public static final byte TYPE_UNKNOWN = 0; // 0x0 + @Deprecated public class GpsClock implements android.os.Parcelable { + method @Deprecated public int describeContents(); + method @Deprecated public double getBiasInNs(); + method @Deprecated public double getBiasUncertaintyInNs(); + method @Deprecated public double getDriftInNsPerSec(); + method @Deprecated public double getDriftUncertaintyInNsPerSec(); + method @Deprecated public long getFullBiasInNs(); + method @Deprecated public short getLeapSecond(); + method @Deprecated public long getTimeInNs(); + method @Deprecated public double getTimeUncertaintyInNs(); + method @Deprecated public byte getType(); + method @Deprecated public boolean hasBiasInNs(); + method @Deprecated public boolean hasBiasUncertaintyInNs(); + method @Deprecated public boolean hasDriftInNsPerSec(); + method @Deprecated public boolean hasDriftUncertaintyInNsPerSec(); + method @Deprecated public boolean hasFullBiasInNs(); + method @Deprecated public boolean hasLeapSecond(); + method @Deprecated public boolean hasTimeUncertaintyInNs(); + method @Deprecated public void reset(); + method @Deprecated public void resetBiasInNs(); + method @Deprecated public void resetBiasUncertaintyInNs(); + method @Deprecated public void resetDriftInNsPerSec(); + method @Deprecated public void resetDriftUncertaintyInNsPerSec(); + method @Deprecated public void resetFullBiasInNs(); + method @Deprecated public void resetLeapSecond(); + method @Deprecated public void resetTimeUncertaintyInNs(); + method @Deprecated public void set(android.location.GpsClock); + method @Deprecated public void setBiasInNs(double); + method @Deprecated public void setBiasUncertaintyInNs(double); + method @Deprecated public void setDriftInNsPerSec(double); + method @Deprecated public void setDriftUncertaintyInNsPerSec(double); + method @Deprecated public void setFullBiasInNs(long); + method @Deprecated public void setLeapSecond(short); + method @Deprecated public void setTimeInNs(long); + method @Deprecated public void setTimeUncertaintyInNs(double); + method @Deprecated public void setType(byte); + method @Deprecated public void writeToParcel(android.os.Parcel, int); + field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.location.GpsClock> CREATOR; + field @Deprecated public static final byte TYPE_GPS_TIME = 2; // 0x2 + field @Deprecated public static final byte TYPE_LOCAL_HW_TIME = 1; // 0x1 + field @Deprecated public static final byte TYPE_UNKNOWN = 0; // 0x0 } - public class GpsNavigationMessageEvent implements android.os.Parcelable { - ctor public GpsNavigationMessageEvent(android.location.GpsNavigationMessage); - method public int describeContents(); - method @NonNull public android.location.GpsNavigationMessage getNavigationMessage(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.location.GpsNavigationMessageEvent> CREATOR; - field public static int STATUS_GPS_LOCATION_DISABLED; - field public static int STATUS_NOT_SUPPORTED; - field public static int STATUS_READY; + @Deprecated public class GpsMeasurement implements android.os.Parcelable { + method @Deprecated public int describeContents(); + method @Deprecated public double getAccumulatedDeltaRangeInMeters(); + method @Deprecated public short getAccumulatedDeltaRangeState(); + method @Deprecated public double getAccumulatedDeltaRangeUncertaintyInMeters(); + method @Deprecated public double getAzimuthInDeg(); + method @Deprecated public double getAzimuthUncertaintyInDeg(); + method @Deprecated public int getBitNumber(); + method @Deprecated public long getCarrierCycles(); + method @Deprecated public float getCarrierFrequencyInHz(); + method @Deprecated public double getCarrierPhase(); + method @Deprecated public double getCarrierPhaseUncertainty(); + method @Deprecated public double getCn0InDbHz(); + method @Deprecated public double getCodePhaseInChips(); + method @Deprecated public double getCodePhaseUncertaintyInChips(); + method @Deprecated public double getDopplerShiftInHz(); + method @Deprecated public double getDopplerShiftUncertaintyInHz(); + method @Deprecated public double getElevationInDeg(); + method @Deprecated public double getElevationUncertaintyInDeg(); + method @Deprecated public byte getLossOfLock(); + method @Deprecated public byte getMultipathIndicator(); + method @Deprecated public byte getPrn(); + method @Deprecated public double getPseudorangeInMeters(); + method @Deprecated public double getPseudorangeRateInMetersPerSec(); + method @Deprecated public double getPseudorangeRateUncertaintyInMetersPerSec(); + method @Deprecated public double getPseudorangeUncertaintyInMeters(); + method @Deprecated public long getReceivedGpsTowInNs(); + method @Deprecated public long getReceivedGpsTowUncertaintyInNs(); + method @Deprecated public double getSnrInDb(); + method @Deprecated public short getState(); + method @Deprecated public short getTimeFromLastBitInMs(); + method @Deprecated public double getTimeOffsetInNs(); + method @Deprecated public boolean hasAzimuthInDeg(); + method @Deprecated public boolean hasAzimuthUncertaintyInDeg(); + method @Deprecated public boolean hasBitNumber(); + method @Deprecated public boolean hasCarrierCycles(); + method @Deprecated public boolean hasCarrierFrequencyInHz(); + method @Deprecated public boolean hasCarrierPhase(); + method @Deprecated public boolean hasCarrierPhaseUncertainty(); + method @Deprecated public boolean hasCodePhaseInChips(); + method @Deprecated public boolean hasCodePhaseUncertaintyInChips(); + method @Deprecated public boolean hasDopplerShiftInHz(); + method @Deprecated public boolean hasDopplerShiftUncertaintyInHz(); + method @Deprecated public boolean hasElevationInDeg(); + method @Deprecated public boolean hasElevationUncertaintyInDeg(); + method @Deprecated public boolean hasPseudorangeInMeters(); + method @Deprecated public boolean hasPseudorangeUncertaintyInMeters(); + method @Deprecated public boolean hasSnrInDb(); + method @Deprecated public boolean hasTimeFromLastBitInMs(); + method @Deprecated public boolean isPseudorangeRateCorrected(); + method @Deprecated public boolean isUsedInFix(); + method @Deprecated public void reset(); + method @Deprecated public void resetAzimuthInDeg(); + method @Deprecated public void resetAzimuthUncertaintyInDeg(); + method @Deprecated public void resetBitNumber(); + method @Deprecated public void resetCarrierCycles(); + method @Deprecated public void resetCarrierFrequencyInHz(); + method @Deprecated public void resetCarrierPhase(); + method @Deprecated public void resetCarrierPhaseUncertainty(); + method @Deprecated public void resetCodePhaseInChips(); + method @Deprecated public void resetCodePhaseUncertaintyInChips(); + method @Deprecated public void resetDopplerShiftInHz(); + method @Deprecated public void resetDopplerShiftUncertaintyInHz(); + method @Deprecated public void resetElevationInDeg(); + method @Deprecated public void resetElevationUncertaintyInDeg(); + method @Deprecated public void resetPseudorangeInMeters(); + method @Deprecated public void resetPseudorangeUncertaintyInMeters(); + method @Deprecated public void resetSnrInDb(); + method @Deprecated public void resetTimeFromLastBitInMs(); + method @Deprecated public void set(android.location.GpsMeasurement); + method @Deprecated public void setAccumulatedDeltaRangeInMeters(double); + method @Deprecated public void setAccumulatedDeltaRangeState(short); + method @Deprecated public void setAccumulatedDeltaRangeUncertaintyInMeters(double); + method @Deprecated public void setAzimuthInDeg(double); + method @Deprecated public void setAzimuthUncertaintyInDeg(double); + method @Deprecated public void setBitNumber(int); + method @Deprecated public void setCarrierCycles(long); + method @Deprecated public void setCarrierFrequencyInHz(float); + method @Deprecated public void setCarrierPhase(double); + method @Deprecated public void setCarrierPhaseUncertainty(double); + method @Deprecated public void setCn0InDbHz(double); + method @Deprecated public void setCodePhaseInChips(double); + method @Deprecated public void setCodePhaseUncertaintyInChips(double); + method @Deprecated public void setDopplerShiftInHz(double); + method @Deprecated public void setDopplerShiftUncertaintyInHz(double); + method @Deprecated public void setElevationInDeg(double); + method @Deprecated public void setElevationUncertaintyInDeg(double); + method @Deprecated public void setLossOfLock(byte); + method @Deprecated public void setMultipathIndicator(byte); + method @Deprecated public void setPrn(byte); + method @Deprecated public void setPseudorangeInMeters(double); + method @Deprecated public void setPseudorangeRateInMetersPerSec(double); + method @Deprecated public void setPseudorangeRateUncertaintyInMetersPerSec(double); + method @Deprecated public void setPseudorangeUncertaintyInMeters(double); + method @Deprecated public void setReceivedGpsTowInNs(long); + method @Deprecated public void setReceivedGpsTowUncertaintyInNs(long); + method @Deprecated public void setSnrInDb(double); + method @Deprecated public void setState(short); + method @Deprecated public void setTimeFromLastBitInMs(short); + method @Deprecated public void setTimeOffsetInNs(double); + method @Deprecated public void setUsedInFix(boolean); + method @Deprecated public void writeToParcel(android.os.Parcel, int); + field @Deprecated public static final short ADR_STATE_CYCLE_SLIP = 4; // 0x4 + field @Deprecated public static final short ADR_STATE_RESET = 2; // 0x2 + field @Deprecated public static final short ADR_STATE_UNKNOWN = 0; // 0x0 + field @Deprecated public static final short ADR_STATE_VALID = 1; // 0x1 + field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.location.GpsMeasurement> CREATOR; + field @Deprecated public static final byte LOSS_OF_LOCK_CYCLE_SLIP = 2; // 0x2 + field @Deprecated public static final byte LOSS_OF_LOCK_OK = 1; // 0x1 + field @Deprecated public static final byte LOSS_OF_LOCK_UNKNOWN = 0; // 0x0 + field @Deprecated public static final byte MULTIPATH_INDICATOR_DETECTED = 1; // 0x1 + field @Deprecated public static final byte MULTIPATH_INDICATOR_NOT_USED = 2; // 0x2 + field @Deprecated public static final byte MULTIPATH_INDICATOR_UNKNOWN = 0; // 0x0 + field @Deprecated public static final short STATE_BIT_SYNC = 2; // 0x2 + field @Deprecated public static final short STATE_CODE_LOCK = 1; // 0x1 + field @Deprecated public static final short STATE_MSEC_AMBIGUOUS = 16; // 0x10 + field @Deprecated public static final short STATE_SUBFRAME_SYNC = 4; // 0x4 + field @Deprecated public static final short STATE_TOW_DECODED = 8; // 0x8 + field @Deprecated public static final short STATE_UNKNOWN = 0; // 0x0 + } + + @Deprecated public class GpsMeasurementsEvent implements android.os.Parcelable { + ctor @Deprecated public GpsMeasurementsEvent(android.location.GpsClock, android.location.GpsMeasurement[]); + method @Deprecated public int describeContents(); + method @Deprecated @NonNull public android.location.GpsClock getClock(); + method @Deprecated @NonNull public java.util.Collection<android.location.GpsMeasurement> getMeasurements(); + method @Deprecated public void writeToParcel(android.os.Parcel, int); + field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.location.GpsMeasurementsEvent> CREATOR; + field @Deprecated public static final int STATUS_GPS_LOCATION_DISABLED = 2; // 0x2 + field @Deprecated public static final int STATUS_NOT_SUPPORTED = 0; // 0x0 + field @Deprecated public static final int STATUS_READY = 1; // 0x1 } - public static interface GpsNavigationMessageEvent.Listener { - method public void onGpsNavigationMessageReceived(android.location.GpsNavigationMessageEvent); - method public void onStatusChanged(int); + @Deprecated public static interface GpsMeasurementsEvent.Listener { + method @Deprecated public void onGpsMeasurementsReceived(android.location.GpsMeasurementsEvent); + method @Deprecated public void onStatusChanged(int); + } + + @Deprecated public class GpsNavigationMessage implements android.os.Parcelable { + method @Deprecated public int describeContents(); + method @Deprecated @NonNull public byte[] getData(); + method @Deprecated public short getMessageId(); + method @Deprecated public byte getPrn(); + method @Deprecated public short getStatus(); + method @Deprecated public short getSubmessageId(); + method @Deprecated public byte getType(); + method @Deprecated public void reset(); + method @Deprecated public void set(android.location.GpsNavigationMessage); + method @Deprecated public void setData(byte[]); + method @Deprecated public void setMessageId(short); + method @Deprecated public void setPrn(byte); + method @Deprecated public void setStatus(short); + method @Deprecated public void setSubmessageId(short); + method @Deprecated public void setType(byte); + method @Deprecated public void writeToParcel(android.os.Parcel, int); + field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.location.GpsNavigationMessage> CREATOR; + field @Deprecated public static final short STATUS_PARITY_PASSED = 1; // 0x1 + field @Deprecated public static final short STATUS_PARITY_REBUILT = 2; // 0x2 + field @Deprecated public static final short STATUS_UNKNOWN = 0; // 0x0 + field @Deprecated public static final byte TYPE_CNAV2 = 4; // 0x4 + field @Deprecated public static final byte TYPE_L1CA = 1; // 0x1 + field @Deprecated public static final byte TYPE_L2CNAV = 2; // 0x2 + field @Deprecated public static final byte TYPE_L5CNAV = 3; // 0x3 + field @Deprecated public static final byte TYPE_UNKNOWN = 0; // 0x0 + } + + @Deprecated public class GpsNavigationMessageEvent implements android.os.Parcelable { + ctor @Deprecated public GpsNavigationMessageEvent(android.location.GpsNavigationMessage); + method @Deprecated public int describeContents(); + method @Deprecated @NonNull public android.location.GpsNavigationMessage getNavigationMessage(); + method @Deprecated public void writeToParcel(android.os.Parcel, int); + field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.location.GpsNavigationMessageEvent> CREATOR; + field @Deprecated public static int STATUS_GPS_LOCATION_DISABLED; + field @Deprecated public static int STATUS_NOT_SUPPORTED; + field @Deprecated public static int STATUS_READY; + } + + @Deprecated public static interface GpsNavigationMessageEvent.Listener { + method @Deprecated public void onGpsNavigationMessageReceived(android.location.GpsNavigationMessageEvent); + method @Deprecated public void onStatusChanged(int); } public class Location implements android.os.Parcelable { method public boolean isComplete(); method public void makeComplete(); method public void setIsFromMockProvider(boolean); - field public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation"; + field @Deprecated public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation"; } public class LocationManager { @@ -4264,6 +4271,7 @@ package android.media { public class AudioManager { method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException; method public void clearAudioServerStateCallback(); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy); method @IntRange(from=0) public int getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo); @@ -4280,6 +4288,7 @@ package android.media { method public boolean isHdmiSystemAudioSupported(); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy); method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean removePreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException; method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException; @@ -4306,6 +4315,10 @@ package android.media { method public void onAudioServerUp(); } + public static interface AudioManager.OnPreferredDeviceForStrategyChangedListener { + method public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDevice); + } + public abstract static class AudioManager.VolumeGroupCallback { ctor public AudioManager.VolumeGroupCallback(); method public void onAudioVolumeGroupChanged(int, int); @@ -5276,9 +5289,8 @@ package android.media.tv.tuner.filter { method public int getType(); } - public static class TsFilterConfiguration.Builder { + public static class TsFilterConfiguration.Builder extends android.media.tv.tuner.filter.FilterConfiguration.Builder<android.media.tv.tuner.filter.TsFilterConfiguration.Builder> { method @NonNull public android.media.tv.tuner.filter.TsFilterConfiguration build(); - method @NonNull public android.media.tv.tuner.filter.TsFilterConfiguration.Builder setSettings(@NonNull android.media.tv.tuner.filter.Settings); method @NonNull public android.media.tv.tuner.filter.TsFilterConfiguration.Builder setTpid(int); } @@ -9093,6 +9105,17 @@ package android.os { @IntDef(flag=true, prefix={"RESTRICTION_"}, value={android.os.UserManager.RESTRICTION_NOT_SET, android.os.UserManager.RESTRICTION_SOURCE_SYSTEM, android.os.UserManager.RESTRICTION_SOURCE_DEVICE_OWNER, android.os.UserManager.RESTRICTION_SOURCE_PROFILE_OWNER}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface UserManager.UserRestrictionSource { } + public abstract class Vibrator { + method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void addVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener); + method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void addVibratorStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.Vibrator.OnVibratorStateChangedListener); + method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public boolean isVibrating(); + method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void removeVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener); + } + + public static interface Vibrator.OnVibratorStateChangedListener { + method public void onVibratorStateChanged(boolean); + } + public class WorkSource implements android.os.Parcelable { ctor public WorkSource(int); ctor public WorkSource(int, @NonNull String); @@ -13196,19 +13219,7 @@ package android.telephony.ims { } public class ImsRcsManager implements android.telephony.ims.RegistrationManager { - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(int) throws android.telephony.ims.ImsException; - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(int, int) throws android.telephony.ims.ImsException; - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException; - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerRcsAvailabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsRcsManager.AvailabilityCallback) throws android.telephony.ims.ImsException; - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterRcsAvailabilityCallback(@NonNull android.telephony.ims.ImsRcsManager.AvailabilityCallback) throws android.telephony.ims.ImsException; - } - - public static class ImsRcsManager.AvailabilityCallback { - ctor public ImsRcsManager.AvailabilityCallback(); - method public void onAvailabilityChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities); + method @NonNull public android.telephony.ims.RcsUceAdapter getUceAdapter(); } public final class ImsReasonInfo implements android.os.Parcelable { @@ -13587,34 +13598,8 @@ package android.telephony.ims { } public class RcsUceAdapter { - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getUcePublishState() throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException; - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void requestCapabilities(@NonNull java.util.concurrent.Executor, @NonNull java.util.List<android.net.Uri>, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException; - field public static final int ERROR_ALREADY_IN_QUEUE = 13; // 0xd - field public static final int ERROR_FORBIDDEN = 6; // 0x6 - field public static final int ERROR_GENERIC_FAILURE = 1; // 0x1 - field public static final int ERROR_INSUFFICIENT_MEMORY = 11; // 0xb - field public static final int ERROR_LOST_NETWORK = 12; // 0xc - field public static final int ERROR_NOT_AUTHORIZED = 5; // 0x5 - field public static final int ERROR_NOT_AVAILABLE = 3; // 0x3 - field public static final int ERROR_NOT_ENABLED = 2; // 0x2 - field public static final int ERROR_NOT_FOUND = 7; // 0x7 - field public static final int ERROR_NOT_REGISTERED = 4; // 0x4 - field public static final int ERROR_REQUEST_TIMEOUT = 10; // 0xa - field public static final int ERROR_REQUEST_TOO_LARGE = 8; // 0x8 - field public static final int PUBLISH_STATE_200_OK = 1; // 0x1 - field public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; // 0x2 - field public static final int PUBLISH_STATE_OTHER_ERROR = 6; // 0x6 - field public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; // 0x4 - field public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; // 0x5 - field public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3; // 0x3 - } - - public static class RcsUceAdapter.CapabilitiesCallback { - ctor public RcsUceAdapter.CapabilitiesCallback(); - method public void onCapabilitiesReceived(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>); - method public void onError(int); } } @@ -13700,23 +13685,8 @@ package android.telephony.ims.feature { public class RcsFeature extends android.telephony.ims.feature.ImsFeature { ctor public RcsFeature(); method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy); - method @NonNull public android.telephony.ims.stub.RcsSipOptionsImplBase getOptionsExchangeImpl(); - method @NonNull public android.telephony.ims.stub.RcsPresenceExchangeImplBase getPresenceExchangeImpl(); - method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities); method public void onFeatureReady(); method public void onFeatureRemoved(); - method public boolean queryCapabilityConfiguration(int, int); - method @NonNull public final android.telephony.ims.feature.RcsFeature.RcsImsCapabilities queryCapabilityStatus(); - } - - public static class RcsFeature.RcsImsCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities { - ctor public RcsFeature.RcsImsCapabilities(int); - method public void addCapabilities(int); - method public boolean isCapable(int); - method public void removeCapabilities(int); - field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0 - field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1 - field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2 } } @@ -13894,71 +13864,6 @@ package android.telephony.ims.stub { field public static final int INVALID_RESULT = -1; // 0xffffffff } - public class RcsCapabilityExchange { - ctor public RcsCapabilityExchange(); - method public final void onCommandUpdate(int, int) throws android.telephony.ims.ImsException; - field public static final int COMMAND_CODE_FETCH_ERROR = 4; // 0x4 - field public static final int COMMAND_CODE_GENERIC_FAILURE = 2; // 0x2 - field public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 6; // 0x6 - field public static final int COMMAND_CODE_INVALID_PARAM = 3; // 0x3 - field public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 7; // 0x7 - field public static final int COMMAND_CODE_NOT_FOUND = 9; // 0x9 - field public static final int COMMAND_CODE_NOT_SUPPORTED = 8; // 0x8 - field public static final int COMMAND_CODE_NO_CHANGE_IN_CAP = 11; // 0xb - field public static final int COMMAND_CODE_REQUEST_TIMEOUT = 5; // 0x5 - field public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 10; // 0xa - field public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0; // 0x0 - field public static final int COMMAND_CODE_SUCCESS = 1; // 0x1 - } - - public class RcsPresenceExchangeImplBase extends android.telephony.ims.stub.RcsCapabilityExchange { - ctor public RcsPresenceExchangeImplBase(); - method public final void onCapabilityRequestResponse(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>, int) throws android.telephony.ims.ImsException; - method public final void onNetworkResponse(int, @NonNull String, int) throws android.telephony.ims.ImsException; - method public final void onNotifyUpdateCapabilites(int) throws android.telephony.ims.ImsException; - method public final void onUnpublish() throws android.telephony.ims.ImsException; - method public void requestCapabilities(@NonNull java.util.List<android.net.Uri>, int); - method public void updateCapabilities(@NonNull android.telephony.ims.RcsContactUceCapability, int); - field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 0; // 0x0 - field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 6; // 0x6 - field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 5; // 0x5 - field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 3; // 0x3 - field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 4; // 0x4 - field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 8; // 0x8 - field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 1; // 0x1 - field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 2; // 0x2 - field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10; // 0xa - field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; // 0xb - field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 7; // 0x7 - field public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 9; // 0x9 - field public static final int RESPONSE_FORBIDDEN = 3; // 0x3 - field public static final int RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE = 2; // 0x2 - field public static final int RESPONSE_NOT_FOUND = 4; // 0x4 - field public static final int RESPONSE_NOT_REGISTERED = 1; // 0x1 - field public static final int RESPONSE_SIP_INTERVAL_TOO_SHORT = 7; // 0x7 - field public static final int RESPONSE_SIP_REQUEST_TIMEOUT = 5; // 0x5 - field public static final int RESPONSE_SIP_SERVICE_UNAVAILABLE = 8; // 0x8 - field public static final int RESPONSE_SUBSCRIBE_GENERIC_FAILURE = -1; // 0xffffffff - field public static final int RESPONSE_SUBSCRIBE_TOO_LARGE = 6; // 0x6 - field public static final int RESPONSE_SUCCESS = 0; // 0x0 - } - - public class RcsSipOptionsImplBase extends android.telephony.ims.stub.RcsCapabilityExchange { - ctor public RcsSipOptionsImplBase(); - method public final void onCapabilityRequestResponse(int, @NonNull String, @Nullable android.telephony.ims.RcsContactUceCapability, int) throws android.telephony.ims.ImsException; - method public final void onRemoteCapabilityRequest(@NonNull android.net.Uri, @NonNull android.telephony.ims.RcsContactUceCapability, int) throws android.telephony.ims.ImsException; - method public void respondToCapabilityRequest(@NonNull String, @NonNull android.telephony.ims.RcsContactUceCapability, int); - method public void respondToCapabilityRequestWithError(@NonNull android.net.Uri, int, @NonNull String, int); - method public void sendCapabilityRequest(@NonNull android.net.Uri, @NonNull android.telephony.ims.RcsContactUceCapability, int); - field public static final int RESPONSE_BAD_REQUEST = 5; // 0x5 - field public static final int RESPONSE_DOES_NOT_EXIST_ANYWHERE = 4; // 0x4 - field public static final int RESPONSE_GENERIC_FAILURE = -1; // 0xffffffff - field public static final int RESPONSE_NOT_FOUND = 3; // 0x3 - field public static final int RESPONSE_REQUEST_TIMEOUT = 2; // 0x2 - field public static final int RESPONSE_SUCCESS = 0; // 0x0 - field public static final int RESPONSE_TEMPORARILY_UNAVAILABLE = 1; // 0x1 - } - } package android.telephony.mbms { diff --git a/api/test-current.txt b/api/test-current.txt index e352cb65156e..7e8eb0c965ca 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -812,18 +812,22 @@ package android.content.integrity { public abstract class IntegrityFormula { method @NonNull public static android.content.integrity.IntegrityFormula all(@NonNull android.content.integrity.IntegrityFormula...); method @NonNull public static android.content.integrity.IntegrityFormula any(@NonNull android.content.integrity.IntegrityFormula...); - method @NonNull public android.content.integrity.IntegrityFormula equalTo(@NonNull String); - method @NonNull public android.content.integrity.IntegrityFormula equalTo(boolean); - method @NonNull public android.content.integrity.IntegrityFormula equalTo(long); - method @NonNull public android.content.integrity.IntegrityFormula greaterThan(long); - method @NonNull public android.content.integrity.IntegrityFormula greaterThanOrEquals(long); method @NonNull public static android.content.integrity.IntegrityFormula not(@NonNull android.content.integrity.IntegrityFormula); - field @NonNull public static final android.content.integrity.IntegrityFormula APP_CERTIFICATE; - field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_CERTIFICATE; - field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_NAME; - field @NonNull public static final android.content.integrity.IntegrityFormula PACKAGE_NAME; - field @NonNull public static final android.content.integrity.IntegrityFormula PRE_INSTALLED; - field @NonNull public static final android.content.integrity.IntegrityFormula VERSION_CODE; + } + + public static final class IntegrityFormula.Application { + method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String); + method @NonNull public static android.content.integrity.IntegrityFormula isPreInstalled(); + method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String); + method @NonNull public static android.content.integrity.IntegrityFormula versionCodeEquals(@NonNull long); + method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThan(@NonNull long); + method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThanOrEqualTo(@NonNull long); + } + + public static final class IntegrityFormula.Installer { + method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String); + method @NonNull public static android.content.integrity.IntegrityFormula notAllowedByManifest(); + method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String); } public final class Rule implements android.os.Parcelable { @@ -1334,7 +1338,7 @@ package android.location { public class Location implements android.os.Parcelable { method public void makeComplete(); - field public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation"; + field @Deprecated public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation"; } public class LocationManager { @@ -2605,6 +2609,17 @@ package android.os { field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect.Waveform> CREATOR; } + public abstract class Vibrator { + method @RequiresPermission("android.permission.ACCESS_VIBRATOR_STATE") public void addVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener); + method @RequiresPermission("android.permission.ACCESS_VIBRATOR_STATE") public void addVibratorStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.Vibrator.OnVibratorStateChangedListener); + method @RequiresPermission("android.permission.ACCESS_VIBRATOR_STATE") public boolean isVibrating(); + method @RequiresPermission("android.permission.ACCESS_VIBRATOR_STATE") public void removeVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener); + } + + public static interface Vibrator.OnVibratorStateChangedListener { + method public void onVibratorStateChanged(boolean); + } + public class VintfObject { method public static String[] getHalNamesAndVersions(); method public static String getSepolicyVersion(); @@ -3242,6 +3257,7 @@ package android.service.contentcapture { method public void onContentCaptureEvent(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.view.contentcapture.ContentCaptureEvent); method public void onCreateContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureContext, @NonNull android.view.contentcapture.ContentCaptureSessionId); method public void onDataRemovalRequest(@NonNull android.view.contentcapture.DataRemovalRequest); + method public void onDataShareRequest(@NonNull android.view.contentcapture.DataShareRequest, @NonNull android.service.contentcapture.DataShareCallback); method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId); method public void onDisconnected(); method public final void setContentCaptureConditions(@NonNull String, @Nullable java.util.Set<android.view.contentcapture.ContentCaptureCondition>); @@ -3250,6 +3266,16 @@ package android.service.contentcapture { field public static final String SERVICE_META_DATA = "android.content_capture"; } + public interface DataShareCallback { + method public void onAccept(@NonNull java.util.concurrent.Executor, @NonNull android.service.contentcapture.DataShareReadAdapter); + method public void onReject(); + } + + public interface DataShareReadAdapter { + method public void onError(int); + method public void onStart(@NonNull android.os.ParcelFileDescriptor); + } + public final class SnapshotData implements android.os.Parcelable { method public int describeContents(); method @Nullable public android.app.assist.AssistContent getAssistContent(); @@ -3961,19 +3987,7 @@ package android.telephony.ims { } public class ImsRcsManager implements android.telephony.ims.RegistrationManager { - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isAvailable(int) throws android.telephony.ims.ImsException; - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isCapable(int, int) throws android.telephony.ims.ImsException; - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException; - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerRcsAvailabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsRcsManager.AvailabilityCallback) throws android.telephony.ims.ImsException; - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback); - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterRcsAvailabilityCallback(@NonNull android.telephony.ims.ImsRcsManager.AvailabilityCallback) throws android.telephony.ims.ImsException; - } - - public static class ImsRcsManager.AvailabilityCallback { - ctor public ImsRcsManager.AvailabilityCallback(); - method public void onAvailabilityChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities); + method @NonNull public android.telephony.ims.RcsUceAdapter getUceAdapter(); } public class ImsService extends android.app.Service { @@ -4348,34 +4362,8 @@ package android.telephony.ims { } public class RcsUceAdapter { - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getUcePublishState() throws android.telephony.ims.ImsException; method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException; - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void requestCapabilities(@NonNull java.util.concurrent.Executor, @NonNull java.util.List<android.net.Uri>, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException; - field public static final int ERROR_ALREADY_IN_QUEUE = 13; // 0xd - field public static final int ERROR_FORBIDDEN = 6; // 0x6 - field public static final int ERROR_GENERIC_FAILURE = 1; // 0x1 - field public static final int ERROR_INSUFFICIENT_MEMORY = 11; // 0xb - field public static final int ERROR_LOST_NETWORK = 12; // 0xc - field public static final int ERROR_NOT_AUTHORIZED = 5; // 0x5 - field public static final int ERROR_NOT_AVAILABLE = 3; // 0x3 - field public static final int ERROR_NOT_ENABLED = 2; // 0x2 - field public static final int ERROR_NOT_FOUND = 7; // 0x7 - field public static final int ERROR_NOT_REGISTERED = 4; // 0x4 - field public static final int ERROR_REQUEST_TIMEOUT = 10; // 0xa - field public static final int ERROR_REQUEST_TOO_LARGE = 8; // 0x8 - field public static final int PUBLISH_STATE_200_OK = 1; // 0x1 - field public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; // 0x2 - field public static final int PUBLISH_STATE_OTHER_ERROR = 6; // 0x6 - field public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; // 0x4 - field public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; // 0x5 - field public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3; // 0x3 - } - - public static class RcsUceAdapter.CapabilitiesCallback { - ctor public RcsUceAdapter.CapabilitiesCallback(); - method public void onCapabilitiesReceived(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>); - method public void onError(int); } } @@ -4461,23 +4449,8 @@ package android.telephony.ims.feature { public class RcsFeature extends android.telephony.ims.feature.ImsFeature { ctor public RcsFeature(); method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy); - method @NonNull public android.telephony.ims.stub.RcsSipOptionsImplBase getOptionsExchangeImpl(); - method @NonNull public android.telephony.ims.stub.RcsPresenceExchangeImplBase getPresenceExchangeImpl(); - method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities); method public void onFeatureReady(); method public void onFeatureRemoved(); - method public boolean queryCapabilityConfiguration(int, int); - method @NonNull public final android.telephony.ims.feature.RcsFeature.RcsImsCapabilities queryCapabilityStatus(); - } - - public static class RcsFeature.RcsImsCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities { - ctor public RcsFeature.RcsImsCapabilities(int); - method public void addCapabilities(int); - method public boolean isCapable(int); - method public void removeCapabilities(int); - field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0 - field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1 - field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2 } } @@ -4655,71 +4628,6 @@ package android.telephony.ims.stub { field public static final int INVALID_RESULT = -1; // 0xffffffff } - public class RcsCapabilityExchange { - ctor public RcsCapabilityExchange(); - method public final void onCommandUpdate(int, int) throws android.telephony.ims.ImsException; - field public static final int COMMAND_CODE_FETCH_ERROR = 4; // 0x4 - field public static final int COMMAND_CODE_GENERIC_FAILURE = 2; // 0x2 - field public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 6; // 0x6 - field public static final int COMMAND_CODE_INVALID_PARAM = 3; // 0x3 - field public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 7; // 0x7 - field public static final int COMMAND_CODE_NOT_FOUND = 9; // 0x9 - field public static final int COMMAND_CODE_NOT_SUPPORTED = 8; // 0x8 - field public static final int COMMAND_CODE_NO_CHANGE_IN_CAP = 11; // 0xb - field public static final int COMMAND_CODE_REQUEST_TIMEOUT = 5; // 0x5 - field public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 10; // 0xa - field public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0; // 0x0 - field public static final int COMMAND_CODE_SUCCESS = 1; // 0x1 - } - - public class RcsPresenceExchangeImplBase extends android.telephony.ims.stub.RcsCapabilityExchange { - ctor public RcsPresenceExchangeImplBase(); - method public final void onCapabilityRequestResponse(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>, int) throws android.telephony.ims.ImsException; - method public final void onNetworkResponse(int, @NonNull String, int) throws android.telephony.ims.ImsException; - method public final void onNotifyUpdateCapabilites(int) throws android.telephony.ims.ImsException; - method public final void onUnpublish() throws android.telephony.ims.ImsException; - method public void requestCapabilities(@NonNull java.util.List<android.net.Uri>, int); - method public void updateCapabilities(@NonNull android.telephony.ims.RcsContactUceCapability, int); - field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 0; // 0x0 - field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 6; // 0x6 - field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 5; // 0x5 - field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 3; // 0x3 - field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 4; // 0x4 - field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 8; // 0x8 - field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 1; // 0x1 - field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 2; // 0x2 - field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10; // 0xa - field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; // 0xb - field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 7; // 0x7 - field public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 9; // 0x9 - field public static final int RESPONSE_FORBIDDEN = 3; // 0x3 - field public static final int RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE = 2; // 0x2 - field public static final int RESPONSE_NOT_FOUND = 4; // 0x4 - field public static final int RESPONSE_NOT_REGISTERED = 1; // 0x1 - field public static final int RESPONSE_SIP_INTERVAL_TOO_SHORT = 7; // 0x7 - field public static final int RESPONSE_SIP_REQUEST_TIMEOUT = 5; // 0x5 - field public static final int RESPONSE_SIP_SERVICE_UNAVAILABLE = 8; // 0x8 - field public static final int RESPONSE_SUBSCRIBE_GENERIC_FAILURE = -1; // 0xffffffff - field public static final int RESPONSE_SUBSCRIBE_TOO_LARGE = 6; // 0x6 - field public static final int RESPONSE_SUCCESS = 0; // 0x0 - } - - public class RcsSipOptionsImplBase extends android.telephony.ims.stub.RcsCapabilityExchange { - ctor public RcsSipOptionsImplBase(); - method public final void onCapabilityRequestResponse(int, @NonNull String, @Nullable android.telephony.ims.RcsContactUceCapability, int) throws android.telephony.ims.ImsException; - method public final void onRemoteCapabilityRequest(@NonNull android.net.Uri, @NonNull android.telephony.ims.RcsContactUceCapability, int) throws android.telephony.ims.ImsException; - method public void respondToCapabilityRequest(@NonNull String, @NonNull android.telephony.ims.RcsContactUceCapability, int); - method public void respondToCapabilityRequestWithError(@NonNull android.net.Uri, int, @NonNull String, int); - method public void sendCapabilityRequest(@NonNull android.net.Uri, @NonNull android.telephony.ims.RcsContactUceCapability, int); - field public static final int RESPONSE_BAD_REQUEST = 5; // 0x5 - field public static final int RESPONSE_DOES_NOT_EXIST_ANYWHERE = 4; // 0x4 - field public static final int RESPONSE_GENERIC_FAILURE = -1; // 0xffffffff - field public static final int RESPONSE_NOT_FOUND = 3; // 0x3 - field public static final int RESPONSE_REQUEST_TIMEOUT = 2; // 0x2 - field public static final int RESPONSE_SUCCESS = 0; // 0x0 - field public static final int RESPONSE_TEMPORARILY_UNAVAILABLE = 1; // 0x1 - } - } package android.telephony.mbms { diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 89b1798587f0..06ceb3979bc1 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -3475,27 +3475,25 @@ message NotificationReported { // A small system-assigned identifier for the notification. // Locally probably-unique, but expect collisions across users and/or days. optional int32 instance_id = 4; - // The app-assigned notification ID and tag - optional int32 notification_id = 5; - optional string notification_tag = 6; - optional string channel_id = 7; // App-assigned channel ID + optional int32 notification_id_hash = 5; // Small hash of the app-assigned notif ID + tag + optional int32 channel_id_hash = 6; // Small hash of app-assigned channel ID // Grouping information - optional string group_id = 8; // Group the notification currently belongs to - optional int32 group_instance_id = 9; // Instance_id of the group-summary notification - optional bool is_group_summary = 10; // Tags the group-summary notification + optional int32 group_id_hash = 7; // Small hash of the group ID of the notification + optional int32 group_instance_id = 8; // Instance_id of the group-summary notification + optional bool is_group_summary = 9; // Tags the group-summary notification // Attributes - optional string category = 11; // App-assigned notification category (API-defined strings) - optional int32 style = 12; // App-assigned notification style - optional int32 num_people = 13; // Number of Person records attached to the notification + optional string category = 10; // App-assigned notification category (API-defined strings) + optional int32 style = 11; // App-assigned notification style + optional int32 num_people = 12; // Number of Person records attached to the notification // Ordering, importance and interruptiveness - optional int32 position = 14; // Position in NotificationManager's list + optional int32 position = 13; // Position in NotificationManager's list - optional android.stats.sysui.NotificationImportance importance = 15; - optional int32 alerting = 16; // Bitfield, 1=buzz 2=beep 4=blink + optional android.stats.sysui.NotificationImportance importance = 14; + optional int32 alerting = 15; // Bitfield, 1=buzz 2=beep 4=blink enum NotificationImportanceExplanation { IMPORTANCE_EXPLANATION_UNKNOWN = 0; @@ -3507,12 +3505,12 @@ message NotificationReported { IMPORTANCE_EXPLANATION_APP_PRE_CHANNELS = 5; } - optional NotificationImportanceExplanation importance_source = 17; - optional android.stats.sysui.NotificationImportance importance_initial = 18; - optional NotificationImportanceExplanation importance_initial_source = 19; - optional android.stats.sysui.NotificationImportance importance_asst = 20; - optional int32 assistant_hash = 21; - optional float assistant_ranking_score = 22; + optional NotificationImportanceExplanation importance_source = 16; + optional android.stats.sysui.NotificationImportance importance_initial = 17; + optional NotificationImportanceExplanation importance_initial_source = 18; + optional android.stats.sysui.NotificationImportance importance_asst = 19; + optional int32 assistant_hash = 20; + optional float assistant_ranking_score = 21; } message Notification { @@ -6986,6 +6984,20 @@ message MediametricsDrmManagerReported { optional string description = 6; optional Method method = 7; optional string mime_types = 8; + + optional int64 get_constraints_count = 9; + optional int64 get_metadata_count = 10; + optional int64 can_handle_count = 11; + optional int64 process_drm_info_count = 12; + optional int64 acquire_drm_info_count = 13; + optional int64 save_rights_count = 14; + optional int64 get_original_mime_type_count = 15; + optional int64 get_drm_object_type_count = 16; + optional int64 check_rights_status_count = 17; + optional int64 remove_rights_count = 18; + optional int64 remove_all_rights_count = 19; + optional int64 open_convert_session_count = 20; + optional int64 open_decrypt_session_count = 21; } /** @@ -7919,6 +7931,10 @@ message SurfaceflingerStatsGlobalInfo { // Total time that was spent performing animations. // This is derived from the present-to-present layer histogram optional int64 animation_millis = 5; + // Total number of event connections tracked by SurfaceFlinger at the time + // of this pull. If this number grows prohibitively large, then this can + // cause jank due to resource contention. + optional int32 event_connection_count = 6; } /** @@ -7960,6 +7976,10 @@ message SurfaceflingerStatsLayerInfo { // presentation, until the buffer was ready to be presented. optional FrameTimingHistogram post_to_acquire = 9 [(android.os.statsd.log_mode) = MODE_BYTES]; + // Frames missed latch because the acquire fence didn't fire + optional int64 late_acquire_frames = 10; + // Frames latched early because the desired present time was bad + optional int64 bad_desired_present_frames = 11; } /** diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index fc37af9d213e..367c2f235f5f 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -924,10 +924,12 @@ public class AppOpsManager { * @hide */ public static final int OP_ACTIVATE_PLATFORM_VPN = 94; + /** @hide */ + public static final int OP_LOADER_USAGE_STATS = 95; /** @hide */ @UnsupportedAppUsage - public static final int _NUM_OP = 95; + public static final int _NUM_OP = 96; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -1224,6 +1226,9 @@ public class AppOpsManager { public static final String OPSTR_INTERACT_ACROSS_PROFILES = "android:interact_across_profiles"; /** @hide Start Platform VPN without user intervention */ public static final String OPSTR_ACTIVATE_PLATFORM_VPN = "android:activate_platform_vpn"; + /** @hide */ + @SystemApi + public static final String OPSTR_LOADER_USAGE_STATS = "android:loader_usage_stats"; /** {@link #sAppOpsToNote} not initialized yet for this op */ @@ -1302,7 +1307,8 @@ public class AppOpsManager { OP_MANAGE_IPSEC_TUNNELS, OP_INSTANT_APP_START_FOREGROUND, OP_MANAGE_EXTERNAL_STORAGE, - OP_INTERACT_ACROSS_PROFILES + OP_INTERACT_ACROSS_PROFILES, + OP_LOADER_USAGE_STATS, }; /** @@ -1409,6 +1415,7 @@ public class AppOpsManager { OP_MANAGE_EXTERNAL_STORAGE, // MANAGE_EXTERNAL_STORAGE OP_INTERACT_ACROSS_PROFILES, //INTERACT_ACROSS_PROFILES OP_ACTIVATE_PLATFORM_VPN, // ACTIVATE_PLATFORM_VPN + OP_LOADER_USAGE_STATS, // LOADER_USAGE_STATS }; /** @@ -1510,6 +1517,7 @@ public class AppOpsManager { OPSTR_MANAGE_EXTERNAL_STORAGE, OPSTR_INTERACT_ACROSS_PROFILES, OPSTR_ACTIVATE_PLATFORM_VPN, + OPSTR_LOADER_USAGE_STATS, }; /** @@ -1612,6 +1620,7 @@ public class AppOpsManager { "MANAGE_EXTERNAL_STORAGE", "INTERACT_ACROSS_PROFILES", "ACTIVATE_PLATFORM_VPN", + "LOADER_USAGE_STATS", }; /** @@ -1715,6 +1724,7 @@ public class AppOpsManager { Manifest.permission.MANAGE_EXTERNAL_STORAGE, android.Manifest.permission.INTERACT_ACROSS_PROFILES, null, // no permission for OP_ACTIVATE_PLATFORM_VPN + android.Manifest.permission.LOADER_USAGE_STATS, }; /** @@ -1818,6 +1828,7 @@ public class AppOpsManager { null, // MANAGE_EXTERNAL_STORAGE null, // INTERACT_ACROSS_PROFILES null, // ACTIVATE_PLATFORM_VPN + null, // LOADER_USAGE_STATS }; /** @@ -1920,6 +1931,7 @@ public class AppOpsManager { false, // MANAGE_EXTERNAL_STORAGE false, // INTERACT_ACROSS_PROFILES false, // ACTIVATE_PLATFORM_VPN + false, // LOADER_USAGE_STATS }; /** @@ -2021,6 +2033,7 @@ public class AppOpsManager { AppOpsManager.MODE_DEFAULT, // MANAGE_EXTERNAL_STORAGE AppOpsManager.MODE_DEFAULT, // INTERACT_ACROSS_PROFILES AppOpsManager.MODE_IGNORED, // ACTIVATE_PLATFORM_VPN + AppOpsManager.MODE_DEFAULT, // LOADER_USAGE_STATS }; /** @@ -2126,6 +2139,7 @@ public class AppOpsManager { false, // MANAGE_EXTERNAL_STORAGE false, // INTERACT_ACROSS_PROFILES false, // ACTIVATE_PLATFORM_VPN + false, // LOADER_USAGE_STATS }; /** diff --git a/core/java/android/app/AsyncNotedAppOp.java b/core/java/android/app/AsyncNotedAppOp.java index 3febf7166837..130e2ecdcc83 100644 --- a/core/java/android/app/AsyncNotedAppOp.java +++ b/core/java/android/app/AsyncNotedAppOp.java @@ -256,10 +256,10 @@ public final class AsyncNotedAppOp implements Parcelable { }; @DataClass.Generated( - time = 1578516519372L, + time = 1580158740502L, codegenVersion = "1.0.14", sourceFile = "frameworks/base/core/java/android/app/AsyncNotedAppOp.java", - inputSignatures = "private final @android.annotation.IntRange(from=0L, to=94L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)") + inputSignatures = "private final @android.annotation.IntRange(from=0L, to=95L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 1d4a1acde434..9aa6b870792d 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -123,7 +123,7 @@ interface INotificationManager @UnsupportedAppUsage StatusBarNotification[] getActiveNotifications(String callingPkg); @UnsupportedAppUsage - StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count); + StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count, boolean includeSnoozed); NotificationHistory getNotificationHistory(String callingPkg); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index dc15b51a442c..a52ee54e655c 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -8723,6 +8723,7 @@ public class DevicePolicyManager { @StringDef({ Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS, + Settings.System.SCREEN_BRIGHTNESS_FLOAT, Settings.System.SCREEN_OFF_TIMEOUT }) @Retention(RetentionPolicy.SOURCE) diff --git a/core/java/android/app/prediction/AppPredictionSessionId.java b/core/java/android/app/prediction/AppPredictionSessionId.java index e5e06f859ac6..876bafdfb7d1 100644 --- a/core/java/android/app/prediction/AppPredictionSessionId.java +++ b/core/java/android/app/prediction/AppPredictionSessionId.java @@ -22,6 +22,8 @@ import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; +import java.util.Objects; + /** * The id for an app prediction session. See {@link AppPredictor}. * @@ -32,18 +34,28 @@ import android.os.Parcelable; public final class AppPredictionSessionId implements Parcelable { private final String mId; + private final int mUserId; /** * Creates a new id for a prediction session. * * @hide */ - public AppPredictionSessionId(@NonNull String id) { + public AppPredictionSessionId(@NonNull final String id, final int userId) { mId = id; + mUserId = userId; } private AppPredictionSessionId(Parcel p) { mId = p.readString(); + mUserId = p.readInt(); + } + + /** + * @hide + */ + public int getUserId() { + return mUserId; } @Override @@ -51,17 +63,17 @@ public final class AppPredictionSessionId implements Parcelable { if (!getClass().equals(o != null ? o.getClass() : null)) return false; AppPredictionSessionId other = (AppPredictionSessionId) o; - return mId.equals(other.mId); + return mId.equals(other.mId) && mUserId == other.mUserId; } @Override public @NonNull String toString() { - return mId; + return mId + "," + mUserId; } @Override public int hashCode() { - return mId.hashCode(); + return Objects.hash(mId, mUserId); } @Override @@ -72,6 +84,7 @@ public final class AppPredictionSessionId implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mId); + dest.writeInt(mUserId); } public static final @android.annotation.NonNull Parcelable.Creator<AppPredictionSessionId> CREATOR = diff --git a/core/java/android/app/prediction/AppPredictor.java b/core/java/android/app/prediction/AppPredictor.java index cd635d635ce1..f0eedf3d18f2 100644 --- a/core/java/android/app/prediction/AppPredictor.java +++ b/core/java/android/app/prediction/AppPredictor.java @@ -96,7 +96,7 @@ public final class AppPredictor { IBinder b = ServiceManager.getService(Context.APP_PREDICTION_SERVICE); mPredictionManager = IPredictionManager.Stub.asInterface(b); mSessionId = new AppPredictionSessionId( - context.getPackageName() + ":" + UUID.randomUUID().toString()); + context.getPackageName() + ":" + UUID.randomUUID().toString(), context.getUserId()); try { mPredictionManager.createPredictionSession(predictionContext, mSessionId); } catch (RemoteException e) { diff --git a/core/java/android/content/integrity/AppInstallMetadata.java b/core/java/android/content/integrity/AppInstallMetadata.java index cd5117be6123..4be7e6df46e0 100644 --- a/core/java/android/content/integrity/AppInstallMetadata.java +++ b/core/java/android/content/integrity/AppInstallMetadata.java @@ -18,7 +18,9 @@ package android.content.integrity; import android.annotation.NonNull; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; /** @@ -40,6 +42,7 @@ public final class AppInstallMetadata { private final List<String> mInstallerCertificates; private final long mVersionCode; private final boolean mIsPreInstalled; + private final Map<String, String> mAllowedInstallersAndCertificates; private AppInstallMetadata(Builder builder) { this.mPackageName = builder.mPackageName; @@ -48,6 +51,7 @@ public final class AppInstallMetadata { this.mInstallerCertificates = builder.mInstallerCertificates; this.mVersionCode = builder.mVersionCode; this.mIsPreInstalled = builder.mIsPreInstalled; + this.mAllowedInstallersAndCertificates = builder.mAllowedInstallersAndCertificates; } @NonNull @@ -80,6 +84,13 @@ public final class AppInstallMetadata { return mIsPreInstalled; } + /** + * Get the allowed installers and their corresponding cert. + */ + public Map<String, String> getAllowedInstallersAndCertificates() { + return mAllowedInstallersAndCertificates; + } + @Override public String toString() { return String.format( @@ -101,6 +112,23 @@ public final class AppInstallMetadata { private List<String> mInstallerCertificates; private long mVersionCode; private boolean mIsPreInstalled; + private Map<String, String> mAllowedInstallersAndCertificates; + + public Builder() { + mAllowedInstallersAndCertificates = new HashMap<>(); + } + + /** + * Add allowed installers and cert. + * + * @see AppInstallMetadata#getAllowedInstallersAndCertificates() + */ + @NonNull + public Builder setAllowedInstallersAndCert( + @NonNull Map<String, String> allowedInstallersAndCertificates) { + this.mAllowedInstallersAndCertificates = allowedInstallersAndCertificates; + return this; + } /** * Set package name of the app to be installed. diff --git a/core/java/android/content/integrity/AtomicFormula.java b/core/java/android/content/integrity/AtomicFormula.java index 42459779e212..d911eabc3b83 100644 --- a/core/java/android/content/integrity/AtomicFormula.java +++ b/core/java/android/content/integrity/AtomicFormula.java @@ -55,14 +55,12 @@ public abstract class AtomicFormula extends IntegrityFormula { PRE_INSTALLED, }) @Retention(RetentionPolicy.SOURCE) - public @interface Key { - } + public @interface Key {} /** @hide */ @IntDef(value = {EQ, GT, GTE}) @Retention(RetentionPolicy.SOURCE) - public @interface Operator { - } + public @interface Operator {} /** * Package name of the app. @@ -354,7 +352,8 @@ public abstract class AtomicFormula extends IntegrityFormula { "Key %s cannot be used with StringAtomicFormula", keyToString(key))); mValue = hashValue(key, value); mIsHashedValue = - key == APP_CERTIFICATE || key == INSTALLER_CERTIFICATE + key == APP_CERTIFICATE + || key == INSTALLER_CERTIFICATE ? true : !mValue.equals(value); } diff --git a/core/java/android/content/integrity/InstallerAllowedByManifestFormula.java b/core/java/android/content/integrity/InstallerAllowedByManifestFormula.java new file mode 100644 index 000000000000..475f019e7b26 --- /dev/null +++ b/core/java/android/content/integrity/InstallerAllowedByManifestFormula.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.integrity; + +import java.util.Map; + +/** + * An atomic formula that evaluates to true if the installer of the current install is specified in + * the "allowed installer" field in the android manifest. Note that an empty "allowed installer" by + * default means containing all possible installers. + * + * @hide + */ +public class InstallerAllowedByManifestFormula extends IntegrityFormula { + + @Override + public int getTag() { + return IntegrityFormula.INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG; + } + + @Override + public boolean matches(AppInstallMetadata appInstallMetadata) { + Map<String, String> allowedInstallersAndCertificates = + appInstallMetadata.getAllowedInstallersAndCertificates(); + return allowedInstallersAndCertificates.isEmpty() + || installerInAllowedInstallersFromManifest( + appInstallMetadata, allowedInstallersAndCertificates); + } + + @Override + public boolean isAppCertificateFormula() { + return false; + } + + @Override + public boolean isInstallerFormula() { + return true; + } + + private static boolean installerInAllowedInstallersFromManifest( + AppInstallMetadata appInstallMetadata, + Map<String, String> allowedInstallersAndCertificates) { + return allowedInstallersAndCertificates.containsKey(appInstallMetadata.getInstallerName()) + && appInstallMetadata.getInstallerCertificates() + .contains( + allowedInstallersAndCertificates + .get(appInstallMetadata.getInstallerName())); + } +} diff --git a/core/java/android/content/integrity/IntegrityFormula.java b/core/java/android/content/integrity/IntegrityFormula.java index a2d937e4df31..ac4c9071f755 100644 --- a/core/java/android/content/integrity/IntegrityFormula.java +++ b/core/java/android/content/integrity/IntegrityFormula.java @@ -42,66 +42,88 @@ import java.util.Arrays; @VisibleForTesting public abstract class IntegrityFormula { - /** - * A static formula base for package name formulas. - * - * This formulation is incomplete and should always be used with {@code equals} formulation. - * Evaluates to false when used directly and cannot be written as a parcel. - */ - @NonNull - public static final IntegrityFormula PACKAGE_NAME = - new StringAtomicFormula(AtomicFormula.PACKAGE_NAME); + /** Factory class for creating integrity formulas based on the app being installed. */ + public static final class Application { + /** Returns an integrity formula that checks the equality to a package name. */ + @NonNull + public static IntegrityFormula packageNameEquals(@NonNull String packageName) { + return new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, packageName); + } - /** - * A static formula base for app certificate formulas. - * - * This formulation is incomplete and should always be used with {@code equals} formulation. - * Evaluates to false when used directly and cannot be written as a parcel. - */ - @NonNull - public static final IntegrityFormula APP_CERTIFICATE = - new StringAtomicFormula(AtomicFormula.APP_CERTIFICATE); + /** + * Returns an integrity formula that checks if the app certificates contain {@code + * appCertificate}. + */ + @NonNull + public static IntegrityFormula certificatesContain(@NonNull String appCertificate) { + return new StringAtomicFormula(AtomicFormula.APP_CERTIFICATE, appCertificate); + } - /** - * A static formula base for installer name formulas. - * - * This formulation is incomplete and should always be used with {@code equals} formulation. - * Evaluates to false when used directly and cannot be written as a parcel. - */ - @NonNull - public static final IntegrityFormula INSTALLER_NAME = - new StringAtomicFormula(AtomicFormula.INSTALLER_NAME); + /** Returns an integrity formula that checks the equality to a version code. */ + @NonNull + public static IntegrityFormula versionCodeEquals(@NonNull long versionCode) { + return new LongAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, versionCode); + } - /** - * A static formula base for installer certificate formulas. - * - * This formulation is incomplete and should always be used with {@code equals} formulation. - * Evaluates to false when used directly and cannot be written as a parcel. - */ - @NonNull - public static final IntegrityFormula INSTALLER_CERTIFICATE = - new StringAtomicFormula(AtomicFormula.INSTALLER_CERTIFICATE); + /** + * Returns an integrity formula that checks the app's version code is greater than the + * provided value. + */ + @NonNull + public static IntegrityFormula versionCodeGreaterThan(@NonNull long versionCode) { + return new LongAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GT, versionCode); + } - /** - * A static formula base for version code name formulas. - * - * This formulation is incomplete and should always be used with {@code equals}, - * {@code greaterThan} and {@code greaterThanEquals} formulation. Evaluates to false when used - * directly and cannot be written as a parcel. - */ - @NonNull - public static final IntegrityFormula VERSION_CODE = - new LongAtomicFormula(AtomicFormula.VERSION_CODE); + /** + * Returns an integrity formula that checks the app's version code is greater than or equal + * to the provided value. + */ + @NonNull + public static IntegrityFormula versionCodeGreaterThanOrEqualTo(@NonNull long versionCode) { + return new LongAtomicFormula( + AtomicFormula.VERSION_CODE, AtomicFormula.GTE, versionCode); + } - /** - * A static formula base for pre-installed status formulas. - * - * This formulation is incomplete and should always be used with {@code equals} formulation. - * Evaluates to false when used directly and cannot be written as a parcel. - */ - @NonNull - public static final IntegrityFormula PRE_INSTALLED = - new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED); + /** Returns an integrity formula that is valid when app is pre-installed. */ + @NonNull + public static IntegrityFormula isPreInstalled() { + return new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true); + } + + private Application() { + } + } + + /** Factory class for creating integrity formulas based on installer. */ + public static final class Installer { + /** Returns an integrity formula that checks the equality to an installer name. */ + @NonNull + public static IntegrityFormula packageNameEquals(@NonNull String installerName) { + return new StringAtomicFormula(AtomicFormula.INSTALLER_NAME, installerName); + } + + /** + * An static formula that evaluates to true if the installer is NOT allowed according to the + * "allowed installer" field in the android manifest. + */ + @NonNull + public static IntegrityFormula notAllowedByManifest() { + return not(new InstallerAllowedByManifestFormula()); + } + + /** + * Returns an integrity formula that checks if the installer certificates contain {@code + * installerCertificate}. + */ + @NonNull + public static IntegrityFormula certificatesContain(@NonNull String installerCertificate) { + return new StringAtomicFormula(AtomicFormula.INSTALLER_CERTIFICATE, + installerCertificate); + } + + private Installer() { + } + } /** @hide */ @IntDef( @@ -109,10 +131,12 @@ public abstract class IntegrityFormula { COMPOUND_FORMULA_TAG, STRING_ATOMIC_FORMULA_TAG, LONG_ATOMIC_FORMULA_TAG, - BOOLEAN_ATOMIC_FORMULA_TAG + BOOLEAN_ATOMIC_FORMULA_TAG, + INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG }) @Retention(RetentionPolicy.SOURCE) - @interface Tag {} + @interface Tag { + } /** @hide */ public static final int COMPOUND_FORMULA_TAG = 0; @@ -122,6 +146,8 @@ public abstract class IntegrityFormula { public static final int LONG_ATOMIC_FORMULA_TAG = 2; /** @hide */ public static final int BOOLEAN_ATOMIC_FORMULA_TAG = 3; + /** @hide */ + public static final int INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG = 4; /** * Returns the tag that identifies the current class. @@ -135,14 +161,14 @@ public abstract class IntegrityFormula { * * @hide */ - public abstract @Tag boolean matches(AppInstallMetadata appInstallMetadata); + public abstract boolean matches(AppInstallMetadata appInstallMetadata); /** * Returns true when the formula (or one of its atomic formulas) has app certificate as key. * * @hide */ - public abstract @Tag boolean isAppCertificateFormula(); + public abstract boolean isAppCertificateFormula(); /** * Returns true when the formula (or one of its atomic formulas) has installer package name @@ -150,7 +176,7 @@ public abstract class IntegrityFormula { * * @hide */ - public abstract @Tag boolean isInstallerFormula(); + public abstract boolean isInstallerFormula(); /** * Write an {@link IntegrityFormula} to {@link android.os.Parcel}. @@ -159,7 +185,6 @@ public abstract class IntegrityFormula { * {@link Parcelable}. * * @throws IllegalArgumentException if {@link IntegrityFormula} is not a recognized subclass - * * @hide */ public static void writeToParcel( @@ -195,70 +220,6 @@ public abstract class IntegrityFormula { } /** - * Returns an integrity formula that evaluates to true when value of the key matches to the - * provided string value. - * - * <p>The value will be hashed with SHA256 and the hex digest will be computed; for - * all cases except when the key is PACKAGE_NAME or INSTALLER_NAME and the value is less than - * 32 characters. - * - * <p>Throws an {@link IllegalArgumentException} if the key is not string typed. - */ - @NonNull - public IntegrityFormula equalTo(@NonNull String value) { - AtomicFormula baseFormula = (AtomicFormula) this; - return new AtomicFormula.StringAtomicFormula(baseFormula.getKey(), value); - } - - /** - * Returns an integrity formula that evaluates to true when the boolean value of the key matches - * the provided boolean value. It can only be used with the boolean comparison keys. - * - * <p>Throws an {@link IllegalArgumentException} if the key is not boolean typed. - */ - @NonNull - public IntegrityFormula equalTo(boolean value) { - AtomicFormula baseFormula = (AtomicFormula) this; - return new AtomicFormula.BooleanAtomicFormula(baseFormula.getKey(), value); - } - - /** - * Returns a formula that evaluates to true when the value of the key in the package being - * installed is equal to {@code value}. - * - * <p>Throws an {@link IllegalArgumentException} if the key is not long typed. - */ - @NonNull - public IntegrityFormula equalTo(long value) { - AtomicFormula baseFormula = (AtomicFormula) this; - return new AtomicFormula.LongAtomicFormula(baseFormula.getKey(), AtomicFormula.EQ, value); - } - - /** - * Returns a formula that evaluates to true when the value of the key in the package being - * installed is greater than {@code value}. - * - * <p>Throws an {@link IllegalArgumentException} if the key is not long typed. - */ - @NonNull - public IntegrityFormula greaterThan(long value) { - AtomicFormula baseFormula = (AtomicFormula) this; - return new AtomicFormula.LongAtomicFormula(baseFormula.getKey(), AtomicFormula.GT, value); - } - - /** - * Returns a formula that evaluates to true when the value of the key in the package being - * installed is greater than or equals to the {@code value}. - * - * <p>Throws an {@link IllegalArgumentException} if the key is not long typed. - */ - @NonNull - public IntegrityFormula greaterThanOrEquals(long value) { - AtomicFormula baseFormula = (AtomicFormula) this; - return new AtomicFormula.LongAtomicFormula(baseFormula.getKey(), AtomicFormula.GTE, value); - } - - /** * Returns a formula that evaluates to true when any formula in {@code formulae} evaluates to * true. * diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java index edc20d9f65ad..25f0ff3cd411 100644 --- a/core/java/android/content/pm/CrossProfileApps.java +++ b/core/java/android/content/pm/CrossProfileApps.java @@ -316,14 +316,21 @@ public class CrossProfileApps { * * <p>If other changes could have affected the app's ability to interact across profiles, as * defined by the return value of {@link #canInteractAcrossProfiles()}, such as changes to the - * admin or OEM consent whitelists, then {@link - * #resetInteractAcrossProfilesAppOpsIfInvalid(List)} should be used. + * admin or OEM consent whitelists, then {@link #resetInteractAcrossProfilesAppOps(Collection, + * Set)} should be used. + * + * <p>If the caller does not have the {@link android.Manifest.permission + * #CONFIGURE_INTERACT_ACROSS_PROFILES} permission, then they must have the permissions that + * would have been required to call {@link android.app.AppOpsManager#setMode(int, int, String, + * int)}, which includes {@link android.Manifest.permission#MANAGE_APP_OPS_MODES}. + * + * <p>Also requires either {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or {@link + * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}. * * @hide */ @RequiresPermission( - allOf={android.Manifest.permission.MANAGE_APP_OPS_MODES, - android.Manifest.permission.UPDATE_APP_OPS_STATS, + allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, android.Manifest.permission.INTERACT_ACROSS_USERS}) public void setInteractAcrossProfilesAppOp(@NonNull String packageName, @Mode int newMode) { try { @@ -360,11 +367,18 @@ public class CrossProfileApps { * have changed as a result of non-user actions, such as changes to admin or OEM consent * whitelists. * + * <p>If the caller does not have the {@link android.Manifest.permission + * #CONFIGURE_INTERACT_ACROSS_PROFILES} permission, then they must have the permissions that + * would have been required to call {@link android.app.AppOpsManager#setMode(int, int, String, + * int)}, which includes {@link android.Manifest.permission#MANAGE_APP_OPS_MODES}. + * + * <p>Also requires either {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or {@link + * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}. + * * @hide */ @RequiresPermission( - allOf={android.Manifest.permission.MANAGE_APP_OPS_MODES, - android.Manifest.permission.UPDATE_APP_OPS_STATS, + allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, android.Manifest.permission.INTERACT_ACROSS_USERS}) public void resetInteractAcrossProfilesAppOps( @NonNull Collection<String> previousCrossProfilePackages, diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index e8668f1368fb..c78d30dd9133 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3373,15 +3373,16 @@ public abstract class PackageManager { * @hide */ @SystemApi - public static final int FLAG_PERMISSION_DONT_AUTO_REVOKE = 1 << 17; + public static final int FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED = 1 << 17; /** - * Permission flag: Whether {@link #FLAG_PERMISSION_DONT_AUTO_REVOKE} state was set by user. + * Permission flag: Whether {@link #FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED} state was set by + * user. * * @hide */ @SystemApi - public static final int FLAG_PERMISSION_DONT_AUTO_REVOKE_USER_SET = 1 << 18; + public static final int FLAG_PERMISSION_AUTO_REVOKE_USER_SET = 1 << 18; /** * Permission flags: Reserved for use by the permission controller. @@ -3435,8 +3436,8 @@ public abstract class PackageManager { | FLAG_PERMISSION_GRANTED_BY_ROLE | FLAG_PERMISSION_REVOKED_COMPAT | FLAG_PERMISSION_ONE_TIME - | FLAG_PERMISSION_DONT_AUTO_REVOKE - | FLAG_PERMISSION_DONT_AUTO_REVOKE_USER_SET; + | FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED + | FLAG_PERMISSION_AUTO_REVOKE_USER_SET; /** * Injected activity in app that forwards user to setting activity of that app. @@ -4260,7 +4261,8 @@ public abstract class PackageManager { FLAG_PERMISSION_GRANTED_BY_ROLE, FLAG_PERMISSION_REVOKED_COMPAT, FLAG_PERMISSION_ONE_TIME, - FLAG_PERMISSION_DONT_AUTO_REVOKE + FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED, + FLAG_PERMISSION_AUTO_REVOKE_USER_SET }) @Retention(RetentionPolicy.SOURCE) public @interface PermissionFlags {} @@ -7397,8 +7399,8 @@ public abstract class PackageManager { case FLAG_PERMISSION_GRANTED_BY_ROLE: return "GRANTED_BY_ROLE"; case FLAG_PERMISSION_REVOKED_COMPAT: return "REVOKED_COMPAT"; case FLAG_PERMISSION_ONE_TIME: return "ONE_TIME"; - case FLAG_PERMISSION_DONT_AUTO_REVOKE: return "DONT_AUTO_REVOKE"; - case FLAG_PERMISSION_DONT_AUTO_REVOKE_USER_SET: return "DONT_AUTO_REVOKE_USER_SET"; + case FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED: return "AUTO_REVOKE_IF_UNUSED"; + case FLAG_PERMISSION_AUTO_REVOKE_USER_SET: return "AUTO_REVOKE_USER_SET"; default: return Integer.toString(flag); } } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index b6b27b6e514a..ef3b0c82c1f0 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -1056,10 +1056,8 @@ public class PackageParser { * has changed since the last parse, it's up to callers to do so. * * @see #parsePackageLite(File, int) - * @deprecated use {@link #parseParsedPackage(File, int, boolean)} */ @UnsupportedAppUsage - @Deprecated public Package parsePackage(File packageFile, int flags, boolean useCaches) throws PackageParserException { if (packageFile.isDirectory()) { @@ -1071,10 +1069,8 @@ public class PackageParser { /** * Equivalent to {@link #parsePackage(File, int, boolean)} with {@code useCaches == false}. - * @deprecated use {@link #parseParsedPackage(File, int, boolean)} */ @UnsupportedAppUsage - @Deprecated public Package parsePackage(File packageFile, int flags) throws PackageParserException { return parsePackage(packageFile, flags, false /* useCaches */); } @@ -1345,12 +1341,7 @@ public class PackageParser { * Note that this <em>does not</em> perform signature verification; that * must be done separately in * {@link ApkParseUtils#collectCertificates(AndroidPackage, boolean)}. - * - * @deprecated external callers should move to - * {@link #parseParsedPackage(File, int, boolean)}. Eventually this method will - * be marked private. */ - @Deprecated @UnsupportedAppUsage public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException { final PackageLite lite = parseMonolithicPackageLite(apkFile, flags); @@ -1547,11 +1538,8 @@ public class PackageParser { * Collect certificates from all the APKs described in the given package, * populating {@link Package#mSigningDetails}. Also asserts that all APK * contents are signed correctly and consistently. - * - * @deprecated use {@link ApkParseUtils#collectCertificates(AndroidPackage, boolean)} */ @UnsupportedAppUsage - @Deprecated public static void collectCertificates(Package pkg, boolean skipVerify) throws PackageParserException { collectCertificatesInternal(pkg, skipVerify); @@ -6472,9 +6460,8 @@ public class PackageParser { * Representation of a full package parsed from APK files on disk. A package * consists of a single base APK, and zero or more split APKs. * - * @deprecated use an {@link AndroidPackage} + * Deprecated internally. Use AndroidPackage instead. */ - @Deprecated public final static class Package implements Parcelable { @UnsupportedAppUsage @@ -7388,10 +7375,6 @@ public class PackageParser { }; } - /** - * @deprecated use a {@link ComponentParseUtils.ParsedComponent} - */ - @Deprecated public static abstract class Component<II extends IntentInfo> { @UnsupportedAppUsage public final ArrayList<II> intents; @@ -7572,10 +7555,6 @@ public class PackageParser { } } - /** - * @deprecated use {@link ComponentParseUtils.ParsedPermission} - */ - @Deprecated public final static class Permission extends Component<IntentInfo> implements Parcelable { @UnsupportedAppUsage public final PermissionInfo info; @@ -7650,10 +7629,6 @@ public class PackageParser { }; } - /** - * @deprecated use {@link ComponentParseUtils.ParsedPermissionGroup} - */ - @Deprecated public final static class PermissionGroup extends Component<IntentInfo> implements Parcelable { @UnsupportedAppUsage public final PermissionGroupInfo info; @@ -7753,12 +7728,7 @@ public class PackageParser { return false; } - /** - * @deprecated use {@link PackageInfoUtils#generateApplicationInfo( - * AndroidPackage, int, PackageUserState, int)} - */ @UnsupportedAppUsage - @Deprecated public static ApplicationInfo generateApplicationInfo(Package p, int flags, PackageUserState state) { return generateApplicationInfo(p, flags, state, UserHandle.getCallingUserId()); @@ -7815,12 +7785,7 @@ public class PackageParser { ai.icon = (sUseRoundIcon && ai.roundIconRes != 0) ? ai.roundIconRes : ai.iconRes; } - /** - * @deprecated use {@link PackageInfoUtils#generateApplicationInfo( - * AndroidPackage, int, PackageUserState, int)} - */ @UnsupportedAppUsage - @Deprecated public static ApplicationInfo generateApplicationInfo(Package p, int flags, PackageUserState state, int userId) { if (p == null) return null; @@ -7860,11 +7825,6 @@ public class PackageParser { return ai; } - /** - * @deprecated use {@link PackageInfoUtils#generateApplicationInfo( - * AndroidPackage, int, PackageUserState, int)} - */ - @Deprecated public static ApplicationInfo generateApplicationInfo(ApplicationInfo ai, int flags, PackageUserState state, int userId) { if (ai == null) return null; @@ -7884,12 +7844,7 @@ public class PackageParser { return ai; } - /** - * @deprecated use {@link PackageInfoUtils#generatePermissionInfo( - * ComponentParseUtils.ParsedPermission, int)} - */ @UnsupportedAppUsage - @Deprecated public static final PermissionInfo generatePermissionInfo( Permission p, int flags) { if (p == null) return null; @@ -7901,12 +7856,7 @@ public class PackageParser { return pi; } - /** - * @deprecated use {@link PackageInfoUtils#generatePermissionGroupInfo( - * ComponentParseUtils.ParsedPermissionGroup, int)} - */ @UnsupportedAppUsage - @Deprecated public static final PermissionGroupInfo generatePermissionGroupInfo( PermissionGroup pg, int flags) { if (pg == null) return null; @@ -7918,10 +7868,6 @@ public class PackageParser { return pgi; } - /** - * @deprecated use {@link ComponentParseUtils.ParsedActivity} - */ - @Deprecated public final static class Activity extends Component<ActivityIntentInfo> implements Parcelable { @UnsupportedAppUsage public final ActivityInfo info; @@ -8037,12 +7983,7 @@ public class PackageParser { }; } - /** - * @deprecated use {@link PackageInfoUtils#generateActivityInfo( - * AndroidPackage, ComponentParseUtils.ParsedActivity, int, PackageUserState, int)} - */ @UnsupportedAppUsage - @Deprecated public static final ActivityInfo generateActivityInfo(Activity a, int flags, PackageUserState state, int userId) { if (a == null) return null; @@ -8060,11 +8001,6 @@ public class PackageParser { return ai; } - /** - * @deprecated use {@link PackageInfoUtils#generateActivityInfo( - * AndroidPackage, ComponentParseUtils.ParsedActivity, int, PackageUserState, int)} - */ - @Deprecated public static final ActivityInfo generateActivityInfo(ActivityInfo ai, int flags, PackageUserState state, int userId) { if (ai == null) return null; @@ -8078,10 +8014,6 @@ public class PackageParser { return ai; } - /** - * @deprecated use {@link ComponentParseUtils.ParsedService} - */ - @Deprecated public final static class Service extends Component<ServiceIntentInfo> implements Parcelable { @UnsupportedAppUsage public final ServiceInfo info; @@ -8143,12 +8075,7 @@ public class PackageParser { }; } - /** - * @deprecated use {@link PackageInfoUtils#generateServiceInfo( - * AndroidPackage, ComponentParseUtils.ParsedService, int, PackageUserState, int)} - */ @UnsupportedAppUsage - @Deprecated public static final ServiceInfo generateServiceInfo(Service s, int flags, PackageUserState state, int userId) { if (s == null) return null; @@ -8166,10 +8093,6 @@ public class PackageParser { return si; } - /** - * @deprecated use {@link ComponentParseUtils.ParsedProvider} - */ - @Deprecated public final static class Provider extends Component<ProviderIntentInfo> implements Parcelable { @UnsupportedAppUsage public final ProviderInfo info; @@ -8250,12 +8173,7 @@ public class PackageParser { }; } - /** - * @deprecated use {@link PackageInfoUtils#generateProviderInfo( - * AndroidPackage, ComponentParseUtils.ParsedProvider, int, PackageUserState, int)} - */ @UnsupportedAppUsage - @Deprecated public static final ProviderInfo generateProviderInfo(Provider p, int flags, PackageUserState state, int userId) { if (p == null) return null; @@ -8278,10 +8196,6 @@ public class PackageParser { return pi; } - /** - * @deprecated use {@link ComponentParseUtils.ParsedInstrumentation} - */ - @Deprecated public final static class Instrumentation extends Component<IntentInfo> implements Parcelable { @UnsupportedAppUsage @@ -8342,12 +8256,7 @@ public class PackageParser { }; } - /** - * @deprecated use {@link PackageInfoUtils#generateInstrumentationInfo( - * ComponentParseUtils.ParsedInstrumentation, int)} - */ @UnsupportedAppUsage - @Deprecated public static final InstrumentationInfo generateInstrumentationInfo( Instrumentation i, int flags) { if (i == null) return null; @@ -8359,10 +8268,6 @@ public class PackageParser { return ii; } - /** - * @deprecated use {@link ComponentParseUtils.ParsedIntentInfo} - */ - @Deprecated public static abstract class IntentInfo extends IntentFilter { @UnsupportedAppUsage public boolean hasDefault; @@ -8406,10 +8311,6 @@ public class PackageParser { } } - /** - * @deprecated use {@link ComponentParseUtils.ParsedActivityIntentInfo} - */ - @Deprecated public final static class ActivityIntentInfo extends IntentInfo { @UnsupportedAppUsage public Activity activity; @@ -8433,10 +8334,6 @@ public class PackageParser { } } - /** - * @deprecated use {@link ComponentParseUtils.ParsedServiceIntentInfo} - */ - @Deprecated public final static class ServiceIntentInfo extends IntentInfo { @UnsupportedAppUsage public Service service; @@ -8460,10 +8357,6 @@ public class PackageParser { } } - /** - * @deprecated use {@link ComponentParseUtils.ParsedProviderIntentInfo} - */ - @Deprecated public static final class ProviderIntentInfo extends IntentInfo { @UnsupportedAppUsage public Provider provider; diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index c686624fb33b..75d4fa7a69a2 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -25,7 +25,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; -import android.app.KeyguardManager; import android.content.Context; import android.content.DialogInterface; import android.hardware.face.FaceManager; @@ -280,12 +279,12 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } /** - * Optional: If enabled, the user will first be prompted to authenticate with biometrics, - * but also given the option to authenticate with their device PIN, pattern, or password. - * Developers should first check {@link KeyguardManager#isDeviceSecure()} before enabling. - * If the device is not secure, {@link BiometricPrompt#BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL} - * will be given to {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}. - * Defaults to false. + * Optional: If enabled, the user will be given the option to authenticate with their device + * PIN, pattern, or password. Developers should first check {@link + * BiometricManager#canAuthenticate(int)} for {@link Authenticators#DEVICE_CREDENTIAL} + * before enabling. If the device is not secured with a credential, + * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} will be invoked + * with {@link BiometricPrompt#BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL}. Defaults to false. * * <p>Note that enabling this option replaces the negative button on the prompt with one * that allows the user to authenticate with their device credential, making it an error to @@ -751,13 +750,13 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * <p>Per the Android CDD, only biometric authenticators that meet or exceed the requirements * for <strong>Strong</strong> are permitted to integrate with Keystore to perform related * cryptographic operations. Therefore, it is an error to call this method after explicitly - * calling {@link Builder#setAllowedAuthenticators(int)} with any value other than + * calling {@link Builder#setAllowedAuthenticators(int)} with any biometric strength other than * {@link Authenticators#BIOMETRIC_STRONG}. * - * @throws IllegalArgumentException If any of the arguments are null, if - * {@link Builder#setDeviceCredentialAllowed(boolean)} was explicitly set to true, or if - * {@link Builder#setAllowedAuthenticators(int)} was explicitly called with any value other than - * {@link Authenticators#BIOMETRIC_STRONG}. + * @throws IllegalArgumentException If any argument is null, or if the allowed biometric + * authenticator strength is explicitly set to {@link Authenticators#BIOMETRIC_WEAK}. Prior to + * {@link android.os.Build.VERSION_CODES#R}, this exception is also thrown if + * {@link Builder#setDeviceCredentialAllowed(boolean)} was explicitly set to true. * * @param crypto A cryptographic operation to be unlocked after successful authentication. * @param cancel An object that can be used to cancel authentication. @@ -782,16 +781,11 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan throw new IllegalArgumentException("Must supply a callback"); } - @Authenticators.Types int authenticators = mBundle.getInt( + // Disallow explicitly setting any non-Strong biometric authenticator types. + final @Authenticators.Types int authenticators = mBundle.getInt( KEY_AUTHENTICATORS_ALLOWED, Authenticators.BIOMETRIC_STRONG); - - if (mBundle.getBoolean(KEY_ALLOW_DEVICE_CREDENTIAL) - || (authenticators & Authenticators.DEVICE_CREDENTIAL) != 0) { - throw new IllegalArgumentException("Device credential not supported with crypto"); - } - - // Disallow any non-Strong biometric authenticator types. - if ((authenticators & ~Authenticators.BIOMETRIC_STRONG) != 0) { + final int biometricStrength = authenticators & Authenticators.BIOMETRIC_WEAK; + if ((biometricStrength & ~Authenticators.BIOMETRIC_STRONG) != 0) { throw new IllegalArgumentException("Only Strong biometrics supported with crypto"); } diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index c5b9a43fb523..17c83f3a7eb9 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -400,7 +400,8 @@ public final class CameraManager { try { info.setCameraId(Integer.parseInt(cameraId)); } catch (NumberFormatException e) { - Log.e(TAG, "Failed to parse camera Id " + cameraId + " to integer"); + // For external camera, reaching here is expected. + Log.v(TAG, "Failed to parse camera Id " + cameraId + " to integer"); } boolean hasConcurrentStreams = CameraManagerGlobal.get().cameraIdHasConcurrentStreamsLocked(cameraId); diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index d67de09dd2a4..25b84c5c01b0 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -766,11 +766,11 @@ public final class DisplayManager { * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission. * </p> * - * @param brightness The brightness value from 0 to 255. + * @param brightness The brightness value from 0.0f to 1.0f. * * @hide Requires signature permission. */ - public void setTemporaryBrightness(int brightness) { + public void setTemporaryBrightness(float brightness) { mGlobal.setTemporaryBrightness(brightness); } diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index fd539e8f1c91..9d92c894f3f6 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -616,7 +616,7 @@ public final class DisplayManagerGlobal { * * @hide Requires signature permission. */ - public void setTemporaryBrightness(int brightness) { + public void setTemporaryBrightness(float brightness) { try { mDm.setTemporaryBrightness(brightness); } catch (RemoteException ex) { diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 79a339fa27aa..ea2b9e79d99c 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -268,8 +268,9 @@ public abstract class DisplayManagerInternal { // nearby, turning it off temporarily until the object is moved away. public boolean useProximitySensor; - // An override of the screen brightness. Set to -1 is used if there's no override. - public int screenBrightnessOverride; + // An override of the screen brightness. + // Set to PowerManager.BRIGHTNESS_INVALID if there's no override. + public float screenBrightnessOverride; // An override of the screen auto-brightness adjustment factor in the range -1 (dimmer) to // 1 (brighter). Set to Float.NaN if there's no override. @@ -300,18 +301,18 @@ public abstract class DisplayManagerInternal { public boolean blockScreenOn; // Overrides the policy for adjusting screen brightness and state while dozing. - public int dozeScreenBrightness; public int dozeScreenState; + public float dozeScreenBrightness; public DisplayPowerRequest() { policy = POLICY_BRIGHT; useProximitySensor = false; - screenBrightnessOverride = -1; + screenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT; useAutoBrightness = false; screenAutoBrightnessAdjustmentOverride = Float.NaN; screenLowPowerBrightnessFactor = 0.5f; blockScreenOn = false; - dozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT; + dozeScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; dozeScreenState = Display.STATE_UNKNOWN; } @@ -351,7 +352,8 @@ public abstract class DisplayManagerInternal { return other != null && policy == other.policy && useProximitySensor == other.useProximitySensor - && screenBrightnessOverride == other.screenBrightnessOverride + && floatEquals(screenBrightnessOverride, + other.screenBrightnessOverride) && useAutoBrightness == other.useAutoBrightness && floatEquals(screenAutoBrightnessAdjustmentOverride, other.screenAutoBrightnessAdjustmentOverride) @@ -360,7 +362,7 @@ public abstract class DisplayManagerInternal { && blockScreenOn == other.blockScreenOn && lowPowerMode == other.lowPowerMode && boostScreenBrightness == other.boostScreenBrightness - && dozeScreenBrightness == other.dozeScreenBrightness + && floatEquals(dozeScreenBrightness, other.dozeScreenBrightness) && dozeScreenState == other.dozeScreenState; } diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index ccf221b693bc..d22188ec5d7f 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -115,7 +115,7 @@ interface IDisplayManager { boolean isMinimalPostProcessingRequested(int displayId); // Temporarily sets the display brightness. - void setTemporaryBrightness(int brightness); + void setTemporaryBrightness(float brightness); // Temporarily sets the auto brightness adjustment factor. void setTemporaryAutoBrightnessAdjustment(float adjustment); diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index 83f01a5dca35..a7b65165b5ee 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -16,6 +16,7 @@ package android.hardware.input; +import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SdkConstant; @@ -51,6 +52,7 @@ import com.android.internal.os.SomeArgs; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.concurrent.Executor; import java.util.List; /** @@ -1236,6 +1238,32 @@ public final class InputManager { } @Override + public boolean isVibrating() { + throw new UnsupportedOperationException( + "isVibrating not supported in InputDeviceVibrator"); + } + + @Override + public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) { + throw new UnsupportedOperationException( + "addVibratorStateListener not supported in InputDeviceVibrator"); + } + + @Override + public void addVibratorStateListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull OnVibratorStateChangedListener listener) { + throw new UnsupportedOperationException( + "addVibratorStateListener not supported in InputDeviceVibrator"); + } + + @Override + public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) { + throw new UnsupportedOperationException( + "removeVibratorStateListener not supported in InputDeviceVibrator"); + } + + @Override public boolean hasAmplitudeControl() { return false; } diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index 96d7a80886a5..2f536ffd8ca6 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -18,6 +18,7 @@ package android.net; import static android.os.Process.CLAT_UID; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; @@ -35,6 +36,8 @@ import libcore.util.EmptyArray; import java.io.CharArrayWriter; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.HashSet; import java.util.Map; @@ -100,6 +103,19 @@ public final class NetworkStats implements Parcelable { */ public static final int SET_DBG_VPN_OUT = 1002; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "SET_" }, value = { + SET_ALL, + SET_DEFAULT, + SET_FOREGROUND, + SET_DEBUG_START, + SET_DBG_VPN_IN, + SET_DBG_VPN_OUT + }) + public @interface State { + } + /** * Include all interfaces when filtering * @hide @@ -120,6 +136,17 @@ public final class NetworkStats implements Parcelable { /** {@link #metered} value where metered data is accounted. */ public static final int METERED_YES = 1; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "METERED_" }, value = { + METERED_ALL, + METERED_NO, + METERED_YES + }) + public @interface Meteredness { + } + + /** * {@link #roaming} value to account for all roaming states. * @hide @@ -130,6 +157,16 @@ public final class NetworkStats implements Parcelable { /** {@link #roaming} value where roaming data is accounted. */ public static final int ROAMING_YES = 1; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "ROAMING_" }, value = { + ROAMING_ALL, + ROAMING_NO, + ROAMING_YES + }) + public @interface Roaming { + } + /** * {@link #onDefaultNetwork} value to account for all default network states. * @hide @@ -140,6 +177,16 @@ public final class NetworkStats implements Parcelable { /** {@link #onDefaultNetwork} value to account for usage while the default network. */ public static final int DEFAULT_NETWORK_YES = 1; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "DEFAULT_NETWORK_" }, value = { + DEFAULT_NETWORK_ALL, + DEFAULT_NETWORK_NO, + DEFAULT_NETWORK_YES + }) + public @interface DefaultNetwork { + } + /** * Denotes a request for stats at the interface level. * @hide @@ -151,6 +198,15 @@ public final class NetworkStats implements Parcelable { */ public static final int STATS_PER_UID = 1; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "STATS_PER_" }, value = { + STATS_PER_IFACE, + STATS_PER_UID + }) + public @interface StatsType { + } + private static final String CLATD_INTERFACE_PREFIX = "v4-"; // Delta between IPv4 header (20b) and IPv6 header (40b). // Used for correct stats accounting on clatd interfaces. @@ -263,9 +319,9 @@ public final class NetworkStats implements Parcelable { rxBytes, rxPackets, txBytes, txPackets, operations); } - public Entry(@Nullable String iface, int uid, int set, int tag, int metered, int roaming, - int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets, - long operations) { + public Entry(@Nullable String iface, int uid, @State int set, int tag, + @Meteredness int metered, @Roaming int roaming, @DefaultNetwork int defaultNetwork, + long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { this.iface = iface; this.uid = uid; this.set = set; diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 3ae570081f4d..1cefbd9afa80 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -49,6 +49,7 @@ interface IPowerManager void goToSleep(long time, int reason, int flags); @UnsupportedAppUsage(maxTargetSdk = 28) void nap(long time); + float getBrightnessConstraint(int constraint); @UnsupportedAppUsage boolean isInteractive(); boolean isPowerSaveMode(); diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl index e201e43b5586..84013e7ebc88 100644 --- a/core/java/android/os/IVibratorService.aidl +++ b/core/java/android/os/IVibratorService.aidl @@ -18,11 +18,15 @@ package android.os; import android.os.VibrationEffect; import android.os.VibrationAttributes; +import android.os.IVibratorStateListener; /** {@hide} */ interface IVibratorService { boolean hasVibrator(); + boolean isVibrating(); + boolean registerVibratorStateListener(in IVibratorStateListener listener); + boolean unregisterVibratorStateListener(in IVibratorStateListener listener); boolean hasAmplitudeControl(); boolean[] areEffectsSupported(in int[] effectIds); boolean[] arePrimitivesSupported(in int[] primitiveIds); diff --git a/core/java/android/os/IVibratorStateListener.aidl b/core/java/android/os/IVibratorStateListener.aidl new file mode 100644 index 000000000000..5ff18a351865 --- /dev/null +++ b/core/java/android/os/IVibratorStateListener.aidl @@ -0,0 +1,29 @@ +/* +** Copyright 2020, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.os; + +/** + * Listener for vibrator state. + * {@hide} + */ +oneway interface IVibratorStateListener { + /** + * Called when a vibrator start/stop vibrating. + * @param state the vibrator state. + */ + void onVibrating(in boolean vibrating); +}
\ No newline at end of file diff --git a/core/java/android/os/NullVibrator.java b/core/java/android/os/NullVibrator.java index 1d0f9d3157c0..6d8ab6d70cdf 100644 --- a/core/java/android/os/NullVibrator.java +++ b/core/java/android/os/NullVibrator.java @@ -39,6 +39,11 @@ public class NullVibrator extends Vibrator { } @Override + public boolean isVibrating() { + return false; + } + + @Override public boolean hasAmplitudeControl() { return false; } diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index f3d3837bbed1..267613f0af83 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -248,8 +248,27 @@ public final class PowerManager { public static final int BRIGHTNESS_DEFAULT = -1; /** + * Brightness value for an invalid value having been stored. + * @hide + */ + public static final int BRIGHTNESS_INVALID = -1; + + //Brightness values for new float implementation: + /** + * Brightness value for fully on as float. + * @hide + */ + public static final float BRIGHTNESS_MAX = 1.0f; + + /** + * Brightness value for minimum valid brightness as float. + * @hide + */ + public static final float BRIGHTNESS_MIN = 0.0f; + + /** * Brightness value for fully off in float. - * TODO: rename this to BRIGHTNES_OFF and remove the integer-based constant. + * TODO(brightnessfloat): rename this to BRIGHTNES_OFF and remove the integer-based constant. * @hide */ public static final float BRIGHTNESS_OFF_FLOAT = -1.0f; @@ -424,6 +443,69 @@ public final class PowerManager { /** * @hide */ + @IntDef(prefix = { "BRIGHTNESS_CONSTRAINT_TYPE" }, value = { + BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM, + BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM, + BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT, + BRIGHTNESS_CONSTRAINT_TYPE_DIM, + BRIGHTNESS_CONSTRAINT_TYPE_DOZE, + BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR, + BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM_VR, + BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_VR + }) + @Retention(RetentionPolicy.SOURCE) + public @interface BrightnessConstraint{} + + /** + * Brightness constraint type: minimum allowed value. + * @hide + */ + public static final int BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM = 0; + /** + * Brightness constraint type: minimum allowed value. + * @hide + */ + public static final int BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM = 1; + + /** + * Brightness constraint type: minimum allowed value. + * @hide + */ + public static final int BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT = 2; + + /** + * Brightness constraint type: minimum allowed value. + * @hide + */ + public static final int BRIGHTNESS_CONSTRAINT_TYPE_DIM = 3; + + /** + * Brightness constraint type: minimum allowed value. + * @hide + */ + public static final int BRIGHTNESS_CONSTRAINT_TYPE_DOZE = 4; + + /** + * Brightness constraint type: minimum allowed value. + * @hide + */ + public static final int BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR = 5; + + /** + * Brightness constraint type: minimum allowed value. + * @hide + */ + public static final int BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM_VR = 6; + + /** + * Brightness constraint type: minimum allowed value. + * @hide + */ + public static final int BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_VR = 7; + + /** + * @hide + */ @IntDef(prefix = { "WAKE_REASON_" }, value = { WAKE_REASON_UNKNOWN, WAKE_REASON_POWER_BUTTON, @@ -889,6 +971,19 @@ public final class PowerManager { } /** + * Gets a float screen brightness setting. + * @hide + */ + @UnsupportedAppUsage + public float getBrightnessConstraint(int constraint) { + try { + return mService.getBrightnessConstraint(constraint); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Creates a new wake lock with the specified level and flags. * <p> * The {@code levelAndFlags} parameter specifies a wake lock level and optional flags diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java index faf4a36ff577..da20c7f2ae70 100644 --- a/core/java/android/os/SystemVibrator.java +++ b/core/java/android/os/SystemVibrator.java @@ -16,11 +16,17 @@ package android.os; +import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.media.AudioAttributes; +import android.os.IVibratorStateListener; +import android.util.ArrayMap; import android.util.Log; +import com.android.internal.annotations.GuardedBy; +import java.util.concurrent.Executor; +import java.util.Objects; /** * Vibrator implementation that controls the main system vibrator. @@ -32,15 +38,22 @@ public class SystemVibrator extends Vibrator { private final IVibratorService mService; private final Binder mToken = new Binder(); + private final Context mContext; + + @GuardedBy("mDelegates") + private final ArrayMap<OnVibratorStateChangedListener, + OnVibratorStateChangedListenerDelegate> mDelegates = new ArrayMap<>(); @UnsupportedAppUsage public SystemVibrator() { + mContext = null; mService = IVibratorService.Stub.asInterface(ServiceManager.getService("vibrator")); } @UnsupportedAppUsage public SystemVibrator(Context context) { super(context); + mContext = context; mService = IVibratorService.Stub.asInterface(ServiceManager.getService("vibrator")); } @@ -57,6 +70,126 @@ public class SystemVibrator extends Vibrator { return false; } + /** + * Check whether the vibrator is vibrating. + * + * @return True if the hardware is vibrating, otherwise false. + */ + @Override + public boolean isVibrating() { + if (mService == null) { + Log.w(TAG, "Failed to vibrate; no vibrator service."); + return false; + } + try { + return mService.isVibrating(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + return false; + } + + private class OnVibratorStateChangedListenerDelegate extends + IVibratorStateListener.Stub { + private final Executor mExecutor; + private final OnVibratorStateChangedListener mListener; + + OnVibratorStateChangedListenerDelegate(@NonNull OnVibratorStateChangedListener listener, + @NonNull Executor executor) { + mExecutor = executor; + mListener = listener; + } + + @Override + public void onVibrating(boolean isVibrating) { + mExecutor.execute(() -> mListener.onVibratorStateChanged(isVibrating)); + } + } + + /** + * Adds a listener for vibrator state change. If the listener was previously added and not + * removed, this call will be ignored. + * + * @param listener Listener to be added. + * @param executor The {@link Executor} on which the listener's callbacks will be executed on. + */ + @Override + public void addVibratorStateListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull OnVibratorStateChangedListener listener) { + Objects.requireNonNull(listener); + Objects.requireNonNull(executor); + if (mService == null) { + Log.w(TAG, "Failed to add vibrate state listener; no vibrator service."); + return; + } + + synchronized (mDelegates) { + // If listener is already registered, reject and return. + if (mDelegates.containsKey(listener)) { + Log.w(TAG, "Listener already registered."); + return; + } + try { + final OnVibratorStateChangedListenerDelegate delegate = + new OnVibratorStateChangedListenerDelegate(listener, executor); + if (!mService.registerVibratorStateListener(delegate)) { + Log.w(TAG, "Failed to register vibrate state listener"); + return; + } + mDelegates.put(listener, delegate); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Adds a listener for vibrator state changes. Callbacks will be executed on the main thread. + * If the listener was previously added and not removed, this call will be ignored. + * + * @param listener listener to be added + */ + @Override + public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) { + Objects.requireNonNull(listener); + if (mContext == null) { + Log.w(TAG, "Failed to add vibrate state listener; no vibrator context."); + return; + } + addVibratorStateListener(mContext.getMainExecutor(), listener); + } + + /** + * Removes the listener for vibrator state changes. If the listener was not previously + * registered, this call will do nothing. + * + * @param listener Listener to be removed. + */ + @Override + public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) { + Objects.requireNonNull(listener); + if (mService == null) { + Log.w(TAG, "Failed to remove vibrate state listener; no vibrator service."); + return; + } + synchronized (mDelegates) { + // Check if the listener is registered, otherwise will return. + if (mDelegates.containsKey(listener)) { + final OnVibratorStateChangedListenerDelegate delegate = mDelegates.get(listener); + try { + if (!mService.unregisterVibratorStateListener(delegate)) { + Log.w(TAG, "Failed to unregister vibrate state listener"); + return; + } + mDelegates.remove(listener); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + } + @Override public boolean hasAmplitudeControl() { if (mService == null) { diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index b202053b5d65..6d1f646f943b 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -34,6 +34,7 @@ import android.annotation.WorkerThread; import android.app.Activity; import android.app.ActivityManager; import android.app.admin.DevicePolicyManager; +import android.app.PropertyInvalidatedCache; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; @@ -2150,16 +2151,38 @@ public class UserManager { return isUserUnlocked(user.getIdentifier()); } + private static final String CACHE_KEY_IS_USER_UNLOCKED_PROPERTY = + "cache_key.is_user_unlocked"; + + private final PropertyInvalidatedCache<Integer, Boolean> mIsUserUnlockedCache = + new PropertyInvalidatedCache<Integer, Boolean>( + 32, CACHE_KEY_IS_USER_UNLOCKED_PROPERTY) { + @Override + protected Boolean recompute(Integer query) { + try { + return mService.isUserUnlocked(query); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + }; + /** {@hide} */ @UnsupportedAppUsage @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true) public boolean isUserUnlocked(@UserIdInt int userId) { - try { - return mService.isUserUnlocked(userId); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } + return mIsUserUnlockedCache.query(userId); + } + + /** {@hide} */ + public void disableIsUserUnlockedCache() { + mIsUserUnlockedCache.disableLocal(); + } + + /** {@hide} */ + public static final void invalidateIsUserUnlockedCache() { + PropertyInvalidatedCache.invalidateCache(CACHE_KEY_IS_USER_UNLOCKED_PROPERTY); } /** diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index f055c60e6a41..d4da7a84d2a1 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -16,11 +16,14 @@ package android.os; +import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.annotation.SystemService; +import android.annotation.TestApi; import android.app.ActivityThread; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -29,6 +32,7 @@ import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; /** * Class that operates the vibrator on the device. @@ -395,4 +399,78 @@ public abstract class Vibrator { */ @RequiresPermission(android.Manifest.permission.VIBRATE) public abstract void cancel(); + + /** + * Check whether the vibrator is vibrating. + * + * @return True if the hardware is vibrating, otherwise false. + * @hide + */ + @SystemApi + @TestApi + @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) + public boolean isVibrating() { + return false; + } + + /** + * Listener for when the vibrator state has changed. + * + * @see #addVibratorStateListener + * @see #removeVibratorStateListener + * @hide + */ + @SystemApi + @TestApi + public interface OnVibratorStateChangedListener { + /** + * Called when the vibrator state has changed. + * + * @param isVibrating If true, the vibrator has started vibrating. If false, + * it's stopped vibrating. + */ + void onVibratorStateChanged(boolean isVibrating); + } + + /** + * Adds a listener for vibrator state changes. Callbacks will be executed on the main thread. + * If the listener was previously added and not removed, this call will be ignored. + * + * @param listener listener to be added + * @hide + */ + @SystemApi + @TestApi + @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) + public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) { + } + + /** + * Adds a listener for vibrator state change. If the listener was previously added and not + * removed, this call will be ignored. + * + * @param listener listener to be added + * @param executor executor of listener + * @hide + */ + @SystemApi + @TestApi + @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) + public void addVibratorStateListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull OnVibratorStateChangedListener listener) { + } + + /** + * Removes the listener for vibrator state changes. If the listener was not previously + * registered, this call will do nothing. + * + * @param listener listener to be removed + * @hide + */ + @SystemApi + @TestApi + @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) + public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) { + } } diff --git a/core/java/android/os/incremental/IIncrementalManager.aidl b/core/java/android/os/incremental/IIncrementalManager.aidl index b415bc02fbcc..be83aaedd773 100644 --- a/core/java/android/os/incremental/IIncrementalManager.aidl +++ b/core/java/android/os/incremental/IIncrementalManager.aidl @@ -33,7 +33,4 @@ interface IIncrementalManager { boolean startDataLoader(int mountId); void showHealthBlockedUI(int mountId); void destroyDataLoader(int mountId); - - // fileId is a 16 byte long identifier. - void newFileForDataLoader(int mountId, in byte[] fileId, in byte[] metadata); } diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl index 9d98b3b7819b..2dbaea860e2a 100644 --- a/core/java/android/os/incremental/IIncrementalService.aidl +++ b/core/java/android/os/incremental/IIncrementalService.aidl @@ -17,6 +17,7 @@ package android.os.incremental; import android.content.pm.DataLoaderParamsParcel; +import android.content.pm.IDataLoaderStatusListener; import android.os.incremental.IncrementalNewFileParams; /** @hide */ @@ -33,7 +34,7 @@ interface IIncrementalService { * Opens or creates a storage given a target path and data loader params. Returns the storage ID. */ int openStorage(in @utf8InCpp String path); - int createStorage(in @utf8InCpp String path, in DataLoaderParamsParcel params, int createMode); + int createStorage(in @utf8InCpp String path, in DataLoaderParamsParcel params, in IDataLoaderStatusListener listener, int createMode); int createLinkedStorage(in @utf8InCpp String path, int otherStorageId, int createMode); /** diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java index 25fb3e0ed907..3f8c0fedc7a6 100644 --- a/core/java/android/os/incremental/IncrementalFileStorages.java +++ b/core/java/android/os/incremental/IncrementalFileStorages.java @@ -35,6 +35,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.pm.DataLoaderParams; +import android.content.pm.IDataLoaderStatusListener; import android.content.pm.InstallationFile; import android.text.TextUtils; import android.util.Slog; @@ -74,6 +75,7 @@ public final class IncrementalFileStorages { public static IncrementalFileStorages initialize(Context context, @NonNull File stageDir, @NonNull DataLoaderParams dataLoaderParams, + @Nullable IDataLoaderStatusListener dataLoaderStatusListener, List<InstallationFile> addedFiles) throws IOException { // TODO(b/136132412): sanity check if session should not be incremental IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService( @@ -85,7 +87,13 @@ public final class IncrementalFileStorages { IncrementalFileStorages result = null; try { - result = new IncrementalFileStorages(stageDir, incrementalManager, dataLoaderParams); + result = new IncrementalFileStorages(stageDir, incrementalManager, dataLoaderParams, + dataLoaderStatusListener); + + if (!addedFiles.isEmpty()) { + result.mDefaultStorage.bind(stageDir.getAbsolutePath()); + } + for (InstallationFile file : addedFiles) { if (file.getLocation() == LOCATION_DATA_APP) { try { @@ -93,14 +101,15 @@ public final class IncrementalFileStorages { } catch (IOException e) { // TODO(b/146080380): add incremental-specific error code throw new IOException( - "Failed to add and configure Incremental File: " + file.getName(), - e); + "Failed to add file to IncFS: " + file.getName() + ", reason: " + + e.getMessage(), e.getCause()); } } else { throw new IOException("Unknown file location: " + file.getLocation()); } } + // TODO(b/146080380): remove 5 secs wait in startLoading if (!result.mDefaultStorage.startLoading()) { // TODO(b/146080380): add incremental-specific error code throw new IOException("Failed to start loading data for Incremental installation."); @@ -117,7 +126,8 @@ public final class IncrementalFileStorages { private IncrementalFileStorages(@NonNull File stageDir, @NonNull IncrementalManager incrementalManager, - @NonNull DataLoaderParams dataLoaderParams) throws IOException { + @NonNull DataLoaderParams dataLoaderParams, + @Nullable IDataLoaderStatusListener dataLoaderStatusListener) throws IOException { mStageDir = stageDir; mIncrementalManager = incrementalManager; if (dataLoaderParams.getComponentName().getPackageName().equals("local")) { @@ -134,6 +144,7 @@ public final class IncrementalFileStorages { } mDefaultStorage = mIncrementalManager.createStorage(mDefaultDir, dataLoaderParams, + dataLoaderStatusListener, IncrementalManager.CREATE_MODE_CREATE | IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false); } @@ -144,10 +155,8 @@ public final class IncrementalFileStorages { } private void addApkFile(@NonNull InstallationFile apk) throws IOException { - final String stageDirPath = mStageDir.getAbsolutePath(); - mDefaultStorage.bind(stageDirPath); - String apkName = apk.getName(); - File targetFile = Paths.get(stageDirPath, apkName).toFile(); + final String apkName = apk.getName(); + final File targetFile = new File(mStageDir, apkName); if (!targetFile.exists()) { mDefaultStorage.makeFile(apkName, apk.getLengthBytes(), null, apk.getMetadata(), apk.getSignature()); diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java index d2d8f85b1b35..74a4215b804f 100644 --- a/core/java/android/os/incremental/IncrementalManager.java +++ b/core/java/android/os/incremental/IncrementalManager.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.annotation.SystemService; import android.content.Context; import android.content.pm.DataLoaderParams; +import android.content.pm.IDataLoaderStatusListener; import android.os.RemoteException; import android.util.SparseArray; @@ -103,10 +104,11 @@ public final class IncrementalManager { */ @Nullable public IncrementalStorage createStorage(@NonNull String path, - @NonNull DataLoaderParams params, @CreateMode int createMode, + @NonNull DataLoaderParams params, @Nullable IDataLoaderStatusListener listener, + @CreateMode int createMode, boolean autoStartDataLoader) { try { - final int id = mService.createStorage(path, params.getData(), createMode); + final int id = mService.createStorage(path, params.getData(), listener, createMode); if (id < 0) { return null; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index ea0afa94c8aa..7c4ec8e00650 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1771,6 +1771,15 @@ public final class Settings { = "android.settings.NOTIFICATION_SETTINGS"; /** + * Activity Action: Show notification history screen. + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_NOTIFICATION_HISTORY + = "android.settings.NOTIFICATION_HISTORY"; + + /** * Activity Action: Show app listing settings, filtered by those that send notifications. * * @hide @@ -2997,6 +3006,7 @@ public final class Settings { private static final HashSet<String> MOVED_TO_SECURE; static { MOVED_TO_SECURE = new HashSet<>(30); + MOVED_TO_SECURE.add(Secure.ADAPTIVE_SLEEP); MOVED_TO_SECURE.add(Secure.ANDROID_ID); MOVED_TO_SECURE.add(Secure.HTTP_PROXY); MOVED_TO_SECURE.add(Secure.LOCATION_PROVIDERS_ALLOWED); @@ -3915,6 +3925,19 @@ public final class Settings { public static final String SCREEN_BRIGHTNESS_FOR_VR = "screen_brightness_for_vr"; /** + * The screen backlight brightness between 0.0f and 1.0f. + * @hide + */ + public static final String SCREEN_BRIGHTNESS_FOR_VR_FLOAT = + "screen_brightness_for_vr_float"; + + /** + * The screen backlight brightness between 0.0f and 1.0f. + * @hide + */ + public static final String SCREEN_BRIGHTNESS_FLOAT = "screen_brightness_float"; + + /** * Control whether to enable automatic brightness mode. */ public static final String SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode"; @@ -3939,6 +3962,7 @@ public final class Settings { /** * Control whether to enable adaptive sleep mode. + * @deprecated Use {@link android.provider.Settings.Secure#ADAPTIVE_SLEEP} instead. * @hide */ public static final String ADAPTIVE_SLEEP = "adaptive_sleep"; @@ -4727,9 +4751,10 @@ public final class Settings { PUBLIC_SETTINGS.add(DIM_SCREEN); PUBLIC_SETTINGS.add(SCREEN_OFF_TIMEOUT); PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS); + PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS_FLOAT); PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS_FOR_VR); + PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS_FOR_VR_FLOAT); PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS_MODE); - PUBLIC_SETTINGS.add(ADAPTIVE_SLEEP); PUBLIC_SETTINGS.add(MODE_RINGER_STREAMS_AFFECTED); PUBLIC_SETTINGS.add(MUTE_STREAMS_AFFECTED); PUBLIC_SETTINGS.add(VIBRATE_ON); @@ -5783,6 +5808,12 @@ public final class Settings { } /** + * Control whether to enable adaptive sleep mode. + * @hide + */ + public static final String ADAPTIVE_SLEEP = "adaptive_sleep"; + + /** * @deprecated Use {@link android.provider.Settings.Global#DEVELOPMENT_SETTINGS_ENABLED} * instead */ @@ -6242,16 +6273,19 @@ public final class Settings { * @hide */ public static final String LOCATION_CHANGER = "location_changer"; + /** * The location changer is unknown or unable to detect. * @hide */ public static final int LOCATION_CHANGER_UNKNOWN = 0; + /** * Location settings in system settings. * @hide */ public static final int LOCATION_CHANGER_SYSTEM_SETTINGS = 1; + /** * The location icon in drop down notification drawer. * @hide @@ -6299,6 +6333,14 @@ public final class Settings { public static final int LOCATION_MODE_ON = LOCATION_MODE_HIGH_ACCURACY; /** + * The accuracy in meters used for coarsening location for clients with only the coarse + * location permission. + * + * @hide + */ + public static final String LOCATION_COARSE_ACCURACY_M = "locationCoarseAccuracy"; + + /** * A flag containing settings used for biometric weak * @hide */ diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java index 36f3a7899700..7032825c3c62 100644 --- a/core/java/android/service/autofill/FillResponse.java +++ b/core/java/android/service/autofill/FillResponse.java @@ -87,7 +87,8 @@ public final class FillResponse implements Parcelable { private final @Nullable UserData mUserData; private final @Nullable int[] mCancelIds; private final boolean mSupportsInlineSuggestions; - private final @Nullable ParceledListSlice<InlinePresentation> mInlineActions; + // TODO(b/149240554): revert back to use ParceledListSlice after the bug is resolved. + private final @Nullable ArrayList<InlinePresentation> mInlineActions; private FillResponse(@NonNull Builder builder) { mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null; @@ -106,8 +107,7 @@ public final class FillResponse implements Parcelable { mUserData = builder.mUserData; mCancelIds = builder.mCancelIds; mSupportsInlineSuggestions = builder.mSupportsInlineSuggestions; - mInlineActions = (builder.mInlineActions != null) ? new ParceledListSlice<>( - builder.mInlineActions) : null; + mInlineActions = builder.mInlineActions; } /** @hide */ @@ -207,7 +207,7 @@ public final class FillResponse implements Parcelable { /** @hide */ public @Nullable List<InlinePresentation> getInlineActions() { - return (mInlineActions != null) ? mInlineActions.getList() : null; + return mInlineActions; } /** @@ -718,7 +718,7 @@ public final class FillResponse implements Parcelable { } builder.append(", mSupportInlinePresentations=").append(mSupportsInlineSuggestions); if (mInlineActions != null) { - builder.append(", mInlineActions=" + mInlineActions.getList()); + builder.append(", mInlineActions=" + mInlineActions); } return builder.append("]").toString(); } @@ -748,7 +748,7 @@ public final class FillResponse implements Parcelable { parcel.writeParcelableArray(mFieldClassificationIds, flags); parcel.writeInt(mFlags); parcel.writeIntArray(mCancelIds); - parcel.writeParcelable(mInlineActions, flags); + parcel.writeTypedList(mInlineActions, flags); parcel.writeInt(mRequestId); } @@ -804,13 +804,12 @@ public final class FillResponse implements Parcelable { final int[] cancelIds = parcel.createIntArray(); builder.setPresentationCancelIds(cancelIds); - final ParceledListSlice<InlinePresentation> inlineActionsSlice = parcel.readParcelable( - null); - final List<InlinePresentation> inlineActions = - (inlineActionsSlice != null) ? inlineActionsSlice.getList() : null; - final int inlineActionsCount = (inlineActions != null) ? inlineActions.size() : 0; - for (int i = 0; i < inlineActionsCount; i++) { - builder.addInlineAction(inlineActions.get(i)); + final List<InlinePresentation> inlineActions = parcel.createTypedArrayList( + InlinePresentation.CREATOR); + if (inlineActions != null) { + for (InlinePresentation inlineAction : inlineActions) { + builder.addInlineAction(inlineAction); + } } final FillResponse response = builder.build(); diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index 0edd01330fdd..cecfe24613a4 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -339,6 +339,7 @@ public abstract class ContentCaptureService extends Service { * @hide */ @SystemApi + @TestApi public void onDataShareRequest(@NonNull DataShareRequest request, @NonNull DataShareCallback callback) { if (sVerbose) Log.v(TAG, "onDataShareRequest()"); diff --git a/core/java/android/service/contentcapture/DataShareCallback.java b/core/java/android/service/contentcapture/DataShareCallback.java index e3c7bb3cd24f..5df8a4b174bf 100644 --- a/core/java/android/service/contentcapture/DataShareCallback.java +++ b/core/java/android/service/contentcapture/DataShareCallback.java @@ -19,6 +19,7 @@ package android.service.contentcapture; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.annotation.TestApi; import java.util.concurrent.Executor; @@ -32,6 +33,7 @@ import java.util.concurrent.Executor; * @hide **/ @SystemApi +@TestApi public interface DataShareCallback { /** Accept the data share. diff --git a/core/java/android/service/contentcapture/DataShareReadAdapter.java b/core/java/android/service/contentcapture/DataShareReadAdapter.java index b9fce6873c5f..a481ec8382ed 100644 --- a/core/java/android/service/contentcapture/DataShareReadAdapter.java +++ b/core/java/android/service/contentcapture/DataShareReadAdapter.java @@ -18,6 +18,7 @@ package android.service.contentcapture; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.os.ParcelFileDescriptor; /** @@ -27,6 +28,7 @@ import android.os.ParcelFileDescriptor; * @hide **/ @SystemApi +@TestApi public interface DataShareReadAdapter { /** diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java index bc6581887048..de4c056e5501 100644 --- a/core/java/android/service/controls/ControlsProviderService.java +++ b/core/java/android/service/controls/ControlsProviderService.java @@ -35,6 +35,7 @@ import android.util.Log; import com.android.internal.util.Preconditions; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.concurrent.Flow.Publisher; import java.util.concurrent.Flow.Subscriber; @@ -73,6 +74,18 @@ public abstract class ControlsProviderService extends Service { public abstract void loadAvailableControls(@NonNull Consumer<List<Control>> consumer); /** + * (Optional) The service may be asked to provide a small number of recommended controls, in + * order to suggest some controls to the user for favoriting. The controls shall be built using + * the stateless builder {@link Control.StatelessBuilder}, followed by an invocation to the + * provided consumer to callback to the call originator. If the number of controls + * is greater than maxNumber, the list will be truncated. + */ + public void loadSuggestedControls(int maxNumber, @NonNull Consumer<List<Control>> consumer) { + // Override to change the default behavior + consumer.accept(Collections.emptyList()); + } + + /** * Return a valid Publisher for the given controlIds. This publisher will be asked * to provide updates for the given list of controlIds as long as the Subscription * is valid. @@ -104,6 +117,11 @@ public abstract class ControlsProviderService extends Service { mHandler.obtainMessage(RequestHandler.MSG_LOAD, cb).sendToTarget(); } + public void loadSuggested(int maxNumber, IControlsLoadCallback cb) { + LoadMessage msg = new LoadMessage(maxNumber, cb); + mHandler.obtainMessage(RequestHandler.MSG_LOAD_SUGGESTED, msg).sendToTarget(); + } + public void subscribe(List<String> controlIds, IControlsSubscriber subscriber) { SubscribeMessage msg = new SubscribeMessage(controlIds, subscriber); @@ -128,6 +146,14 @@ public abstract class ControlsProviderService extends Service { private static final int MSG_LOAD = 1; private static final int MSG_SUBSCRIBE = 2; private static final int MSG_ACTION = 3; + private static final int MSG_LOAD_SUGGESTED = 4; + + /** + * This the maximum number of controls that can be loaded via + * {@link ControlsProviderService#loadAvailablecontrols}. Anything over this number + * will be truncated. + */ + private static final int MAX_NUMBER_OF_CONTROLS_ALLOWED = 1000; RequestHandler(Looper looper) { super(looper); @@ -137,7 +163,14 @@ public abstract class ControlsProviderService extends Service { switch(msg.what) { case MSG_LOAD: final IControlsLoadCallback cb = (IControlsLoadCallback) msg.obj; - ControlsProviderService.this.loadAvailableControls(consumerFor(cb)); + ControlsProviderService.this.loadAvailableControls(consumerFor( + MAX_NUMBER_OF_CONTROLS_ALLOWED, cb)); + break; + + case MSG_LOAD_SUGGESTED: + final LoadMessage lMsg = (LoadMessage) msg.obj; + ControlsProviderService.this.loadSuggestedControls(lMsg.mMaxNumber, + consumerFor(lMsg.mMaxNumber, lMsg.mCb)); break; case MSG_SUBSCRIBE: @@ -201,9 +234,15 @@ public abstract class ControlsProviderService extends Service { }; } - private Consumer<List<Control>> consumerFor(IControlsLoadCallback cb) { + private Consumer<List<Control>> consumerFor(int maxNumber, IControlsLoadCallback cb) { return (@NonNull List<Control> controls) -> { Preconditions.checkNotNull(controls); + if (controls.size() > maxNumber) { + Log.w(TAG, "Too many controls. Provided: " + controls.size() + ", Max allowed: " + + maxNumber + ". Truncating the list."); + controls = controls.subList(0, maxNumber); + } + List<Control> list = new ArrayList<>(); for (Control control: controls) { if (control == null) { @@ -268,4 +307,14 @@ public abstract class ControlsProviderService extends Service { this.mSubscriber = subscriber; } } + + private static class LoadMessage { + final int mMaxNumber; + final IControlsLoadCallback mCb; + + LoadMessage(int maxNumber, IControlsLoadCallback cb) { + this.mMaxNumber = maxNumber; + this.mCb = cb; + } + } } diff --git a/core/java/android/service/controls/IControlsProvider.aidl b/core/java/android/service/controls/IControlsProvider.aidl index 4ce658ed6990..4375fbb289db 100644 --- a/core/java/android/service/controls/IControlsProvider.aidl +++ b/core/java/android/service/controls/IControlsProvider.aidl @@ -27,6 +27,8 @@ import android.service.controls.actions.ControlActionWrapper; oneway interface IControlsProvider { void load(IControlsLoadCallback cb); + void loadSuggested(int maxNumber, IControlsLoadCallback cb); + void subscribe(in List<String> controlIds, IControlsSubscriber subscriber); diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java index b9700b2b80db..41900015d6a5 100644 --- a/core/java/android/service/dataloader/DataLoaderService.java +++ b/core/java/android/service/dataloader/DataLoaderService.java @@ -240,7 +240,7 @@ public abstract class DataLoaderService extends Service { private native boolean nativeDestroyDataLoader(int storageId); private native boolean nativePrepareImage(int storageId, - Collection<InstallationFile> addedFiles, Collection<String> removedFiles); + List<InstallationFile> addedFiles, List<String> removedFiles); private static native void nativeWriteData(long nativeInstance, String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor incomingFd); diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 5fdac810af29..afeb6c391009 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -50,6 +50,8 @@ public class FeatureFlagUtils { /** @hide */ public static final String SETTINGS_DO_NOT_RESTORE_PRESERVED = "settings_do_not_restore_preserved"; + /** @hide */ + public static final String SETTINGS_SCHEDULES_FLAG = "settings_schedules"; private static final Map<String, String> DEFAULT_FLAGS; @@ -76,6 +78,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "false"); DEFAULT_FLAGS.put("settings_tether_all_in_one", "false"); + DEFAULT_FLAGS.put(SETTINGS_SCHEDULES_FLAG, "false"); } /** diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 54de1bb3739d..15be364c3e1c 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -32,6 +32,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Insets; import android.graphics.Rect; +import android.os.CancellationSignal; import android.os.Handler; import android.os.RemoteException; import android.util.ArraySet; @@ -39,7 +40,6 @@ import android.util.Log; import android.util.Pair; import android.util.Property; import android.util.SparseArray; -import android.view.InputDevice.MotionRange; import android.view.InsetsSourceConsumer.ShowResult; import android.view.InsetsState.InternalInsetsType; import android.view.SurfaceControl.Transaction; @@ -242,13 +242,15 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation PendingControlRequest(@InsetsType int types, WindowInsetsAnimationControlListener listener, long durationMs, Interpolator interpolator, @AnimationType int animationType, - @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) { + @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation, + CancellationSignal cancellationSignal) { this.types = types; this.listener = listener; this.durationMs = durationMs; this.interpolator = interpolator; this.animationType = animationType; this.layoutInsetsDuringAnimation = layoutInsetsDuringAnimation; + this.cancellationSignal = cancellationSignal; } final @InsetsType int types; @@ -257,6 +259,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation final Interpolator interpolator; final @AnimationType int animationType; final @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation; + final CancellationSignal cancellationSignal; } private final String TAG = "InsetsControllerImpl"; @@ -455,10 +458,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation PendingControlRequest pendingRequest = mPendingImeControlRequest; mPendingImeControlRequest = null; mHandler.removeCallbacks(mPendingControlTimeout); - controlAnimationUnchecked(pendingRequest.types, pendingRequest.listener, mFrame, + CancellationSignal cancellationSignal = controlAnimationUnchecked( + pendingRequest.types, + pendingRequest.listener, mFrame, true /* fromIme */, pendingRequest.durationMs, pendingRequest.interpolator, false /* fade */, pendingRequest.animationType, pendingRequest.layoutInsetsDuringAnimation); + pendingRequest.cancellationSignal.setOnCancelListener(cancellationSignal::cancel); return; } @@ -504,35 +510,39 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } @Override - public void controlWindowInsetsAnimation(@InsetsType int types, long durationMs, + public CancellationSignal controlWindowInsetsAnimation(@InsetsType int types, long durationMs, @Nullable Interpolator interpolator, @NonNull WindowInsetsAnimationControlListener listener) { - controlWindowInsetsAnimation(types, listener, false /* fromIme */, durationMs, interpolator, - ANIMATION_TYPE_USER); + return controlWindowInsetsAnimation(types, listener, false /* fromIme */, durationMs, + interpolator, ANIMATION_TYPE_USER); } - private void controlWindowInsetsAnimation(@InsetsType int types, + private CancellationSignal controlWindowInsetsAnimation(@InsetsType int types, WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs, @Nullable Interpolator interpolator, @AnimationType int animationType) { // If the frame of our window doesn't span the entire display, the control API makes very // little sense, as we don't deal with negative insets. So just cancel immediately. if (!mState.getDisplayFrame().equals(mFrame)) { listener.onCancelled(); - return; + CancellationSignal cancellationSignal = new CancellationSignal(); + cancellationSignal.cancel(); + return cancellationSignal; } - controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, interpolator, + return controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, interpolator, false /* fade */, animationType, getLayoutInsetsDuringAnimationMode(types)); } - private void controlAnimationUnchecked(@InsetsType int types, + private CancellationSignal controlAnimationUnchecked(@InsetsType int types, WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme, long durationMs, Interpolator interpolator, boolean fade, @AnimationType int animationType, @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) { + CancellationSignal cancellationSignal = new CancellationSignal(); if (types == 0) { // nothing to animate. listener.onCancelled(); - return; + cancellationSignal.cancel(); + return cancellationSignal; } cancelExistingControllers(types); @@ -546,21 +556,31 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (!imeReady) { // IME isn't ready, all requested types will be animated once IME is ready abortPendingImeControlRequest(); - mPendingImeControlRequest = new PendingControlRequest(types, listener, durationMs, - interpolator, animationType, layoutInsetsDuringAnimation); + final PendingControlRequest request = new PendingControlRequest(types, + listener, durationMs, + interpolator, animationType, layoutInsetsDuringAnimation, cancellationSignal); + mPendingImeControlRequest = request; mHandler.postDelayed(mPendingControlTimeout, PENDING_CONTROL_TIMEOUT_MS); - return; + cancellationSignal.setOnCancelListener(() -> { + if (mPendingImeControlRequest == request) { + abortPendingImeControlRequest(); + } + }); + return cancellationSignal; } if (typesReady == 0) { listener.onCancelled(); - return; + cancellationSignal.cancel(); + return cancellationSignal; } final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(controls, frame, mState, listener, typesReady, this, durationMs, interpolator, fade, layoutInsetsDuringAnimation); mRunningAnimations.add(new RunningAnimation(controller, animationType)); + cancellationSignal.setOnCancelListener(controller::onCancelled); + return cancellationSignal; } /** diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 637a088e4c5d..d5ed36b57c02 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -385,7 +385,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall * This gets called on a RenderThread worker thread, so members accessed here must * be protected by a lock. */ - final boolean useBLAST = WindowManagerGlobal.getInstance().useBLAST(); + final boolean useBLAST = WindowManagerGlobal.useBLAST(); viewRoot.registerRtFrameCallback(frame -> { try { final SurfaceControl.Transaction t = useBLAST ? @@ -1119,7 +1119,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall private void applySurfaceTransforms(SurfaceControl surface, SurfaceControl.Transaction t, Rect position, long frameNumber) { - if (frameNumber > 0 && !WindowManagerGlobal.getInstance().useBLAST()) { + if (frameNumber > 0 && !WindowManagerGlobal.useBLAST()) { final ViewRootImpl viewRoot = getViewRootImpl(); t.deferTransactionUntil(surface, viewRoot.getRenderSurfaceControl(), @@ -1137,7 +1137,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } private void setParentSpaceRectangle(Rect position, long frameNumber) { - final boolean useBLAST = WindowManagerGlobal.getInstance().useBLAST(); + final boolean useBLAST = WindowManagerGlobal.useBLAST(); final ViewRootImpl viewRoot = getViewRootImpl(); final SurfaceControl.Transaction t = useBLAST ? viewRoot.getBLASTSyncTransaction() : mRtTransaction; @@ -1198,7 +1198,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall @Override public void positionLost(long frameNumber) { - boolean useBLAST = WindowManagerGlobal.getInstance().useBLAST(); + boolean useBLAST = WindowManagerGlobal.useBLAST(); if (DEBUG) { Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d", System.identityHashCode(this), frameNumber)); @@ -1537,7 +1537,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall @Override public void invalidate(boolean invalidateCache) { super.invalidate(invalidateCache); - if (!WindowManagerGlobal.getInstance().useBLAST()) { + if (!WindowManagerGlobal.useBLAST()) { return; } final ViewRootImpl viewRoot = getViewRootImpl(); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index fa4fafaf3b26..857bc5058d60 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -742,7 +742,7 @@ public final class ViewRootImpl implements ViewParent, loadSystemProperties(); mImeFocusController = new ImeFocusController(this); - mUseBLASTAdapter = WindowManagerGlobal.getInstance().useBLAST(); + mUseBLASTAdapter = WindowManagerGlobal.useBLAST(); } public static void addFirstDrawHandler(Runnable callback) { diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java index ea8e73885980..27e92e559b09 100644 --- a/core/java/android/view/WindowInsetsController.java +++ b/core/java/android/view/WindowInsetsController.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Insets; +import android.os.CancellationSignal; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsetsAnimationCallback.InsetsAnimation; import android.view.animation.Interpolator; @@ -154,13 +155,15 @@ public interface WindowInsetsController { * {@link InsetsAnimation#getInterpolatedFraction()}. * @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the * windows are ready to be controlled, among other callbacks. - * + * @return A cancellation signal that the caller can use to cancel the request to obtain + * control, or once they have control, to cancel the control. * @see InsetsAnimation#getFraction() * @see InsetsAnimation#getInterpolatedFraction() * @see InsetsAnimation#getInterpolator() * @see InsetsAnimation#getDurationMillis() */ - void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis, + @NonNull + CancellationSignal controlWindowInsetsAnimation(@InsetsType int types, long durationMillis, @Nullable Interpolator interpolator, @NonNull WindowInsetsAnimationControlListener listener); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index dad767142ba1..e731323845d3 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -3687,6 +3687,8 @@ public interface WindowManager extends ViewManager { return "always"; case LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER: return "never"; + case LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES: + return "shortEdges"; default: return "unknown(" + mode + ")"; } diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index c22b8921390c..01a1c77d50af 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -56,7 +56,7 @@ import java.util.ArrayList; public final class WindowManagerGlobal { private static final String TAG = "WindowManager"; - private final boolean mUseBLASTAdapter; + private static boolean sUseBLASTAdapter = false; /** * The user is navigating with keys (not the touch screen), so @@ -159,11 +159,6 @@ public final class WindowManagerGlobal { private Runnable mSystemPropertyUpdater; private WindowManagerGlobal() { - try { - mUseBLASTAdapter = getWindowManagerService().useBLAST(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } } @UnsupportedAppUsage @@ -191,6 +186,7 @@ public final class WindowManagerGlobal { if (sWindowManagerService != null) { ValueAnimator.setDurationScale( sWindowManagerService.getCurrentAnimatorScale()); + sUseBLASTAdapter = sWindowManagerService.useBLAST(); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -235,8 +231,8 @@ public final class WindowManagerGlobal { /** * Whether or not to use BLAST for ViewRootImpl */ - public boolean useBLAST() { - return mUseBLASTAdapter; + public static boolean useBLAST() { + return sUseBLASTAdapter; } @UnsupportedAppUsage diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 4e4a9837b32c..46b8b771de87 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -5111,8 +5111,10 @@ public class Editor { int lineRight = (int) layout.getLineRight(line); lineRight += mTextView.getTotalPaddingLeft() - mTextView.getScrollX(); mMagnifierAnimator.mMagnifier.setSourceHorizontalBounds(lineLeft, lineRight); + mMagnifierAnimator.mMagnifier.show(showPosInView.x, showPosInView.y); + } else { + mMagnifierAnimator.show(showPosInView.x, showPosInView.y); } - mMagnifierAnimator.show(showPosInView.x, showPosInView.y); updateHandlesVisibility(); } else { dismissMagnifier(); @@ -5248,9 +5250,6 @@ public class Editor { private boolean mIsInActionMode; // The timestamp for the last up event, which is used for double tap detection. private long mLastUpTime; - // The text height of the font of the text view, which is used to calculate the Y coordinate - // of the touch through events. - private float mTextHeight; // The delta height applied to the insertion handle view. private final int mDeltaHeight; @@ -5403,8 +5402,6 @@ public class Editor { if (ev.getEventTime() - mLastUpTime < ViewConfiguration.getDoubleTapTimeout()) { stopTextActionMode(); // Avoid crash when double tap and drag backwards. } - final Paint.FontMetrics fontMetrics = mTextView.getPaint().getFontMetrics(); - mTextHeight = fontMetrics.descent - fontMetrics.ascent; mTouchState.setIsOnHandle(true); break; case MotionEvent.ACTION_UP: @@ -5443,6 +5440,10 @@ public class Editor { } private MotionEvent transformEventForTouchThrough(MotionEvent ev) { + final Layout layout = mTextView.getLayout(); + final int line = layout.getLineForOffset(getCurrentCursorOffset()); + final int textHeight = + layout.getLineBottomWithoutSpacing(line) - layout.getLineTop(line); // Transforms the touch events to screen coordinates. // And also shift up to make the hit point is on the text. // Note: @@ -5450,7 +5451,7 @@ public class Editor { // - The revised Y should be at the top of the text. Matrix m = new Matrix(); m.setTranslate(ev.getRawX() - ev.getX() + (getMeasuredWidth() >> 1) - mTouchDownX, - ev.getRawY() - ev.getY() - mTouchDownY - mTextHeight); + ev.getRawY() - ev.getY() - (textHeight >> 1) - mTouchDownY); ev.transform(m); // Transforms the touch events to text view coordinates. mTextView.toLocalMotionEvent(ev); diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java index 50da6ce19f44..05cf60be5a96 100644 --- a/core/java/android/widget/Magnifier.java +++ b/core/java/android/widget/Magnifier.java @@ -76,6 +76,8 @@ public final class Magnifier { // the Handler of this Thread when the copy is finished. private static final HandlerThread sPixelCopyHandlerThread = new HandlerThread("magnifier pixel copy result handler"); + // The width of the ramp region in pixels on the left & right sides of the fish-eye effect. + private static final int FISHEYE_RAMP_WIDTH = 30; // The view to which this magnifier is attached. private final View mView; @@ -303,16 +305,54 @@ public final class Magnifier { // The magnifier center is the same as source center in new style. magnifierCenterX = mClampedCenterZoomCoords.x - mViewCoordinatesInSurface[0]; magnifierCenterY = mClampedCenterZoomCoords.y - mViewCoordinatesInSurface[1]; + + // mLeftBound & mRightBound (typically the text line left/right) is for magnified + // content. However the PixelCopy requires the pre-magnified bounds. + // The below logic calculates the leftBound & rightBound for the pre-magnified bounds. + final float rampPre = + (mSourceWidth - (mSourceWidth - 2 * FISHEYE_RAMP_WIDTH) / mZoom) / 2; + + // Calculates the pre-zoomed left edge. + // The leftEdge moves from the left of view towards to sourceCenterX, considering the + // fisheye-like zooming. + final float x0 = sourceCenterX - mSourceWidth / 2; + final float rampX0 = x0 + FISHEYE_RAMP_WIDTH; + float leftEdge = 0; + if (leftEdge > rampX0) { + // leftEdge is in the zoom range, the distance from leftEdge to sourceCenterX + // should reduce per mZoom. + leftEdge = sourceCenterX - (sourceCenterX - leftEdge) / mZoom; + } else if (leftEdge > x0) { + // leftEdge is in the ramp range, the distance from leftEdge to rampX0 should + // increase per ramp zoom (ramp / rampPre). + leftEdge = x0 + rampPre - (rampX0 - leftEdge) * rampPre / FISHEYE_RAMP_WIDTH; + } + int leftBound = Math.min(Math.max((int) leftEdge, mLeftBound), mRightBound); + + // Calculates the pre-zoomed right edge. + // The rightEdge moves from the right of view towards to sourceCenterX, considering the + // fisheye-like zooming. + final float x1 = sourceCenterX + mSourceWidth / 2; + final float rampX1 = x1 - FISHEYE_RAMP_WIDTH; + float rightEdge = mView.getWidth(); + if (rightEdge < rampX1) { + // rightEdge is in the zoom range, the distance from rightEdge to sourceCenterX + // should reduce per mZoom. + rightEdge = sourceCenterX + (rightEdge - sourceCenterX) / mZoom; + } else if (rightEdge < x1) { + // rightEdge is in the ramp range, the distance from rightEdge to rampX1 should + // increase per ramp zoom (ramp / rampPre). + rightEdge = x1 - rampPre + (rightEdge - rampX1) * rampPre / FISHEYE_RAMP_WIDTH; + } + int rightBound = Math.max(leftBound, Math.min((int) rightEdge, mRightBound)); + // Gets the startX for new style, which should be bounded by the horizontal bounds. // Also calculates the left/right cut width for pixel copy. - final int left = startX; - final int right = startX + mSourceWidth; - final int leftBound = mViewCoordinatesInSurface[0] + Math.max(0, mLeftBound); - final int rightBound = - mViewCoordinatesInSurface[0] + Math.min(mView.getWidth(), mRightBound); - startX = Math.max(left, leftBound); - mLeftCutWidth = Math.max(0, leftBound - left); - mRightCutWidth = Math.max(0, right - rightBound); + leftBound += mViewCoordinatesInSurface[0]; + rightBound += mViewCoordinatesInSurface[0]; + mLeftCutWidth = Math.max(0, leftBound - startX); + mRightCutWidth = Math.max(0, startX + mSourceWidth - rightBound); + startX = Math.max(startX, leftBound); } obtainWindowCoordinates(magnifierCenterX, magnifierCenterY); @@ -322,7 +362,7 @@ public final class Magnifier { synchronized (mLock) { mWindow = new InternalPopupWindow(mView.getContext(), mView.getDisplay(), mParentSurface.mSurfaceControl, mWindowWidth, mWindowHeight, mZoom, - mWindowElevation, mWindowCornerRadius, + FISHEYE_RAMP_WIDTH, mWindowElevation, mWindowCornerRadius, mOverlay != null ? mOverlay : new ColorDrawable(Color.TRANSPARENT), Handler.getMain() /* draw the magnifier on the UI thread */, mLock, mCallback, mIsFishEyeStyle); @@ -854,8 +894,6 @@ public final class Magnifier { // The z of the magnifier surface, defining its z order in the list of // siblings having the same parent surface (usually the main app surface). private static final int SURFACE_Z = 5; - // The width of the ramp region in pixels on the left & right sides of the fish-eye effect. - private static final int FISHEYE_RAMP_WIDTH = 30; // Display associated to the view the magnifier is attached to. private final Display mDisplay; @@ -905,6 +943,8 @@ public final class Magnifier { private Bitmap mCurrentContent; private final float mZoom; + // The width of the ramp region in pixels on the left & right sides of the fish-eye effect. + private final int mRamp; // Whether is in the new magnifier style. private boolean mIsFishEyeStyle; // The mesh matrix for the fish-eye effect. @@ -915,7 +955,7 @@ public final class Magnifier { InternalPopupWindow(final Context context, final Display display, final SurfaceControl parentSurfaceControl, final int width, final int height, - final float zoom, final float elevation, final float cornerRadius, + final float zoom, final int ramp, final float elevation, final float cornerRadius, final Drawable overlay, final Handler handler, final Object lock, final Callback callback, final boolean isFishEyeStyle) { mDisplay = display; @@ -926,6 +966,7 @@ public final class Magnifier { mContentWidth = width; mContentHeight = height; mZoom = zoom; + mRamp = ramp; mOffsetX = (int) (1.05f * elevation); mOffsetY = (int) (1.05f * elevation); // Setup the surface we will use for drawing the content and shadow. @@ -995,14 +1036,13 @@ public final class Magnifier { final float h = mContentHeight; final float h0 = h / mZoom; final float dh = h - h0; - final float ramp = FISHEYE_RAMP_WIDTH; mMeshLeft = new float[2 * (mMeshWidth + 1) * (mMeshHeight + 1)]; mMeshRight = new float[2 * (mMeshWidth + 1) * (mMeshHeight + 1)]; for (int i = 0; i < 2 * (mMeshWidth + 1) * (mMeshHeight + 1); i += 2) { // Calculates X value. final int colIndex = i % (2 * (mMeshWidth + 1)) / 2; - mMeshLeft[i] = (float) colIndex * ramp / mMeshWidth; - mMeshRight[i] = w - ramp + colIndex * ramp / mMeshWidth; + mMeshLeft[i] = (float) colIndex * mRamp / mMeshWidth; + mMeshRight[i] = w - mRamp + colIndex * mRamp / mMeshWidth; // Calculates Y value. final int rowIndex = i / 2 / (mMeshWidth + 1); @@ -1174,14 +1214,13 @@ public final class Magnifier { final Paint paint = new Paint(); paint.setFilterBitmap(true); if (mIsFishEyeStyle) { - final int ramp = FISHEYE_RAMP_WIDTH; final int margin = - (int)((mContentWidth - (mContentWidth - 2 * ramp) / mZoom) / 2); + (int)((mContentWidth - (mContentWidth - 2 * mRamp) / mZoom) / 2); // Draws the middle part. final Rect srcRect = new Rect(margin, 0, w - margin, h); final Rect dstRect = new Rect( - ramp, 0, mContentWidth - ramp, mContentHeight); + mRamp, 0, mContentWidth - mRamp, mContentHeight); canvas.drawBitmap(mBitmap, srcRect, dstRect, paint); // Draws the left/right parts with mesh matrixes. diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index 8c52d1fadc52..9e4ebfe44b58 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -460,8 +460,19 @@ public class Toast { * @hide */ @UnsupportedAppUsage - public WindowManager.LayoutParams getWindowParams() { - return mTN.mParams; + @Nullable public WindowManager.LayoutParams getWindowParams() { + if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) { + if (mNextView != null) { + // Custom toasts + return mTN.mParams; + } else { + // Text toasts + return null; + } + } else { + // Text and custom toasts are app-rendered + return mTN.mParams; + } } /** diff --git a/core/java/com/android/internal/BrightnessSynchronizer.java b/core/java/com/android/internal/BrightnessSynchronizer.java new file mode 100644 index 000000000000..aa23251b9b85 --- /dev/null +++ b/core/java/com/android/internal/BrightnessSynchronizer.java @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal; + + +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.os.Message; +import android.os.PowerManager; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.MathUtils; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * BrightnessSynchronizer helps convert between the int (old) system and float + * (new) system for storing the brightness. It has methods to convert between the two and also + * observes for when one of the settings is changed and syncs this with the other. + */ +public class BrightnessSynchronizer{ + + private static final int MSG_UPDATE_FLOAT = 1; + private static final int MSG_UPDATE_INT = 2; + + private static final String TAG = "BrightnessSynchronizer"; + private static final Uri BRIGHTNESS_URI = + Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS); + private static final Uri BRIGHTNESS_FLOAT_URI = + Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FLOAT); + + // The tolerance within which we consider brightness values approximately equal to eachother. + // This value is approximately 1/3 of the smallest possible brightness value. + public static final float EPSILON = 0.001f; + + private final Context mContext; + + private final Queue<Object> mWriteHistory = new LinkedList<>(); + + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_UPDATE_FLOAT: + updateBrightnessFloatFromInt(msg.arg1); + break; + case MSG_UPDATE_INT: + updateBrightnessIntFromFloat(Float.intBitsToFloat(msg.arg1)); + break; + default: + super.handleMessage(msg); + } + + } + }; + + + public BrightnessSynchronizer(Context context) { + final BrightnessSyncObserver mBrightnessSyncObserver; + mContext = context; + mBrightnessSyncObserver = new BrightnessSyncObserver(mHandler); + mBrightnessSyncObserver.startObserving(); + } + + /** + * Converts between the int brightness system and the float brightness system. + */ + public static float brightnessIntToFloat(Context context, int brightnessInt) { + PowerManager pm = context.getSystemService(PowerManager.class); + float pmMinBrightness = pm.getBrightnessConstraint( + PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM); + float pmMaxBrightness = pm.getBrightnessConstraint( + PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM); + int minBrightnessInt = brightnessFloatToInt(pmMinBrightness, PowerManager.BRIGHTNESS_MIN, + PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1, + PowerManager.BRIGHTNESS_ON); + int maxBrightnessInt = brightnessFloatToInt(pmMaxBrightness, PowerManager.BRIGHTNESS_MIN, + PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1, + PowerManager.BRIGHTNESS_ON); + + return brightnessIntToFloat(brightnessInt, minBrightnessInt, maxBrightnessInt, + pmMinBrightness, pmMaxBrightness); + } + + /** + * Converts between the int brightness system and the float brightness system. + */ + public static float brightnessIntToFloat(int brightnessInt, int minInt, int maxInt, + float minFloat, float maxFloat) { + if (brightnessInt == PowerManager.BRIGHTNESS_OFF) { + return PowerManager.BRIGHTNESS_OFF_FLOAT; + } else if (brightnessInt == PowerManager.BRIGHTNESS_INVALID) { + return PowerManager.BRIGHTNESS_INVALID_FLOAT; + } else { + return MathUtils.constrainedMap(minFloat, maxFloat, (float) minInt, (float) maxInt, + brightnessInt); + } + } + + /** + * Converts between the float brightness system and the int brightness system. + */ + public static int brightnessFloatToInt(Context context, float brightnessFloat) { + PowerManager pm = context.getSystemService(PowerManager.class); + float pmMinBrightness = pm.getBrightnessConstraint( + PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM); + float pmMaxBrightness = pm.getBrightnessConstraint( + PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM); + int minBrightnessInt = brightnessFloatToInt(pmMinBrightness, PowerManager.BRIGHTNESS_MIN, + PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1, + PowerManager.BRIGHTNESS_ON); + int maxBrightnessInt = brightnessFloatToInt(pmMaxBrightness, PowerManager.BRIGHTNESS_MIN, + PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1, + PowerManager.BRIGHTNESS_ON); + + return brightnessFloatToInt(brightnessFloat, pmMinBrightness, pmMaxBrightness, + minBrightnessInt, maxBrightnessInt); + } + + /** + * Converts between the float brightness system and the int brightness system. + */ + public static int brightnessFloatToInt(float brightnessFloat, float minFloat, float maxFloat, + int minInt, int maxInt) { + if (floatEquals(brightnessFloat, PowerManager.BRIGHTNESS_OFF_FLOAT)) { + return PowerManager.BRIGHTNESS_OFF; + } else if (Float.isNaN(brightnessFloat)) { + return PowerManager.BRIGHTNESS_INVALID; + } else { + return Math.round(MathUtils.constrainedMap((float) minInt, (float) maxInt, minFloat, + maxFloat, brightnessFloat)); + } + } + + private static float getScreenBrightnessFloat(Context context) { + return Settings.System.getFloatForUser(context.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_FLOAT, Float.NaN, UserHandle.USER_CURRENT); + } + + private static int getScreenBrightnessInt(Context context) { + return Settings.System.getIntForUser(context.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS, 0, UserHandle.USER_CURRENT); + } + + private float mPreferredSettingValue; + + /** + * Updates the float setting based on a passed in int value. This is called whenever the int + * setting changes. mWriteHistory keeps a record of the values that been written to the settings + * from either this method or updateBrightnessIntFromFloat. This is to ensure that the value + * being set is due to an external value being set, rather than the updateBrightness* methods. + * The intention of this is to avoid race conditions when the setting is being changed + * frequently and to ensure we are not reacting to settings changes from this file. + * @param value Brightness value as int to store in the float setting. + */ + private void updateBrightnessFloatFromInt(int value) { + Object topOfQueue = mWriteHistory.peek(); + if (topOfQueue != null && topOfQueue.equals(value)) { + mWriteHistory.poll(); + } else { + if (brightnessFloatToInt(mContext, mPreferredSettingValue) == value) { + return; + } + float newBrightnessFloat = brightnessIntToFloat(mContext, value); + mWriteHistory.offer(newBrightnessFloat); + mPreferredSettingValue = newBrightnessFloat; + Settings.System.putFloatForUser(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_FLOAT, newBrightnessFloat, + UserHandle.USER_CURRENT); + } + } + + /** + * Updates the int setting based on a passed in float value. This is called whenever the float + * setting changes. mWriteHistory keeps a record of the values that been written to the settings + * from either this method or updateBrightnessFloatFromInt. This is to ensure that the value + * being set is due to an external value being set, rather than the updateBrightness* methods. + * The intention of this is to avoid race conditions when the setting is being changed + * frequently and to ensure we are not reacting to settings changes from this file. + * @param value Brightness setting as float to store in int setting. + */ + private void updateBrightnessIntFromFloat(float value) { + int newBrightnessInt = brightnessFloatToInt(mContext, value); + Object topOfQueue = mWriteHistory.peek(); + if (topOfQueue != null && topOfQueue.equals(value)) { + mWriteHistory.poll(); + } else { + mWriteHistory.offer(newBrightnessInt); + mPreferredSettingValue = value; + Settings.System.putIntForUser(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS, newBrightnessInt, UserHandle.USER_CURRENT); + } + } + + /** + * Tests whether two brightness float values are within a small enough tolerance + * of each other. + * @param a first float to compare + * @param b second float to compare + * @return whether the two values are within a small enough tolerance value + */ + public static boolean floatEquals(float a, float b) { + if (a == b) { + return true; + } else if (Float.isNaN(a) && Float.isNaN(b)) { + return true; + } else if (Math.abs(a - b) < EPSILON) { + return true; + } else { + return false; + } + } + + private class BrightnessSyncObserver extends ContentObserver { + /** + * Creates a content observer. + * @param handler The handler to run {@link #onChange} on, or null if none. + */ + BrightnessSyncObserver(Handler handler) { + super(handler); + } + + @Override + public void onChange(boolean selfChange) { + onChange(selfChange, null); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + if (selfChange) { + return; + } + if (BRIGHTNESS_URI.equals(uri)) { + int currentBrightness = getScreenBrightnessInt(mContext); + mHandler.obtainMessage(MSG_UPDATE_FLOAT, currentBrightness, 0).sendToTarget(); + } else if (BRIGHTNESS_FLOAT_URI.equals(uri)) { + float currentFloat = getScreenBrightnessFloat(mContext); + int toSend = Float.floatToIntBits(currentFloat); + mHandler.obtainMessage(MSG_UPDATE_INT, toSend, 0).sendToTarget(); + } + } + + public void startObserving() { + final ContentResolver cr = mContext.getContentResolver(); + cr.unregisterContentObserver(this); + cr.registerContentObserver(BRIGHTNESS_URI, false, this, UserHandle.USER_ALL); + cr.registerContentObserver(BRIGHTNESS_FLOAT_URI, false, this, UserHandle.USER_ALL); + } + + public void stopObserving() { + final ContentResolver cr = mContext.getContentResolver(); + cr.unregisterContentObserver(this); + } + } +} diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java index 5f35622cad51..86d2ed6c15c0 100644 --- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java +++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java @@ -30,10 +30,12 @@ import static com.android.internal.app.AccessibilityButtonChooserActivity.WhiteL import static com.android.internal.util.Preconditions.checkArgument; import android.accessibilityservice.AccessibilityServiceInfo; +import android.accessibilityservice.AccessibilityShortcutInfo; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; +import android.app.ActivityManager; import android.app.AlertDialog; import android.content.ComponentName; import android.content.Context; @@ -265,6 +267,7 @@ public class AccessibilityButtonChooserActivity extends Activity { @ShortcutType int shortcutType) { final List<AccessibilityButtonTarget> targets = new ArrayList<>(); targets.addAll(getAccessibilityServiceTargets(context)); + targets.addAll(getAccessibilityActivityTargets(context)); targets.addAll(getWhiteListingServiceTargets(context)); final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class); @@ -291,6 +294,24 @@ public class AccessibilityButtonChooserActivity extends Activity { return targets; } + private static List<AccessibilityButtonTarget> getAccessibilityActivityTargets( + @NonNull Context context) { + final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class); + final List<AccessibilityShortcutInfo> installedServices = + ams.getInstalledAccessibilityShortcutListAsUser(context, + ActivityManager.getCurrentUser()); + if (installedServices == null) { + return Collections.emptyList(); + } + + final List<AccessibilityButtonTarget> targets = new ArrayList<>(installedServices.size()); + for (AccessibilityShortcutInfo info : installedServices) { + targets.add(new AccessibilityButtonTarget(context, info)); + } + + return targets; + } + private static List<AccessibilityButtonTarget> getWhiteListingServiceTargets( @NonNull Context context) { final List<AccessibilityButtonTarget> targets = new ArrayList<>(); @@ -540,6 +561,14 @@ public class AccessibilityButtonChooserActivity extends Activity { this.mFragmentType = getAccessibilityServiceFragmentType(serviceInfo); } + AccessibilityButtonTarget(@NonNull Context context, + @NonNull AccessibilityShortcutInfo shortcutInfo) { + this.mId = shortcutInfo.getComponentName().flattenToString(); + this.mLabel = shortcutInfo.getActivityInfo().loadLabel(context.getPackageManager()); + this.mDrawable = shortcutInfo.getActivityInfo().loadIcon(context.getPackageManager()); + this.mFragmentType = AccessibilityServiceFragmentType.BOUNCE; + } + AccessibilityButtonTarget(Context context, @NonNull String id, int labelResId, int iconRes, @AccessibilityServiceFragmentType int fragmentType) { this.mId = id; @@ -594,7 +623,7 @@ public class AccessibilityButtonChooserActivity extends Activity { onIntuitiveTargetSelected(target); break; case AccessibilityServiceFragmentType.BOUNCE: - // Do nothing + onBounceTargetSelected(target); break; default: throw new IllegalStateException("Unexpected fragment type"); @@ -625,6 +654,15 @@ public class AccessibilityButtonChooserActivity extends Activity { switchServiceState(target); } + private void onBounceTargetSelected(AccessibilityButtonTarget target) { + final AccessibilityManager ams = getSystemService(AccessibilityManager.class); + if (mShortcutType == ACCESSIBILITY_BUTTON) { + ams.notifyAccessibilityButtonClicked(getDisplayId(), target.getId()); + } else if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) { + ams.performAccessibilityShortcut(target.getId()); + } + } + private void switchServiceState(AccessibilityButtonTarget target) { final ComponentName componentName = ComponentName.unflattenFromString(target.getId()); @@ -656,7 +694,7 @@ public class AccessibilityButtonChooserActivity extends Activity { onIntuitiveTargetDeleted(position, componentId); break; case AccessibilityServiceFragmentType.BOUNCE: - // Do nothing + onBounceTargetDeleted(position, componentId); break; default: throw new IllegalStateException("Unexpected fragment type"); @@ -694,6 +732,12 @@ public class AccessibilityButtonChooserActivity extends Activity { mTargetAdapter.notifyDataSetChanged(); } + private void onBounceTargetDeleted(int position, String componentId) { + optOutValueFromSettings(this, mShortcutUserType, componentId); + mTargets.remove(position); + mTargetAdapter.notifyDataSetChanged(); + } + private void onCancelButtonClicked() { mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.LAUNCH); mTargetAdapter.notifyDataSetChanged(); diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index 3322834c9ed1..086b9d83fed5 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -377,6 +377,11 @@ public final class SystemUiDeviceConfigFlags { public static final String NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN = "nav_bar_handle_show_over_lockscreen"; + /** + * (boolean) Whether to enable user-drag resizing for PIP. + */ + public static final String PIP_USER_RESIZE = "pip_user_resize"; + private SystemUiDeviceConfigFlags() { } } diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index 2a56fd6d6d17..0fd9cc7e9989 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -27,8 +27,6 @@ #include <nativehelper/ScopedPrimitiveArray.h> #include <nativehelper/ScopedUtfChars.h> #include <android_runtime/AndroidRuntime.h> -#include <android_runtime/android_util_AssetManager.h> -#include <androidfw/AssetManager2.h> #include "Utils.h" #include "FontUtils.h" @@ -212,63 +210,6 @@ static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); } -static void releaseAsset(const void* ptr, void* context) { - delete static_cast<Asset*>(context); -} - -static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong builderPtr, - jobject jassetMgr, jstring jpath, jint cookie, jboolean isAsset, jint ttcIndex, - jint weight, jint isItalic) { -#ifdef __ANDROID__ // Layoutlib does not support native AssetManager - NPE_CHECK_RETURN_ZERO(env, jassetMgr); - NPE_CHECK_RETURN_ZERO(env, jpath); - - NativeFamilyBuilder* builder = toNativeBuilder(builderPtr); - Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(env, jassetMgr); - if (NULL == mgr) { - builder->axes.clear(); - return false; - } - - ScopedUtfChars str(env, jpath); - if (str.c_str() == nullptr) { - builder->axes.clear(); - return false; - } - - std::unique_ptr<Asset> asset; - { - ScopedLock<AssetManager2> locked_mgr(*mgr); - if (isAsset) { - asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); - } else if (cookie > 0) { - // Valid java cookies are 1-based, but AssetManager cookies are 0-based. - asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast<ApkAssetsCookie>(cookie - 1), - Asset::ACCESS_BUFFER); - } else { - asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER); - } - } - - if (nullptr == asset) { - builder->axes.clear(); - return false; - } - - const void* buf = asset->getBuffer(false); - if (NULL == buf) { - builder->axes.clear(); - return false; - } - - sk_sp<SkData> data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, - asset.release())); - return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); -#else - return false; -#endif -} - static void FontFamily_addAxisValue(CRITICAL_JNI_PARAMS_COMMA jlong builderPtr, jint tag, jfloat value) { NativeFamilyBuilder* builder = toNativeBuilder(builderPtr); builder->axes.push_back({static_cast<minikin::AxisTag>(tag), value}); @@ -284,8 +225,6 @@ static const JNINativeMethod gFontFamilyMethods[] = { { "nAddFont", "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFont }, { "nAddFontWeightStyle", "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFontWeightStyle }, - { "nAddFontFromAssetManager", "(JLandroid/content/res/AssetManager;Ljava/lang/String;IZIII)Z", - (void*)FontFamily_addFontFromAssetManager }, { "nAddAxisValue", "(JIF)V", (void*)FontFamily_addAxisValue }, }; diff --git a/core/jni/android/graphics/fonts/Font.cpp b/core/jni/android/graphics/fonts/Font.cpp index 8d84e870d205..bfb9bae45f0c 100644 --- a/core/jni/android/graphics/fonts/Font.cpp +++ b/core/jni/android/graphics/fonts/Font.cpp @@ -26,8 +26,6 @@ #include "GraphicsJNI.h" #include <nativehelper/ScopedUtfChars.h> #include <android_runtime/AndroidRuntime.h> -#include <android_runtime/android_util_AssetManager.h> -#include <androidfw/AssetManager2.h> #include "Utils.h" #include "FontUtils.h" @@ -48,14 +46,6 @@ static inline NativeFontBuilder* toBuilder(jlong ptr) { return reinterpret_cast<NativeFontBuilder*>(ptr); } -static inline Asset* toAsset(jlong ptr) { - return reinterpret_cast<Asset*>(ptr); -} - -static void releaseAsset(jlong asset) { - delete toAsset(asset); -} - static void releaseFont(jlong font) { delete reinterpret_cast<FontWrapper*>(font); } @@ -79,54 +69,6 @@ static void release_global_ref(const void* /*data*/, void* context) { } // Regular JNI -static jlong Font_Builder_getNativeAsset( - JNIEnv* env, jobject clazz, jobject assetMgr, jstring path, jboolean isAsset, jint cookie) { -#ifdef __ANDROID__ // Layoutlib does not support native AssetManager - NPE_CHECK_RETURN_ZERO(env, assetMgr); - NPE_CHECK_RETURN_ZERO(env, path); - - Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(env, assetMgr); - if (mgr == nullptr) { - return 0; - } - - ScopedUtfChars str(env, path); - if (str.c_str() == nullptr) { - return 0; - } - - std::unique_ptr<Asset> asset; - { - ScopedLock<AssetManager2> locked_mgr(*mgr); - if (isAsset) { - asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); - } else if (cookie > 0) { - // Valid java cookies are 1-based, but AssetManager cookies are 0-based. - asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast<ApkAssetsCookie>(cookie - 1), - Asset::ACCESS_BUFFER); - } else { - asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER); - } - } - - return reinterpret_cast<jlong>(asset.release()); -#else - return 0; -#endif -} - -// Regular JNI -static jobject Font_Builder_getAssetBuffer(JNIEnv* env, jobject clazz, jlong nativeAsset) { - Asset* asset = toAsset(nativeAsset); - return env->NewDirectByteBuffer(const_cast<void*>(asset->getBuffer(false)), asset->getLength()); -} - -// CriticalNative -static jlong Font_Builder_getReleaseNativeAssetFunc(CRITICAL_JNI_PARAMS) { - return reinterpret_cast<jlong>(&releaseAsset); -} - -// Regular JNI static jlong Font_Builder_initBuilder(JNIEnv*, jobject) { return reinterpret_cast<jlong>(new NativeFontBuilder()); } @@ -196,11 +138,6 @@ static const JNINativeMethod gFontBuilderMethods[] = { { "nAddAxis", "(JIF)V", (void*) Font_Builder_addAxis }, { "nBuild", "(JLjava/nio/ByteBuffer;Ljava/lang/String;IZI)J", (void*) Font_Builder_build }, { "nGetReleaseNativeFont", "()J", (void*) Font_Builder_getReleaseNativeFont }, - - { "nGetNativeAsset", "(Landroid/content/res/AssetManager;Ljava/lang/String;ZI)J", - (void*) Font_Builder_getNativeAsset }, - { "nGetAssetBuffer", "(J)Ljava/nio/ByteBuffer;", (void*) Font_Builder_getAssetBuffer }, - { "nGetReleaseNativeAssetFunc", "()J", (void*) Font_Builder_getReleaseNativeAssetFunc }, }; int register_android_graphics_fonts_Font(JNIEnv* env) { diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index b52381113895..0d0dc3ede754 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -263,6 +263,8 @@ static bool load_maps(int pid, stats_t* stats, bool* foundSwapPss) which_heap = HEAP_NATIVE; } else if (base::StartsWith(name, "[anon:scudo:")) { which_heap = HEAP_NATIVE; + } else if (base::StartsWith(name, "[anon:GWP-ASan")) { + which_heap = HEAP_NATIVE; } else if (base::StartsWith(name, "[stack")) { which_heap = HEAP_STACK; } else if (base::StartsWith(name, "[anon:stack_and_tls:")) { diff --git a/core/jni/android_service_DataLoaderService.cpp b/core/jni/android_service_DataLoaderService.cpp index b307a73ed05b..ed0d381c67bb 100644 --- a/core/jni/android_service_DataLoaderService.cpp +++ b/core/jni/android_service_DataLoaderService.cpp @@ -75,8 +75,9 @@ static const JNINativeMethod dlc_method_table[] = { {"nativeStartDataLoader", "(I)Z", (void*)nativeStartDataLoader}, {"nativeStopDataLoader", "(I)Z", (void*)nativeStopDataLoader}, {"nativeDestroyDataLoader", "(I)Z", (void*)nativeDestroyDataLoader}, - {"nativePrepareImage", "(ILjava/util/Collection;Ljava/util/Collection;)Z", (void*)nativePrepareImage}, - {"nativeWriteData", "(JLjava/lang/String;JJLandroid/os/ParcelFileDescriptor;)V", (void*)nativeWriteData}, + {"nativePrepareImage", "(ILjava/util/List;Ljava/util/List;)Z", (void*)nativePrepareImage}, + {"nativeWriteData", "(JLjava/lang/String;JJLandroid/os/ParcelFileDescriptor;)V", + (void*)nativeWriteData}, }; } // namespace diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 7290e3031d21..7a9a3f8643c0 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -1496,6 +1496,11 @@ static void isolateJitProfile(JNIEnv* env, jobjectArray pkg_data_info_list, std::string mirrorCurPackageProfile = StringPrintf("/data_mirror/cur_profiles/%d/%s", user_id, packageName.c_str()); + if (access(mirrorCurPackageProfile.c_str(), F_OK) != 0) { + ALOGW("Can't access app profile directory: %s", mirrorCurPackageProfile.c_str()); + continue; + } + PrepareDir(actualCurPackageProfile, DEFAULT_DATA_DIR_PERMISSION, uid, uid, fail_fn); BindMount(mirrorCurPackageProfile, actualCurPackageProfile, fail_fn); } diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index 77d4e87cec56..6d9e8ab776b7 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -80,6 +80,11 @@ message SecureSettingsProto { } optional Accessibility accessibility = 2; + message AdaptiveSleep { + optional SettingProto enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ]; + } + optional AdaptiveSleep adaptive_sleep = 78; + // Origins for which browsers should allow geolocation by default. // The value is a space-separated list of origins. optional SettingProto allowed_geolocation_origins = 3; @@ -574,5 +579,5 @@ message SecureSettingsProto { // Please insert fields in alphabetical order and group them into messages // if possible (to avoid reaching the method limit). - // Next tag = 78; + // Next tag = 79; } diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto index f8143de8121f..b723b5307bc1 100644 --- a/core/proto/android/providers/settings/system.proto +++ b/core/proto/android/providers/settings/system.proto @@ -161,6 +161,8 @@ message SystemSettingsProto { optional SettingProto brightness_for_vr = 3 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto brightness_mode = 4 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto auto_brightness_adj = 5 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto brightness_float = 6 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto brightness_for_vr_float = 7 [ (android.privacy).dest = DEST_AUTOMATIC ]; } optional Screen screen = 22; diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto index 546c5a092a50..4bef2e38ad55 100644 --- a/core/proto/android/server/jobscheduler.proto +++ b/core/proto/android/server/jobscheduler.proto @@ -170,30 +170,13 @@ message JobSchedulerServiceDumpProto { message ConstantsProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; - // Minimum # of idle jobs that must be ready in order to force the JMS to - // schedule things early. - optional int32 min_idle_count = 1; - // Minimum # of charging jobs that must be ready in order to force the JMS - // to schedule things early. - optional int32 min_charging_count = 2; - // Minimum # of "battery not low" jobs that must be ready in order to force - // the JMS to schedule things early. - optional int32 min_battery_not_low_count = 3; - // Minimum # of "storage not low" jobs that must be ready in order to force - // the JMS to schedule things early. - optional int32 min_storage_not_low_count = 4; - // Minimum # of connectivity jobs that must be ready in order to force the - // JMS to schedule things early. 1 == Run connectivity jobs as soon as - // ready. - optional int32 min_connectivity_count = 5; - // Minimum # of content trigger jobs that must be ready in order to force - // the JMS to schedule things early. - optional int32 min_content_count = 6; - // Minimum # of jobs (with no particular constraints) for which the JMS will - // be happy running some work early. This (and thus the other min counts) - // is now set to 1, to prevent any batching at this level. Since we now do - // batching through doze, that is a much better mechanism. - optional int32 min_ready_jobs_count = 7; + reserved 1; // min_idle_count + reserved 2; // min_charging_count + reserved 3; // min_battery_not_low_count + reserved 4; // min_storage_not_low_count + reserved 5; // min_connectivity_count + reserved 6; // min_content_count + reserved 7; // min_ready_jobs_count // Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early. optional int32 min_ready_non_active_jobs_count = 29; // Don't batch a non-ACTIVE job if it's been delayed due to force batching attempts for diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 71a42e454f30..d1031f4ef882 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1912,6 +1912,15 @@ <permission android:name="android.permission.VIBRATE_ALWAYS_ON" android:protectionLevel="signature" /> + <!-- @SystemApi Allows access to the vibrator state. + <p>Protection level: signature + @hide + --> + <permission android:name="android.permission.ACCESS_VIBRATOR_STATE" + android:label="@string/permdesc_vibrator_state" + android:description="@string/permdesc_vibrator_state" + android:protectionLevel="signature|privileged" /> + <!-- Allows using PowerManager WakeLocks to keep processor from sleeping or screen from dimming. <p>Protection level: normal @@ -2383,6 +2392,12 @@ <permission android:name="android.permission.INTERACT_ACROSS_PROFILES" android:protectionLevel="signature|appop|documenter|wellbeing" /> + <!-- Allows configuring apps to have the INTERACT_ACROSS_PROFILES permission so that they can + interact across profiles in the same profile group. + @hide --> + <permission android:name="android.permission.CONFIGURE_INTERACT_ACROSS_PROFILES" + android:protectionLevel="signature" /> + <!-- @SystemApi @hide Allows an application to call APIs that allow it to query and manage users on the device. This permission is not available to third party applications. --> @@ -4032,6 +4047,15 @@ android:protectionLevel="signature|privileged|development|appop|retailDemo" /> <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" /> + <!-- Allows a data loader to read a package's access logs. The access logs contain the + set of pages referenced over time. + <p>Declaring the permission implies intention to use the API and the user of the + device can grant permission through the Settings application. + <p>Protection level: signature|privileged|appop --> + <permission android:name="android.permission.LOADER_USAGE_STATS" + android:protectionLevel="signature|privileged|appop" /> + <uses-permission android:name="android.permission.LOADER_USAGE_STATS" /> + <!-- @hide @SystemApi Allows an application to observe usage time of apps. The app can register for callbacks when apps reach a certain usage time limit, etc. --> <permission android:name="android.permission.OBSERVE_APP_USAGE" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index df6330654e77..9282925ecb5e 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -244,7 +244,7 @@ correctly use them when installed on your device. Otherwise, keep this disabled so that applications can still use their own mechanisms. --> <bool name="config_enableAutoPowerModes">false</bool> - + <!-- Whether (if true) this is a kind of device that can be moved around (eg. phone/laptop), or (if false) something for which movement is either not measurable or should not count toward power states (eg. tv/soundbar). --> @@ -269,6 +269,11 @@ when there's no network connection. If the scan doesn't timeout, use zero --> <integer name="config_radioScanningTimeout">0</integer> + <!-- When true, Android uses the PAC implementation included in WebView to handle + networks with PAC scripts. + When false, Android's own implementation of libpac is used.--> + <bool name ="config_useWebViewPacProcessor">false</bool> + <!-- XXXXX NOTE THE FOLLOWING RESOURCES USE THE WRONG NAMING CONVENTION. Please don't copy them, copy anything else. --> @@ -1258,32 +1263,63 @@ --> <integer name="config_doubleTapOnHomeBehavior">0</integer> - <!-- Minimum screen brightness setting allowed by the power manager. - The user is forbidden from setting the brightness below this level. --> + <!-- Note: This setting is deprecated, please use + config_screenBrightnessSettingMinimumFloat instead --> <integer name="config_screenBrightnessSettingMinimum">10</integer> - <!-- Maximum screen brightness allowed by the power manager. - The user is forbidden from setting the brightness above this level. --> + <!-- Note: This setting is deprecated, please use + config_screenBrightnessSettingMaximumFloat instead --> <integer name="config_screenBrightnessSettingMaximum">255</integer> - <!-- Default screen brightness setting. - Must be in the range specified by minimum and maximum. --> + <!-- Note: This setting is deprecated, please use + config_screenBrightnessSettingDefaultFloat instead --> <integer name="config_screenBrightnessSettingDefault">102</integer> - <!-- Default screen brightness for VR setting. --> + <!-- Minimum screen brightness setting allowed by power manager. + The user is forbidden from setting the brightness below this level. + Equivalent to 10/255. --> + <item name="config_screenBrightnessSettingMinimumFloat" format="float" type="dimen">0.035433073</item> + + <!-- Maximum screen brightness allowed by the power manager. + The user is forbidden from setting the brightness above this level. + This value is a fraction between 3.5% and 100%. --> + <item name="config_screenBrightnessSettingMaximumFloat" format="float" type="dimen">1.0</item> + + <!-- Default screen brightness setting. + Must be in the range specified by minimum and maximum. + This value is a fraction between 3.5% and 100%. + Equivalent to 102/255 (default for this device) --> + <item name="config_screenBrightnessSettingDefaultFloat" format="float" type="dimen">0.397637795276</item> + + <!-- Note: This setting is deprecated, please use + config_screenBrightnessSettingForVrDefaultFloat instead --> <integer name="config_screenBrightnessForVrSettingDefault">86</integer> - <!-- Minimum screen brightness setting allowed for VR. Device panels start increasing pulse - width as brightness decreases below this theshold. --> + <!-- Note: This setting is deprecated, please use + config_screenBrightnessSettingForVrMinimumFloat instead --> <integer name="config_screenBrightnessForVrSettingMinimum">79</integer> - <!-- Maximum screen brightness setting allowed for VR. --> + <!-- Note: This setting is deprecated, please use + config_screenBrightnessSettingForVrMaximumFloat instead --> <integer name="config_screenBrightnessForVrSettingMaximum">255</integer> + <!-- Default screen brightness for VR setting as a float. + Equivalent to 86/255--> + <item name="config_screenBrightnessSettingForVrDefaultFloat" format="float" type="dimen">0.33464</item> + + <!-- Minimum screen brightness setting allowed for VR. Device panels start increasing pulse + width as brightness decreases below this threshold as float. + Equivalent to 79/255 --> + <item name="config_screenBrightnessSettingForVrMinimumFloat" format="float" type="dimen">0.307087</item> + + <!-- Maximum screen brightness setting allowed for VR as float. --> + <item name="config_screenBrightnessSettingForVrMaximumFloat" format="float" type="dimen">1.0</item> + <!-- Screen brightness used to dim the screen while dozing in a very low power state. May be less than the minimum allowed brightness setting that can be set by the user. --> <integer name="config_screenBrightnessDoze">1</integer> + <item name="config_screenBrightnessDozeFloat" format="float" type="dimen">0.0</item> <!-- Delay that allows some content to arrive at the display before switching from DOZE to ON. --> @@ -1339,6 +1375,7 @@ timeout expires. May be less than the minimum allowed brightness setting that can be set by the user. --> <integer name="config_screenBrightnessDim">10</integer> + <item name="config_screenBrightnessDimFloat" format="float" type="dimen">0.05</item> <!-- Minimum allowable screen brightness to use in a very dark room. This value sets the floor for the darkest possible auto-brightness diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 08c840380fe5..93e73b098544 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1156,6 +1156,8 @@ <string name="permlab_vibrate">control vibration</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_vibrate">Allows the app to control the vibrator.</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_vibrator_state">Allows the app to access the vibrator state.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_callPhone">directly call phone numbers</string> diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml index c63be7b2adbe..64768cf4c730 100644 --- a/core/res/res/values/styles_device_defaults.xml +++ b/core/res/res/values/styles_device_defaults.xml @@ -285,7 +285,7 @@ easier. <item name="fontFamily">@string/config_bodyFontFamily</item> </style> <style name="TextAppearance.DeviceDefault.Notification" parent="TextAppearance.Material.Notification"> - <item name="fontFamily">@string/config_headlineFontFamily</item> + <item name="fontFamily">@string/config_bodyFontFamily</item> </style> <style name="TextAppearance.DeviceDefault.Notification.Title" parent="TextAppearance.Material.Notification.Title"> <item name="fontFamily">@string/config_headlineFontFamilyMedium</item> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 0babe48e832e..7f66fd0ff612 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -286,6 +286,7 @@ <java-symbol type="bool" name="config_duplicate_port_omadm_wappush" /> <java-symbol type="bool" name="config_disableTransitionAnimation" /> <java-symbol type="bool" name="config_enableAutoPowerModes" /> + <java-symbol type="bool" name="config_useWebViewPacProcessor" /> <java-symbol type="integer" name="config_autoPowerModeThresholdAngle" /> <java-symbol type="integer" name="config_autoPowerModeAnyMotionSensor" /> <java-symbol type="bool" name="config_autoPowerModePreferWristTilt" /> @@ -1980,6 +1981,14 @@ <java-symbol type="integer" name="config_screenBrightnessForVrSettingDefault" /> <java-symbol type="integer" name="config_screenBrightnessForVrSettingMaximum" /> <java-symbol type="integer" name="config_screenBrightnessForVrSettingMinimum" /> + <java-symbol type="dimen" name="config_screenBrightnessSettingForVrMinimumFloat" /> + <java-symbol type="dimen" name="config_screenBrightnessSettingForVrMaximumFloat" /> + <java-symbol type="dimen" name="config_screenBrightnessSettingForVrDefaultFloat" /> + <java-symbol type="dimen" name="config_screenBrightnessSettingMinimumFloat" /> + <java-symbol type="dimen" name="config_screenBrightnessSettingMaximumFloat" /> + <java-symbol type="dimen" name="config_screenBrightnessSettingDefaultFloat" /> + <java-symbol type="dimen" name="config_screenBrightnessDozeFloat" /> + <java-symbol type="dimen" name="config_screenBrightnessDimFloat" /> <java-symbol type="integer" name="config_screenBrightnessDark" /> <java-symbol type="integer" name="config_screenBrightnessDim" /> <java-symbol type="integer" name="config_screenBrightnessDoze" /> diff --git a/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java b/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java new file mode 100644 index 000000000000..c897ace0e0b5 --- /dev/null +++ b/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.integrity; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.ImmutableMap; + +import org.testng.annotations.Test; + +import java.util.Arrays; +import java.util.Collections; + +public class InstallerAllowedByManifestFormulaTest { + + private static final InstallerAllowedByManifestFormula + FORMULA = new InstallerAllowedByManifestFormula(); + + @Test + public void testFormulaMatches_installerAndCertBothInManifest() { + AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder() + .setInstallerName("installer1") + .setInstallerCertificates(Arrays.asList("installer_cert1", "random_cert")) + .setAllowedInstallersAndCert(ImmutableMap.of( + "installer1", "installer_cert1", + "installer2", "installer_cert2" + )).build(); + + assertThat(FORMULA.matches(appInstallMetadata)).isTrue(); + } + + @Test + public void testFormulaMatches_installerAndCertDoesNotMatchInManifest() { + AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder() + .setInstallerName("installer1") + .setInstallerCertificates(Arrays.asList("installer_cert1", "random_cert")) + .setAllowedInstallersAndCert(ImmutableMap.of( + "installer1", "installer_cert2", + "installer2", "installer_cert1" + )).build(); + + assertThat(FORMULA.matches(appInstallMetadata)).isFalse(); + } + + @Test + public void testFormulaMatches_installerNotInManifest() { + AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder() + .setInstallerName("installer3") + .setInstallerCertificates(Arrays.asList("installer_cert1", "random_cert")) + .setAllowedInstallersAndCert(ImmutableMap.of( + "installer1", "installer_cert2", + "installer2", "installer_cert1" + )).build(); + + assertThat(FORMULA.matches(appInstallMetadata)).isFalse(); + } + + @Test + public void testFormulaMatches_certificateNotInManifest() { + AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder() + .setInstallerName("installer1") + .setInstallerCertificates(Arrays.asList("installer_cert3", "random_cert")) + .setAllowedInstallersAndCert(ImmutableMap.of( + "installer1", "installer_cert2", + "installer2", "installer_cert1" + )).build(); + + assertThat(FORMULA.matches(appInstallMetadata)).isFalse(); + } + + @Test + public void testFormulaMatches_emptyManifest() { + AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder() + .setInstallerName("installer1") + .setInstallerCertificates(Arrays.asList("installer_cert3", "random_cert")) + .setAllowedInstallersAndCert(ImmutableMap.of()).build(); + + assertThat(FORMULA.matches(appInstallMetadata)).isTrue(); + } + + /** Returns a builder with all fields filled with some dummy data. */ + private AppInstallMetadata.Builder getAppInstallMetadataBuilder() { + return new AppInstallMetadata.Builder() + .setPackageName("abc") + .setAppCertificates(Collections.emptyList()) + .setInstallerCertificates(Collections.emptyList()) + .setInstallerName("abc") + .setVersionCode(-1) + .setIsPreInstalled(true); + } +} diff --git a/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java b/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java index 75ef1f22b819..62c9c98f4e1d 100644 --- a/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java +++ b/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java @@ -20,8 +20,6 @@ import static android.content.integrity.IntegrityFormula.COMPOUND_FORMULA_TAG; import static com.google.common.truth.Truth.assertThat; -import static org.testng.Assert.assertThrows; - import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -32,8 +30,7 @@ public class IntegrityFormulaTest { @Test public void createEqualsFormula_packageName() { String packageName = "com.test.app"; - IntegrityFormula formula = - IntegrityFormula.PACKAGE_NAME.equalTo(packageName); + IntegrityFormula formula = IntegrityFormula.Application.packageNameEquals(packageName); AtomicFormula.StringAtomicFormula stringAtomicFormula = (AtomicFormula.StringAtomicFormula) formula; @@ -46,8 +43,7 @@ public class IntegrityFormulaTest { @Test public void createEqualsFormula_appCertificate() { String appCertificate = "com.test.app"; - IntegrityFormula formula = - IntegrityFormula.APP_CERTIFICATE.equalTo(appCertificate); + IntegrityFormula formula = IntegrityFormula.Application.certificatesContain(appCertificate); AtomicFormula.StringAtomicFormula stringAtomicFormula = (AtomicFormula.StringAtomicFormula) formula; @@ -60,8 +56,7 @@ public class IntegrityFormulaTest { @Test public void createEqualsFormula_installerName() { String installerName = "com.test.app"; - IntegrityFormula formula = - IntegrityFormula.INSTALLER_NAME.equalTo(installerName); + IntegrityFormula formula = IntegrityFormula.Installer.packageNameEquals(installerName); AtomicFormula.StringAtomicFormula stringAtomicFormula = (AtomicFormula.StringAtomicFormula) formula; @@ -75,7 +70,7 @@ public class IntegrityFormulaTest { public void createEqualsFormula_installerCertificate() { String installerCertificate = "com.test.app"; IntegrityFormula formula = - IntegrityFormula.INSTALLER_CERTIFICATE.equalTo(installerCertificate); + IntegrityFormula.Installer.certificatesContain(installerCertificate); AtomicFormula.StringAtomicFormula stringAtomicFormula = (AtomicFormula.StringAtomicFormula) formula; @@ -88,8 +83,7 @@ public class IntegrityFormulaTest { @Test public void createEqualsFormula_versionCode() { int versionCode = 12; - IntegrityFormula formula = - IntegrityFormula.VERSION_CODE.equalTo(versionCode); + IntegrityFormula formula = IntegrityFormula.Application.versionCodeEquals(versionCode); AtomicFormula.LongAtomicFormula stringAtomicFormula = (AtomicFormula.LongAtomicFormula) formula; @@ -100,24 +94,9 @@ public class IntegrityFormulaTest { } @Test - public void createEqualsFormula_invalidKeyTypeForStringParameter() { - assertThrows( - IllegalArgumentException.class, - () -> IntegrityFormula.PRE_INSTALLED.equalTo("wrongString")); - } - - @Test - public void createEqualsFormula_invalidKeyTypeForLongParameter() { - assertThrows( - IllegalArgumentException.class, - () -> IntegrityFormula.PACKAGE_NAME.equalTo(12)); - } - - @Test public void createGreaterThanFormula_versionCode() { int versionCode = 12; - IntegrityFormula formula = - IntegrityFormula.VERSION_CODE.greaterThan(versionCode); + IntegrityFormula formula = IntegrityFormula.Application.versionCodeGreaterThan(versionCode); AtomicFormula.LongAtomicFormula stringAtomicFormula = (AtomicFormula.LongAtomicFormula) formula; @@ -128,17 +107,10 @@ public class IntegrityFormulaTest { } @Test - public void createGreaterThanFormula_invalidKeyTypeForLongParameter() { - assertThrows( - IllegalArgumentException.class, - () -> IntegrityFormula.PACKAGE_NAME.greaterThan(12)); - } - - @Test public void createGreaterThanOrEqualsToFormula_versionCode() { int versionCode = 12; - IntegrityFormula formula = - IntegrityFormula.VERSION_CODE.greaterThanOrEquals(versionCode); + IntegrityFormula formula = IntegrityFormula.Application.versionCodeGreaterThanOrEqualTo( + versionCode); AtomicFormula.LongAtomicFormula stringAtomicFormula = (AtomicFormula.LongAtomicFormula) formula; @@ -149,15 +121,8 @@ public class IntegrityFormulaTest { } @Test - public void createGreaterThanOrEqualsToFormula_invalidKeyTypeForLongParameter() { - assertThrows( - IllegalArgumentException.class, - () -> IntegrityFormula.PACKAGE_NAME.greaterThanOrEquals(12)); - } - - @Test public void createIsTrueFormula_preInstalled() { - IntegrityFormula formula = IntegrityFormula.PRE_INSTALLED.equalTo(true); + IntegrityFormula formula = IntegrityFormula.Application.isPreInstalled(); AtomicFormula.BooleanAtomicFormula stringAtomicFormula = (AtomicFormula.BooleanAtomicFormula) formula; @@ -167,20 +132,12 @@ public class IntegrityFormulaTest { } @Test - public void createIsTrueFormula_invalidKeyTypeForBoolParameter() { - assertThrows( - IllegalArgumentException.class, - () -> IntegrityFormula.PACKAGE_NAME.equalTo(true)); - } - - @Test public void createAllFormula() { String packageName = "com.test.package"; String certificateName = "certificate"; - IntegrityFormula formula1 = - IntegrityFormula.PACKAGE_NAME.equalTo(packageName); - IntegrityFormula formula2 = - IntegrityFormula.APP_CERTIFICATE.equalTo(certificateName); + IntegrityFormula formula1 = IntegrityFormula.Application.packageNameEquals(packageName); + IntegrityFormula formula2 = IntegrityFormula.Application.certificatesContain( + certificateName); IntegrityFormula compoundFormula = IntegrityFormula.all(formula1, formula2); @@ -191,10 +148,9 @@ public class IntegrityFormulaTest { public void createAnyFormula() { String packageName = "com.test.package"; String certificateName = "certificate"; - IntegrityFormula formula1 = - IntegrityFormula.PACKAGE_NAME.equalTo(packageName); - IntegrityFormula formula2 = - IntegrityFormula.APP_CERTIFICATE.equalTo(certificateName); + IntegrityFormula formula1 = IntegrityFormula.Application.packageNameEquals(packageName); + IntegrityFormula formula2 = IntegrityFormula.Application.certificatesContain( + certificateName); IntegrityFormula compoundFormula = IntegrityFormula.any(formula1, formula2); @@ -206,8 +162,7 @@ public class IntegrityFormulaTest { String packageName = "com.test.package"; IntegrityFormula compoundFormula = - IntegrityFormula.not( - IntegrityFormula.PACKAGE_NAME.equalTo(packageName)); + IntegrityFormula.not(IntegrityFormula.Application.packageNameEquals(packageName)); assertThat(compoundFormula.getTag()).isEqualTo(COMPOUND_FORMULA_TAG); } diff --git a/core/tests/coretests/src/android/os/BrightnessLimit.java b/core/tests/coretests/src/android/os/BrightnessLimit.java index 5a3724f7aa24..be7935545543 100644 --- a/core/tests/coretests/src/android/os/BrightnessLimit.java +++ b/core/tests/coretests/src/android/os/BrightnessLimit.java @@ -42,7 +42,7 @@ public class BrightnessLimit extends Activity implements OnClickListener { public void onClick(View v) { DisplayManager dm = getSystemService(DisplayManager.class); - dm.setTemporaryBrightness(0); + dm.setTemporaryBrightness(0.0f); Settings.System.putInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, 0); } } diff --git a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java index 2648a0644dc1..a24b4e06225a 100644 --- a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java +++ b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java @@ -147,6 +147,34 @@ public class ControlProviderServiceTest { } @Test + public void testLoadSuggested_withMaxNumber() throws RemoteException { + Control control1 = new Control.StatelessBuilder("TEST_ID", mPendingIntent).build(); + Control control2 = new Control.StatelessBuilder("TEST_ID_2", mPendingIntent) + .setDeviceType(DeviceTypes.TYPE_AIR_FRESHENER).build(); + + @SuppressWarnings("unchecked") + ArgumentCaptor<List<Control>> captor = ArgumentCaptor.forClass(List.class); + + ArrayList<Control> list = new ArrayList<>(); + list.add(control1); + list.add(control2); + + final int maxSuggested = 1; + + mControlsProviderService.setControls(list); + mControlsProvider.loadSuggested(maxSuggested, mLoadCallback); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + + verify(mLoadCallback).accept(eq(mToken), captor.capture()); + List<Control> l = captor.getValue(); + assertEquals(maxSuggested, l.size()); + + for (int i = 0; i < maxSuggested; ++i) { + assertTrue(equals(list.get(i), l.get(i))); + } + } + + @Test public void testSubscribe() throws RemoteException { Control control = new Control.StatefulBuilder("TEST_ID", mPendingIntent) .setTitle("TEST_TITLE") @@ -216,6 +244,11 @@ public class ControlProviderServiceTest { } @Override + public void loadSuggestedControls(int maxNumber, Consumer<List<Control>> cb) { + cb.accept(mControls); + } + + @Override public Publisher<Control> publisherFor(List<String> ids) { return new Publisher<Control>() { public void subscribe(final Subscriber s) { diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index 2c9dba1d59fd..24fe2a0a1823 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -45,6 +45,7 @@ import android.content.Context; import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; +import android.os.CancellationSignal; import android.platform.test.annotations.Presubmit; import android.view.SurfaceControl.Transaction; import android.view.WindowInsets.Type; @@ -516,6 +517,34 @@ public class InsetsControllerTest { } @Test + public void testCancellation_afterGainingControl() throws Exception { + InsetsSourceControl control = + new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()); + mController.onControlsChanged(new InsetsSourceControl[] { control }); + + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + WindowInsetsAnimationControlListener mockListener = + mock(WindowInsetsAnimationControlListener.class); + CancellationSignal cancellationSignal = mController.controlWindowInsetsAnimation( + statusBars(), 0 /* durationMs */, + new LinearInterpolator(), mockListener); + + // Ready gets deferred until next predraw + mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw(); + + verify(mockListener).onReady(any(), anyInt()); + + cancellationSignal.cancel(); + verify(mockListener).onCancelled(); + }); + waitUntilNextFrame(); + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible()); + }); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + } + + @Test public void testControlImeNotReady() { prepareControls(); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { @@ -580,6 +609,28 @@ public class InsetsControllerTest { }); } + @Test + public void testControlImeNotReady_cancel() { + prepareControls(); + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + WindowInsetsAnimationControlListener listener = + mock(WindowInsetsAnimationControlListener.class); + mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(), listener) + .cancel(); + + verify(listener).onCancelled(); + + // Ready gets deferred until next predraw + mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw(); + + verify(listener, never()).onReady(any(), anyInt()); + + // Pretend that timeout is happening + mTestClock.fastForward(2500); + mTestHandler.timeAdvance(); + }); + } + private void waitUntilNextFrame() throws Exception { final CountDownLatch latch = new CountDownLatch(1); Choreographer.getMainThreadInstance().postCallback(Choreographer.CALLBACK_COMMIT, diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index f83fb3f312e9..af115b1e80c1 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -70,6 +70,7 @@ applications that come with the platform <privapp-permissions package="com.android.managedprovisioning"> <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/> <permission name="android.permission.CHANGE_CONFIGURATION"/> + <permission name="android.permission.CONFIGURE_INTERACT_ACROSS_PROFILES"/> <permission name="android.permission.CRYPT_KEEPER"/> <permission name="android.permission.DELETE_PACKAGES"/> <permission name="android.permission.INSTALL_PACKAGES"/> @@ -378,6 +379,8 @@ applications that come with the platform <!-- Permission required for ShortcutManagerUsageTest CTS test. --> <permission name="android.permission.ACCESS_SHORTCUTS"/> <permission name="android.permission.REBOOT"/> + <!-- Permission required for access VIBRATOR_STATE. --> + <permission name="android.permission.ACCESS_VIBRATOR_STATE"/> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java index 447f043392c2..f50de1665453 100644 --- a/graphics/java/android/graphics/FontFamily.java +++ b/graphics/java/android/graphics/FontFamily.java @@ -19,6 +19,7 @@ package android.graphics; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.res.AssetManager; +import android.graphics.fonts.Font; import android.graphics.fonts.FontVariationAxis; import android.os.Build; import android.text.TextUtils; @@ -195,18 +196,13 @@ public class FontFamily { if (mBuilderPtr == 0) { throw new IllegalStateException("Unable to call addFontFromAsset after freezing."); } - if (axes != null) { - for (FontVariationAxis axis : axes) { - nAddAxisValue(mBuilderPtr, axis.getOpenTypeTagValue(), axis.getStyleValue()); - } - } - return nAddFontFromAssetManager(mBuilderPtr, mgr, path, cookie, isAsset, ttcIndex, weight, - isItalic); - } - // TODO: Remove once internal user stop using private API. - private static boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex) { - return nAddFont(builderPtr, font, ttcIndex, -1, -1); + try { + ByteBuffer buffer = Font.Builder.createBuffer(mgr, path, isAsset, cookie); + return addFontFromBuffer(buffer, ttcIndex, axes, weight, isItalic); + } catch (IOException e) { + return false; + } } private static native long nInitBuilder(String langs, int variant); @@ -225,8 +221,6 @@ public class FontFamily { int weight, int isItalic); private static native boolean nAddFontWeightStyle(long builderPtr, ByteBuffer font, int ttcIndex, int weight, int isItalic); - private static native boolean nAddFontFromAssetManager(long builderPtr, AssetManager mgr, - String path, int cookie, boolean isAsset, int ttcIndex, int weight, int isItalic); // The added axis values are only valid for the next nAddFont* method call. @CriticalNative diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java index ba96a06cc852..4899fbe431cc 100644 --- a/graphics/java/android/graphics/fonts/Font.java +++ b/graphics/java/android/graphics/fonts/Font.java @@ -19,6 +19,7 @@ package android.graphics.fonts; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; import android.content.res.Resources; import android.os.LocaleList; @@ -35,7 +36,9 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.util.Arrays; import java.util.Objects; @@ -54,10 +57,6 @@ public final class Font { * A builder class for creating new Font. */ public static final class Builder { - private static final NativeAllocationRegistry sAssetByteBufferRegistry = - NativeAllocationRegistry.createMalloced(ByteBuffer.class.getClassLoader(), - nGetReleaseNativeAssetFunc()); - private static final NativeAllocationRegistry sFontRegistry = NativeAllocationRegistry.createMalloced(Font.class.getClassLoader(), nGetReleaseNativeFont()); @@ -151,7 +150,11 @@ public final class Font { * @param path the file name of the font data in the asset directory */ public Builder(@NonNull AssetManager am, @NonNull String path) { - this(am, path, true /* is asset */, 0 /* cookie */); + try { + mBuffer = createBuffer(am, path, true /* is asset */, 0 /* cookie */); + } catch (IOException e) { + mException = e; + } } /** @@ -165,18 +168,11 @@ public final class Font { */ public Builder(@NonNull AssetManager am, @NonNull String path, boolean isAsset, int cookie) { - final long nativeAsset = nGetNativeAsset(am, path, isAsset, cookie); - if (nativeAsset == 0) { - mException = new FileNotFoundException("Unable to open " + path); - return; - } - final ByteBuffer b = nGetAssetBuffer(nativeAsset); - sAssetByteBufferRegistry.registerNativeAllocation(b, nativeAsset); - if (b == null) { - mException = new FileNotFoundException(path + " not found"); - return; + try { + mBuffer = createBuffer(am, path, isAsset, cookie); + } catch (IOException e) { + mException = e; } - mBuffer = b; } /** @@ -199,19 +195,64 @@ public final class Font { mException = new FileNotFoundException(resId + " must be font file."); return; } - final long nativeAsset = nGetNativeAsset(res.getAssets(), str, false /* is asset */, - value.assetCookie); - if (nativeAsset == 0) { - mException = new FileNotFoundException("Unable to open " + str); - return; + + try { + mBuffer = createBuffer(res.getAssets(), str, false, value.assetCookie); + } catch (IOException e) { + mException = e; } - final ByteBuffer b = nGetAssetBuffer(nativeAsset); - sAssetByteBufferRegistry.registerNativeAllocation(b, nativeAsset); - if (b == null) { - mException = new FileNotFoundException(str + " not found"); - return; + } + + /** + * Creates a buffer containing font data using the assetManager and other + * provided inputs. + * + * @param am the application's asset manager + * @param path the file name of the font data in the asset directory + * @param isAsset true if the undelying data is in asset + * @param cookie set asset cookie + * @return buffer containing the contents of the file + * + * @hide + */ + public static ByteBuffer createBuffer(@NonNull AssetManager am, @NonNull String path, + boolean isAsset, int cookie) throws IOException { + Preconditions.checkNotNull(am, "assetManager can not be null"); + Preconditions.checkNotNull(path, "path can not be null"); + + if (!isAsset) { + // Attempt to open as FD, which should work unless the asset is compressed + AssetFileDescriptor assetFD; + try { + if (cookie > 0) { + assetFD = am.openNonAssetFd(cookie, path); + } else { + assetFD = am.openNonAssetFd(path); + } + + try (FileInputStream fis = assetFD.createInputStream()) { + final FileChannel fc = fis.getChannel(); + return fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()); + } + } catch (IOException e) { + // failed to open as FD so now we will attempt to open as an input stream + } + } + + try (InputStream assetStream = isAsset ? am.open(path, AssetManager.ACCESS_BUFFER) + : am.openNonAsset(cookie, path, AssetManager.ACCESS_BUFFER)) { + + int capacity = assetStream.available(); + ByteBuffer buffer = ByteBuffer.allocateDirect(capacity); + buffer.order(ByteOrder.nativeOrder()); + assetStream.read(buffer.array(), buffer.arrayOffset(), assetStream.available()); + + if (assetStream.read() != -1) { + throw new IOException("Unable to access full contents of " + path); + } + + return buffer; } - mBuffer = b; } /** @@ -396,15 +437,6 @@ public final class Font { } /** - * Native methods for accessing underlying buffer in Asset - */ - private static native long nGetNativeAsset( - @NonNull AssetManager am, @NonNull String path, boolean isAsset, int cookie); - private static native ByteBuffer nGetAssetBuffer(long nativeAsset); - @CriticalNative - private static native long nGetReleaseNativeAssetFunc(); - - /** * Native methods for creating Font */ private static native long nInitBuilder(); diff --git a/libs/androidfw/tests/CommonHelpers.h b/libs/androidfw/tests/CommonHelpers.h index c160fbba4c01..8af13f20fb0d 100644 --- a/libs/androidfw/tests/CommonHelpers.h +++ b/libs/androidfw/tests/CommonHelpers.h @@ -44,10 +44,6 @@ static inline ::std::ostream& operator<<(::std::ostream& out, const String8& str return out << str.string(); } -static inline ::std::ostream& operator<<(::std::ostream& out, const String16& str) { - return out << String8(str).string(); -} - static inline ::std::ostream& operator<<(::std::ostream& out, const ResTable_config& c) { return out << c.toString(); } diff --git a/location/java/android/location/GnssAntennaInfo.aidl b/location/java/android/location/GnssAntennaInfo.aidl new file mode 100644 index 000000000000..2b956afda01f --- /dev/null +++ b/location/java/android/location/GnssAntennaInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location; + +parcelable GnssAntennaInfo;
\ No newline at end of file diff --git a/location/java/android/location/GnssAntennaInfo.java b/location/java/android/location/GnssAntennaInfo.java new file mode 100644 index 000000000000..dfcaf814f9c9 --- /dev/null +++ b/location/java/android/location/GnssAntennaInfo.java @@ -0,0 +1,654 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; + +/** + * A class that contains information about a GNSS antenna. GNSS antenna characteristics can change + * with device configuration, such as when a device is folded open or closed. Antenna information is + * delivered to registered instances of {@link Callback}. + */ +public final class GnssAntennaInfo implements Parcelable { + private final double mCarrierFrequencyMHz; + private final PhaseCenterOffsetCoordinates mPhaseCenterOffsetCoordinates; + private final PhaseCenterVariationCorrections mPhaseCenterVariationCorrections; + private final SignalGainCorrections mSignalGainCorrections; + + /** + * Used for receiving GNSS antenna info from the GNSS engine. You can implement this interface + * and call {@link LocationManager#registerAntennaInfoCallback}; + */ + public abstract static class Callback { + /** + * The status of GNSS antenna info. + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({STATUS_NOT_SUPPORTED, STATUS_READY, STATUS_LOCATION_DISABLED}) + public @interface GnssAntennaInfoStatus { + } + + /** + * The system does not support GNSS antenna info. + * + * This status will not change in the future. + */ + public static final int STATUS_NOT_SUPPORTED = 0; + + /** + * GNSS antenna info updates are being successfully tracked. + */ + public static final int STATUS_READY = 1; + + /** + * GNSS provider or Location is disabled, updated will not be received until they are + * enabled. + */ + public static final int STATUS_LOCATION_DISABLED = 2; + + /** + * Returns the latest GNSS antenna info. This event is triggered when a callback is + * registered, and whenever the antenna info changes (due to a device configuration change). + */ + public void onGnssAntennaInfoReceived(@NonNull List<GnssAntennaInfo> gnssAntennaInfos) {} + + /** + * Returns the latest status of the GNSS antenna info sub-system. + */ + public void onStatusChanged(@GnssAntennaInfoStatus int status) {} + } + + /** + * Class containing information about the antenna phase center offset (PCO). PCO is defined with + * respect to the origin of the Android sensor coordinate system, e.g., center of primary screen + * for mobiles - see sensor or form factor documents for details. Uncertainties are reported + * to 1-sigma. + */ + public static final class PhaseCenterOffsetCoordinates implements Parcelable { + private final double mPhaseCenterOffsetCoordinateXMillimeters; + private final double mPhaseCenterOffsetCoordinateXUncertaintyMillimeters; + private final double mPhaseCenterOffsetCoordinateYMillimeters; + private final double mPhaseCenterOffsetCoordinateYUncertaintyMillimeters; + private final double mPhaseCenterOffsetCoordinateZMillimeters; + private final double mPhaseCenterOffsetCoordinateZUncertaintyMillimeters; + + @VisibleForTesting + public PhaseCenterOffsetCoordinates(double phaseCenterOffsetCoordinateXMillimeters, + double phaseCenterOffsetCoordinateXUncertaintyMillimeters, + double phaseCenterOffsetCoordinateYMillimeters, + double phaseCenterOffsetCoordinateYUncertaintyMillimeters, + double phaseCenterOffsetCoordinateZMillimeters, + double phaseCenterOffsetCoordinateZUncertaintyMillimeters) { + mPhaseCenterOffsetCoordinateXMillimeters = phaseCenterOffsetCoordinateXMillimeters; + mPhaseCenterOffsetCoordinateYMillimeters = phaseCenterOffsetCoordinateYMillimeters; + mPhaseCenterOffsetCoordinateZMillimeters = phaseCenterOffsetCoordinateZMillimeters; + mPhaseCenterOffsetCoordinateXUncertaintyMillimeters = + phaseCenterOffsetCoordinateXUncertaintyMillimeters; + mPhaseCenterOffsetCoordinateYUncertaintyMillimeters = + phaseCenterOffsetCoordinateYUncertaintyMillimeters; + mPhaseCenterOffsetCoordinateZUncertaintyMillimeters = + phaseCenterOffsetCoordinateZUncertaintyMillimeters; + } + + public static final @NonNull Creator<PhaseCenterOffsetCoordinates> CREATOR = + new Creator<PhaseCenterOffsetCoordinates>() { + @Override + public PhaseCenterOffsetCoordinates createFromParcel(Parcel in) { + return new PhaseCenterOffsetCoordinates( + in.readDouble(), + in.readDouble(), + in.readDouble(), + in.readDouble(), + in.readDouble(), + in.readDouble() + ); + } + + @Override + public PhaseCenterOffsetCoordinates[] newArray(int size) { + return new PhaseCenterOffsetCoordinates[size]; + } + }; + + public double getXCoordMillimeters() { + return mPhaseCenterOffsetCoordinateXMillimeters; + } + + public double getXCoordUncertaintyMillimeters() { + return mPhaseCenterOffsetCoordinateXUncertaintyMillimeters; + } + + public double getYCoordMillimeters() { + return mPhaseCenterOffsetCoordinateYMillimeters; + } + + public double getYCoordUncertaintyMillimeters() { + return mPhaseCenterOffsetCoordinateYUncertaintyMillimeters; + } + + public double getZCoordMillimeters() { + return mPhaseCenterOffsetCoordinateZMillimeters; + } + + public double getZCoordUncertaintyMillimeters() { + return mPhaseCenterOffsetCoordinateZUncertaintyMillimeters; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeDouble(mPhaseCenterOffsetCoordinateXMillimeters); + dest.writeDouble(mPhaseCenterOffsetCoordinateXUncertaintyMillimeters); + dest.writeDouble(mPhaseCenterOffsetCoordinateYMillimeters); + dest.writeDouble(mPhaseCenterOffsetCoordinateYUncertaintyMillimeters); + dest.writeDouble(mPhaseCenterOffsetCoordinateZMillimeters); + dest.writeDouble(mPhaseCenterOffsetCoordinateZUncertaintyMillimeters); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("PhaseCenteroffset:\n"); + builder.append("X: " + mPhaseCenterOffsetCoordinateXMillimeters + " +/- " + + mPhaseCenterOffsetCoordinateXUncertaintyMillimeters + "\n"); + builder.append("Y: " + mPhaseCenterOffsetCoordinateYMillimeters + " +/- " + + mPhaseCenterOffsetCoordinateYUncertaintyMillimeters + "\n"); + builder.append("Z: " + mPhaseCenterOffsetCoordinateZMillimeters + " +/- " + + mPhaseCenterOffsetCoordinateZUncertaintyMillimeters + "\n"); + return builder.toString(); + } + } + + /** + * Class containing information about the phase center variation (PCV) corrections. The PCV + * correction is added to the phase measurement to obtain the corrected value. + * + * The corrections and associated (1-sigma) uncertainties are represented by respect 2D arrays. + * + * Each row (major indices) represents a fixed theta. The first row corresponds to a + * theta angle of 0 degrees. The last row corresponds to a theta angle of (360 - deltaTheta) + * degrees, where deltaTheta is the regular spacing between azimuthal angles, i.e., deltaTheta + * = 360 / (number of rows). + * + * The columns (minor indices) represent fixed zenith angles, beginning at 0 degrees and ending + * at 180 degrees. They are separated by deltaPhi, the regular spacing between zenith angles, + * i.e., deltaPhi = 180 / (number of columns - 1). + */ + public static final class PhaseCenterVariationCorrections extends SphericalCorrections { + + @VisibleForTesting + public PhaseCenterVariationCorrections( + @NonNull double[][] phaseCenterVariationCorrectionsMillimeters, + @NonNull double[][] phaseCenterVariationCorrectionUncertaintiesMillimeters) { + super(phaseCenterVariationCorrectionsMillimeters, + phaseCenterVariationCorrectionUncertaintiesMillimeters); + } + + private PhaseCenterVariationCorrections(@NonNull Parcel in) { + super(in); + } + + /** + * Get the phase center variation correction in millimeters at the specified row and column + * in the underlying 2D array. + * @param row zero-based major index in the array + * @param column zero-based minor index in the array + * @return phase center correction in millimeters + */ + public double getPhaseCenterVariationCorrectionMillimetersAt(int row, int column) { + return super.getCorrectionAt(row, column); + } + + /** + * Get the phase center variation correction uncertainty in millimeters at the specified row + * and column in the underlying 2D array. + * @param row zero-based major index in the array + * @param column zero-based minor index in the array + * @return 1-sigma phase center correction uncertainty in millimeters + */ + public double getPhaseCenterVariationCorrectionUncertaintyMillimetersAt( + int row, int column) { + return super.getCorrectionUncertaintyAt(row, column); + } + + public @NonNull double[][] getRawCorrectionsArray() { + return super.getRawCorrectionsArray().clone(); + } + + public @NonNull double[][] getRawCorrectionUncertaintiesArray() { + return super.getRawCorrectionUncertaintiesArray().clone(); + } + + public int getNumRows() { + return super.getNumRows(); + } + + public int getNumColumns() { + return super.getNumColumns(); + } + + /** + * The fixed theta angle separation between successive rows. + */ + public double getDeltaTheta() { + return super.getDeltaTheta(); + } + + /** + * The fixed phi angle separation between successive columns. + */ + public double getDeltaPhi() { + return super.getDeltaPhi(); + } + + public static final @NonNull Creator<PhaseCenterVariationCorrections> CREATOR = + new Creator<PhaseCenterVariationCorrections>() { + @Override + public PhaseCenterVariationCorrections createFromParcel(Parcel in) { + return new PhaseCenterVariationCorrections(in); + } + + @Override + public PhaseCenterVariationCorrections[] newArray(int size) { + return new PhaseCenterVariationCorrections[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + super.writeToParcel(dest, flags); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("PhaseCenterVariationCorrections:\n"); + builder.append(super.toString()); + return builder.toString(); + } + } + + /** + * Class containing information about the signal gain (SG) corrections. The SG + * correction is added to the signal gain to obtain the corrected value. + * + * The corrections and associated (1-sigma) uncertainties are represented by respect 2D arrays. + * + * Each row (major indices) represents a fixed theta. The first row corresponds to a + * theta angle of 0 degrees. The last row corresponds to a theta angle of (360 - deltaTheta) + * degrees, where deltaTheta is the regular spacing between azimuthal angles, i.e., deltaTheta + * = 360 / (number of rows). + * + * The columns (minor indices) represent fixed zenith angles, beginning at 0 degrees and ending + * at 180 degrees. They are separated by deltaPhi, the regular spacing between zenith angles, + * i.e., deltaPhi = 180 / (number of columns - 1). + */ + public static final class SignalGainCorrections extends SphericalCorrections { + + @VisibleForTesting + public SignalGainCorrections( + @NonNull double[][] signalGainCorrectionsDbi, + @NonNull double[][] signalGainCorrectionUncertaintiesDbi) { + super(signalGainCorrectionsDbi, + signalGainCorrectionUncertaintiesDbi); + } + + private SignalGainCorrections(@NonNull Parcel in) { + super(in); + } + + /** + * Get the signal gain correction in dbi at the specified row and column + * in the underlying 2D array. + * @param row zero-based major index in the array + * @param column zero-based minor index in the array + * @return signal gain correction in dbi + */ + public double getSignalGainCorrectionDbiAt(int row, int column) { + return super.getCorrectionAt(row, column); + } + + /** + * Get the signal gain correction correction uncertainty in dbi at the specified row + * and column in the underlying 2D array. + * @param row zero-based major index in the array + * @param column zero-based minor index in the array + * @return 1-sigma signal gain correction uncertainty in dbi + */ + public double getSignalGainCorrectionUncertaintyDbiAt(int row, int column) { + return super.getCorrectionUncertaintyAt(row, column); + } + + public @NonNull double[][] getRawCorrectionsArray() { + return super.getRawCorrectionsArray().clone(); + } + + public @NonNull double[][] getRawCorrectionUncertaintiesArray() { + return super.getRawCorrectionUncertaintiesArray().clone(); + } + + public int getNumRows() { + return super.getNumRows(); + } + + public int getNumColumns() { + return super.getNumColumns(); + } + + /** + * The fixed theta angle separation between successive rows. + */ + public double getDeltaTheta() { + return super.getDeltaTheta(); + } + + /** + * The fixed phi angle separation between successive columns. + */ + public double getDeltaPhi() { + return super.getDeltaPhi(); + } + + public static final @NonNull Creator<SignalGainCorrections> CREATOR = + new Creator<SignalGainCorrections>() { + @Override + public SignalGainCorrections createFromParcel(Parcel in) { + return new SignalGainCorrections(in); + } + + @Override + public SignalGainCorrections[] newArray(int size) { + return new SignalGainCorrections[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + super.writeToParcel(dest, flags); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("SignalGainCorrections:\n"); + builder.append(super.toString()); + return builder.toString(); + } + } + + /** + * Represents corrections on a spherical mapping. + * + * Each row (major indices) represents a fixed theta. The first row corresponds to a + * theta angle of 0 degrees. The last row corresponds to a theta angle of (360 - deltaTheta) + * degrees, where deltaTheta is the regular spacing between azimuthal angles, i.e., deltaTheta + * = 360 / (number of rows). + * + * The columns (minor indices) represent fixed zenith angles, beginning at 0 degrees and ending + * at 180 degrees. They are separated by deltaPhi, the regular spacing between zenith angles, + * i.e., deltaPhi = 180 / (number of columns - 1). + */ + private abstract static class SphericalCorrections implements Parcelable { + private final double[][] mCorrections; + private final double[][] mCorrectionUncertainties; + private final double mDeltaTheta; + private final double mDeltaPhi; + private final int mNumRows; + private final int mNumColumns; + + SphericalCorrections(@NonNull double[][] corrections, + @NonNull double[][] correctionUncertainties) { + if (corrections.length != correctionUncertainties.length + || corrections[0].length != correctionUncertainties[0].length) { + throw new IllegalArgumentException("Correction and correction uncertainty arrays " + + "must have the same dimensions."); + } + + mNumRows = corrections.length; + if (mNumRows < 1) { + throw new IllegalArgumentException("Arrays must have at least one row."); + } + + mNumColumns = corrections[0].length; + if (mNumColumns < 2) { + throw new IllegalArgumentException("Arrays must have at least two columns."); + } + + mCorrections = corrections; + mCorrectionUncertainties = correctionUncertainties; + mDeltaTheta = 360.0d / mNumRows; + mDeltaPhi = 180.0d / (mNumColumns - 1); + } + + SphericalCorrections(Parcel in) { + int numRows = in.readInt(); + int numColumns = in.readInt(); + + double[][] corrections = + new double[numRows][numColumns]; + double[][] correctionUncertainties = + new double[numRows][numColumns]; + + for (int row = 0; row < numRows; row++) { + in.readDoubleArray(corrections[row]); + } + + for (int row = 0; row < numRows; row++) { + in.readDoubleArray(correctionUncertainties[row]); + } + + mNumRows = corrections.length; + mNumColumns = corrections[0].length; + mCorrections = corrections; + mCorrectionUncertainties = correctionUncertainties; + mDeltaTheta = 360.0d / mNumRows; + mDeltaPhi = 180.0d / (mNumColumns - 1); + } + + private double getCorrectionAt(int row, int column) { + return mCorrections[row][column]; + } + + private double getCorrectionUncertaintyAt(int row, int column) { + return mCorrectionUncertainties[row][column]; + } + + @NonNull + private double[][] getRawCorrectionsArray() { + return mCorrections; + } + + @NonNull + private double[][] getRawCorrectionUncertaintiesArray() { + return mCorrectionUncertainties; + } + + private int getNumRows() { + return mNumRows; + } + + private int getNumColumns() { + return mNumColumns; + } + + /** + * The fixed theta angle separation between successive rows. + */ + private double getDeltaTheta() { + return mDeltaTheta; + } + + /** + * The fixed phi angle separation between successive columns. + */ + private double getDeltaPhi() { + return mDeltaPhi; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mNumRows); + dest.writeInt(mNumColumns); + for (double[] row: mCorrections) { + dest.writeDoubleArray(row); + } + for (double[] row: mCorrectionUncertainties) { + dest.writeDoubleArray(row); + } + } + + private String arrayToString(double[][] array) { + StringBuilder builder = new StringBuilder(); + for (int row = 0; row < mNumRows; row++) { + builder.append("[ "); + for (int column = 0; column < mNumColumns - 1; column++) { + builder.append(array[row][column] + ", "); + } + builder.append(array[row][mNumColumns - 1] + " ]\n"); + } + return builder.toString(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("DeltaTheta: " + mDeltaTheta + "\n"); + builder.append("DeltaPhi: " + mDeltaPhi + "\n"); + builder.append("CorrectionsArray:\n"); + builder.append(arrayToString(mCorrections)); + builder.append("CorrectionUncertaintiesArray:\n"); + builder.append(arrayToString(mCorrectionUncertainties)); + return builder.toString(); + } + } + + @VisibleForTesting + public GnssAntennaInfo( + double carrierFrequencyMHz, + @NonNull PhaseCenterOffsetCoordinates phaseCenterOffsetCoordinates, + @Nullable PhaseCenterVariationCorrections phaseCenterVariationCorrections, + @Nullable SignalGainCorrections signalGainCorrectionDbi) { + if (phaseCenterOffsetCoordinates == null) { + throw new IllegalArgumentException("Phase Center Offset Coordinates cannot be null."); + } + mCarrierFrequencyMHz = carrierFrequencyMHz; + mPhaseCenterOffsetCoordinates = phaseCenterOffsetCoordinates; + mPhaseCenterVariationCorrections = phaseCenterVariationCorrections; + mSignalGainCorrections = signalGainCorrectionDbi; + } + + public double getCarrierFrequencyMHz() { + return mCarrierFrequencyMHz; + } + + @NonNull + public PhaseCenterOffsetCoordinates getPhaseCenterOffsetCoordinates() { + return mPhaseCenterOffsetCoordinates; + } + + @Nullable + public PhaseCenterVariationCorrections getPhaseCenterVariationCorrections() { + return mPhaseCenterVariationCorrections; + } + + @Nullable + public SignalGainCorrections getSignalGainCorrections() { + return mSignalGainCorrections; + } + + public static final @android.annotation.NonNull + Creator<GnssAntennaInfo> CREATOR = new Creator<GnssAntennaInfo>() { + @Override + public GnssAntennaInfo createFromParcel(Parcel in) { + double carrierFrequencyMHz = in.readDouble(); + + ClassLoader classLoader = getClass().getClassLoader(); + PhaseCenterOffsetCoordinates phaseCenterOffsetCoordinates = + in.readParcelable(classLoader); + PhaseCenterVariationCorrections phaseCenterVariationCorrections = + in.readParcelable(classLoader); + SignalGainCorrections signalGainCorrections = + in.readParcelable(classLoader); + + return new GnssAntennaInfo(carrierFrequencyMHz, + phaseCenterOffsetCoordinates, + phaseCenterVariationCorrections, signalGainCorrections); + } + + @Override + public GnssAntennaInfo[] newArray(int size) { + return new GnssAntennaInfo[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel parcel, int flags) { + parcel.writeDouble(mCarrierFrequencyMHz); + + // Write Phase Center Offset + parcel.writeParcelable(mPhaseCenterOffsetCoordinates, flags); + + // Write Phase Center Variation Corrections + parcel.writeParcelable(mPhaseCenterVariationCorrections, flags); + + // Write Signal Gain Corrections + parcel.writeParcelable(mSignalGainCorrections, flags); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("[ GnssAntennaInfo:\n"); + builder.append("CarrierFrequencyMHz: " + mCarrierFrequencyMHz + "\n"); + builder.append(mPhaseCenterOffsetCoordinates.toString()); + builder.append(mPhaseCenterVariationCorrections == null + ? "PhaseCenterVariationCorrections: null\n" + : mPhaseCenterVariationCorrections.toString()); + builder.append(mSignalGainCorrections == null + ? "SignalGainCorrections: null\n" + : mSignalGainCorrections.toString()); + builder.append("]"); + return builder.toString(); + } +} diff --git a/location/java/android/location/GnssCapabilities.java b/location/java/android/location/GnssCapabilities.java index 2e2f98432d2e..930180c6870f 100644 --- a/location/java/android/location/GnssCapabilities.java +++ b/location/java/android/location/GnssCapabilities.java @@ -82,6 +82,12 @@ public final class GnssCapabilities { */ public static final long MEASUREMENT_CORRECTIONS_REFLECTING_PLANE = 1L << 8; + /** + * Bit mask indicating GNSS chipset supports GNSS antenna info. + * @hide + */ + public static final long ANTENNA_INFO = 1L << 9; + /** @hide */ public static final long INVALID_CAPABILITIES = -1; @@ -165,6 +171,13 @@ public final class GnssCapabilities { return hasCapability(MEASUREMENT_CORRECTIONS_REFLECTING_PLANE); } + /** + * Returns {@code true} if GNSS chipset supports antenna info, {@code false} otherwise. + */ + public boolean hasGnssAntennaInfo() { + return hasCapability(ANTENNA_INFO); + } + @NonNull @Override public String toString() { @@ -172,6 +185,7 @@ public final class GnssCapabilities { if (hasLowPowerMode()) sb.append("LOW_POWER_MODE "); if (hasSatelliteBlacklist()) sb.append("SATELLITE_BLACKLIST "); if (hasGeofencing()) sb.append("GEOFENCING "); + if (hasGnssAntennaInfo()) sb.append("ANTENNA_INFO "); if (hasMeasurements()) sb.append("MEASUREMENTS "); if (hasNavMessages()) sb.append("NAV_MESSAGES "); if (hasMeasurementCorrections()) sb.append("MEASUREMENT_CORRECTIONS "); diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java index f17fa399dace..4d01cdc8dac3 100644 --- a/location/java/android/location/GnssStatus.java +++ b/location/java/android/location/GnssStatus.java @@ -24,6 +24,8 @@ import android.annotation.NonNull; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Objects; /** * This class represents the current state of the GNSS engine and is used in conjunction with @@ -339,6 +341,33 @@ public final class GnssStatus { } } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof GnssStatus)) { + return false; + } + + GnssStatus that = (GnssStatus) o; + return mSvCount == that.mSvCount + && Arrays.equals(mSvidWithFlags, that.mSvidWithFlags) + && Arrays.equals(mCn0DbHzs, that.mCn0DbHzs) + && Arrays.equals(mElevations, that.mElevations) + && Arrays.equals(mAzimuths, that.mAzimuths) + && Arrays.equals(mCarrierFrequencies, that.mCarrierFrequencies) + && Arrays.equals(mBasebandCn0DbHzs, that.mBasebandCn0DbHzs); + } + + @Override + public int hashCode() { + int result = Objects.hash(mSvCount); + result = 31 * result + Arrays.hashCode(mSvidWithFlags); + result = 31 * result + Arrays.hashCode(mCn0DbHzs); + return result; + } + /** * Builder class to help create new GnssStatus instances. */ @@ -451,8 +480,8 @@ public final class GnssStatus { mCn0DbHz = cn0DbHz; mElevation = elevation; mAzimuth = azimuth; - mCarrierFrequency = carrierFrequency; - mBasebandCn0DbHz = basebandCn0DbHz; + mCarrierFrequency = hasCarrierFrequency ? carrierFrequency : 0; + mBasebandCn0DbHz = hasBasebandCn0DbHz ? basebandCn0DbHz : 0; } } } diff --git a/location/java/android/location/GpsClock.java b/location/java/android/location/GpsClock.java index f1237662a559..58af6ee83af6 100644 --- a/location/java/android/location/GpsClock.java +++ b/location/java/android/location/GpsClock.java @@ -25,8 +25,11 @@ import android.os.Parcelable; * A class containing a GPS clock timestamp. * It represents a measurement of the GPS receiver's clock. * + * @deprecated use {@link GnssClock} instead. + * * @hide */ +@Deprecated @SystemApi public class GpsClock implements Parcelable { diff --git a/location/java/android/location/GpsMeasurement.java b/location/java/android/location/GpsMeasurement.java index 27a81899bedc..c2ab4ab22145 100644 --- a/location/java/android/location/GpsMeasurement.java +++ b/location/java/android/location/GpsMeasurement.java @@ -24,8 +24,11 @@ import android.os.Parcelable; /** * A class representing a GPS satellite measurement, containing raw and computed information. * + * @deprecated use {@link GnssMeasurement} instead. + * * @hide */ +@Deprecated @SystemApi public class GpsMeasurement implements Parcelable { private int mFlags; diff --git a/location/java/android/location/GpsMeasurementsEvent.java b/location/java/android/location/GpsMeasurementsEvent.java index d69158d335f4..f3feb7a4c7b6 100644 --- a/location/java/android/location/GpsMeasurementsEvent.java +++ b/location/java/android/location/GpsMeasurementsEvent.java @@ -30,8 +30,11 @@ import java.util.Collections; * A class implementing a container for data associated with a measurement event. * Events are delivered to registered instances of {@link Listener}. * + * @deprecated use {@link GnssMeasurementsEvent} instead. + * * @hide */ +@Deprecated @SystemApi public class GpsMeasurementsEvent implements Parcelable { diff --git a/location/java/android/location/GpsNavigationMessage.java b/location/java/android/location/GpsNavigationMessage.java index 6eeea2623a8b..dc1e99fd6a4f 100644 --- a/location/java/android/location/GpsNavigationMessage.java +++ b/location/java/android/location/GpsNavigationMessage.java @@ -26,8 +26,11 @@ import java.security.InvalidParameterException; /** * A class containing a GPS satellite Navigation Message. * + * @deprecated use {@link GnssNavigationMessage} instead. + * * @hide */ +@Deprecated @SystemApi public class GpsNavigationMessage implements Parcelable { diff --git a/location/java/android/location/GpsNavigationMessageEvent.java b/location/java/android/location/GpsNavigationMessageEvent.java index f60e5c71ab16..2d5d6ebd5990 100644 --- a/location/java/android/location/GpsNavigationMessageEvent.java +++ b/location/java/android/location/GpsNavigationMessageEvent.java @@ -27,8 +27,11 @@ import java.security.InvalidParameterException; * A class implementing a container for data associated with a navigation message event. * Events are delivered to registered instances of {@link Listener}. * + * @deprecated use {@link GnssNavigationMessage} instead. + * * @hide */ +@Deprecated @SystemApi public class GpsNavigationMessageEvent implements Parcelable { diff --git a/location/java/android/location/IGnssAntennaInfoListener.aidl b/location/java/android/location/IGnssAntennaInfoListener.aidl new file mode 100644 index 000000000000..30bf546759f1 --- /dev/null +++ b/location/java/android/location/IGnssAntennaInfoListener.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location; + +import android.location.GnssAntennaInfo; + +/** + * {@hide} + */ +oneway interface IGnssAntennaInfoListener { + void onGnssAntennaInfoReceived(in List<GnssAntennaInfo> gnssAntennaInfo); + void onStatusChanged(in int status); +}
\ No newline at end of file diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index a99e68fbb7b6..8600dc48c6a4 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -24,6 +24,7 @@ import android.location.Geofence; import android.location.GnssMeasurementCorrections; import android.location.GnssRequest; import android.location.IBatchedLocationCallback; +import android.location.IGnssAntennaInfoListener; import android.location.IGnssMeasurementsListener; import android.location.IGnssStatusListener; import android.location.IGnssNavigationMessageListener; @@ -79,6 +80,10 @@ interface ILocationManager long getGnssCapabilities(in String packageName); void removeGnssMeasurementsListener(in IGnssMeasurementsListener listener); + boolean addGnssAntennaInfoListener(in IGnssAntennaInfoListener listener, + String packageName, String featureId, String listenerIdentifier); + void removeGnssAntennaInfoListener(in IGnssAntennaInfoListener listener); + boolean addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener, String packageName, String featureId, String listenerIdentifier); void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener); @@ -93,7 +98,7 @@ interface ILocationManager boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName); void flushGnssBatch(String packageName); boolean stopGnssBatch(); - boolean injectLocation(in Location location); + void injectLocation(in Location location); @UnsupportedAppUsage List<String> getAllProviders(); diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java index eb76c29301c6..6724324bfcb9 100644 --- a/location/java/android/location/Location.java +++ b/location/java/android/location/Location.java @@ -64,23 +64,18 @@ public class Location implements Parcelable { public static final int FORMAT_SECONDS = 2; /** - * Bundle key for a version of the location that has been fed through - * LocationFudger. Allows location providers to flag locations as being - * safe for use with ACCESS_COARSE_LOCATION permission. - * - * @hide - */ - public static final String EXTRA_COARSE_LOCATION = "coarseLocation"; - - /** * Bundle key for a version of the location containing no GPS data. * Allows location providers to flag locations as being safe to * feed to LocationFudger. * * @hide + * @deprecated As of Android R, this extra is longer in use, since it is not necessary to keep + * gps locations separate from other locations for coarsening. Providers that do not need to + * support platforms below Android R should not use this constant. */ @TestApi @SystemApi + @Deprecated public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation"; /** diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 8ae967fe79c2..3d0765bb0855 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -251,7 +251,7 @@ public class LocationManager { * @hide */ public static final String HIGH_POWER_REQUEST_CHANGE_ACTION = - "android.location.HIGH_POWER_REQUEST_CHANGE"; + "android.location.HIGH_POWER_REQUEST_CHANGE"; /** * Broadcast intent action for Settings app to inject a footer at the bottom of location @@ -310,6 +310,8 @@ public class LocationManager { new GnssMeasurementsListenerManager(); private final GnssNavigationMessageListenerManager mGnssNavigationMessageListenerTransport = new GnssNavigationMessageListenerManager(); + private final GnssAntennaInfoListenerManager mGnssAntennaInfoListenerManager = + new GnssAntennaInfoListenerManager(); /** * @hide @@ -1215,7 +1217,7 @@ public class LocationManager { * the first fix. * * @param location newly available {@link Location} object - * @return true if the location was successfully injected, false otherwise + * @return true if the location was injected, false otherwise * * @throws IllegalArgumentException if location is null * @throws SecurityException if permissions are not present @@ -1229,7 +1231,8 @@ public class LocationManager { "incomplete location object, missing timestamp or accuracy?"); try { - return mService.injectLocation(location); + mService.injectLocation(location); + return true; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2260,6 +2263,41 @@ public class LocationManager { } /** + * Registers a Gnss Antenna Info callback. + * + * @param executor the executor that the callback runs on. + * @param callback a {@link GnssAntennaInfo.Callback} object to register. + * @return {@code true} if the callback was added successfully, {@code false} otherwise. + * + * @throws IllegalArgumentException if executor is null + * @throws IllegalArgumentException if callback is null + * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present + */ + @RequiresPermission(ACCESS_FINE_LOCATION) + public boolean registerAntennaInfoCallback( + @NonNull @CallbackExecutor Executor executor, + @NonNull GnssAntennaInfo.Callback callback) { + try { + return mGnssAntennaInfoListenerManager.addListener(callback, executor); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Unregisters a GNSS Antenna Info callback. + * + * @param callback a {@link GnssAntennaInfo.Callback} object to remove. + */ + public void unregisterAntennaInfoCallback(@NonNull GnssAntennaInfo.Callback callback) { + try { + mGnssAntennaInfoListenerManager.removeListener(callback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * No-op method to keep backward-compatibility. * * @hide @@ -2988,6 +3026,48 @@ public class LocationManager { } } + private class GnssAntennaInfoListenerManager extends + AbstractListenerManager<Void, GnssAntennaInfo.Callback> { + + @Nullable + private IGnssAntennaInfoListener mListenerTransport; + + @Override + protected boolean registerService(Void ignored) throws RemoteException { + Preconditions.checkState(mListenerTransport == null); + + GnssAntennaInfoListener transport = new GnssAntennaInfoListener(); + if (mService.addGnssAntennaInfoListener(transport, mContext.getPackageName(), + mContext.getFeatureId(), "gnss antenna info callback")) { + mListenerTransport = transport; + return true; + } else { + return false; + } + } + + @Override + protected void unregisterService() throws RemoteException { + Preconditions.checkState(mListenerTransport != null); + + mService.removeGnssAntennaInfoListener(mListenerTransport); + mListenerTransport = null; + } + + private class GnssAntennaInfoListener extends IGnssAntennaInfoListener.Stub { + @Override + public void onGnssAntennaInfoReceived(final List<GnssAntennaInfo> gnssAntennaInfos) { + execute((callback) -> callback.onGnssAntennaInfoReceived(gnssAntennaInfos)); + } + + @Override + public void onStatusChanged(int status) { + execute((listener) -> listener.onStatusChanged(status)); + } + } + + } + private class BatchedLocationCallbackManager extends AbstractListenerManager<Void, BatchedLocationCallback> { @@ -3000,7 +3080,7 @@ public class LocationManager { BatchedLocationCallback transport = new BatchedLocationCallback(); if (mService.addGnssBatchingCallback(transport, mContext.getPackageName(), - mContext.getFeatureId(), "batched location callback")) { + mContext.getFeatureId(), "batched location callback")) { mListenerTransport = transport; return true; } else { diff --git a/location/java/android/location/LocationManagerInternal.java b/location/java/android/location/LocationManagerInternal.java index 69162bab3167..085602cbcd4f 100644 --- a/location/java/android/location/LocationManagerInternal.java +++ b/location/java/android/location/LocationManagerInternal.java @@ -43,6 +43,15 @@ public abstract class LocationManagerInternal { public abstract void requestSetProviderAllowed(@NonNull String provider, boolean allowed); /** + * Returns true if the given provider is enabled for the given user. + * + * @param provider A location provider as listed by {@link LocationManager#getAllProviders()} + * @param userId The user id to check + * @return True if the provider is enabled, false otherwise + */ + public abstract boolean isProviderEnabledForUser(@NonNull String provider, int userId); + + /** * Returns true if the given package belongs to a location provider, and so should be afforded * some special privileges. * diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java index f67d08e045e2..bd29d8ac2a85 100644 --- a/location/lib/java/com/android/location/provider/LocationProviderBase.java +++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java @@ -224,6 +224,19 @@ public abstract class LocationProviderBase { public void reportLocation(Location location) { ILocationProviderManager manager = mManager; if (manager != null) { + // remove deprecated extras to save on serialization + Bundle extras = location.getExtras(); + if (extras != null && (extras.containsKey("noGPSLocation") + || extras.containsKey("coarseLocation"))) { + location = new Location(location); + extras = location.getExtras(); + extras.remove("noGPSLocation"); + extras.remove("coarseLocation"); + if (extras.isEmpty()) { + location.setExtras(null); + } + } + try { manager.onReportLocation(location); } catch (RemoteException | RuntimeException e) { diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 4a1088bfa877..112bb9c31276 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -17,6 +17,7 @@ package android.media; +import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -1649,6 +1650,179 @@ public class AudioManager { } } + /** + * @hide + * Interface to be notified of changes in the preferred audio device set for a given audio + * strategy. + * @see #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDevice) + * @see #removePreferredDeviceForStrategy(AudioProductStrategy) + * @see #getPreferredDeviceForStrategy(AudioProductStrategy) + */ + @SystemApi + public interface OnPreferredDeviceForStrategyChangedListener { + /** + * Called on the listener to indicate that the preferred audio device for the given + * strategy has changed. + * @param strategy the {@link AudioProductStrategy} whose preferred device changed + * @param device <code>null</code> if the preferred device was removed, or the newly set + * preferred audio device + */ + void onPreferredDeviceForStrategyChanged(@NonNull AudioProductStrategy strategy, + @Nullable AudioDevice device); + } + + /** + * @hide + * Adds a listener for being notified of changes to the strategy-preferred audio device. + * @param executor + * @param listener + * @throws SecurityException if the caller doesn't hold the required permission + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public void addOnPreferredDeviceForStrategyChangedListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull OnPreferredDeviceForStrategyChangedListener listener) + throws SecurityException { + Objects.requireNonNull(executor); + Objects.requireNonNull(listener); + synchronized (mPrefDevListenerLock) { + if (hasPrefDevListener(listener)) { + throw new IllegalArgumentException( + "attempt to call addOnPreferredDeviceForStrategyChangedListener() " + + "on a previously registered listener"); + } + // lazy initialization of the list of strategy-preferred device listener + if (mPrefDevListeners == null) { + mPrefDevListeners = new ArrayList<>(); + } + final int oldCbCount = mPrefDevListeners.size(); + mPrefDevListeners.add(new PrefDevListenerInfo(listener, executor)); + if (oldCbCount == 0 && mPrefDevListeners.size() > 0) { + // register binder for callbacks + if (mPrefDevDispatcherStub == null) { + mPrefDevDispatcherStub = new StrategyPreferredDeviceDispatcherStub(); + } + try { + getService().registerStrategyPreferredDeviceDispatcher(mPrefDevDispatcherStub); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + } + + /** + * @hide + * Removes a previously added listener of changes to the strategy-preferred audio device. + * @param listener + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public void removeOnPreferredDeviceForStrategyChangedListener( + @NonNull OnPreferredDeviceForStrategyChangedListener listener) { + Objects.requireNonNull(listener); + synchronized (mPrefDevListenerLock) { + if (!removePrefDevListener(listener)) { + throw new IllegalArgumentException( + "attempt to call removeOnPreferredDeviceForStrategyChangedListener() " + + "on an unregistered listener"); + } + if (mPrefDevListeners.size() == 0) { + // unregister binder for callbacks + try { + getService().unregisterStrategyPreferredDeviceDispatcher( + mPrefDevDispatcherStub); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } finally { + mPrefDevDispatcherStub = null; + mPrefDevListeners = null; + } + } + } + } + + + private final Object mPrefDevListenerLock = new Object(); + /** + * List of listeners for preferred device for strategy and their associated Executor. + * List is lazy-initialized on first registration + */ + @GuardedBy("mPrefDevListenerLock") + private @Nullable ArrayList<PrefDevListenerInfo> mPrefDevListeners; + + private static class PrefDevListenerInfo { + final @NonNull OnPreferredDeviceForStrategyChangedListener mListener; + final @NonNull Executor mExecutor; + PrefDevListenerInfo(OnPreferredDeviceForStrategyChangedListener listener, Executor exe) { + mListener = listener; + mExecutor = exe; + } + } + + @GuardedBy("mPrefDevListenerLock") + private StrategyPreferredDeviceDispatcherStub mPrefDevDispatcherStub; + + private final class StrategyPreferredDeviceDispatcherStub + extends IStrategyPreferredDeviceDispatcher.Stub { + + @Override + public void dispatchPrefDeviceChanged(int strategyId, @Nullable AudioDevice device) { + // make a shallow copy of listeners so callback is not executed under lock + final ArrayList<PrefDevListenerInfo> prefDevListeners; + synchronized (mPrefDevListenerLock) { + if (mPrefDevListeners == null || mPrefDevListeners.size() == 0) { + return; + } + prefDevListeners = (ArrayList<PrefDevListenerInfo>) mPrefDevListeners.clone(); + } + final AudioProductStrategy strategy = + AudioProductStrategy.getAudioProductStrategyWithId(strategyId); + final long ident = Binder.clearCallingIdentity(); + try { + for (PrefDevListenerInfo info : prefDevListeners) { + info.mExecutor.execute(() -> + info.mListener.onPreferredDeviceForStrategyChanged(strategy, device)); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + @GuardedBy("mPrefDevListenerLock") + private @Nullable PrefDevListenerInfo getPrefDevListenerInfo( + OnPreferredDeviceForStrategyChangedListener listener) { + if (mPrefDevListeners == null) { + return null; + } + for (PrefDevListenerInfo info : mPrefDevListeners) { + if (info.mListener == listener) { + return info; + } + } + return null; + } + + @GuardedBy("mPrefDevListenerLock") + private boolean hasPrefDevListener(OnPreferredDeviceForStrategyChangedListener listener) { + return getPrefDevListenerInfo(listener) != null; + } + + @GuardedBy("mPrefDevListenerLock") + /** + * @return true if the listener was removed from the list + */ + private boolean removePrefDevListener(OnPreferredDeviceForStrategyChangedListener listener) { + final PrefDevListenerInfo infoToRemove = getPrefDevListenerInfo(listener); + if (infoToRemove != null) { + mPrefDevListeners.remove(infoToRemove); + return true; + } + return false; + } + //==================================================================== // Offload query /** diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 0fbc0d2180ba..27bf3fe6f023 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -29,6 +29,7 @@ import android.media.IAudioServerStateDispatcher; import android.media.IPlaybackConfigDispatcher; import android.media.IRecordingConfigDispatcher; import android.media.IRingtonePlayer; +import android.media.IStrategyPreferredDeviceDispatcher; import android.media.IVolumeController; import android.media.IVolumeController; import android.media.PlayerBase; @@ -286,6 +287,11 @@ interface IAudioService { int getAllowedCapturePolicy(); + void registerStrategyPreferredDeviceDispatcher(IStrategyPreferredDeviceDispatcher dispatcher); + + oneway void unregisterStrategyPreferredDeviceDispatcher( + IStrategyPreferredDeviceDispatcher dispatcher); + // WARNING: read warning at top of file, new methods that need to be used by native // code via IAudioManager.h need to be added to the top section. } diff --git a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl b/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl new file mode 100644 index 000000000000..6db9e52c9d47 --- /dev/null +++ b/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.media.AudioDevice; + +/** + * AIDL for AudioService to signal audio strategy-preferred device updates. + * + * {@hide} + */ +oneway interface IStrategyPreferredDeviceDispatcher { + + void dispatchPrefDeviceChanged(int strategyId, in AudioDevice device); + +} diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.java b/media/java/android/media/audiopolicy/AudioProductStrategy.java index 60b3fc642ee4..f9dbc50e20cf 100644 --- a/media/java/android/media/audiopolicy/AudioProductStrategy.java +++ b/media/java/android/media/audiopolicy/AudioProductStrategy.java @@ -83,6 +83,27 @@ public final class AudioProductStrategy implements Parcelable { /** * @hide + * Return the AudioProductStrategy object for the given strategy ID. + * @param id the ID of the strategy to find + * @return an AudioProductStrategy on which getId() would return id, null if no such strategy + * exists. + */ + public static @Nullable AudioProductStrategy getAudioProductStrategyWithId(int id) { + synchronized (sLock) { + if (sAudioProductStrategies == null) { + sAudioProductStrategies = initializeAudioProductStrategies(); + } + for (AudioProductStrategy strategy : sAudioProductStrategies) { + if (strategy.getId() == id) { + return strategy; + } + } + } + return null; + } + + /** + * @hide * Create an invalid AudioProductStrategy instance for testing * @param id the ID for the invalid strategy, always use a different one than in use * @return an invalid instance that cannot successfully be used for volume groups or routing diff --git a/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java index f186de60b74a..6a8b6daa433c 100644 --- a/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java +++ b/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java @@ -17,7 +17,6 @@ package android.media.tv.tuner.filter; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.Context; @@ -43,13 +42,6 @@ public class TsFilterConfiguration extends FilterConfiguration { } /** - * Gets the {@link Settings} object of this filter configuration. - */ - @Nullable - public Settings getSettings() { - return mSettings; - } - /** * Gets Tag Protocol ID. */ public int getTpid() { @@ -71,25 +63,13 @@ public class TsFilterConfiguration extends FilterConfiguration { /** * Builder for {@link TsFilterConfiguration}. */ - public static class Builder { - private Settings mSettings; + public static class Builder extends FilterConfiguration.Builder<Builder> { private int mTpid; private Builder() { } /** - * Sets filter settings. - * - * @param settings the filter settings. - */ - @NonNull - public Builder setSettings(@NonNull Settings settings) { - mSettings = settings; - return this; - } - - /** * Sets Tag Protocol ID. * * @param tpid the Tag Protocol ID. @@ -107,5 +87,10 @@ public class TsFilterConfiguration extends FilterConfiguration { public TsFilterConfiguration build() { return new TsFilterConfiguration(mSettings, mTpid); } + + @Override + Builder self() { + return this; + } } } diff --git a/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp b/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp index 01150b773557..6bd5a69fe98b 100644 --- a/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp +++ b/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp @@ -261,6 +261,11 @@ private: mReadLogFd.reset(); } + // Installation callback + bool onPrepareImage(const android::dataloader::DataLoaderInstallationFiles& addedFiles) final { + return true; + } + // IFS callbacks. void onPendingReads(const android::dataloader::PendingReads& pendingReads) final { std::lock_guard lock{mMapsMutex}; @@ -520,6 +525,6 @@ private: int JNI_OnLoad(JavaVM* jvm, void* /* reserved */) { android::dataloader::DataLoader::initialize( - [](auto) { return std::make_unique<AdbDataLoader>(); }); + [](auto, auto) { return std::make_unique<AdbDataLoader>(); }); return JNI_VERSION_1_6; } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 3aa35cb48c38..5d1e4cbf8b72 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -41,7 +41,6 @@ import com.android.settingslib.Utils; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -71,10 +70,10 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> short mRssi; // mProfiles and mRemovedProfiles does not do swap() between main and sub device. It is // because current sub device is only for HearingAid and its profile is the same. - private final List<LocalBluetoothProfile> mProfiles = new ArrayList<>(); + private final Collection<LocalBluetoothProfile> mProfiles = new CopyOnWriteArrayList<>(); // List of profiles that were previously in mProfiles, but have been removed - private final List<LocalBluetoothProfile> mRemovedProfiles = new ArrayList<>(); + private final Collection<LocalBluetoothProfile> mRemovedProfiles = new CopyOnWriteArrayList<>(); // Device supports PANU but not NAP: remove PanProfile after device disconnects from NAP private boolean mLocalNapRoleConnected; @@ -717,7 +716,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } public List<LocalBluetoothProfile> getProfiles() { - return Collections.unmodifiableList(mProfiles); + return new ArrayList<>(mProfiles); } public List<LocalBluetoothProfile> getConnectableProfiles() { @@ -734,7 +733,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } public List<LocalBluetoothProfile> getRemovedProfiles() { - return mRemovedProfiles; + return new ArrayList<>(mRemovedProfiles); } public void registerCallback(Callback callback) { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java index c72efb7eec83..35bbbc0e8b39 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java @@ -48,6 +48,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; /** @@ -232,7 +233,7 @@ public class LocalBluetoothProfileManager { } private final Collection<ServiceListener> mServiceListeners = - new ArrayList<ServiceListener>(); + new CopyOnWriteArrayList<ServiceListener>(); private void addProfile(LocalBluetoothProfile profile, String profileName, String stateChangedAction) { @@ -361,14 +362,18 @@ public class LocalBluetoothProfileManager { // not synchronized: use only from UI thread! (TODO: verify) void callServiceConnectedListeners() { - for (ServiceListener l : mServiceListeners) { + final Collection<ServiceListener> listeners = new ArrayList<>(mServiceListeners); + + for (ServiceListener l : listeners) { l.onServiceConnected(); } } // not synchronized: use only from UI thread! (TODO: verify) void callServiceDisconnectedListeners() { - for (ServiceListener listener : mServiceListeners) { + final Collection<ServiceListener> listeners = new ArrayList<>(mServiceListeners); + + for (ServiceListener listener : listeners) { listener.onServiceDisconnected(); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/display/BrightnessUtils.java b/packages/SettingsLib/src/com/android/settingslib/display/BrightnessUtils.java index 55723f9d8ed4..57d95942bcb9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/display/BrightnessUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/display/BrightnessUtils.java @@ -20,7 +20,8 @@ import android.util.MathUtils; public class BrightnessUtils { - public static final int GAMMA_SPACE_MAX = 1023; + public static final int GAMMA_SPACE_MIN = 0; + public static final int GAMMA_SPACE_MAX = 65535; // Hybrid Log Gamma constant values private static final float R = 0.5f; @@ -51,7 +52,7 @@ public class BrightnessUtils { * @return The corresponding setting value. */ public static final int convertGammaToLinear(int val, int min, int max) { - final float normalizedVal = MathUtils.norm(0, GAMMA_SPACE_MAX, val); + final float normalizedVal = MathUtils.norm(GAMMA_SPACE_MIN, GAMMA_SPACE_MAX, val); final float ret; if (normalizedVal <= R) { ret = MathUtils.sq(normalizedVal / R); @@ -65,6 +66,29 @@ public class BrightnessUtils { } /** + * Version of {@link #convertGammaToLinear} that takes and returns float values. + * TODO: brightnessfloat Merge with above method later. + * + * @param val The slider value. + * @param min The minimum acceptable value for the setting. + * @param max The maximum acceptable value for the setting. + * @return The corresponding setting value. + */ + public static final float convertGammaToLinearFloat(int val, float min, float max) { + final float normalizedVal = MathUtils.norm(GAMMA_SPACE_MIN, GAMMA_SPACE_MAX, val); + final float ret; + if (normalizedVal <= R) { + ret = MathUtils.sq(normalizedVal / R); + } else { + ret = MathUtils.exp((normalizedVal - C) / A) + B; + } + + // HLG is normalized to the range [0, 12], so we need to re-normalize to the range [0, 1] + // in order to derive the correct setting value. + return MathUtils.lerp(min, max, ret / 12); + } + + /** * A function for converting from the linear space that the setting works in to the * gamma space that the slider works in. * @@ -96,6 +120,27 @@ public class BrightnessUtils { ret = A * MathUtils.log(normalizedVal - B) + C; } - return Math.round(MathUtils.lerp(0, GAMMA_SPACE_MAX, ret)); + return Math.round(MathUtils.lerp(GAMMA_SPACE_MIN, GAMMA_SPACE_MAX, ret)); + } + + /** + * Version of {@link #convertLinearToGamma} that takes float values. + * TODO: brightnessfloat merge with above method(?) + * @param val The brightness setting value. + * @param min The minimum acceptable value for the setting. + * @param max The maximum acceptable value for the setting. + * @return The corresponding slider value + */ + public static final int convertLinearToGammaFloat(float val, float min, float max) { + // For some reason, HLG normalizes to the range [0, 12] rather than [0, 1] + final float normalizedVal = MathUtils.norm(min, max, val) * 12; + final float ret; + if (normalizedVal <= 1f) { + ret = MathUtils.sqrt(normalizedVal) * R; + } else { + ret = A * MathUtils.log(normalizedVal - B) + C; + } + + return Math.round(MathUtils.lerp(GAMMA_SPACE_MIN, GAMMA_SPACE_MAX, ret)); } } diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index de4817cc4c49..2431381f3033 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -37,6 +37,7 @@ public class SecureSettings { Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, + Settings.Secure.ADAPTIVE_SLEEP, Settings.Secure.AUTOFILL_SERVICE, Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java index 3f5b0daa06a7..c4330e16d236 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java @@ -46,7 +46,7 @@ public class SystemSettings { Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, Settings.System.SCREEN_BRIGHTNESS_FOR_VR, - Settings.System.ADAPTIVE_SLEEP, + Settings.System.ADAPTIVE_SLEEP, // moved to secure Settings.System.VIBRATE_INPUT_DEVICES, Settings.System.MODE_RINGER_STREAMS_AFFECTED, Settings.System.TEXT_AUTO_REPLACE, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 849f22f7e0a2..5553469b0420 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -59,6 +59,7 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put( Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.ADAPTIVE_SLEEP, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.AUTOFILL_SERVICE, NULLABLE_COMPONENT_NAME_VALIDATOR); VALIDATORS.put( Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java index c5d4fa9f1b40..8037266ac92f 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java @@ -126,7 +126,6 @@ public class SystemSettingsValidators { VALIDATORS.put(System.SCREEN_OFF_TIMEOUT, NON_NEGATIVE_INTEGER_VALIDATOR); VALIDATORS.put(System.SCREEN_BRIGHTNESS_FOR_VR, new InclusiveIntegerRangeValidator(0, 255)); VALIDATORS.put(System.SCREEN_BRIGHTNESS_MODE, BOOLEAN_VALIDATOR); - VALIDATORS.put(System.ADAPTIVE_SLEEP, BOOLEAN_VALIDATOR); VALIDATORS.put(System.MODE_RINGER_STREAMS_AFFECTED, NON_NEGATIVE_INTEGER_VALIDATOR); VALIDATORS.put(System.MUTE_STREAMS_AFFECTED, NON_NEGATIVE_INTEGER_VALIDATOR); VALIDATORS.put(System.VIBRATE_ON, BOOLEAN_VALIDATOR); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 78b9f16ef355..d6776879254d 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1813,6 +1813,12 @@ class SettingsProtoDumpUtil { SecureSettingsProto.Accessibility.ACCESSIBILITY_MAGNIFICATION_MODE); p.end(accessibilityToken); + final long adaptiveSleepToken = p.start(SecureSettingsProto.ADAPTIVE_SLEEP); + dumpSetting(s, p, + Settings.Secure.ADAPTIVE_SLEEP, + SecureSettingsProto.AdaptiveSleep.ENABLED); + p.end(adaptiveSleepToken); + dumpSetting(s, p, Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS, SecureSettingsProto.ALLOWED_GEOLOCATION_ORIGINS); @@ -2727,6 +2733,12 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, SystemSettingsProto.Screen.AUTO_BRIGHTNESS_ADJ); + dumpSetting(s, p, + Settings.System.SCREEN_BRIGHTNESS_FLOAT, + SystemSettingsProto.Screen.BRIGHTNESS_FLOAT); + dumpSetting(s, p, + Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT, + SystemSettingsProto.Screen.BRIGHTNESS_FOR_VR_FLOAT); p.end(screenToken); dumpSetting(s, p, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index c969bfd193b5..aad46e9f0959 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -2193,7 +2193,7 @@ public class SettingsProvider extends ContentProvider { if (prefix == null) { return; } - String callingPackage = getCallingPackage(); + String callingPackage = resolveCallingPackage(); String namespace = prefix.replace("/", ""); if (DeviceConfig.getPublicNamespaces().contains(namespace)) { return; @@ -3426,7 +3426,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 187; + private static final int SETTINGS_VERSION = 188; private final int mUserId; @@ -4732,6 +4732,23 @@ public class SettingsProvider extends ContentProvider { currentVersion = 187; } + if (currentVersion == 187) { + // Migrate adaptive sleep setting from System to Secure. + if (userId == UserHandle.USER_OWNER) { + // Remove from the system settings. + SettingsState systemSettings = getSystemSettingsLocked(userId); + String name = Settings.System.ADAPTIVE_SLEEP; + Setting setting = systemSettings.getSettingLocked(name); + systemSettings.deleteSettingLocked(name); + + // Add to the secure settings. + SettingsState secureSettings = getSecureSettingsLocked(userId); + secureSettings.insertSettingLocked(name, setting.getValue(), null /* tag */, + false /* makeDefault */, SettingsState.SYSTEM_PACKAGE_NAME); + } + currentVersion = 188; + } + // vXXX: Add new settings above this point. if (currentVersion != newVersion) { diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index dee1d7eced66..bf817b1ccafd 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -99,7 +99,9 @@ public class SettingsBackupTest { Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug? Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only Settings.System.MIN_REFRESH_RATE, // depends on hardware capabilities - Settings.System.PEAK_REFRESH_RATE // depends on hardware capabilities + Settings.System.PEAK_REFRESH_RATE, // depends on hardware capabilities + Settings.System.SCREEN_BRIGHTNESS_FLOAT, + Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT ); private static final Set<String> BACKUP_BLACKLISTED_GLOBAL_SETTINGS = diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 5946f2158ac8..772f6e43c0ab 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -139,6 +139,7 @@ <uses-permission android:name="android.permission.GET_APP_OPS_STATS" /> <uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES" /> <uses-permission android:name="android.permission.VIBRATE" /> + <uses-permission android:name="android.permission.ACCESS_VIBRATOR_STATE" /> <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" /> <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" /> <uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" /> diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java index 0a2dd6c027de..bcff63471302 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java @@ -32,7 +32,7 @@ import java.io.PrintWriter; public interface FalsingManager { int VERSION = 3; - void onSucccessfulUnlock(); + void onSuccessfulUnlock(); void onNotificationActive(); diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml index d6855286142b..0dd3352b2df0 100644 --- a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml +++ b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml @@ -17,7 +17,7 @@ --> <!-- LinearLayout --> -<com.android.systemui.qs.tiles.UserDetailItemView +<com.android.systemui.statusbar.policy.KeyguardUserDetailItemView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:sysui="http://schemas.android.com/apk/res-auto" android:layout_width="wrap_content" @@ -34,7 +34,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="13dp" - android:textAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.UserName" + android:textAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher" /> <com.android.systemui.statusbar.phone.UserAvatarView android:id="@+id/user_picture" android:layout_width="@dimen/framed_avatar_size" @@ -47,4 +47,4 @@ sysui:badgeDiameter="18dp" sysui:badgeMargin="1dp" sysui:frameColor="@color/qs_user_detail_avatar_frame" /> -</com.android.systemui.qs.tiles.UserDetailItemView> +</com.android.systemui.statusbar.policy.KeyguardUserDetailItemView> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 0db7d67efd8d..ec56c1fe2c05 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -977,6 +977,9 @@ Equal to pip_action_size - pip_action_padding. --> <dimen name="pip_expand_container_edge_margin">30dp</dimen> + <!-- The touchable/draggable edge size for PIP resize. --> + <dimen name="pip_resize_edge_size">30dp</dimen> + <dimen name="default_gear_space">18dp</dimen> <dimen name="cell_overlay_padding">18dp</dimen> @@ -1225,4 +1228,6 @@ <!-- Screen Record --> <dimen name="screenrecord_dialog_padding">18dp</dimen> <dimen name="screenrecord_logo_size">24dp</dimen> + + <dimen name="kg_user_switcher_text_size">16sp</dimen> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 8f9e934c97e5..d596a5d4ecc1 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -487,6 +487,9 @@ <!-- Content description of the data connection type 5Ge. [CHAR LIMIT=NONE] --> <string name="data_connection_5ge" translatable="false">5Ge</string> + <!-- Content description of the data connection type 5Ge with HTML styling. DO NOT TRANSLATE [CHAR LIMIT=NONE] --> + <string name="data_connection_5ge_html" translate="false"> <i>5G <small>E</small></i> </string> + <!-- Content description of the data connection type 5G. [CHAR LIMIT=NONE] --> <string name="data_connection_5g" translatable="false">5G</string> @@ -1195,6 +1198,9 @@ <!-- The text for the manage notifications link. [CHAR LIMIT=40] --> <string name="manage_notifications_text">Manage</string> + <!-- The text for the notification history link. [CHAR LIMIT=40] --> + <string name="manage_notifications_history_text">History</string> + <!-- Section title for notifications that do not vibrate or make noise. [CHAR LIMIT=40] --> <string name="notification_section_header_gentle">Silent notifications</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index bcffa8d0feea..6fedac0a775d 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -129,11 +129,10 @@ parent="TextAppearance.StatusBar.Expanded.AboveDateTime" /> <style name="TextAppearance.StatusBar.Expanded.UserSwitcher"> - <item name="android:textSize">16sp</item> + <item name="android:textSize">@dimen/kg_user_switcher_text_size</item> <item name="android:textStyle">normal</item> <item name="android:textColor">?android:attr/textColorSecondary</item> </style> - <style name="TextAppearance.StatusBar.Expanded.UserSwitcher.UserName" /> <style name="TextAppearance" /> diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index e50d08c0cf9d..03bc738c8ab9 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -18,12 +18,14 @@ package com.android.systemui; import android.app.ActivityManager; import android.content.Context; +import android.content.res.Configuration; import android.graphics.Rect; import android.os.HandlerThread; import android.os.Trace; import android.service.wallpaper.WallpaperService; import android.util.Log; import android.util.Size; +import android.view.DisplayInfo; import android.view.SurfaceHolder; import com.android.internal.annotations.VisibleForTesting; @@ -93,14 +95,20 @@ public class ImageWallpaper extends WallpaperService { private StatusBarStateController mController; private final Runnable mFinishRenderingTask = this::finishRendering; private final boolean mNeedTransition; + private boolean mShouldStopTransition; + @VisibleForTesting + final boolean mIsHighEndGfx; + private final boolean mDisplayNeedsBlanking; + private final DisplayInfo mDisplayInfo = new DisplayInfo(); private final Object mMonitor = new Object(); private boolean mNeedRedraw; // This variable can only be accessed in synchronized block. private boolean mWaitingForRendering; GLEngine(Context context, DozeParameters dozeParameters) { - mNeedTransition = ActivityManager.isHighEndGfx() - && !dozeParameters.getDisplayNeedsBlanking(); + mIsHighEndGfx = ActivityManager.isHighEndGfx(); + mDisplayNeedsBlanking = dozeParameters.getDisplayNeedsBlanking(); + mNeedTransition = mIsHighEndGfx && !mDisplayNeedsBlanking; // We will preserve EGL context when we are in lock screen or aod // to avoid janking in following transition, we need to release when back to home. @@ -112,14 +120,23 @@ public class ImageWallpaper extends WallpaperService { @Override public void onCreate(SurfaceHolder surfaceHolder) { - mEglHelper = new EglHelper(); + mEglHelper = getEglHelperInstance(); // Deferred init renderer because we need to get wallpaper by display context. - mRenderer = new ImageWallpaperRenderer(getDisplayContext(), this /* SurfaceProxy */); + mRenderer = getRendererInstance(); + getDisplayContext().getDisplay().getDisplayInfo(mDisplayInfo); setFixedSizeAllowed(true); setOffsetNotificationsEnabled(true); updateSurfaceSize(); } + EglHelper getEglHelperInstance() { + return new EglHelper(); + } + + ImageWallpaperRenderer getRendererInstance() { + return new ImageWallpaperRenderer(getDisplayContext(), this /* SurfaceProxy */); + } + private void updateSurfaceSize() { SurfaceHolder holder = getSurfaceHolder(); Size frameSize = mRenderer.reportSurfaceSize(); @@ -128,6 +145,26 @@ public class ImageWallpaper extends WallpaperService { holder.setFixedSize(width, height); } + /** + * Check if necessary to stop transition with current wallpaper on this device. <br/> + * This should only be invoked after {@link #onSurfaceCreated(SurfaceHolder)}} + * is invoked since it needs display context and surface frame size. + * @return true if need to stop transition. + */ + @VisibleForTesting + boolean checkIfShouldStopTransition() { + int orientation = getDisplayContext().getResources().getConfiguration().orientation; + Rect frame = getSurfaceHolder().getSurfaceFrame(); + Rect display = new Rect(); + if (orientation == Configuration.ORIENTATION_PORTRAIT) { + display.set(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); + } else { + display.set(0, 0, mDisplayInfo.logicalHeight, mDisplayInfo.logicalWidth); + } + return mNeedTransition + && (frame.width() < display.width() || frame.height() < display.height()); + } + @Override public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep, float yOffsetStep, int xPixelOffset, int yPixelOffset) { @@ -137,12 +174,14 @@ public class ImageWallpaper extends WallpaperService { @Override public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) { if (!mNeedTransition) return; + final long duration = mShouldStopTransition ? 0 : animationDuration; if (DEBUG) { Log.d(TAG, "onAmbientModeChanged: inAmbient=" + inAmbientMode - + ", duration=" + animationDuration); + + ", duration=" + duration + + ", mShouldStopTransition=" + mShouldStopTransition); } mWorker.getThreadHandler().post( - () -> mRenderer.updateAmbientMode(inAmbientMode, animationDuration)); + () -> mRenderer.updateAmbientMode(inAmbientMode, duration)); if (inAmbientMode && animationDuration == 0) { // This means that we are transiting from home to aod, to avoid // race condition between window visibility and transition, @@ -183,6 +222,7 @@ public class ImageWallpaper extends WallpaperService { @Override public void onSurfaceCreated(SurfaceHolder holder) { + mShouldStopTransition = checkIfShouldStopTransition(); mWorker.getThreadHandler().post(() -> { mEglHelper.init(holder, needSupportWideColorGamut()); mRenderer.onSurfaceCreated(); @@ -348,15 +388,13 @@ public class ImageWallpaper extends WallpaperService { protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) { super.dump(prefix, fd, out, args); out.print(prefix); out.print("Engine="); out.println(this); - - boolean isHighEndGfx = ActivityManager.isHighEndGfx(); - out.print(prefix); out.print("isHighEndGfx="); out.println(isHighEndGfx); - + out.print(prefix); out.print("isHighEndGfx="); out.println(mIsHighEndGfx); out.print(prefix); out.print("displayNeedsBlanking="); - out.println( - mDozeParameters != null ? mDozeParameters.getDisplayNeedsBlanking() : "null"); - + out.println(mDisplayNeedsBlanking); + out.print(prefix); out.print("displayInfo="); out.print(mDisplayInfo); out.print(prefix); out.print("mNeedTransition="); out.println(mNeedTransition); + out.print(prefix); out.print("mShouldStopTransition="); + out.println(mShouldStopTransition); out.print(prefix); out.print("StatusBarState="); out.println(mController != null ? mController.getState() : "null"); diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java index 099909d000f9..e105795ad57f 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java @@ -35,7 +35,7 @@ public class FalsingManagerFake implements FalsingManager { private boolean mIsReportingEnabled; @Override - public void onSucccessfulUnlock() { + public void onSuccessfulUnlock() { } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java index d6faed5413d8..6a64c8386798 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java @@ -375,7 +375,7 @@ public class FalsingManagerImpl implements FalsingManager { sessionExitpoint(false /* force */); } - public void onSucccessfulUnlock() { + public void onSuccessfulUnlock() { if (FalsingLog.ENABLED) { FalsingLog.i("onSucccessfulUnlock", ""); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java index b2131e72be56..0db9ffc4f5bd 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java @@ -25,9 +25,12 @@ import android.provider.DeviceConfig; import android.util.DisplayMetrics; import android.view.MotionEvent; +import androidx.annotation.NonNull; + import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.Dependency; +import com.android.systemui.DumpController; +import com.android.systemui.Dumpable; import com.android.systemui.classifier.brightline.BrightLineFalsingManager; import com.android.systemui.classifier.brightline.FalsingDataProvider; import com.android.systemui.dagger.qualifiers.Main; @@ -40,6 +43,7 @@ import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.sensors.ProximitySensor; +import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.concurrent.Executor; @@ -52,7 +56,7 @@ import javax.inject.Singleton; * {@link FalsingManagerImpl} is used when a Plugin is not loaded. */ @Singleton -public class FalsingManagerProxy implements FalsingManager { +public class FalsingManagerProxy implements FalsingManager, Dumpable { private static final String PROXIMITY_SENSOR_TAG = "FalsingManager"; @@ -63,16 +67,20 @@ public class FalsingManagerProxy implements FalsingManager { private final DeviceConfigProxy mDeviceConfig; private boolean mBrightlineEnabled; private final DockManager mDockManager; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private Executor mUiBgExecutor; @Inject FalsingManagerProxy(Context context, PluginManager pluginManager, @Main Executor executor, DisplayMetrics displayMetrics, ProximitySensor proximitySensor, DeviceConfigProxy deviceConfig, DockManager dockManager, + KeyguardUpdateMonitor keyguardUpdateMonitor, + DumpController dumpController, @UiBackground Executor uiBgExecutor) { mDisplayMetrics = displayMetrics; mProximitySensor = proximitySensor; mDockManager = dockManager; + mKeyguardUpdateMonitor = keyguardUpdateMonitor; mUiBgExecutor = uiBgExecutor; mProximitySensor.setTag(PROXIMITY_SENSOR_TAG); mProximitySensor.setSensorDelay(SensorManager.SENSOR_DELAY_GAME); @@ -101,6 +109,8 @@ public class FalsingManagerProxy implements FalsingManager { }; pluginManager.addPluginListener(mPluginListener, FalsingPlugin.class); + + dumpController.registerDumpable("FalsingManager", this); } private void onDeviceConfigPropertiesChanged(Context context, String namespace) { @@ -130,7 +140,7 @@ public class FalsingManagerProxy implements FalsingManager { } else { mInternalFalsingManager = new BrightLineFalsingManager( new FalsingDataProvider(mDisplayMetrics), - Dependency.get(KeyguardUpdateMonitor.class), + mKeyguardUpdateMonitor, mProximitySensor, mDeviceConfig, mDockManager @@ -147,8 +157,8 @@ public class FalsingManagerProxy implements FalsingManager { } @Override - public void onSucccessfulUnlock() { - mInternalFalsingManager.onSucccessfulUnlock(); + public void onSuccessfulUnlock() { + mInternalFalsingManager.onSuccessfulUnlock(); } @Override @@ -322,6 +332,11 @@ public class FalsingManagerProxy implements FalsingManager { } @Override + public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { + mInternalFalsingManager.dump(pw); + } + + @Override public void dump(PrintWriter pw) { mInternalFalsingManager.dump(pw); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java index b2e61a2564d3..2f3e3364a50a 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java @@ -21,6 +21,7 @@ import static com.android.systemui.classifier.FalsingManagerImpl.FALSING_SUCCESS import android.hardware.biometrics.BiometricSourceType; import android.net.Uri; +import android.os.Build; import android.util.Log; import android.view.MotionEvent; @@ -40,15 +41,19 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Queue; +import java.util.StringJoiner; +import java.util.stream.Collectors; /** * FalsingManager designed to make clear why a touch was rejected. */ public class BrightLineFalsingManager implements FalsingManager { - static final boolean DEBUG = false; private static final String TAG = "FalsingManager"; - private static final int RECENT_INFO_LOG_SIZE = 20; + static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private static final int RECENT_INFO_LOG_SIZE = 40; + private static final int RECENT_SWIPE_LOG_SIZE = 20; private final FalsingDataProvider mDataProvider; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @@ -62,6 +67,8 @@ public class BrightLineFalsingManager implements FalsingManager { private boolean mJustUnlockedWithFace; private static final Queue<String> RECENT_INFO_LOG = new ArrayDeque<>(RECENT_INFO_LOG_SIZE + 1); + private static final Queue<DebugSwipeRecord> RECENT_SWIPES = + new ArrayDeque<>(RECENT_SWIPE_LOG_SIZE + 1); private final List<FalsingClassifier> mClassifiers; @@ -78,6 +85,7 @@ public class BrightLineFalsingManager implements FalsingManager { } } }; + private boolean mPreviousResult = false; public BrightLineFalsingManager(FalsingDataProvider falsingDataProvider, KeyguardUpdateMonitor keyguardUpdateMonitor, ProximitySensor proximitySensor, @@ -138,7 +146,7 @@ public class BrightLineFalsingManager implements FalsingManager { private void updateInteractionType(@Classifier.InteractionType int type) { logDebug("InteractionType: " + type); - mClassifiers.forEach((classifier) -> classifier.setInteractionType(type)); + mDataProvider.setInteractionType(type); } @Override @@ -148,7 +156,11 @@ public class BrightLineFalsingManager implements FalsingManager { @Override public boolean isFalseTouch() { - boolean r = !mJustUnlockedWithFace && !mDockManager.isDocked() + if (!mDataProvider.isDirty()) { + return mPreviousResult; + } + + mPreviousResult = !mJustUnlockedWithFace && !mDockManager.isDocked() && mClassifiers.stream().anyMatch(falsingClassifier -> { boolean result = falsingClassifier.isFalseTouch(); if (result) { @@ -167,9 +179,26 @@ public class BrightLineFalsingManager implements FalsingManager { return result; }); - logDebug("Is false touch? " + r); + logDebug("Is false touch? " + mPreviousResult); + + if (Build.IS_ENG || Build.IS_USERDEBUG) { + // Copy motion events, as the passed in list gets emptied out elsewhere in the code. + RECENT_SWIPES.add(new DebugSwipeRecord( + mPreviousResult, + mDataProvider.getInteractionType(), + mDataProvider.getRecentMotionEvents().stream().map( + motionEvent -> new XYDt( + (int) motionEvent.getX(), + (int) motionEvent.getY(), + (int) (motionEvent.getEventTime() - motionEvent.getDownTime()))) + .collect(Collectors.toList()))); + while (RECENT_SWIPES.size() > RECENT_INFO_LOG_SIZE) { + DebugSwipeRecord record = RECENT_SWIPES.remove(); + } + + } - return r; + return mPreviousResult; } @Override @@ -187,7 +216,7 @@ public class BrightLineFalsingManager implements FalsingManager { } @Override - public void onSucccessfulUnlock() { + public void onSuccessfulUnlock() { if (mIsFalseTouchCalls != 0) { mMetricsLogger.histogram(FALSING_SUCCESS, mIsFalseTouchCalls); mIsFalseTouchCalls = 0; @@ -212,7 +241,6 @@ public class BrightLineFalsingManager implements FalsingManager { @Override public void onNotificatonStartDraggingDown() { updateInteractionType(Classifier.NOTIFICATION_DRAG_DOWN); - } @Override @@ -235,7 +263,12 @@ public class BrightLineFalsingManager implements FalsingManager { } @Override - public void setQsExpanded(boolean b) { + public void setQsExpanded(boolean expanded) { + if (expanded) { + unregisterSensors(); + } else if (mSessionStarted) { + registerSensors(); + } } @Override @@ -338,25 +371,49 @@ public class BrightLineFalsingManager implements FalsingManager { @Override public void onBouncerShown() { + unregisterSensors(); } @Override public void onBouncerHidden() { + if (mSessionStarted) { + registerSensors(); + } } @Override public void dump(PrintWriter pw) { IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); ipw.println("BRIGHTLINE FALSING MANAGER"); - ipw.print("classifierEnabled="); pw.println(isClassifierEnabled() ? 1 : 0); - ipw.print("mJustUnlockedWithFace="); pw.println(mJustUnlockedWithFace ? 1 : 0); - ipw.print("isDocked="); pw.println(mDockManager.isDocked() ? 1 : 0); + ipw.print("classifierEnabled="); + ipw.println(isClassifierEnabled() ? 1 : 0); + ipw.print("mJustUnlockedWithFace="); + ipw.println(mJustUnlockedWithFace ? 1 : 0); + ipw.print("isDocked="); + ipw.println(mDockManager.isDocked() ? 1 : 0); + ipw.print("width="); + ipw.println(mDataProvider.getWidthPixels()); + ipw.print("height="); + ipw.println(mDataProvider.getHeightPixels()); + ipw.println(); + if (RECENT_SWIPES.size() != 0) { + ipw.println("Recent swipes:"); + ipw.increaseIndent(); + for (DebugSwipeRecord record : RECENT_SWIPES) { + ipw.println(record.getString()); + ipw.println(); + } + ipw.decreaseIndent(); + } else { + ipw.println("No recent swipes"); + } ipw.println(); ipw.println("Recent falsing info:"); ipw.increaseIndent(); for (String msg : RECENT_INFO_LOG) { ipw.println(msg); } + ipw.println(); } @Override @@ -386,4 +443,46 @@ public class BrightLineFalsingManager implements FalsingManager { static void logError(String msg) { Log.e(TAG, msg); } + + private static class DebugSwipeRecord { + private static final byte VERSION = 1; // opaque version number indicating format of data. + private final boolean mIsFalse; + private final int mInteractionType; + private final List<XYDt> mRecentMotionEvents; + + DebugSwipeRecord(boolean isFalse, int interactionType, + List<XYDt> recentMotionEvents) { + mIsFalse = isFalse; + mInteractionType = interactionType; + mRecentMotionEvents = recentMotionEvents; + } + + String getString() { + StringJoiner sj = new StringJoiner(","); + sj.add(Integer.toString(VERSION)) + .add(mIsFalse ? "1" : "0") + .add(Integer.toString(mInteractionType)); + for (XYDt event : mRecentMotionEvents) { + sj.add(event.toString()); + } + return sj.toString(); + } + } + + private static class XYDt { + private final int mX; + private final int mY; + private final int mDT; + + XYDt(int x, int y, int dT) { + mX = x; + mY = y; + mDT = dT; + } + + @Override + public String toString() { + return mX + "," + mY + "," + mDT; + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java index 7555051fac48..cf088213644e 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java @@ -85,10 +85,6 @@ abstract class FalsingClassifier { return mDataProvider.getInteractionType(); } - final void setInteractionType(@Classifier.InteractionType int interactionType) { - mDataProvider.setInteractionType(interactionType); - } - /** * Called whenever a MotionEvent occurs. * diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java index 8b11ceb34ea0..5494c644c22c 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java @@ -113,6 +113,10 @@ public class FalsingDataProvider { this.mInteractionType = interactionType; } + public boolean isDirty() { + return mDirty; + } + final int getInteractionType() { return mInteractionType; } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt new file mode 100644 index 000000000000..59eb00dd3f5a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.controls.ui + +import android.app.PendingIntent +import android.content.Intent +import android.service.controls.actions.BooleanAction +import android.util.Log +import android.view.HapticFeedbackConstants + +object ControlActionCoordinator { + public const val MIN_LEVEL = 0 + public const val MAX_LEVEL = 10000 + + fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) { + cvh.action(BooleanAction(templateId, !isChecked)) + + val nextLevel = if (isChecked) MIN_LEVEL else MAX_LEVEL + cvh.clipLayer.setLevel(nextLevel) + } + + fun longPress(cvh: ControlViewHolder) { + // Long press snould only be called when there is valid control state, otherwise ignore + cvh.cws.control?.let { + try { + cvh.layout.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) + it.getAppIntent().send() + val closeDialog = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS) + cvh.context.sendBroadcast(closeDialog) + } catch (e: PendingIntent.CanceledException) { + Log.e(ControlsUiController.TAG, "Error sending pending intent", e) + cvh.setTransientStatus("Error opening application") + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt index 78e0e8b81b44..55c1b6a1e369 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt @@ -17,7 +17,6 @@ package com.android.systemui.controls.ui import android.content.Context -import android.content.Intent import android.graphics.drawable.ClipDrawable import android.graphics.drawable.GradientDrawable import android.graphics.drawable.Icon @@ -37,8 +36,6 @@ import com.android.systemui.controls.controller.ControlsController import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.R -const val MIN_LEVEL = 0 -const val MAX_LEVEL = 10000 private const val UPDATE_DELAY_IN_MILLIS = 3000L class ControlViewHolder( @@ -79,12 +76,9 @@ class ControlViewHolder( Pair(Control.STATUS_UNKNOWN, ControlTemplate.NO_TEMPLATE) } - cws.control?.let { c -> + cws.control?.let { layout.setOnLongClickListener(View.OnLongClickListener() { - val closeDialog = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS) - context.sendBroadcast(closeDialog) - - c.getAppIntent().send() + ControlActionCoordinator.longPress(this@ControlViewHolder) true }) } @@ -100,17 +94,21 @@ class ControlViewHolder( } if (!text.isEmpty()) { - val previousText = status.getText() - val previousTextExtra = statusExtra.getText() + setTransientStatus(text) + } + } - cancelUpdate = uiExecutor.executeDelayed({ - status.setText(previousText) - statusExtra.setText(previousTextExtra) - }, UPDATE_DELAY_IN_MILLIS) + fun setTransientStatus(tempStatus: String) { + val previousText = status.getText() + val previousTextExtra = statusExtra.getText() - status.setText(text) - statusExtra.setText("") - } + cancelUpdate = uiExecutor.executeDelayed({ + status.setText(previousText) + statusExtra.setText(previousTextExtra) + }, UPDATE_DELAY_IN_MILLIS) + + status.setText(tempStatus) + statusExtra.setText("") } fun action(action: ControlAction) { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt index d70c86fc3266..0f105376847f 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt @@ -24,6 +24,10 @@ import android.view.ViewGroup interface ControlsUiController { val available: Boolean + companion object { + public const val TAG = "ControlsUiController" + } + fun show(parent: ViewGroup) fun hide() fun onRefreshState(componentName: ComponentName, controls: List<Control>) diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index f029dfbe1bb2..9e6636fca97d 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -51,8 +51,6 @@ import java.text.Collator import javax.inject.Inject import javax.inject.Singleton -private const val TAG = "ControlsUi" - // TEMP CODE for MOCK private const val TOKEN = "https://www.googleapis.com/auth/assistant" private const val SCOPE = "oauth2:" + TOKEN @@ -63,13 +61,13 @@ class TokenProviderConnection(val cc: ControlsController, val context: Context) override fun onServiceConnected(cName: ComponentName, binder: IBinder) { Thread({ - Log.i(TAG, "TokenProviderConnection connected") + Log.i(ControlsUiController.TAG, "TokenProviderConnection connected") mTokenProvider = TokenProvider.Stub.asInterface(binder) val mLastAccountName = mTokenProvider?.getAccountName() if (mLastAccountName == null || mLastAccountName.isEmpty()) { - Log.e(TAG, "NO ACCOUNT IS SET. Open HomeMock app") + Log.e(ControlsUiController.TAG, "NO ACCOUNT IS SET. Open HomeMock app") } else { mTokenProvider?.setAuthToken(getAuthToken(mLastAccountName)) cc.subscribeToFavorites() @@ -85,7 +83,7 @@ class TokenProviderConnection(val cc: ControlsController, val context: Context) val am = AccountManager.get(context) val accounts = am.getAccountsByType("com.google") if (accounts == null || accounts.size == 0) { - Log.w(TAG, "No com.google accounts found") + Log.w(ControlsUiController.TAG, "No com.google accounts found") return null } @@ -104,7 +102,7 @@ class TokenProviderConnection(val cc: ControlsController, val context: Context) try { return am.blockingGetAuthToken(account!!, SCOPE, true) } catch (e: Throwable) { - Log.e(TAG, "Error getting auth token", e) + Log.e(ControlsUiController.TAG, "Error getting auth token", e) return null } } @@ -146,7 +144,7 @@ class ControlsUiControllerImpl @Inject constructor ( } override fun show(parent: ViewGroup) { - Log.d(TAG, "show()") + Log.d(ControlsUiController.TAG, "show()") this.parent = parent @@ -218,7 +216,7 @@ class ControlsUiControllerImpl @Inject constructor ( val listView = parent.requireViewById(R.id.global_actions_controls_list) as ViewGroup var lastRow: ViewGroup = createRow(inflater, listView) controlInfos.forEach { - Log.d(TAG, "favorited control id: " + it.controlId) + Log.d(ControlsUiController.TAG, "favorited control id: " + it.controlId) if (lastRow.getChildCount() == 2) { lastRow = createRow(inflater, listView) } @@ -240,7 +238,7 @@ class ControlsUiControllerImpl @Inject constructor ( } override fun hide() { - Log.d(TAG, "hide()") + Log.d(ControlsUiController.TAG, "hide()") controlsController.get().unsubscribe() context.unbindService(tokenProviderConnection) tokenProviderConnection = null @@ -252,10 +250,10 @@ class ControlsUiControllerImpl @Inject constructor ( } override fun onRefreshState(componentName: ComponentName, controls: List<Control>) { - Log.d(TAG, "onRefreshState()") + Log.d(ControlsUiController.TAG, "onRefreshState()") controls.forEach { c -> controlsById.get(ControlKey(componentName, c.getControlId()))?.let { - Log.d(TAG, "onRefreshState() for id: " + c.getControlId()) + Log.d(ControlsUiController.TAG, "onRefreshState() for id: " + c.getControlId()) val cws = ControlWithState(it.ci, c) val key = ControlKey(componentName, c.getControlId()) controlsById.put(key, cws) diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt index ae0ebbb9e1bb..7661c6fb0bec 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt @@ -24,6 +24,8 @@ import android.service.controls.templates.TemperatureControlTemplate import android.widget.TextView import com.android.systemui.R +import com.android.systemui.controls.ui.ControlActionCoordinator.MIN_LEVEL +import com.android.systemui.controls.ui.ControlActionCoordinator.MAX_LEVEL class TemperatureControlBehavior : Behavior { lateinit var clipLayer: Drawable diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt index 7cd3ab795678..4c35d26c6281 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt @@ -22,10 +22,11 @@ import android.graphics.drawable.LayerDrawable import android.view.View import android.widget.TextView import android.service.controls.Control -import android.service.controls.actions.BooleanAction import android.service.controls.templates.ToggleTemplate import com.android.systemui.R +import com.android.systemui.controls.ui.ControlActionCoordinator.MIN_LEVEL +import com.android.systemui.controls.ui.ControlActionCoordinator.MAX_LEVEL class ToggleBehavior : Behavior { lateinit var clipLayer: Drawable @@ -42,13 +43,15 @@ class ToggleBehavior : Behavior { status.setText(control.getStatusText()) - cvh.layout.setOnClickListener(View.OnClickListener() { toggle() }) + template = control.getControlTemplate() as ToggleTemplate + + cvh.layout.setOnClickListener(View.OnClickListener() { + ControlActionCoordinator.toggle(cvh, template.getTemplateId(), template.isChecked()) + }) val ld = cvh.layout.getBackground() as LayerDrawable clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) - template = control.getControlTemplate() as ToggleTemplate - val checked = template.isChecked() val deviceType = control.getDeviceType() @@ -56,11 +59,4 @@ class ToggleBehavior : Behavior { cvh.setEnabled(checked) cvh.applyRenderInfo(RenderInfo.lookup(deviceType, checked)) } - - fun toggle() { - cvh.action(BooleanAction(template.getTemplateId(), !template.isChecked())) - - val nextLevel = if (template.isChecked()) MIN_LEVEL else MAX_LEVEL - clipLayer.setLevel(nextLevel) - } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt index a6918f50a977..8d65ca344d17 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt @@ -19,17 +19,23 @@ package com.android.systemui.controls.ui import android.content.Context import android.graphics.drawable.Drawable import android.graphics.drawable.LayerDrawable +import android.util.Log +import android.view.GestureDetector +import android.view.GestureDetector.SimpleOnGestureListener import android.view.MotionEvent import android.view.View import android.widget.TextView import android.service.controls.Control -import android.service.controls.actions.BooleanAction import android.service.controls.actions.FloatAction import android.service.controls.templates.RangeTemplate import android.service.controls.templates.ToggleRangeTemplate import android.util.TypedValue import com.android.systemui.R +import com.android.systemui.controls.ui.ControlActionCoordinator.MIN_LEVEL +import com.android.systemui.controls.ui.ControlActionCoordinator.MAX_LEVEL + +import java.util.IllegalFormatException class ToggleRangeBehavior : Behavior { lateinit var clipLayer: Drawable @@ -41,6 +47,10 @@ class ToggleRangeBehavior : Behavior { lateinit var status: TextView lateinit var context: Context + companion object { + private const val DEFAULT_FORMAT = "%.1f" + } + override fun apply(cvh: ControlViewHolder, cws: ControlWithState) { this.control = cws.control!! this.cvh = cvh @@ -52,10 +62,25 @@ class ToggleRangeBehavior : Behavior { context = status.getContext() - cvh.layout.setOnTouchListener(ToggleRangeTouchListener()) + val gestureListener = ToggleRangeGestureListener(cvh.layout) + val gestureDetector = GestureDetector(context, gestureListener) + cvh.layout.setOnTouchListener({ v: View, e: MotionEvent -> + if (gestureDetector.onTouchEvent(e)) { + return@setOnTouchListener true + } + + if (e.getAction() == MotionEvent.ACTION_UP && gestureListener.isDragging) { + gestureListener.isDragging = false + endUpdateRange() + return@setOnTouchListener true + } + + return@setOnTouchListener false + }) val ld = cvh.layout.getBackground() as LayerDrawable clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) + clipLayer.setLevel(MIN_LEVEL) template = control.getControlTemplate() as ToggleRangeTemplate rangeTemplate = template.getRange() @@ -63,30 +88,31 @@ class ToggleRangeBehavior : Behavior { val checked = template.isChecked() val deviceType = control.getDeviceType() - updateRange((rangeTemplate.getCurrentValue() / 100.0f), checked) + val currentRatio = rangeTemplate.getCurrentValue() / + (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue()) + updateRange(currentRatio, checked) cvh.setEnabled(checked) cvh.applyRenderInfo(RenderInfo.lookup(deviceType, checked)) } - fun toggle() { - cvh.action(BooleanAction(template.getTemplateId(), !template.isChecked())) - - val nextLevel = if (template.isChecked()) MIN_LEVEL else MAX_LEVEL - clipLayer.setLevel(nextLevel) - } - fun beginUpdateRange() { status.setVisibility(View.GONE) statusExtra.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources() .getDimensionPixelSize(R.dimen.control_status_expanded).toFloat()) } - fun updateRange(f: Float, checked: Boolean) { - clipLayer.setLevel(if (checked) (MAX_LEVEL * f).toInt() else MIN_LEVEL) + fun updateRange(ratioDiff: Float, checked: Boolean) { + val changeAmount = if (checked) (MAX_LEVEL * ratioDiff).toInt() else MIN_LEVEL + val newLevel = Math.max(MIN_LEVEL, Math.min(MAX_LEVEL, clipLayer.getLevel() + changeAmount)) + clipLayer.setLevel(newLevel) + + if (checked) { + val newValue = levelToRangeValue() + val formattedNewValue = format(rangeTemplate.getFormatString().toString(), + DEFAULT_FORMAT, newValue) - if (checked && f < 100.0f && f > 0.0f) { - statusExtra.setText("" + (f * 100.0).toInt() + "%") + statusExtra.setText(formattedNewValue) statusExtra.setVisibility(View.VISIBLE) } else { statusExtra.setText("") @@ -94,17 +120,30 @@ class ToggleRangeBehavior : Behavior { } } - fun endUpdateRange(f: Float) { - statusExtra.setText(" - " + (f * 100.0).toInt() + "%") + private fun format(primaryFormat: String, backupFormat: String, value: Float): String { + return try { + String.format(primaryFormat, value) + } catch (e: IllegalFormatException) { + Log.w(ControlsUiController.TAG, "Illegal format in range template", e) + if (backupFormat == "") { + "" + } else { + format(backupFormat, "", value) + } + } + } - val newValue = rangeTemplate.getMinValue() + - (f * (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue())) + private fun levelToRangeValue(): Float { + val ratio = clipLayer.getLevel().toFloat() / MAX_LEVEL + return rangeTemplate.getMinValue() + + (ratio * (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue())) + } + fun endUpdateRange() { statusExtra.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources() .getDimensionPixelSize(R.dimen.control_status_normal).toFloat()) status.setVisibility(View.VISIBLE) - - cvh.action(FloatAction(rangeTemplate.getTemplateId(), findNearestStep(newValue))) + cvh.action(FloatAction(rangeTemplate.getTemplateId(), findNearestStep(levelToRangeValue()))) } fun findNearestStep(value: Float): Float { @@ -125,59 +164,39 @@ class ToggleRangeBehavior : Behavior { return rangeTemplate.getMaxValue() } - inner class ToggleRangeTouchListener() : View.OnTouchListener { - private var initialTouchX: Float = 0.0f - private var initialTouchY: Float = 0.0f - private var isDragging: Boolean = false - private val minDragDiff = 20 - - override fun onTouch(v: View, e: MotionEvent): Boolean { - when (e.getActionMasked()) { - MotionEvent.ACTION_DOWN -> setupTouch(e) - MotionEvent.ACTION_MOVE -> detectDrag(v, e) - MotionEvent.ACTION_UP -> endTouch(v, e) - } + inner class ToggleRangeGestureListener( + val v: View + ) : SimpleOnGestureListener() { + var isDragging: Boolean = false + override fun onDown(e: MotionEvent): Boolean { return true } - private fun setupTouch(e: MotionEvent) { - initialTouchX = e.getX() - initialTouchY = e.getY() + override fun onLongPress(e: MotionEvent) { + ControlActionCoordinator.longPress(this@ToggleRangeBehavior.cvh) } - private fun detectDrag(v: View, e: MotionEvent) { - val xDiff = Math.abs(e.getX() - initialTouchX) - val yDiff = Math.abs(e.getY() - initialTouchY) - - if (xDiff < minDragDiff) { - isDragging = false - } else { - if (!isDragging) { - this@ToggleRangeBehavior.beginUpdateRange() - } - v.getParent().requestDisallowInterceptTouchEvent(true) + override fun onScroll( + e1: MotionEvent, + e2: MotionEvent, + xDiff: Float, + yDiff: Float + ): Boolean { + if (!isDragging) { + this@ToggleRangeBehavior.beginUpdateRange() isDragging = true - if (yDiff > xDiff) { - endTouch(v, e) - } else { - val percent = Math.max(0.0f, Math.min(1.0f, e.getX() / v.getWidth())) - this@ToggleRangeBehavior.updateRange(percent, true) - } } - } - private fun endTouch(v: View, e: MotionEvent) { - if (!isDragging) { - this@ToggleRangeBehavior.toggle() - } else { - val percent = Math.max(0.0f, Math.min(1.0f, e.getX() / v.getWidth())) - this@ToggleRangeBehavior.endUpdateRange(percent) - } + this@ToggleRangeBehavior.updateRange(-xDiff / v.getWidth(), true) + return true + } - initialTouchX = 0.0f - initialTouchY = 0.0f - isDragging = false + override fun onSingleTapUp(e: MotionEvent): Boolean { + val th = this@ToggleRangeBehavior + ControlActionCoordinator.toggle(th.cvh, th.template.getTemplateId(), + th.template.isChecked()) + return true } } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index 0ec739fecaa7..bf501ced6293 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -26,8 +26,10 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.ServiceManager; import android.util.DisplayMetrics; +import android.view.Choreographer; import android.view.IWindowManager; import android.view.LayoutInflater; +import android.view.WindowManager; import com.android.internal.logging.MetricsLogger; import com.android.internal.util.NotificationMessagingUtil; @@ -98,10 +100,10 @@ public class DependencyProvider { @Singleton @Provides - // Single instance of DisplayMetrics, gets updated by StatusBar, but can be used - // anywhere it is needed. - public DisplayMetrics provideDisplayMetrics() { - return new DisplayMetrics(); + public DisplayMetrics provideDisplayMetrics(Context context, WindowManager windowManager) { + DisplayMetrics displayMetrics = new DisplayMetrics(); + context.getDisplay().getMetrics(displayMetrics); + return displayMetrics; } /** */ @@ -203,4 +205,11 @@ public class DependencyProvider { public ViewMediatorCallback providesViewMediatorCallback(KeyguardViewMediator viewMediator) { return viewMediator.getViewMediatorCallback(); } + + /** */ + @Singleton + @Provides + public Choreographer providesChoreographer() { + return Choreographer.getInstance(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index f068d9c10e86..91aeb224e143 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -19,14 +19,13 @@ package com.android.systemui.dagger; import android.annotation.Nullable; import android.content.Context; import android.content.pm.PackageManager; -import android.content.res.Resources; +import android.view.Choreographer; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.BootCompleteCache; import com.android.systemui.BootCompleteCacheImpl; import com.android.systemui.DumpController; import com.android.systemui.assist.AssistModule; -import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.log.dagger.LogModule; import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -34,18 +33,22 @@ import com.android.systemui.recents.Recents; import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.BlurUtils; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.StatusBarWindowBlurController; +import com.android.systemui.statusbar.NotificationShadeWindowBlurController; import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.dagger.NotificationsModule; import com.android.systemui.statusbar.notification.people.PeopleHubModule; import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent; import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent; +import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.KeyguardLiftController; +import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.ConcurrencyModule; import com.android.systemui.util.sensors.AsyncSensorManager; import com.android.systemui.util.time.SystemClock; @@ -100,11 +103,16 @@ public abstract class SystemUIModule { @Singleton @Provides @Nullable - static StatusBarWindowBlurController providesBlurController(BlurUtils blurUtils, - @Main Resources resources, SysuiStatusBarStateController statusBarStateController, - DumpController dumpController) { - return blurUtils.supportsBlursOnWindows() ? new StatusBarWindowBlurController(resources, - statusBarStateController, blurUtils, dumpController) : null; + static NotificationShadeWindowBlurController providesBlurController(BlurUtils blurUtils, + SysuiStatusBarStateController statusBarStateController, + DumpController dumpController, BiometricUnlockController biometricUnlockController, + KeyguardStateController keyguardStateController, + NotificationShadeWindowController notificationShadeWindowController, + Choreographer choreographer) { + return blurUtils.supportsBlursOnWindows() ? new NotificationShadeWindowBlurController( + statusBarStateController, blurUtils, biometricUnlockController, + keyguardStateController, notificationShadeWindowController, choreographer, + dumpController) : null; } /** */ diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java index 4e887262659e..c45063a52f78 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java @@ -167,6 +167,10 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi // again, it will only show after the brightness sensor has stabilized, // avoiding a potential flicker. scrimOpacity = 255; + } else if (!mScreenOff && mLightSensor == null) { + // No light sensor but previous state turned the screen black. Make the scrim + // transparent and below views visible. + scrimOpacity = 0; } else if (brightnessReady) { // Only unblank scrim once brightness is ready. scrimOpacity = computeScrimOpacity(sensorValue); @@ -199,7 +203,7 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi mDozeService.setDozeScreenBrightness(clampToUserSetting(mDefaultDozeBrightness)); mDozeHost.setAodDimmingScrim(0f); } - + //TODO: brightnessfloat change usages to float. private int clampToUserSetting(int brightness) { int userSetting = Settings.System.getIntForUser(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, Integer.MAX_VALUE, diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index a3cd5fdd771b..d298b992d418 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -101,6 +101,7 @@ import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; import com.android.systemui.plugins.GlobalActionsPanelPlugin; import com.android.systemui.statusbar.BlurUtils; import com.android.systemui.statusbar.phone.NotificationShadeWindowController; +import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.EmergencyDialerConstants; @@ -1668,7 +1669,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } if (mBackgroundDrawable == null) { mBackgroundDrawable = new ScrimDrawable(); - mScrimAlpha = 0.8f; + mScrimAlpha = ScrimController.BUSY_SCRIM_ALPHA; } getWindow().setBackgroundDrawable(mBackgroundDrawable); } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java index 146f36bcd04f..280a24870ef3 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java @@ -149,7 +149,7 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks } if (mBlurUtils.supportsBlursOnWindows()) { - background.setAlpha((int) (ScrimController.GRADIENT_SCRIM_ALPHA_BUSY * 255)); + background.setAlpha((int) (ScrimController.BUSY_SCRIM_ALPHA * 255)); mBlurUtils.applyBlur(d.getWindow().getDecorView().getViewRootImpl(), mBlurUtils.radiusForRatio(1)); } else { diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java index fa8269d1c198..ed6675dcba2e 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java @@ -31,7 +31,6 @@ import android.util.Log; import android.util.MathUtils; import android.util.Size; import android.view.DisplayInfo; -import android.view.WindowManager; import com.android.systemui.R; @@ -71,8 +70,7 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer, } DisplayInfo displayInfo = new DisplayInfo(); - WindowManager wm = context.getSystemService(WindowManager.class); - wm.getDefaultDisplay().getDisplayInfo(displayInfo); + context.getDisplay().getDisplayInfo(displayInfo); // We only do transition in portrait currently, b/137962047. int orientation = context.getResources().getConfiguration().orientation; @@ -88,6 +86,10 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer, mImageProcessHelper = new ImageProcessHelper(); mImageRevealHelper = new ImageRevealHelper(this); + startProcessingImage(); + } + + protected void startProcessingImage() { if (loadBitmap()) { // Compute threshold of the image, this is an async work. mImageProcessHelper.start(mBitmap); @@ -113,7 +115,7 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer, mBitmap = null; } - private boolean loadBitmap() { + protected boolean loadBitmap() { if (DEBUG) { Log.d(TAG, "loadBitmap: mBitmap=" + mBitmap); } @@ -122,12 +124,7 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer, mWcgContent = mWallpaperManager.wallpaperSupportsWcg(WallpaperManager.FLAG_SYSTEM); mWallpaperManager.forgetLoadedWallpaper(); if (mBitmap != null) { - float scale = (float) mScissor.height() / mBitmap.getHeight(); - int surfaceHeight = Math.max(mScissor.height(), mBitmap.getHeight()); - int surfaceWidth = scale > 1f - ? Math.round(mBitmap.getWidth() * scale) - : mBitmap.getWidth(); - mSurfaceSize.set(0, 0, surfaceWidth, surfaceHeight); + mSurfaceSize.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); } } if (DEBUG) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 9da99c453022..83a6d7528dbb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -1651,7 +1651,7 @@ public class KeyguardViewMediator extends SystemUI { "KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM"); StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj; handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration); - mFalsingManager.onSucccessfulUnlock(); + mFalsingManager.onSuccessfulUnlock(); Trace.endSection(); break; case KEYGUARD_DONE_PENDING_TIMEOUT: diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java index 6f03f18ef64b..41b31306a931 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java @@ -345,6 +345,14 @@ public class PipBoundsHandler { } /** + * Sets the current bound with the currently store aspect ratio. + * @param stackBounds + */ + public void transformBoundsToAspectRatio(Rect stackBounds) { + transformBoundsToAspectRatio(stackBounds, mAspectRatio, true); + } + + /** * Set the current bounds (or the default bounds if there are no current bounds) with the * specified aspect ratio. */ diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java new file mode 100644 index 000000000000..9fb623471bf7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.pip.phone; + +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_USER_RESIZE; +import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM; +import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT; +import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE; +import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT; +import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Point; +import android.graphics.PointF; +import android.graphics.Rect; +import android.graphics.Region; +import android.hardware.input.InputManager; +import android.os.Looper; +import android.provider.DeviceConfig; +import android.util.DisplayMetrics; +import android.view.InputChannel; +import android.view.InputEvent; +import android.view.InputEventReceiver; +import android.view.InputMonitor; +import android.view.MotionEvent; + +import com.android.internal.policy.TaskResizingAlgorithm; +import com.android.systemui.R; +import com.android.systemui.pip.PipBoundsHandler; + +import java.util.concurrent.Executor; + +/** + * Helper on top of PipTouchHandler that handles inputs OUTSIDE of the PIP window, which is used to + * trigger dynamic resize. + */ +public class PipResizeGestureHandler { + + private static final String TAG = "PipResizeGestureHandler"; + + private final DisplayMetrics mDisplayMetrics = new DisplayMetrics(); + private final PipBoundsHandler mPipBoundsHandler; + private final PipTouchHandler mPipTouchHandler; + private final PipMotionHelper mMotionHelper; + private final int mDisplayId; + private final Executor mMainExecutor; + private final Region mTmpRegion = new Region(); + + private final PointF mDownPoint = new PointF(); + private final Point mMaxSize = new Point(); + private final Point mMinSize = new Point(); + private final Rect mTmpBounds = new Rect(); + private final int mDelta; + + private boolean mAllowGesture = false; + private boolean mIsAttached; + private boolean mIsEnabled; + private boolean mEnablePipResize; + + private InputMonitor mInputMonitor; + private InputEventReceiver mInputEventReceiver; + + private int mCtrlType; + + public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler, + PipTouchHandler pipTouchHandler, PipMotionHelper motionHelper) { + final Resources res = context.getResources(); + context.getDisplay().getMetrics(mDisplayMetrics); + mDisplayId = context.getDisplayId(); + mMainExecutor = context.getMainExecutor(); + mPipBoundsHandler = pipBoundsHandler; + mPipTouchHandler = pipTouchHandler; + mMotionHelper = motionHelper; + + context.getDisplay().getRealSize(mMaxSize); + mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size); + + mEnablePipResize = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_SYSTEMUI, + PIP_USER_RESIZE, + /* defaultValue = */ false); + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mMainExecutor, + new DeviceConfig.OnPropertiesChangedListener() { + @Override + public void onPropertiesChanged(DeviceConfig.Properties properties) { + if (properties.getKeyset().contains(PIP_USER_RESIZE)) { + mEnablePipResize = properties.getBoolean( + PIP_USER_RESIZE, /* defaultValue = */ false); + } + } + }); + } + + private void disposeInputChannel() { + if (mInputEventReceiver != null) { + mInputEventReceiver.dispose(); + mInputEventReceiver = null; + } + if (mInputMonitor != null) { + mInputMonitor.dispose(); + mInputMonitor = null; + } + } + + void onActivityPinned() { + mIsAttached = true; + updateIsEnabled(); + } + + void onActivityUnpinned() { + mIsAttached = false; + updateIsEnabled(); + } + + private void updateIsEnabled() { + boolean isEnabled = mIsAttached && mEnablePipResize; + if (isEnabled == mIsEnabled) { + return; + } + mIsEnabled = isEnabled; + disposeInputChannel(); + + if (mIsEnabled) { + // Register input event receiver + mInputMonitor = InputManager.getInstance().monitorGestureInput( + "pip-resize", mDisplayId); + mInputEventReceiver = new SysUiInputEventReceiver( + mInputMonitor.getInputChannel(), Looper.getMainLooper()); + } + } + + private void onInputEvent(InputEvent ev) { + if (ev instanceof MotionEvent) { + onMotionEvent((MotionEvent) ev); + } + } + + private boolean isWithinTouchRegion(int x, int y) { + final Rect currentPipBounds = mMotionHelper.getBounds(); + if (currentPipBounds == null) { + return false; + } + + mTmpBounds.set(currentPipBounds); + mTmpBounds.inset(-mDelta, -mDelta); + + mTmpRegion.set(mTmpBounds); + mTmpRegion.op(currentPipBounds, Region.Op.DIFFERENCE); + + if (mTmpRegion.contains(x, y)) { + if (x < currentPipBounds.left) { + mCtrlType |= CTRL_LEFT; + } + if (x > currentPipBounds.right) { + mCtrlType |= CTRL_RIGHT; + } + if (y < currentPipBounds.top) { + mCtrlType |= CTRL_TOP; + } + if (y > currentPipBounds.bottom) { + mCtrlType |= CTRL_BOTTOM; + } + return true; + } + return false; + } + + private void onMotionEvent(MotionEvent ev) { + int action = ev.getActionMasked(); + if (action == MotionEvent.ACTION_DOWN) { + mAllowGesture = isWithinTouchRegion((int) ev.getX(), (int) ev.getY()); + if (mAllowGesture) { + mDownPoint.set(ev.getX(), ev.getY()); + } + + } else if (mAllowGesture) { + final Rect currentPipBounds = mMotionHelper.getBounds(); + Rect newSize = TaskResizingAlgorithm.resizeDrag(ev.getX(), ev.getY(), mDownPoint.x, + mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x, mMinSize.y, mMaxSize, + true, true); + mPipBoundsHandler.transformBoundsToAspectRatio(newSize); + switch (action) { + case MotionEvent.ACTION_POINTER_DOWN: + // We do not support multi touch for resizing via drag + mAllowGesture = false; + break; + case MotionEvent.ACTION_MOVE: + // Capture inputs + mInputMonitor.pilferPointers(); + //TODO: Actually do resize here. + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + //TODO: Finish resize operation here. + mMotionHelper.synchronizePinnedStackBounds(); + mCtrlType = CTRL_NONE; + mAllowGesture = false; + break; + } + } + } + + void updateMaxSize(int maxX, int maxY) { + mMaxSize.set(maxX, maxY); + } + + void updateMiniSize(int minX, int minY) { + mMinSize.set(minX, minY); + } + + class SysUiInputEventReceiver extends InputEventReceiver { + SysUiInputEventReceiver(InputChannel channel, Looper looper) { + super(channel, looper); + } + + public void onInputEvent(InputEvent event) { + PipResizeGestureHandler.this.onInputEvent(event); + finishInputEvent(event, true); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 65cc666d5164..924edb6fe312 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -73,6 +73,7 @@ public class PipTouchHandler { private final ViewConfiguration mViewConfig; private final PipMenuListener mMenuListener = new PipMenuListener(); private final PipBoundsHandler mPipBoundsHandler; + private final PipResizeGestureHandler mPipResizeGestureHandler; private IPinnedStackController mPinnedStackController; private final PipMenuActivityController mMenuController; @@ -188,6 +189,8 @@ public class PipTouchHandler { mGesture = new DefaultPipTouchGesture(); mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mActivityTaskManager, mMenuController, mSnapAlgorithm, mFlingAnimationUtils); + mPipResizeGestureHandler = + new PipResizeGestureHandler(context, pipBoundsHandler, this, mMotionHelper); mTouchState = new PipTouchState(mViewConfig, mHandler, () -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(), mMovementBounds, true /* allowMenuTimeout */, willResizeMenu())); @@ -227,6 +230,7 @@ public class PipTouchHandler { public void onActivityPinned() { cleanUp(); mShowPipMenuOnAnimationEnd = true; + mPipResizeGestureHandler.onActivityPinned(); } public void onActivityUnpinned(ComponentName topPipActivity) { @@ -234,11 +238,14 @@ public class PipTouchHandler { // Clean up state after the last PiP activity is removed cleanUp(); } + mPipResizeGestureHandler.onActivityUnpinned(); } public void onPinnedStackAnimationEnded() { // Always synchronize the motion helper bounds once PiP animations finish mMotionHelper.synchronizePinnedStackBounds(); + mPipResizeGestureHandler.updateMiniSize(mMotionHelper.getBounds().width(), + mMotionHelper.getBounds().height()); if (mShowPipMenuOnAnimationEnd) { mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(), @@ -279,6 +286,7 @@ public class PipTouchHandler { Size expandedSize = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, mExpandedShortestEdgeSize, displaySize.x, displaySize.y); mExpandedBounds.set(0, 0, expandedSize.getWidth(), expandedSize.getHeight()); + mPipResizeGestureHandler.updateMaxSize(expandedSize.getWidth(), expandedSize.getHeight()); Rect expandedMovementBounds = new Rect(); mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds, expandedMovementBounds, bottomOffset); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java index 86fccd7f9d52..eb5b4cc4c0df 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java @@ -69,8 +69,10 @@ public class QSCarrierGroupController { @Override public void setMobileDataIndicators(NetworkController.IconState statusIcon, NetworkController.IconState qsIcon, int statusType, int qsType, - boolean activityIn, boolean activityOut, String typeContentDescription, - String description, boolean isWide, int subId, boolean roaming) { + boolean activityIn, boolean activityOut, + CharSequence typeContentDescription, + CharSequence typeContentDescriptionHtml, CharSequence description, + boolean isWide, int subId, boolean roaming) { int slotIndex = getSlotIndex(subId); if (slotIndex >= SIM_SLOTS) { Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex); @@ -83,7 +85,7 @@ public class QSCarrierGroupController { mInfos[slotIndex].visible = statusIcon.visible; mInfos[slotIndex].mobileSignalIconId = statusIcon.icon; mInfos[slotIndex].contentDescription = statusIcon.contentDescription; - mInfos[slotIndex].typeContentDescription = typeContentDescription; + mInfos[slotIndex].typeContentDescription = typeContentDescription.toString(); mInfos[slotIndex].roaming = roaming; mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 14a117ca40a1..b3d96f8fc29f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -38,7 +38,6 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.FrameLayout; import android.widget.HorizontalScrollView; import android.widget.LinearLayout; @@ -60,7 +59,6 @@ import com.android.systemui.qs.customize.QSCustomizer; import com.android.systemui.qs.external.CustomTile; import com.android.systemui.settings.BrightnessController; import com.android.systemui.settings.ToggleSliderView; -import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.policy.BrightnessMirrorController; import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener; import com.android.systemui.tuner.TunerService; @@ -117,9 +115,6 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne private BrightnessMirrorController mBrightnessMirrorController; private View mDivider; - private FrameLayout mPluginFrame; - private final PluginManager mPluginManager; - private final LocalMediaManager.DeviceCallback mDeviceCallback = new LocalMediaManager.DeviceCallback() { @Override @@ -154,13 +149,12 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne } public QSPanel(Context context, AttributeSet attrs, DumpController dumpController) { - this(context, attrs, dumpController, null, Dependency.get(BroadcastDispatcher.class)); + this(context, attrs, dumpController, Dependency.get(BroadcastDispatcher.class)); } @Inject public QSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, - DumpController dumpController, PluginManager pluginManager, - BroadcastDispatcher broadcastDispatcher) { + DumpController dumpController, BroadcastDispatcher broadcastDispatcher) { super(context, attrs); mContext = context; @@ -198,7 +192,6 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne mBrightnessController = new BrightnessController(getContext(), findViewById(R.id.brightness_slider), broadcastDispatcher); mDumpController = dumpController; - mPluginManager = pluginManager; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java index d5f86c951407..436869d5ef88 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java @@ -26,6 +26,7 @@ import android.content.res.Resources; import android.provider.Settings; import android.service.quicksettings.Tile; import android.telephony.SubscriptionManager; +import android.text.Html; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; @@ -205,12 +206,13 @@ public class CellularTile extends QSTileImpl<SignalState> { private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) { if (TextUtils.isEmpty(dataType)) { - return current; + return Html.fromHtml(current.toString(), 0); } if (TextUtils.isEmpty(current)) { - return dataType; + return Html.fromHtml(dataType.toString(), 0); } - return mContext.getString(R.string.mobile_carrier_text_format, current, dataType); + String concat = mContext.getString(R.string.mobile_carrier_text_format, current, dataType); + return Html.fromHtml(concat, 0); } private CharSequence getMobileDataContentName(CallbackInfo cb) { @@ -251,14 +253,17 @@ public class CellularTile extends QSTileImpl<SignalState> { @Override public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType, - int qsType, boolean activityIn, boolean activityOut, String typeContentDescription, - String description, boolean isWide, int subId, boolean roaming) { + int qsType, boolean activityIn, boolean activityOut, + CharSequence typeContentDescription, + CharSequence typeContentDescriptionHtml, CharSequence description, + boolean isWide, int subId, boolean roaming) { if (qsIcon == null) { // Not data sim, don't display. return; } mInfo.dataSubscriptionName = mController.getMobileDataNetworkName(); - mInfo.dataContentDescription = (description != null) ? typeContentDescription : null; + mInfo.dataContentDescription = + (description != null) ? typeContentDescriptionHtml : null; mInfo.activityIn = activityIn; mInfo.activityOut = activityOut; mInfo.roaming = roaming; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java index 64fe54aa656d..7d4343c9b583 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java @@ -128,7 +128,7 @@ public class UserDetailItemView extends LinearLayout { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - FontSizeUtils.updateFontSize(mName, R.dimen.qs_detail_item_secondary_text_size); + FontSizeUtils.updateFontSize(mName, getFontSizeDimen()); } @Override @@ -146,4 +146,8 @@ public class UserDetailItemView extends LinearLayout { public boolean hasOverlappingRendering() { return false; } + + protected int getFontSizeDimen() { + return R.dimen.qs_detail_item_secondary_text_size; + } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index 4f38a15824a4..197fe2125bc0 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -442,27 +442,35 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset ValueAnimator screenshotDropInAnim = screenRect != null ? createRectAnimation(screenRect) : createScreenshotDropInAnimation(); - ValueAnimator screenshotFadeOutAnim = createScreenshotToCornerAnimation(w, h); + ValueAnimator screenshotToCornerAnimation = createScreenshotToCornerAnimation(w, h); mScreenshotAnimation = new AnimatorSet(); - mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim); - mScreenshotAnimation.addListener(new AnimatorListenerAdapter() { + mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotToCornerAnimation); + + saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() { @Override - public void onAnimationEnd(Animator animation) { - // Save the screenshot once we have a bit of time now - saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() { - @Override - void onActionsReady(Uri uri, List<Notification.Action> smartActions, - List<Notification.Action> actions) { - if (uri == null) { - mNotificationsController.notifyScreenshotError( - R.string.screenshot_failed_to_capture_text); + void onActionsReady(Uri uri, List<Notification.Action> smartActions, + List<Notification.Action> actions) { + if (uri == null) { + mNotificationsController.notifyScreenshotError( + R.string.screenshot_failed_to_capture_text); + } else { + mScreenshotHandler.post(() -> { + if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) { + mScreenshotAnimation.addListener( + new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + createScreenshotActionsShadeAnimation( + smartActions, actions).start(); + } + }); } else { - mScreenshotHandler.post(() -> - createScreenshotActionsShadeAnimation(smartActions, - actions).start()); + createScreenshotActionsShadeAnimation(smartActions, + actions).start(); } - } - }); + }); + } mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT); mScreenshotHandler.sendMessageDelayed( mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT), diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java index e7e1ba8c28b4..92236ae0f0fa 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java @@ -38,7 +38,6 @@ import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.ParcelFileDescriptor; -import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; @@ -124,10 +123,6 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { return null; } - // By default, AsyncTask sets the worker thread to have background thread priority, - // so bump it back up so that we save a little quicker. - Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND); - ContentResolver resolver = mContext.getContentResolver(); Bitmap image = mParams.image; Resources r = mContext.getResources(); diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java index b1f1f3844cac..821144a7f12d 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java +++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java @@ -17,8 +17,8 @@ package com.android.systemui.settings; import static com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MAX; -import static com.android.settingslib.display.BrightnessUtils.convertGammaToLinear; -import static com.android.settingslib.display.BrightnessUtils.convertLinearToGamma; +import static com.android.settingslib.display.BrightnessUtils.convertGammaToLinearFloat; +import static com.android.settingslib.display.BrightnessUtils.convertLinearToGammaFloat; import android.animation.ValueAnimator; import android.content.ContentResolver; @@ -39,7 +39,9 @@ import android.provider.Settings; import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; import android.util.Log; +import android.util.MathUtils; +import com.android.internal.BrightnessSynchronizer; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settingslib.RestrictedLockUtilsInternal; @@ -58,12 +60,21 @@ public class BrightnessController implements ToggleSlider.Listener { private static final int MSG_DETACH_LISTENER = 4; private static final int MSG_VR_MODE_CHANGED = 5; - private final int mMinimumBacklight; - private final int mMaximumBacklight; - private final int mDefaultBacklight; - private final int mMinimumBacklightForVr; - private final int mMaximumBacklightForVr; - private final int mDefaultBacklightForVr; + private static final Uri BRIGHTNESS_MODE_URI = + Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE); + private static final Uri BRIGHTNESS_URI = + Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS); + private static final Uri BRIGHTNESS_FLOAT_URI = + Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FLOAT); + private static final Uri BRIGHTNESS_FOR_VR_FLOAT_URI = + Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT); + + private final float mMinimumBacklight; + private final float mMaximumBacklight; + private final float mDefaultBacklight; + private final float mMinimumBacklightForVr; + private final float mMaximumBacklightForVr; + private final float mDefaultBacklightForVr; private final Context mContext; private final ToggleSlider mControl; @@ -90,16 +101,9 @@ public class BrightnessController implements ToggleSlider.Listener { public void onBrightnessLevelChanged(); } - /** ContentObserver to watch brightness **/ + /** ContentObserver to watch brightness */ private class BrightnessObserver extends ContentObserver { - private final Uri BRIGHTNESS_MODE_URI = - Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE); - private final Uri BRIGHTNESS_URI = - Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS); - private final Uri BRIGHTNESS_FOR_VR_URI = - Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR); - public BrightnessObserver(Handler handler) { super(handler); } @@ -116,9 +120,9 @@ public class BrightnessController implements ToggleSlider.Listener { if (BRIGHTNESS_MODE_URI.equals(uri)) { mBackgroundHandler.post(mUpdateModeRunnable); mBackgroundHandler.post(mUpdateSliderRunnable); - } else if (BRIGHTNESS_URI.equals(uri)) { + } else if (BRIGHTNESS_FLOAT_URI.equals(uri)) { mBackgroundHandler.post(mUpdateSliderRunnable); - } else if (BRIGHTNESS_FOR_VR_URI.equals(uri)) { + } else if (BRIGHTNESS_FOR_VR_FLOAT_URI.equals(uri)) { mBackgroundHandler.post(mUpdateSliderRunnable); } else { mBackgroundHandler.post(mUpdateModeRunnable); @@ -139,7 +143,10 @@ public class BrightnessController implements ToggleSlider.Listener { BRIGHTNESS_URI, false, this, UserHandle.USER_ALL); cr.registerContentObserver( - BRIGHTNESS_FOR_VR_URI, + BRIGHTNESS_FLOAT_URI, + false, this, UserHandle.USER_ALL); + cr.registerContentObserver( + BRIGHTNESS_FOR_VR_FLOAT_URI, false, this, UserHandle.USER_ALL); } @@ -229,18 +236,21 @@ public class BrightnessController implements ToggleSlider.Listener { private final Runnable mUpdateSliderRunnable = new Runnable() { @Override public void run() { - final int val; + final float valFloat; final boolean inVrMode = mIsVrModeEnabled; if (inVrMode) { - val = Settings.System.getIntForUser(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_FOR_VR, mDefaultBacklightForVr, + valFloat = Settings.System.getFloatForUser(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT, mDefaultBacklightForVr, UserHandle.USER_CURRENT); } else { - val = Settings.System.getIntForUser(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS, mDefaultBacklight, + valFloat = Settings.System.getFloatForUser(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_FLOAT, mDefaultBacklight, UserHandle.USER_CURRENT); } - mHandler.obtainMessage(MSG_UPDATE_SLIDER, val, inVrMode ? 1 : 0).sendToTarget(); + // Value is passed as intbits, since this is what the message takes. + final int valueAsIntBits = Float.floatToIntBits(valFloat); + mHandler.obtainMessage(MSG_UPDATE_SLIDER, valueAsIntBits, + inVrMode ? 1 : 0).sendToTarget(); } }; @@ -259,7 +269,7 @@ public class BrightnessController implements ToggleSlider.Listener { try { switch (msg.what) { case MSG_UPDATE_SLIDER: - updateSlider(msg.arg1, msg.arg2 != 0); + updateSlider(Float.intBitsToFloat(msg.arg1), msg.arg2 != 0); break; case MSG_SET_CHECKED: mControl.setChecked(msg.arg1 != 0); @@ -298,12 +308,19 @@ public class BrightnessController implements ToggleSlider.Listener { mBrightnessObserver = new BrightnessObserver(mHandler); PowerManager pm = context.getSystemService(PowerManager.class); - mMinimumBacklight = pm.getMinimumScreenBrightnessSetting(); - mMaximumBacklight = pm.getMaximumScreenBrightnessSetting(); - mDefaultBacklight = pm.getDefaultScreenBrightnessSetting(); - mMinimumBacklightForVr = pm.getMinimumScreenBrightnessForVrSetting(); - mMaximumBacklightForVr = pm.getMaximumScreenBrightnessForVrSetting(); - mDefaultBacklightForVr = pm.getDefaultScreenBrightnessForVrSetting(); + mMinimumBacklight = pm.getBrightnessConstraint( + PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM); + mMaximumBacklight = pm.getBrightnessConstraint( + PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM); + mDefaultBacklight = pm.getBrightnessConstraint( + PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT); + mMinimumBacklightForVr = pm.getBrightnessConstraint( + PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR); + mMaximumBacklightForVr = pm.getBrightnessConstraint( + PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM_VR); + mDefaultBacklightForVr = pm.getBrightnessConstraint( + PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_VR); + mAutomaticAvailable = context.getResources().getBoolean( com.android.internal.R.bool.config_automatic_brightness_available); @@ -344,37 +361,39 @@ public class BrightnessController implements ToggleSlider.Listener { mSliderAnimator.cancel(); } - final int min; - final int max; + final float minBacklight; + final float maxBacklight; final int metric; - final String setting; + final String settingToChange; if (mIsVrModeEnabled) { metric = MetricsEvent.ACTION_BRIGHTNESS_FOR_VR; - min = mMinimumBacklightForVr; - max = mMaximumBacklightForVr; - setting = Settings.System.SCREEN_BRIGHTNESS_FOR_VR; + minBacklight = mMinimumBacklightForVr; + maxBacklight = mMaximumBacklightForVr; + settingToChange = Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT; } else { metric = mAutomatic ? MetricsEvent.ACTION_BRIGHTNESS_AUTO : MetricsEvent.ACTION_BRIGHTNESS; - min = mMinimumBacklight; - max = mMaximumBacklight; - setting = Settings.System.SCREEN_BRIGHTNESS; + minBacklight = mMinimumBacklight; + maxBacklight = mMaximumBacklight; + settingToChange = Settings.System.SCREEN_BRIGHTNESS_FLOAT; } - - final int val = convertGammaToLinear(value, min, max); - + final float valFloat = MathUtils.min(convertGammaToLinearFloat(value, + minBacklight, maxBacklight), + 1.0f); if (stopTracking) { - MetricsLogger.action(mContext, metric, val); - } + // TODO(brightnessfloat): change to use float value instead. + MetricsLogger.action(mContext, metric, + BrightnessSynchronizer.brightnessFloatToInt(mContext, valFloat)); - setBrightness(val); + } + setBrightness(valFloat); if (!tracking) { AsyncTask.execute(new Runnable() { public void run() { - Settings.System.putIntForUser(mContext.getContentResolver(), - setting, val, UserHandle.USER_CURRENT); + Settings.System.putFloatForUser(mContext.getContentResolver(), + settingToChange, valFloat, UserHandle.USER_CURRENT); } }); } @@ -402,7 +421,7 @@ public class BrightnessController implements ToggleSlider.Listener { mUserTracker.getCurrentUserId()); } - private void setBrightness(int brightness) { + private void setBrightness(float brightness) { mDisplayManager.setTemporaryBrightness(brightness); } @@ -413,9 +432,9 @@ public class BrightnessController implements ToggleSlider.Listener { } } - private void updateSlider(int val, boolean inVrMode) { - final int min; - final int max; + private void updateSlider(float brightnessValue, boolean inVrMode) { + final float min; + final float max; if (inVrMode) { min = mMinimumBacklightForVr; max = mMaximumBacklightForVr; @@ -423,7 +442,10 @@ public class BrightnessController implements ToggleSlider.Listener { min = mMinimumBacklight; max = mMaximumBacklight; } - if (val == convertGammaToLinear(mControl.getValue(), min, max)) { + // convertGammaToLinearFloat returns 0-1 + if (BrightnessSynchronizer.brightnessFloatToInt(mContext, brightnessValue) + == BrightnessSynchronizer.brightnessFloatToInt(mContext, + convertGammaToLinearFloat(mControl.getValue(), min, max))) { // If we have more resolution on the slider than we do in the actual setting, then // multiple slider positions will map to the same setting value. Thus, if we see a // setting value here that maps to the current slider position, we don't bother to @@ -431,7 +453,8 @@ public class BrightnessController implements ToggleSlider.Listener { // change to the user even though it isn't one. return; } - final int sliderVal = convertLinearToGamma(val, min, max); + // Returns GAMMA_SPACE_MIN - GAMMA_SPACE_MAX + final int sliderVal = convertLinearToGammaFloat(brightnessValue, min, max); animateSliderTo(sliderVal); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowBlurController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowBlurController.kt new file mode 100644 index 000000000000..85215ed3c40b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowBlurController.kt @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar + +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.animation.ValueAnimator +import android.view.Choreographer +import android.view.View +import com.android.internal.util.IndentingPrintWriter +import com.android.systemui.DumpController +import com.android.systemui.Dumpable +import com.android.systemui.Interpolators +import com.android.systemui.statusbar.phone.BiometricUnlockController +import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK +import com.android.systemui.statusbar.phone.NotificationShadeWindowController +import com.android.systemui.statusbar.phone.PanelExpansionListener +import com.android.systemui.statusbar.policy.KeyguardStateController +import java.io.FileDescriptor +import java.io.PrintWriter +import javax.inject.Inject +import javax.inject.Singleton +import kotlin.math.max + +/** + * Controller responsible for statusbar window blur. + */ +@Singleton +class NotificationShadeWindowBlurController @Inject constructor( + private val statusBarStateController: SysuiStatusBarStateController, + private val blurUtils: BlurUtils, + private val biometricUnlockController: BiometricUnlockController, + private val keyguardStateController: KeyguardStateController, + private val notificationShadeWindowController: NotificationShadeWindowController, + private val choreographer: Choreographer, + dumpController: DumpController +) : PanelExpansionListener, Dumpable { + companion object { + private const val WAKE_UP_ANIMATION_ENABLED = true + private const val SHADE_BLUR_ENABLED = true + } + + lateinit var root: View + private var keyguardAnimator: Animator? = null + private var notificationAnimator: Animator? = null + private var updateScheduled: Boolean = false + private var shadeExpansion = 1.0f + private var shadeBlurRadius = 0 + set(value) { + if (field == value) return + field = value + scheduleUpdate() + } + private var wakeAndUnlockBlurRadius = 0 + set(value) { + if (field == value) return + field = value + scheduleUpdate() + } + private var incomingNotificationBlurRadius = 0 + set(value) { + if (field == value) return + field = value + scheduleUpdate() + } + + /** + * Callback that updates the window blur value and is called only once per frame. + */ + private val updateBlurCallback = Choreographer.FrameCallback { + updateScheduled = false + + var notificationBlur = 0 + if (statusBarStateController.state == StatusBarState.KEYGUARD) { + notificationBlur = (incomingNotificationBlurRadius * shadeExpansion).toInt() + } + + val blur = max(max(shadeBlurRadius, wakeAndUnlockBlurRadius), notificationBlur) + blurUtils.applyBlur(root.viewRootImpl, blur) + } + + /** + * Animate blurs when unlocking. + */ + private val keyguardStateCallback = object : KeyguardStateController.Callback { + override fun onKeyguardFadingAwayChanged() { + if (!keyguardStateController.isKeyguardFadingAway || + biometricUnlockController.mode != MODE_WAKE_AND_UNLOCK) { + return + } + + keyguardAnimator?.cancel() + keyguardAnimator = ValueAnimator.ofFloat(1f, 0f).apply { + duration = keyguardStateController.keyguardFadingAwayDuration + startDelay = keyguardStateController.keyguardFadingAwayDelay + interpolator = Interpolators.DECELERATE_QUINT + addUpdateListener { animation: ValueAnimator -> + wakeAndUnlockBlurRadius = + blurUtils.radiusForRatio(animation.animatedValue as Float) + } + addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator?) { + keyguardAnimator = null + scheduleUpdate() + } + }) + start() + } + } + + override fun onKeyguardShowingChanged() { + if (keyguardStateController.isShowing) { + keyguardAnimator?.cancel() + notificationAnimator?.cancel() + } + } + } + + init { + dumpController.registerDumpable(this) + if (WAKE_UP_ANIMATION_ENABLED) { + keyguardStateController.addCallback(keyguardStateCallback) + } + } + + /** + * Update blurs when pulling down the shade + */ + override fun onPanelExpansionChanged(expansion: Float, tracking: Boolean) { + if (!SHADE_BLUR_ENABLED) { + return + } + + var newBlur = 0 + if (statusBarStateController.state == StatusBarState.SHADE) { + newBlur = blurUtils.radiusForRatio(expansion) + } + + if (shadeBlurRadius == newBlur) { + return + } + shadeBlurRadius = newBlur + scheduleUpdate() + } + + private fun scheduleUpdate() { + if (updateScheduled) { + return + } + updateScheduled = true + choreographer.postFrameCallback(updateBlurCallback) + } + + override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { + IndentingPrintWriter(pw, " ").use { + it.println("StatusBarWindowBlurController:") + it.increaseIndent() + it.println("shadeBlurRadius: $shadeBlurRadius") + it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius") + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWindowBlurController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWindowBlurController.kt deleted file mode 100644 index 2e72163f1840..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWindowBlurController.kt +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar - -import android.content.res.Resources -import android.view.View -import com.android.internal.util.IndentingPrintWriter -import com.android.systemui.DumpController -import com.android.systemui.Dumpable -import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.statusbar.phone.PanelExpansionListener -import java.io.FileDescriptor -import java.io.PrintWriter -import javax.inject.Inject -import javax.inject.Singleton - -/** - * Controller responsible for statusbar window blur. - */ -@Singleton -class StatusBarWindowBlurController @Inject constructor( - @Main private val resources: Resources, - private val statusBarStateController: SysuiStatusBarStateController, - private val blurUtils: BlurUtils, - dumpController: DumpController -) : PanelExpansionListener, Dumpable { - - lateinit var root: View - private var blurRadius = 0 - - init { - dumpController.registerDumpable(this) - } - - override fun onPanelExpansionChanged(expansion: Float, tracking: Boolean) { - val newBlur = if (statusBarStateController.state == StatusBarState.SHADE) - blurUtils.radiusForRatio(expansion) - else - 0 - - if (blurRadius == newBlur) { - return - } - blurRadius = newBlur - blurUtils.applyBlur(root.viewRootImpl, blurRadius) - } - - override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { - IndentingPrintWriter(pw, " ").use { - it.println("StatusBarWindowBlurController:") - it.increaseIndent() - it.println("blurRadius: $blurRadius") - } - } -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index 916da6eca0c9..b0bf8130f3a9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -290,6 +290,9 @@ public class NotificationEntryManager implements NotificationEntry entry = mPendingNotifications.get(key); entry.abortTask(); mPendingNotifications.remove(key); + for (NotifCollectionListener listener : mNotifCollectionListeners) { + listener.onEntryCleanUp(entry); + } mLogger.logInflationAborted(key, "pending", reason); } NotificationEntry addedEntry = getActiveNotificationUnfiltered(key); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java index 3fa1954a7fcc..b5c81b2adfa0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java @@ -151,10 +151,6 @@ public class ListDumper { .append(" "); } - if (notifEntry.hasInflationError()) { - rksb.append("(!)hasInflationError "); - } - if (notifEntry.getDismissState() != NOT_DISMISSED) { rksb.append("dismissState=") .append(notifEntry.getDismissState()) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java index 38d8d979a4da..ec3285f2b241 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java @@ -44,6 +44,7 @@ import static java.util.Objects.requireNonNull; import android.annotation.IntDef; import android.annotation.MainThread; import android.annotation.Nullable; +import android.app.Notification; import android.os.RemoteException; import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.NotificationListenerService.RankingMap; @@ -239,8 +240,7 @@ public class NotifCollection implements Dumpable { // Also mark any children as dismissed as system server will auto-dismiss them as well if (entry.getSbn().getNotification().isGroupSummary()) { for (NotificationEntry otherEntry : mNotificationSet.values()) { - if (otherEntry.getSbn().getGroupKey().equals(entry.getSbn().getGroupKey()) - && otherEntry.getDismissState() != DISMISSED) { + if (shouldAutoDismiss(otherEntry, entry.getSbn().getGroupKey())) { otherEntry.setDismissState(PARENT_DISMISSED); if (isCanceled(otherEntry)) { canceledEntries.add(otherEntry); @@ -544,6 +544,28 @@ public class NotifCollection implements Dumpable { return entry.getDismissState() != NOT_DISMISSED; } + /** + * When a group summary is dismissed, NotificationManager will also try to dismiss its children. + * Returns true if we think dismissing the group summary with group key + * <code>dismissedGroupKey</code> will cause NotificationManager to also dismiss + * <code>entry</code>. + * + * See NotificationManager.cancelGroupChildrenByListLocked() for corresponding code. + */ + private static boolean shouldAutoDismiss( + NotificationEntry entry, + String dismissedGroupKey) { + return entry.getSbn().getGroupKey().equals(dismissedGroupKey) + && !entry.getSbn().getNotification().isGroupSummary() + && !hasFlag(entry, Notification.FLAG_FOREGROUND_SERVICE) + && !hasFlag(entry, Notification.FLAG_BUBBLE) + && entry.getDismissState() != DISMISSED; + } + + private static boolean hasFlag(NotificationEntry entry, int flag) { + return (entry.getSbn().getNotification().flags & flag) != 0; + } + private void dispatchOnEntryInit(NotificationEntry entry) { mAmDispatchingToOtherCode = true; for (NotifCollectionListener listener : mNotifCollectionListeners) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java index 9272e51b6aa0..83f56cc1e83d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java @@ -18,9 +18,7 @@ package com.android.systemui.statusbar.notification.collection; import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL; -import android.os.RemoteException; import android.service.notification.NotificationStats; -import android.service.notification.StatusBarNotification; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; @@ -29,6 +27,7 @@ import com.android.systemui.statusbar.notification.collection.inflation.NotifInf import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.logging.NotificationLogger; +import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager; import com.android.systemui.statusbar.notification.row.NotificationContentInflater; import javax.inject.Inject; @@ -44,6 +43,7 @@ public class NotifInflaterImpl implements NotifInflater { private final IStatusBarService mStatusBarService; private final NotifCollection mNotifCollection; + private final NotifInflationErrorManager mNotifErrorManager; private NotificationRowBinderImpl mNotificationRowBinder; private InflationCallback mExternalInflationCallback; @@ -51,9 +51,11 @@ public class NotifInflaterImpl implements NotifInflater { @Inject public NotifInflaterImpl( IStatusBarService statusBarService, - NotifCollection notifCollection) { + NotifCollection notifCollection, + NotifInflationErrorManager errorManager) { mStatusBarService = statusBarService; mNotifCollection = notifCollection; + mNotifErrorManager = errorManager; } /** @@ -81,7 +83,6 @@ public class NotifInflaterImpl implements NotifInflater { @Override public void inflateViews(NotificationEntry entry) { try { - entry.setHasInflationError(false); requireBinder().inflateViews(entry, getDismissCallback(entry)); } catch (InflationException e) { // logged in mInflationCallback.handleInflationException @@ -131,25 +132,12 @@ public class NotifInflaterImpl implements NotifInflater { public void handleInflationException( NotificationEntry entry, Exception e) { - entry.setHasInflationError(true); - try { - final StatusBarNotification sbn = entry.getSbn(); - // report notification inflation errors back up - // to notification delegates - mStatusBarService.onNotificationError( - sbn.getPackageName(), - sbn.getTag(), - sbn.getId(), - sbn.getUid(), - sbn.getInitialPid(), - e.getMessage(), - sbn.getUserId()); - } catch (RemoteException ex) { - } + mNotifErrorManager.setInflationError(entry, e); } @Override public void onAsyncInflationFinished(NotificationEntry entry) { + mNotifErrorManager.clearInflationError(entry); if (mExternalInflationCallback != null) { mExternalInflationCallback.onInflationFinished(entry); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index 006d40ddbac5..f482d37deb55 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -122,9 +122,6 @@ public final class NotificationEntry extends ListEntry { */ @CancellationReason int mCancellationReason = REASON_NOT_CANCELED; - /** @see #hasInflationError() */ - private boolean mHasInflationError; - /** @see #getDismissState() */ @NonNull private DismissState mDismissState = DismissState.NOT_DISMISSED; @@ -274,23 +271,6 @@ public final class NotificationEntry extends ListEntry { */ /** - * Whether this notification had an error when attempting to inflate. This is only used in - * the NewNotifPipeline - */ - public boolean hasInflationError() { - return mHasInflationError; - } - - /** - * Set whether the notification has an error while inflating. - * - * TODO: Move this into an inflation error manager class. - */ - public void setHasInflationError(boolean hasError) { - mHasInflationError = hasError; - } - - /** * Set if the user has dismissed this notif but we haven't yet heard back from system server to * confirm the dismissal. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java index 1e5946a85cfa..1c8fdac4c51b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java @@ -16,12 +16,18 @@ package com.android.systemui.statusbar.notification.collection.coordinator; +import android.os.RemoteException; +import android.service.notification.StatusBarNotification; + +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.ShadeListBuilder; import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; +import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager; import java.util.ArrayList; import java.util.List; @@ -34,7 +40,7 @@ import javax.inject.Singleton; * Aborts inflation when a notification is removed. * * If a notification is not done inflating, this coordinator will filter the notification out - * from the NotifListBuilder. + * from the {@link ShadeListBuilder}. */ @Singleton public class PreparationCoordinator implements Coordinator { @@ -42,15 +48,22 @@ public class PreparationCoordinator implements Coordinator { private final PreparationCoordinatorLogger mLogger; private final NotifInflater mNotifInflater; + private final NotifInflationErrorManager mNotifErrorManager; private final List<NotificationEntry> mPendingNotifications = new ArrayList<>(); + private final IStatusBarService mStatusBarService; @Inject public PreparationCoordinator( PreparationCoordinatorLogger logger, - NotifInflaterImpl notifInflater) { + NotifInflaterImpl notifInflater, + NotifInflationErrorManager errorManager, + IStatusBarService service) { mLogger = logger; mNotifInflater = notifInflater; mNotifInflater.setInflationCallback(mInflationCallback); + mNotifErrorManager = errorManager; + mNotifErrorManager.addInflationErrorListener(mInflationErrorListener); + mStatusBarService = service; } @Override @@ -84,8 +97,7 @@ public class PreparationCoordinator implements Coordinator { */ @Override public boolean shouldFilterOut(NotificationEntry entry, long now) { - if (entry.hasInflationError()) { - mPendingNotifications.remove(entry); + if (mNotifErrorManager.hasInflationError(entry)) { return true; } return false; @@ -112,6 +124,34 @@ public class PreparationCoordinator implements Coordinator { } }; + private final NotifInflationErrorManager.NotifInflationErrorListener mInflationErrorListener = + new NotifInflationErrorManager.NotifInflationErrorListener() { + @Override + public void onNotifInflationError(NotificationEntry entry, Exception e) { + mPendingNotifications.remove(entry); + try { + final StatusBarNotification sbn = entry.getSbn(); + // report notification inflation errors back up + // to notification delegates + mStatusBarService.onNotificationError( + sbn.getPackageName(), + sbn.getTag(), + sbn.getId(), + sbn.getUid(), + sbn.getInitialPid(), + e.getMessage(), + sbn.getUserId()); + } catch (RemoteException ex) { + } + mNotifInflationErrorFilter.invalidateList(); + } + + @Override + public void onNotifInflationErrorCleared(NotificationEntry entry) { + mNotifInflationErrorFilter.invalidateList(); + } + }; + private void inflateEntry(NotificationEntry entry, String reason) { abortInflation(entry, reason); mPendingNotifications.add(entry); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt index 3e0bcbb796bf..8a6d5c70d6f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt @@ -91,6 +91,7 @@ class NotificationsControllerImpl @Inject constructor( presenter, listContainer, bindRowCallback) + notifBindPipelineInitializer.initialize() if (featureFlags.isNewNotifPipelineEnabled) { newNotifPipeline.get().initialize(notificationListener, notificationRowBinder) @@ -99,7 +100,6 @@ class NotificationsControllerImpl @Inject constructor( if (featureFlags.isNewNotifPipelineRenderingEnabled) { // TODO } else { - notifBindPipelineInitializer.initialize() notificationRowBinder.setInflationCallback(entryManager) remoteInputUriController.attach(entryManager) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java index c2da51777210..41b248fb9634 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java @@ -78,9 +78,9 @@ public class FooterView extends StackScrollerDecorView { mDismissButton.setText(R.string.clear_all_notifications_text); mDismissButton.setContentDescription( mContext.getString(R.string.accessibility_clear_all)); - mManageButton.setText(R.string.manage_notifications_text); + mManageButton.setText(R.string.manage_notifications_history_text); mManageButton.setContentDescription( - mContext.getString(R.string.accessibility_manage_notification)); + mContext.getString(R.string.manage_notifications_history_text)); } public boolean isButtonVisible() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java index af2d0844412a..e2513dac44d8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java @@ -25,11 +25,10 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.os.CancellationSignal; -import com.android.internal.statusbar.NotificationVisibility; -import com.android.systemui.statusbar.notification.NotificationEntryListener; -import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; +import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; +import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; import java.util.Map; @@ -78,8 +77,8 @@ public final class NotifBindPipeline { private BindStage mStage; @Inject - NotifBindPipeline(NotificationEntryManager entryManager) { - entryManager.addNotificationEntryListener(mEntryListener); + NotifBindPipeline(CommonNotifCollection collection) { + collection.addCollectionListener(mCollectionListener); } /** @@ -158,18 +157,15 @@ public final class NotifBindPipeline { callbacks.clear(); } - //TODO: Move this to onManageEntry hook when we split that from add/remove - private final NotificationEntryListener mEntryListener = new NotificationEntryListener() { + private final NotifCollectionListener mCollectionListener = new NotifCollectionListener() { @Override - public void onPendingEntryAdded(NotificationEntry entry) { + public void onEntryInit(NotificationEntry entry) { mBindEntries.put(entry, new BindEntry()); mStage.createStageParams(entry); } @Override - public void onEntryRemoved(NotificationEntry entry, - @Nullable NotificationVisibility visibility, - boolean removedByUser) { + public void onEntryCleanUp(NotificationEntry entry) { BindEntry bindEntry = mBindEntries.remove(entry); ExpandableNotificationRow row = bindEntry.row; if (row != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManager.java new file mode 100644 index 000000000000..a3ca08432745 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManager.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.row; + +import androidx.annotation.NonNull; +import androidx.collection.ArraySet; + +import com.android.systemui.statusbar.notification.collection.NotificationEntry; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * A manager handling the error state of a notification when it encounters an exception while + * inflating. We don't want to show these notifications to the user but may want to keep them + * around for logging purposes. + */ +@Singleton +public class NotifInflationErrorManager { + + Set<NotificationEntry> mErroredNotifs = new ArraySet<>(); + List<NotifInflationErrorListener> mListeners = new ArrayList<>(); + + @Inject + public NotifInflationErrorManager() { } + + /** + * Mark the notification as errored out due to encountering an exception while inflating. + * + * @param e the exception encountered while inflating + */ + public void setInflationError(NotificationEntry entry, Exception e) { + mErroredNotifs.add(entry); + for (int i = 0; i < mListeners.size(); i++) { + mListeners.get(i).onNotifInflationError(entry, e); + } + } + + /** + * Notification inflated successfully and is no longer errored out. + */ + public void clearInflationError(NotificationEntry entry) { + if (mErroredNotifs.contains(entry)) { + mErroredNotifs.remove(entry); + for (int i = 0; i < mListeners.size(); i++) { + mListeners.get(i).onNotifInflationErrorCleared(entry); + } + } + } + + /** + * Whether or not the notification encountered an exception while inflating. + */ + public boolean hasInflationError(@NonNull NotificationEntry entry) { + return mErroredNotifs.contains(entry); + } + + /** + * Add listener for changes in inflation error state. + */ + public void addInflationErrorListener(NotifInflationErrorListener listener) { + mListeners.add(listener); + } + + /** + * Listener for changes in notification inflation error state. + */ + public interface NotifInflationErrorListener { + + /** + * Called when notification encounters an inflation exception. + * + * @param e the exception encountered while inflating + */ + void onNotifInflationError(NotificationEntry entry, Exception e); + + /** + * Called when notification inflation error is cleared. + */ + default void onNotifInflationErrorCleared(NotificationEntry entry) {} + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java index f1241799d3d1..f78324596fb4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java @@ -18,12 +18,8 @@ package com.android.systemui.statusbar.notification.row; import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL; -import android.os.RemoteException; -import android.service.notification.StatusBarNotification; - import androidx.annotation.NonNull; -import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback; @@ -41,14 +37,14 @@ import javax.inject.Singleton; @Singleton public class RowContentBindStage extends BindStage<RowContentBindParams> { private final NotificationRowContentBinder mBinder; - private final IStatusBarService mStatusBarService; + private final NotifInflationErrorManager mNotifInflationErrorManager; @Inject RowContentBindStage( NotificationRowContentBinder binder, - IStatusBarService statusBarService) { + NotifInflationErrorManager errorManager) { mBinder = binder; - mStatusBarService = statusBarService; + mNotifInflationErrorManager = errorManager; } @Override @@ -78,24 +74,12 @@ public class RowContentBindStage extends BindStage<RowContentBindParams> { InflationCallback inflationCallback = new InflationCallback() { @Override public void handleInflationException(NotificationEntry entry, Exception e) { - entry.setHasInflationError(true); - try { - final StatusBarNotification sbn = entry.getSbn(); - mStatusBarService.onNotificationError( - sbn.getPackageName(), - sbn.getTag(), - sbn.getId(), - sbn.getUid(), - sbn.getInitialPid(), - e.getMessage(), - sbn.getUserId()); - } catch (RemoteException ex) { - } + mNotifInflationErrorManager.setInflationError(entry, e); } @Override public void onAsyncInflationFinished(NotificationEntry entry) { - entry.setHasInflationError(false); + mNotifInflationErrorManager.clearInflationError(entry); getStageParams(entry).clearDirtyContentViews(); callback.onStageFinished(entry); } 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 4b9976cc2097..0cc337187f02 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 @@ -5481,7 +5481,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void manageNotifications(View v) { - Intent intent = new Intent(Settings.ACTION_ALL_APPS_NOTIFICATION_SETTINGS); + Intent intent = new Intent(Settings.ACTION_NOTIFICATION_HISTORY); mStatusBar.startActivity(intent, true, true, Intent.FLAG_ACTIVITY_SINGLE_TOP); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java index 44a320419309..745843deeddb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java @@ -375,6 +375,7 @@ public class EdgeBackGestureHandler implements DisplayListener, mDownPoint.set(ev.getX(), ev.getY()); mThresholdCrossed = false; } + } else if (mAllowGesture) { if (!mThresholdCrossed) { if (action == MotionEvent.ACTION_POINTER_DOWN) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java index 10b68b9a518d..40b1610fa4c3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java @@ -18,8 +18,8 @@ package com.android.systemui.statusbar.phone; import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED; + import static com.android.systemui.DejankUtils.whitelistIpcs; import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java index ab1c8ade7001..8729e04b2dec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java @@ -45,7 +45,7 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.PulseExpansionHandler; -import com.android.systemui.statusbar.StatusBarWindowBlurController; +import com.android.systemui.statusbar.NotificationShadeWindowBlurController; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -81,7 +81,7 @@ public class NotificationShadeWindowViewController { private final CommandQueue mCommandQueue; private final NotificationShadeWindowView mView; private final ShadeController mShadeController; - private final StatusBarWindowBlurController mBlurController; + private final NotificationShadeWindowBlurController mBlurController; private GestureDetector mGestureDetector; private View mBrightnessMirror; @@ -123,7 +123,7 @@ public class NotificationShadeWindowViewController { CommandQueue commandQueue, ShadeController shadeController, DockManager dockManager, - @Nullable StatusBarWindowBlurController blurController, + @Nullable NotificationShadeWindowBlurController blurController, NotificationShadeWindowView statusBarWindowView, NotificationPanelViewController notificationPanelViewController) { mInjectionInflationController = injectionInflationController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 2b9fc8d3bfbe..10821d63d4cc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -107,7 +107,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo /** * Default alpha value for most scrims. */ - public static final float GRADIENT_SCRIM_ALPHA = 0.2f; + public static final float SCRIM_ALPHA = 0.2f; /** * Scrim opacity when the phone is about to wake-up. */ @@ -116,12 +116,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo * A scrim varies its opacity based on a busyness factor, for example * how many notifications are currently visible. */ - public static final float GRADIENT_SCRIM_ALPHA_BUSY = 0.7f; + public static final float BUSY_SCRIM_ALPHA = 0.54f; /** * The most common scrim, the one under the keyguard. */ - protected static final float SCRIM_BEHIND_ALPHA_KEYGUARD = GRADIENT_SCRIM_ALPHA; + protected static final float SCRIM_BEHIND_ALPHA_KEYGUARD = SCRIM_ALPHA; static final int TAG_KEY_ANIM = R.id.scrim; private static final int TAG_START_ALPHA = R.id.scrim_alpha_start; @@ -481,7 +481,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo // Darken scrim as you pull down the shade when unlocked float behindFraction = getInterpolatedFraction(); behindFraction = (float) Math.pow(behindFraction, 0.8f); - mBehindAlpha = behindFraction * GRADIENT_SCRIM_ALPHA_BUSY; + mBehindAlpha = behindFraction * BUSY_SCRIM_ALPHA; mInFrontAlpha = 0; } else if (mState == ScrimState.KEYGUARD || mState == ScrimState.PULSING) { // Either darken of make the scrim transparent when you @@ -489,7 +489,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo float interpolatedFract = getInterpolatedFraction(); float alphaBehind = mState.getBehindAlpha(); if (mDarkenWhileDragging) { - mBehindAlpha = MathUtils.lerp(GRADIENT_SCRIM_ALPHA_BUSY, alphaBehind, + mBehindAlpha = MathUtils.lerp(BUSY_SCRIM_ALPHA, alphaBehind, interpolatedFract); mInFrontAlpha = mState.getFrontAlpha(); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index 40f8d58ad695..c23fd0a3d5d6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -92,7 +92,7 @@ public enum ScrimState { BOUNCER { @Override public void prepare(ScrimState previousState) { - mBehindAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY; + mBehindAlpha = ScrimController.BUSY_SCRIM_ALPHA; mFrontAlpha = 0f; mBubbleAlpha = 0f; } @@ -106,7 +106,7 @@ public enum ScrimState { public void prepare(ScrimState previousState) { mBehindAlpha = 0; mBubbleAlpha = 0f; - mFrontAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY; + mFrontAlpha = ScrimController.BUSY_SCRIM_ALPHA; } }, @@ -234,8 +234,8 @@ public enum ScrimState { mBubbleTint = Color.TRANSPARENT; mFrontAlpha = ScrimController.TRANSPARENT; - mBehindAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY; - mBubbleAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY; + mBehindAlpha = ScrimController.BUSY_SCRIM_ALPHA; + mBubbleAlpha = ScrimController.BUSY_SCRIM_ALPHA; mAnimationDuration = ScrimController.ANIMATION_DURATION; mBlankScreen = false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 6a046884e835..ef40acc7e59b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.phone; import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; import static android.view.ViewRootImpl.sNewInsetsMode; import static android.view.WindowInsets.Type.navigationBars; + import static com.android.systemui.plugins.ActivityStarter.OnDismissAction; import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_UNLOCK_FADING; import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java index d2e92629ed78..690d57345db6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java @@ -177,8 +177,10 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba @Override public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType, - int qsType, boolean activityIn, boolean activityOut, String typeContentDescription, - String description, boolean isWide, int subId, boolean roaming) { + int qsType, boolean activityIn, boolean activityOut, + CharSequence typeContentDescription, + CharSequence typeContentDescriptionHtml, CharSequence description, + boolean isWide, int subId, boolean roaming) { MobileIconState state = getState(subId); if (state == null) { return; @@ -387,7 +389,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba public int typeId; public boolean roaming; public boolean needsLeadingPadding; - public String typeContentDescription; + public CharSequence typeContentDescription; private MobileIconState(int subId) { super(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java index e1bb19a6f69f..97d348bb6a22 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java @@ -111,30 +111,25 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa public void setWifiIndicators(final boolean enabled, final IconState statusIcon, final IconState qsIcon, final boolean activityIn, final boolean activityOut, final String description, boolean isTransient, String secondaryLabel) { - post(new Runnable() { - @Override - public void run() { - for (SignalCallback callback : mSignalCallbacks) { - callback.setWifiIndicators(enabled, statusIcon, qsIcon, activityIn, activityOut, - description, isTransient, secondaryLabel); - } + post(() -> { + for (SignalCallback callback : mSignalCallbacks) { + callback.setWifiIndicators(enabled, statusIcon, qsIcon, activityIn, activityOut, + description, isTransient, secondaryLabel); } }); } @Override public void setMobileDataIndicators(final IconState statusIcon, final IconState qsIcon, - final int statusType, final int qsType,final boolean activityIn, - final boolean activityOut, final String typeContentDescription, - final String description, final boolean isWide, final int subId, boolean roaming) { - post(new Runnable() { - @Override - public void run() { - for (SignalCallback signalCluster : mSignalCallbacks) { - signalCluster.setMobileDataIndicators(statusIcon, qsIcon, statusType, qsType, - activityIn, activityOut, typeContentDescription, description, isWide, - subId, roaming); - } + final int statusType, final int qsType, final boolean activityIn, + final boolean activityOut, final CharSequence typeContentDescription, + CharSequence typeContentDescriptionHtml, final CharSequence description, + final boolean isWide, final int subId, boolean roaming) { + post(() -> { + for (SignalCallback signalCluster : mSignalCallbacks) { + signalCluster.setMobileDataIndicators(statusIcon, qsIcon, statusType, qsType, + activityIn, activityOut, typeContentDescription, + typeContentDescriptionHtml, description, isWide, subId, roaming); } }); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java index 159bd41a2094..4fbf5ff3d35f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java @@ -49,7 +49,7 @@ public class EthernetSignalController extends @Override public void notifyListeners(SignalCallback callback) { boolean ethernetVisible = mCurrentState.connected; - String contentDescription = getStringIfExists(getContentDescription()); + String contentDescription = getTextIfExists(getContentDescription()).toString(); // TODO: wire up data transfer using WifiSignalPoller. callback.setEthernetIndicators(new IconState(ethernetVisible, getCurrentIconId(), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java new file mode 100644 index 000000000000..07433e13104c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import android.content.Context; +import android.util.AttributeSet; + +import com.android.systemui.R; +import com.android.systemui.qs.tiles.UserDetailItemView; + +/** + * Displays a user on the keyguard user switcher. + */ +public class KeyguardUserDetailItemView extends UserDetailItemView { + + public KeyguardUserDetailItemView(Context context) { + this(context, null); + } + + public KeyguardUserDetailItemView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public KeyguardUserDetailItemView(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public KeyguardUserDetailItemView(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected int getFontSizeDimen() { + return R.dimen.kg_user_switcher_text_size; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index 28ba1257524d..f6e1681e7b58 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -39,6 +39,7 @@ import android.telephony.SignalStrength; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.text.Html; import android.text.TextUtils; import android.util.Log; @@ -114,9 +115,10 @@ public class MobileSignalController extends SignalController< mDefaults = defaults; mSubscriptionInfo = info; mPhoneStateListener = new MobilePhoneStateListener((new Handler(receiverLooper))::post); - mNetworkNameSeparator = getStringIfExists(R.string.status_bar_network_name_separator); - mNetworkNameDefault = getStringIfExists( - com.android.internal.R.string.lockscreen_carrier_default); + mNetworkNameSeparator = getTextIfExists(R.string.status_bar_network_name_separator) + .toString(); + mNetworkNameDefault = getTextIfExists( + com.android.internal.R.string.lockscreen_carrier_default).toString(); mapIconSets(); @@ -153,10 +155,6 @@ public class MobileSignalController extends SignalController< updateTelephony(); } - public int getDataContentDescription() { - return getIcons().mDataContentDescription; - } - public void setAirplaneMode(boolean airplaneMode) { mCurrentState.airplaneMode = airplaneMode; notifyListenersIfNecessary(); @@ -360,8 +358,14 @@ public class MobileSignalController extends SignalController< public void notifyListeners(SignalCallback callback) { MobileIconGroup icons = getIcons(); - String contentDescription = getStringIfExists(getContentDescription()); - String dataContentDescription = getStringIfExists(icons.mDataContentDescription); + String contentDescription = getTextIfExists(getContentDescription()).toString(); + CharSequence dataContentDescriptionHtml = getTextIfExists(icons.mDataContentDescription); + + //TODO: Hacky + // The data content description can sometimes be shown in a text view and might come to us + // as HTML. Strip any styling here so that listeners don't have to care + CharSequence dataContentDescription = Html.fromHtml( + dataContentDescriptionHtml.toString(), 0).toString(); if (mCurrentState.inetCondition == 0) { dataContentDescription = mContext.getString(R.string.data_connection_no_internet); } @@ -376,7 +380,7 @@ public class MobileSignalController extends SignalController< int qsTypeIcon = 0; IconState qsIcon = null; - String description = null; + CharSequence description = null; // Only send data sim callbacks to QS. if (mCurrentState.dataSim) { qsTypeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.mQsDataType : 0; @@ -393,8 +397,9 @@ public class MobileSignalController extends SignalController< showDataIcon &= mCurrentState.isDefault || dataDisabled; int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.mDataType : 0; callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon, - activityIn, activityOut, dataContentDescription, description, icons.mIsWide, - mSubscriptionInfo.getSubscriptionId(), mCurrentState.roaming); + activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml, + description, icons.mIsWide, mSubscriptionInfo.getSubscriptionId(), + mCurrentState.roaming); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index 71db618d6076..95a97729936b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -44,6 +44,7 @@ public interface NetworkController extends CallbackController<SignalCallback>, D void addEmergencyListener(EmergencyListener listener); void removeEmergencyListener(EmergencyListener listener); boolean hasEmergencyCryptKeeperText(); + boolean isRadioOn(); public interface SignalCallback { @@ -51,10 +52,31 @@ public interface NetworkController extends CallbackController<SignalCallback>, D boolean activityIn, boolean activityOut, String description, boolean isTransient, String statusLabel) {} + /** + * Callback for listeners to be able to update the state of any UI tracking connectivity + * @param statusIcon the icon that should be shown in the status bar + * @param qsIcon the icon to show in Quick Settings + * @param statusType the resId of the data type icon (e.g. LTE) to show in the status bar + * @param qsType similar to above, the resId of the data type icon to show in Quick Settings + * @param activityIn indicates whether there is inbound activity + * @param activityOut indicates outbound activity + * @param typeContentDescription the contentDescription of the data type + * @param typeContentDescriptionHtml the (possibly HTML-styled) contentDescription of the + * data type. Suitable for display + * @param description description of the network (usually just the network name) + * @param isWide //TODO: unused? + * @param subId subscription ID for which to update the UI + * @param roaming indicates roaming + */ default void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType, - int qsType, boolean activityIn, boolean activityOut, String typeContentDescription, - String description, boolean isWide, int subId, boolean roaming) {} + int qsType, boolean activityIn, boolean activityOut, + CharSequence typeContentDescription, + CharSequence typeContentDescriptionHtml, CharSequence description, + boolean isWide, int subId, boolean roaming) { + } + default void setSubs(List<SubscriptionInfo> subs) {} + default void setNoSims(boolean show, boolean simDetected) {} default void setEthernetIndicators(IconState icon) {} 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 46143ca0dd24..4f382e7049cf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -1016,6 +1016,7 @@ public class NetworkControllerImpl extends BroadcastReceiver datatype.equals("4g") ? TelephonyIcons.FOUR_G : datatype.equals("4g+") ? TelephonyIcons.FOUR_G_PLUS : datatype.equals("5g") ? TelephonyIcons.NR_5G : + datatype.equals("5ge") ? TelephonyIcons.LTE_CA_5G_E : datatype.equals("5g+") ? TelephonyIcons.NR_5G_PLUS : datatype.equals("e") ? TelephonyIcons.E : datatype.equals("g") ? TelephonyIcons.G : diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java index fc6e5e22c2e8..749b56ce28c0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.policy; import static com.android.systemui.statusbar.policy.NetworkControllerImpl.TAG; +import android.annotation.NonNull; import android.content.Context; import android.util.Log; @@ -166,8 +167,8 @@ public abstract class SignalController<T extends SignalController.State, /** * Returns the resource if resId is not 0, and an empty string otherwise. */ - protected String getStringIfExists(int resId) { - return resId != 0 ? mContext.getString(resId) : ""; + @NonNull CharSequence getTextIfExists(int resId) { + return resId != 0 ? mContext.getText(resId) : ""; } protected I getIcons() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java index c22ff8ba594b..d9591cf5f5e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java @@ -214,7 +214,7 @@ class TelephonyIcons { 0, 0, AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0], - R.string.data_connection_5ge, + R.string.data_connection_5ge_html, TelephonyIcons.ICON_5G_E, true); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java index 6baf36c81d30..8bd0f2cadb2b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java @@ -90,7 +90,7 @@ public class WifiSignalController extends || !mHasMobileData || visibleWhenEnabled); String wifiDesc = wifiVisible ? mCurrentState.ssid : null; boolean ssidPresent = wifiVisible && mCurrentState.ssid != null; - String contentDescription = getStringIfExists(getContentDescription()); + String contentDescription = getTextIfExists(getContentDescription()).toString(); if (mCurrentState.inetCondition == 0) { contentDescription += ("," + mContext.getString(R.string.data_connection_no_internet)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java index 48a536924475..fc331d6adccc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java @@ -16,35 +16,185 @@ package com.android.systemui; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.WallpaperManager; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.ColorSpace; +import android.graphics.Rect; +import android.hardware.display.DisplayManagerGlobal; import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.util.Size; +import android.view.Display; +import android.view.DisplayInfo; +import android.view.SurfaceHolder; -import androidx.test.runner.AndroidJUnit4; +import com.android.systemui.glwallpaper.ImageWallpaperRenderer; +import com.android.systemui.statusbar.phone.DozeParameters; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.concurrent.CountDownLatch; @SmallTest -@RunWith(AndroidJUnit4.class) +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper public class ImageWallpaperTest extends SysuiTestCase { + private static final int LOW_BMP_WIDTH = 128; + private static final int LOW_BMP_HEIGHT = 128; + private static final int INVALID_BMP_WIDTH = 1; + private static final int INVALID_BMP_HEIGHT = 1; + private static final int DISPLAY_WIDTH = 1920; + private static final int DISPLAY_HEIGHT = 1080; + + @Mock + private SurfaceHolder mSurfaceHolder; + @Mock + private Context mMockContext; + @Mock + private Bitmap mWallpaperBitmap; + @Mock + private DozeParameters mDozeParam; private CountDownLatch mEventCountdown; - private CountDownLatch mAmbientEventCountdown; @Before public void setUp() throws Exception { + com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper(); MockitoAnnotations.initMocks(this); mEventCountdown = new CountDownLatch(1); - mAmbientEventCountdown = new CountDownLatch(2); + + WallpaperManager wallpaperManager = mock(WallpaperManager.class); + Resources resources = mock(Resources.class); + + when(mMockContext.getSystemService(WallpaperManager.class)).thenReturn(wallpaperManager); + when(mMockContext.getResources()).thenReturn(resources); + when(resources.getConfiguration()).thenReturn(mock(Configuration.class)); + + DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.logicalWidth = DISPLAY_WIDTH; + displayInfo.logicalHeight = DISPLAY_HEIGHT; + when(mMockContext.getDisplay()).thenReturn( + new Display(mock(DisplayManagerGlobal.class), 0, displayInfo, (Resources) null)); + + when(wallpaperManager.getBitmap(false)).thenReturn(mWallpaperBitmap); + when(mWallpaperBitmap.getColorSpace()).thenReturn(ColorSpace.get(ColorSpace.Named.SRGB)); + when(mWallpaperBitmap.getConfig()).thenReturn(Bitmap.Config.ARGB_8888); + when(mDozeParam.getDisplayNeedsBlanking()).thenReturn(false); + } + + private ImageWallpaper createImageWallpaper() { + return new ImageWallpaper(mDozeParam) { + @Override + public Engine onCreateEngine() { + return new GLEngine(mMockContext, mDozeParam) { + @Override + public Context getDisplayContext() { + return mMockContext; + } + + @Override + public SurfaceHolder getSurfaceHolder() { + return mSurfaceHolder; + } + + @Override + public void setFixedSizeAllowed(boolean allowed) { + super.setFixedSizeAllowed(allowed); + assertWithMessage("mFixedSizeAllowed should be true").that( + allowed).isTrue(); + mEventCountdown.countDown(); + } + }; + } + }; + } + + private ImageWallpaperRenderer createImageWallpaperRenderer(ImageWallpaper.GLEngine engine) { + return new ImageWallpaperRenderer(mMockContext, engine) { + @Override + public void startProcessingImage() { + loadBitmap(); + } + }; } @Test - public void testDeliversAmbientModeChanged() { - //TODO: We need add tests for GLEngine. + public void testBitmapWallpaper_normal() { + // Will use a image wallpaper with dimensions DISPLAY_WIDTH x DISPLAY_WIDTH. + // Then we expect the surface size will be also DISPLAY_WIDTH x DISPLAY_WIDTH. + // Finally, we assert the transition will not be stopped. + verifySurfaceSizeAndAssertTransition(DISPLAY_WIDTH /* bmpWidth */, + DISPLAY_WIDTH /* bmpHeight */, + DISPLAY_WIDTH /* surfaceWidth */, + DISPLAY_WIDTH /* surfaceHeight */, + false /* assertion */); } - // TODO: Add more test cases for GLEngine, tracing in b/124838911. + @Test + public void testBitmapWallpaper_low_resolution() { + // Will use a image wallpaper with dimensions BMP_WIDTH x BMP_HEIGHT. + // Then we expect the surface size will be also BMP_WIDTH x BMP_HEIGHT. + // Finally, we assert the transition will be stopped. + verifySurfaceSizeAndAssertTransition(LOW_BMP_WIDTH /* bmpWidth */, + LOW_BMP_HEIGHT /* bmpHeight */, + LOW_BMP_WIDTH /* surfaceWidth */, + LOW_BMP_HEIGHT /* surfaceHeight */, + true /* assertion */); + } + + @Test + public void testBitmapWallpaper_too_small() { + // Will use a image wallpaper with dimensions INVALID_BMP_WIDTH x INVALID_BMP_HEIGHT. + // Then we expect the surface size will be also MIN_SURFACE_WIDTH x MIN_SURFACE_HEIGHT. + // Finally, we assert the transition will be stopped. + verifySurfaceSizeAndAssertTransition(INVALID_BMP_WIDTH /* bmpWidth */, + INVALID_BMP_HEIGHT /* bmpHeight */, + ImageWallpaper.GLEngine.MIN_SURFACE_WIDTH /* surfaceWidth */, + ImageWallpaper.GLEngine.MIN_SURFACE_HEIGHT /* surfaceHeight */, + true /* assertion */); + } + + private void verifySurfaceSizeAndAssertTransition(int bmpWidth, int bmpHeight, + int surfaceWidth, int surfaceHeight, boolean assertion) { + ImageWallpaper.GLEngine wallpaperEngine = + (ImageWallpaper.GLEngine) createImageWallpaper().onCreateEngine(); + + ImageWallpaper.GLEngine engineSpy = spy(wallpaperEngine); + when(engineSpy.mIsHighEndGfx).thenReturn(true); + + when(mWallpaperBitmap.getWidth()).thenReturn(bmpWidth); + when(mWallpaperBitmap.getHeight()).thenReturn(bmpHeight); + + ImageWallpaperRenderer renderer = createImageWallpaperRenderer(engineSpy); + doReturn(renderer).when(engineSpy).getRendererInstance(); + engineSpy.onCreate(engineSpy.getSurfaceHolder()); + + verify(mSurfaceHolder, times(1)).setFixedSize(surfaceWidth, surfaceHeight); + assertWithMessage("setFixedSizeAllowed should have been called.").that( + mEventCountdown.getCount()).isEqualTo(0); + + Size frameSize = renderer.reportSurfaceSize(); + Rect frame = new Rect(0, 0, frameSize.getWidth(), frameSize.getHeight()); + when(mSurfaceHolder.getSurfaceFrame()).thenReturn(frame); + + assertThat(engineSpy.checkIfShouldStopTransition()).isEqualTo(assertion); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java index 25efd320e554..7fb711f2af50 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java @@ -28,6 +28,7 @@ import android.util.DisplayMetrics; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.DumpController; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.brightline.BrightLineFalsingManager; import com.android.systemui.dock.DockManager; @@ -53,6 +54,8 @@ public class FalsingManagerProxyTest extends SysuiTestCase { PluginManager mPluginManager; @Mock(stubOnly = true) ProximitySensor mProximitySensor; + @Mock(stubOnly = true) + private KeyguardUpdateMonitor mKeyguardUpdateMonitor; private FalsingManagerProxy mProxy; private DeviceConfigProxy mDeviceConfig; private DisplayMetrics mDisplayMetrics = new DisplayMetrics(); @@ -63,7 +66,6 @@ public class FalsingManagerProxyTest extends SysuiTestCase { @Before public void setup() { MockitoAnnotations.initMocks(this); - mDependency.injectMockDependency(KeyguardUpdateMonitor.class); mDeviceConfig = new DeviceConfigProxyFake(); mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_MANAGER_ENABLED, "false", false); @@ -79,7 +81,8 @@ public class FalsingManagerProxyTest extends SysuiTestCase { @Test public void test_brightLineFalsingManagerDisabled() { mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics, - mProximitySensor, mDeviceConfig, mDockManager, mUiBgExecutor); + mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor, + new DumpController(), mUiBgExecutor); assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class)); } @@ -89,14 +92,16 @@ public class FalsingManagerProxyTest extends SysuiTestCase { BRIGHTLINE_FALSING_MANAGER_ENABLED, "true", false); mExecutor.runAllReady(); mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics, - mProximitySensor, mDeviceConfig, mDockManager, mUiBgExecutor); + mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor, + new DumpController(), mUiBgExecutor); assertThat(mProxy.getInternalFalsingManager(), instanceOf(BrightLineFalsingManager.class)); } @Test public void test_brightLineFalsingManagerToggled() throws InterruptedException { mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics, - mProximitySensor, mDeviceConfig, mDockManager, mUiBgExecutor); + mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor, + new DumpController(), mUiBgExecutor); assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class)); mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java new file mode 100644 index 000000000000..0aaa3b6ad329 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.classifier.brightline; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; + +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.util.DisplayMetrics; + +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.dock.DockManager; +import com.android.systemui.dock.DockManagerFake; +import com.android.systemui.util.DeviceConfigProxy; +import com.android.systemui.util.DeviceConfigProxyFake; +import com.android.systemui.util.sensors.ProximitySensor; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class BrightLineFalsingManagerTest extends SysuiTestCase { + + + @Mock + private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @Mock + private ProximitySensor mProximitySensor; + + private BrightLineFalsingManager mFalsingManager; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + DisplayMetrics dm = new DisplayMetrics(); + dm.xdpi = 100; + dm.ydpi = 100; + dm.widthPixels = 100; + dm.heightPixels = 100; + FalsingDataProvider falsingDataProvider = new FalsingDataProvider(dm); + DeviceConfigProxy deviceConfigProxy = new DeviceConfigProxyFake(); + DockManager dockManager = new DockManagerFake(); + mFalsingManager = new BrightLineFalsingManager(falsingDataProvider, + mKeyguardUpdateMonitor, mProximitySensor, deviceConfigProxy, dockManager); + } + + @Test + public void testRegisterSensor() { + mFalsingManager.onScreenTurningOn(); + verify(mProximitySensor).register(any(ProximitySensor.ProximitySensorListener.class)); + } + + @Test + public void testUnregisterSensor() { + mFalsingManager.onScreenTurningOn(); + reset(mProximitySensor); + mFalsingManager.onScreenOff(); + verify(mProximitySensor).unregister(any(ProximitySensor.ProximitySensorListener.class)); + } + + @Test + public void testUnregisterSensor_QS() { + mFalsingManager.onScreenTurningOn(); + reset(mProximitySensor); + mFalsingManager.setQsExpanded(true); + verify(mProximitySensor).unregister(any(ProximitySensor.ProximitySensorListener.class)); + mFalsingManager.setQsExpanded(false); + verify(mProximitySensor).register(any(ProximitySensor.ProximitySensorListener.class)); + } + + @Test + public void testUnregisterSensor_Bouncer() { + mFalsingManager.onScreenTurningOn(); + reset(mProximitySensor); + mFalsingManager.onBouncerShown(); + verify(mProximitySensor).unregister(any(ProximitySensor.ProximitySensorListener.class)); + mFalsingManager.onBouncerHidden(); + verify(mProximitySensor).register(any(ProximitySensor.ProximitySensorListener.class)); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java index 399f723f4d62..9117ea8f9fc2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java @@ -173,6 +173,21 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { } @Test + public void testPulsing_withoutLightSensor_setsAoDDimmingScrimTransparent() throws Exception { + mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager, + null /* sensor */, mBroadcastDispatcher, mDozeHost, null /* handler */, + DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY, + true /* debuggable */); + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.transitionTo(INITIALIZED, DOZE); + reset(mDozeHost); + + mScreen.transitionTo(DOZE, DOZE_REQUEST_PULSE); + + verify(mDozeHost).setAodDimmingScrim(eq(0f)); + } + + @Test public void testDockedAod_usesLightSensor() { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD_DOCKED); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupControllerTest.java index 1bfe1b10299b..715087dcd2b1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupControllerTest.java @@ -217,6 +217,6 @@ public class QSCarrierGroupControllerTest extends LeakCheckedTest { mSignalCallback.setMobileDataIndicators( mock(NetworkController.IconState.class), mock(NetworkController.IconState.class), - 0, 0, true, true, "", "", true, 0, true); + 0, 0, true, true, "", "", "", true, 0, true); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java index 26054027ed5f..62f406ff835a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java @@ -140,6 +140,11 @@ public class SbnBuilder { return this; } + public SbnBuilder setFlag(Context context, int mask, boolean value) { + modifyNotification(context).setFlag(mask, value); + return this; + } + public Notification.Builder modifyNotification(Context context) { if (mNotification != null) { mNotificationBuilder = new Notification.Builder(context, mNotification); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java index abc0f3ee8a52..96db16adb7dc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java @@ -46,9 +46,12 @@ import static org.mockito.Mockito.when; import static java.util.Objects.requireNonNull; import android.annotation.Nullable; +import android.app.Notification; import android.os.RemoteException; import android.service.notification.NotificationListenerService.Ranking; +import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.NotificationStats; +import android.service.notification.StatusBarNotification; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.ArrayMap; @@ -734,6 +737,78 @@ public class NotifCollectionTest extends SysuiTestCase { } @Test + public void testDismissingSummaryDoesNotDismissForegroundServiceChildren() { + // GIVEN a collection with three grouped notifs in it + CollectionEvent notif0 = postNotif( + buildNotif(TEST_PACKAGE, 0) + .setGroup(mContext, GROUP_1) + .setGroupSummary(mContext, true)); + CollectionEvent notif1 = postNotif( + buildNotif(TEST_PACKAGE, 1) + .setGroup(mContext, GROUP_1) + .setFlag(mContext, Notification.FLAG_FOREGROUND_SERVICE, true)); + CollectionEvent notif2 = postNotif( + buildNotif(TEST_PACKAGE, 2) + .setGroup(mContext, GROUP_1)); + + // WHEN the summary is dismissed + mCollection.dismissNotification(notif0.entry, defaultStats(notif0.entry)); + + // THEN the foreground service child is not dismissed + assertEquals(DISMISSED, notif0.entry.getDismissState()); + assertEquals(NOT_DISMISSED, notif1.entry.getDismissState()); + assertEquals(PARENT_DISMISSED, notif2.entry.getDismissState()); + } + + @Test + public void testDismissingSummaryDoesNotDismissBubbledChildren() { + // GIVEN a collection with three grouped notifs in it + CollectionEvent notif0 = postNotif( + buildNotif(TEST_PACKAGE, 0) + .setGroup(mContext, GROUP_1) + .setGroupSummary(mContext, true)); + CollectionEvent notif1 = postNotif( + buildNotif(TEST_PACKAGE, 1) + .setGroup(mContext, GROUP_1) + .setFlag(mContext, Notification.FLAG_BUBBLE, true)); + CollectionEvent notif2 = postNotif( + buildNotif(TEST_PACKAGE, 2) + .setGroup(mContext, GROUP_1)); + + // WHEN the summary is dismissed + mCollection.dismissNotification(notif0.entry, defaultStats(notif0.entry)); + + // THEN the bubbled child is not dismissed + assertEquals(DISMISSED, notif0.entry.getDismissState()); + assertEquals(NOT_DISMISSED, notif1.entry.getDismissState()); + assertEquals(PARENT_DISMISSED, notif2.entry.getDismissState()); + } + + @Test + public void testDismissingSummaryDoesNotDismissDuplicateSummaries() { + // GIVEN a group with a two summaries + CollectionEvent notif0 = postNotif( + buildNotif(TEST_PACKAGE, 0) + .setGroup(mContext, GROUP_1) + .setGroupSummary(mContext, true)); + CollectionEvent notif1 = postNotif( + buildNotif(TEST_PACKAGE, 1) + .setGroup(mContext, GROUP_1) + .setGroupSummary(mContext, true)); + CollectionEvent notif2 = postNotif( + buildNotif(TEST_PACKAGE, 2) + .setGroup(mContext, GROUP_1)); + + // WHEN the first summary is dismissed + mCollection.dismissNotification(notif0.entry, defaultStats(notif0.entry)); + + // THEN the second summary is not auto-dismissed (but the child is) + assertEquals(DISMISSED, notif0.entry.getDismissState()); + assertEquals(NOT_DISMISSED, notif1.entry.getDismissState()); + assertEquals(PARENT_DISMISSED, notif2.entry.getDismissState()); + } + + @Test public void testLifetimeExtendersAreQueriedWhenNotifRemoved() { // GIVEN a couple notifications and a few lifetime extenders mExtender1.shouldExtendLifetime = true; @@ -1000,6 +1075,13 @@ public class NotifCollectionTest extends SysuiTestCase { NotificationVisibility.obtain(entry.getKey(), 7, 2, true)); } + public CollectionEvent postNotif(NotificationEntryBuilder builder) { + clearInvocations(mCollectionListener); + NotifEvent rawEvent = mNoMan.postNotif(builder); + verify(mCollectionListener).onEntryAdded(mEntryCaptor.capture()); + return new CollectionEvent(rawEvent, requireNonNull(mEntryCaptor.getValue())); + } + private static class RecordingCollectionListener implements NotifCollectionListener { private final Map<String, NotificationEntry> mLastSeenEntries = new ArrayMap<>(); @@ -1014,6 +1096,7 @@ public class NotifCollectionTest extends SysuiTestCase { @Override public void onEntryUpdated(NotificationEntry entry) { + mLastSeenEntries.put(entry.getKey(), entry); } @Override @@ -1098,6 +1181,26 @@ public class NotifCollectionTest extends SysuiTestCase { } } + /** + * Wrapper around {@link NotifEvent} that adds the NotificationEntry that the collection under + * test creates. + */ + private static class CollectionEvent { + public final String key; + public final StatusBarNotification sbn; + public final Ranking ranking; + public final RankingMap rankingMap; + public final NotificationEntry entry; + + private CollectionEvent(NotifEvent rawEvent, NotificationEntry entry) { + this.key = rawEvent.key; + this.sbn = rawEvent.sbn; + this.ranking = rawEvent.ranking; + this.rankingMap = rawEvent.rankingMap; + this.entry = entry; + } + } + private static final String TEST_PACKAGE = "com.android.test.collection"; private static final String TEST_PACKAGE2 = "com.android.test.collection2"; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java index 300ec189e31d..d2bb0119c0b3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java @@ -162,6 +162,11 @@ public class NotificationEntryBuilder { return this; } + public NotificationEntryBuilder setFlag(Context context, int mask, boolean value) { + mSbnBuilder.setFlag(context, mask, value); + return this; + } + /* Delegated to RankingBuilder */ public NotificationEntryBuilder setRank(int rank) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java new file mode 100644 index 000000000000..61a4fbe3526c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection.coordinator; + +import static junit.framework.Assert.assertTrue; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.os.RemoteException; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.internal.statusbar.IStatusBarService; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl; +import com.android.systemui.statusbar.notification.collection.NotifPipeline; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; +import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.List; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class PreparationCoordinatorTest extends SysuiTestCase { + private static final String TEST_MESSAGE = "TEST_MESSAGE"; + + private PreparationCoordinator mCoordinator; + private NotifFilter mInflationErrorFilter; + private NotifInflationErrorManager mErrorManager; + private NotificationEntry mEntry; + private Exception mInflationError; + + @Mock + private NotifPipeline mNotifPipeline; + @Mock private IStatusBarService mService; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mEntry = new NotificationEntryBuilder().build(); + mInflationError = new Exception(TEST_MESSAGE); + mErrorManager = new NotifInflationErrorManager(); + + mCoordinator = new PreparationCoordinator( + mock(PreparationCoordinatorLogger.class), + mock(NotifInflaterImpl.class), + mErrorManager, + mService); + + ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class); + mCoordinator.attach(mNotifPipeline); + verify(mNotifPipeline, times(2)).addPreRenderFilter(filterCaptor.capture()); + List<NotifFilter> filters = filterCaptor.getAllValues(); + mInflationErrorFilter = filters.get(0); + } + + @Test + public void testErrorLogsToService() throws RemoteException { + // WHEN an entry has an inflation error. + mErrorManager.setInflationError(mEntry, mInflationError); + + // THEN we log to status bar service. + verify(mService).onNotificationError( + eq(mEntry.getSbn().getPackageName()), + eq(mEntry.getSbn().getTag()), + eq(mEntry.getSbn().getId()), + eq(mEntry.getSbn().getUid()), + eq(mEntry.getSbn().getInitialPid()), + eq(mInflationError.getMessage()), + eq(mEntry.getSbn().getUserId())); + } + + @Test + public void testFiltersOutErroredNotifications() { + // WHEN an entry has an inflation error. + mErrorManager.setInflationError(mEntry, mInflationError); + + // THEN we filter it from the notification list. + assertTrue(mInflationErrorFilter.shouldFilterOut(mEntry, 0)); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java index 8f9f65d12762..408bba48d422 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java @@ -28,9 +28,9 @@ import androidx.core.os.CancellationSignal; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.notification.NotificationEntryListener; -import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; +import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback; import org.junit.Before; @@ -57,17 +57,17 @@ public class NotifBindPipelineTest extends SysuiTestCase { @Before public void setUp() { MockitoAnnotations.initMocks(this); - NotificationEntryManager entryManager = mock(NotificationEntryManager.class); + CommonNotifCollection collection = mock(CommonNotifCollection.class); - mBindPipeline = new NotifBindPipeline(entryManager); + mBindPipeline = new NotifBindPipeline(collection); mBindPipeline.setStage(mStage); - ArgumentCaptor<NotificationEntryListener> entryListenerCaptor = - ArgumentCaptor.forClass(NotificationEntryListener.class); - verify(entryManager).addNotificationEntryListener(entryListenerCaptor.capture()); - NotificationEntryListener entryListener = entryListenerCaptor.getValue(); + ArgumentCaptor<NotifCollectionListener> collectionListenerCaptor = + ArgumentCaptor.forClass(NotifCollectionListener.class); + verify(collection).addCollectionListener(collectionListenerCaptor.capture()); + NotifCollectionListener listener = collectionListenerCaptor.getValue(); - entryListener.onPendingEntryAdded(mEntry); + listener.onEntryInit(mEntry); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index 35b55087873b..fd5512d62968 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -41,7 +41,6 @@ import android.text.TextUtils; import android.view.LayoutInflater; import android.widget.RemoteViews; -import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.TestableDependency; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.bubbles.BubblesTestActivity; @@ -50,10 +49,10 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.SmartReplyController; -import com.android.systemui.statusbar.notification.NotificationEntryListener; -import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; +import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpansionLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; @@ -90,7 +89,7 @@ public class NotificationTestHelper { private ExpandableNotificationRow mRow; private HeadsUpManagerPhone mHeadsUpManager; private final NotifBindPipeline mBindPipeline; - private final NotificationEntryListener mBindPipelineEntryListener; + private final NotifCollectionListener mBindPipelineEntryListener; private final RowContentBindStage mBindStage; private StatusBarStateController mStatusBarStateController; @@ -112,17 +111,17 @@ public class NotificationTestHelper { mock(NotifRemoteViewCache.class), mock(NotificationRemoteInputManager.class)); contentBinder.setInflateSynchronously(true); - mBindStage = new RowContentBindStage(contentBinder, mock(IStatusBarService.class)); + mBindStage = new RowContentBindStage(contentBinder, mock(NotifInflationErrorManager.class)); - NotificationEntryManager entryManager = mock(NotificationEntryManager.class); + CommonNotifCollection collection = mock(CommonNotifCollection.class); - mBindPipeline = new NotifBindPipeline(entryManager); + mBindPipeline = new NotifBindPipeline(collection); mBindPipeline.setStage(mBindStage); - ArgumentCaptor<NotificationEntryListener> entryListenerCaptor = - ArgumentCaptor.forClass(NotificationEntryListener.class); - verify(entryManager).addNotificationEntryListener(entryListenerCaptor.capture()); - mBindPipelineEntryListener = entryListenerCaptor.getValue(); + ArgumentCaptor<NotifCollectionListener> collectionListenerCaptor = + ArgumentCaptor.forClass(NotifCollectionListener.class); + verify(collection).addCollectionListener(collectionListenerCaptor.capture()); + mBindPipelineEntryListener = collectionListenerCaptor.getValue(); } /** @@ -377,7 +376,7 @@ public class NotificationTestHelper { entry.createIcons(mContext, entry.getSbn()); row.setEntry(entry); - mBindPipelineEntryListener.onPendingEntryAdded(entry); + mBindPipelineEntryListener.onEntryInit(entry); mBindPipeline.manageRow(entry, row); row.initialize( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java index 775f722b13f9..d9fe6551ba1c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java @@ -34,7 +34,6 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; -import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams; @@ -62,7 +61,7 @@ public class RowContentBindStageTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mRowContentBindStage = new RowContentBindStage(mBinder, - mock(IStatusBarService.class)); + mock(NotifInflationErrorManager.class)); mRowContentBindStage.createStageParams(mEntry); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java index 8936a2dcab7a..e917c93597ee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java @@ -38,7 +38,7 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.PulseExpansionHandler; -import com.android.systemui.statusbar.StatusBarWindowBlurController; +import com.android.systemui.statusbar.NotificationShadeWindowBlurController; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -80,7 +80,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { @Mock private DockManager mDockManager; @Mock private NotificationPanelViewController mNotificationPanelViewController; @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout; - @Mock private StatusBarWindowBlurController mStatusBarWindowBlurController; + @Mock private NotificationShadeWindowBlurController mNotificationShadeWindowBlurController; @Before public void setUp() { @@ -114,7 +114,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { new CommandQueue(mContext), mShadeController, mDockManager, - mStatusBarWindowBlurController, + mNotificationShadeWindowBlurController, mView, mNotificationPanelViewController); mController.setupExpandedStatusBar(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 008a3499997d..2e6fbe7d1ddb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -521,10 +521,10 @@ public class ScrimControllerTest extends SysuiTestCase { Assert.assertEquals(ScrimController.TRANSPARENT, mScrimInFront.getViewAlpha(), 0.0f); // Back scrim should be visible - Assert.assertEquals(ScrimController.GRADIENT_SCRIM_ALPHA_BUSY, + Assert.assertEquals(ScrimController.BUSY_SCRIM_ALPHA, mScrimBehind.getViewAlpha(), 0.0f); // Bubble scrim should be visible - Assert.assertEquals(ScrimController.GRADIENT_SCRIM_ALPHA_BUSY, + Assert.assertEquals(ScrimController.BUSY_SCRIM_ALPHA, mScrimBehind.getViewAlpha(), 0.0f); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java index 85f60338b6e8..18e7840f1201 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java @@ -115,15 +115,16 @@ public class CallbackHandlerTest extends SysuiTestCase { IconState qs = new IconState(true, 1, ""); boolean in = true; boolean out = true; - String typeDescription = "Test 1"; - String description = "Test 2"; + CharSequence typeDescription = "Test 1"; + CharSequence typeDescriptionHtml = "<b>Test 1</b>"; + CharSequence description = "Test 2"; int type = TelephonyIcons.ICON_1X; int qsType = TelephonyIcons.ICON_1X; boolean wide = true; int subId = 5; boolean roaming = true; mHandler.setMobileDataIndicators(status, qs, type, qsType, in, out, typeDescription, - description, wide, subId, roaming); + typeDescriptionHtml, description, wide, subId, roaming); waitForCallbacks(); ArgumentCaptor<IconState> statusArg = ArgumentCaptor.forClass(IconState.class); @@ -132,14 +133,16 @@ public class CallbackHandlerTest extends SysuiTestCase { ArgumentCaptor<Integer> qsTypeIconArg = ArgumentCaptor.forClass(Integer.class); ArgumentCaptor<Boolean> inArg = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor<Boolean> outArg = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor<String> typeContentArg = ArgumentCaptor.forClass(String.class); - ArgumentCaptor<String> descArg = ArgumentCaptor.forClass(String.class); + ArgumentCaptor<CharSequence> typeContentArg = ArgumentCaptor.forClass(CharSequence.class); + ArgumentCaptor<CharSequence> typeContentHtmlArg = + ArgumentCaptor.forClass(CharSequence.class); + ArgumentCaptor<CharSequence> descArg = ArgumentCaptor.forClass(CharSequence.class); ArgumentCaptor<Boolean> wideArg = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor<Integer> subIdArg = ArgumentCaptor.forClass(Integer.class); Mockito.verify(mSignalCallback).setMobileDataIndicators(statusArg.capture(), qsArg.capture(), typeIconArg.capture(), qsTypeIconArg.capture(), inArg.capture(), - outArg.capture(), typeContentArg.capture(), descArg.capture(), wideArg.capture(), - subIdArg.capture(), eq(roaming)); + outArg.capture(), typeContentArg.capture(), typeContentHtmlArg.capture(), + descArg.capture(), wideArg.capture(), subIdArg.capture(), eq(roaming)); assertEquals(status, statusArg.getValue()); assertEquals(qs, qsArg.getValue()); assertEquals(type, (int) typeIconArg.getValue()); @@ -147,6 +150,7 @@ public class CallbackHandlerTest extends SysuiTestCase { assertEquals(in, (boolean) inArg.getValue()); assertEquals(out, (boolean) outArg.getValue()); assertEquals(typeDescription, typeContentArg.getValue()); + assertEquals(typeDescriptionHtml, typeContentHtmlArg.getValue()); assertEquals(description, descArg.getValue()); assertEquals(wide, (boolean) wideArg.getValue()); assertEquals(subId, (int) subIdArg.getValue()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index 294d546e087b..9a0e97aad9c2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java @@ -413,7 +413,8 @@ public class NetworkControllerBaseTest extends SysuiTestCase { iconArg.capture(), anyInt(), typeIconArg.capture(), dataInArg.capture(), dataOutArg.capture(), - anyString(), anyString(), anyBoolean(), anyInt(), anyBoolean()); + any(CharSequence.class), any(CharSequence.class), any(CharSequence.class), + anyBoolean(), anyInt(), anyBoolean()); IconState iconState = iconArg.getValue(); int state = SignalDrawable.getState(icon, CellSignalStrength.getNumSignalStrengthLevels(), false); @@ -445,8 +446,9 @@ public class NetworkControllerBaseTest extends SysuiTestCase { iconArg.capture(), any(), typeIconArg.capture(), - anyInt(), anyBoolean(), anyBoolean(), anyString(), anyString(), anyBoolean(), - anyInt(), eq(roaming)); + anyInt(), anyBoolean(), anyBoolean(), + any(CharSequence.class), any(CharSequence.class), any(CharSequence.class), + anyBoolean(), anyInt(), eq(roaming)); IconState iconState = iconArg.getValue(); int state = icon == -1 ? 0 @@ -468,19 +470,23 @@ public class NetworkControllerBaseTest extends SysuiTestCase { boolean cutOut) { verifyLastMobileDataIndicators( visible, icon, typeIcon, qsVisible, qsIcon, qsTypeIcon, dataIn, dataOut, cutOut, - null); + null, null); } protected void verifyLastMobileDataIndicators(boolean visible, int icon, int typeIcon, boolean qsVisible, int qsIcon, int qsTypeIcon, boolean dataIn, boolean dataOut, - boolean cutOut, String typeContentDescription) { + boolean cutOut, CharSequence typeContentDescription, + CharSequence typeContentDescriptionHtml) { ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class); ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class); ArgumentCaptor<IconState> qsIconArg = ArgumentCaptor.forClass(IconState.class); ArgumentCaptor<Integer> qsTypeIconArg = ArgumentCaptor.forClass(Integer.class); ArgumentCaptor<Boolean> dataInArg = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor<Boolean> dataOutArg = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor<String> typeContentDescriptionArg = ArgumentCaptor.forClass(String.class); + ArgumentCaptor<CharSequence> typeContentDescriptionArg = + ArgumentCaptor.forClass(CharSequence.class); + ArgumentCaptor<CharSequence> typeContentDescriptionHtmlArg = + ArgumentCaptor.forClass(CharSequence.class); Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators( iconArg.capture(), @@ -490,6 +496,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { dataInArg.capture(), dataOutArg.capture(), typeContentDescriptionArg.capture(), + typeContentDescriptionHtmlArg.capture(), anyString(), anyBoolean(), anyInt(), anyBoolean()); IconState iconState = iconArg.getValue(); @@ -516,6 +523,10 @@ public class NetworkControllerBaseTest extends SysuiTestCase { assertEquals("Type content description", typeContentDescription, typeContentDescriptionArg.getValue()); } + if (typeContentDescriptionHtml != null) { // Only check if it was provided + assertEquals("Type content description (html)", typeContentDescriptionHtml, + typeContentDescriptionHtmlArg.getValue()); + } } protected void assertNetworkNameEquals(String expected) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java index 1eb59396e525..a906d9fa12bd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java @@ -132,7 +132,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { // Verify that a SignalDrawable with a cut out is used to display data disabled. verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, 0, true, DEFAULT_QS_SIGNAL_STRENGTH, 0, false, - false, true, NO_DATA_STRING); + false, true, NO_DATA_STRING, NO_DATA_STRING); } @Test @@ -146,7 +146,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { // Verify that a SignalDrawable with a cut out is used to display data disabled. verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, 0, true, DEFAULT_QS_SIGNAL_STRENGTH, 0, false, - false, true, NO_DATA_STRING); + false, true, NO_DATA_STRING, NO_DATA_STRING); } @Test @@ -161,7 +161,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { // Verify that a SignalDrawable with a cut out is used to display data disabled. verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, 0, true, DEFAULT_QS_SIGNAL_STRENGTH, 0, false, - false, false, NOT_DEFAULT_DATA_STRING); + false, false, NOT_DEFAULT_DATA_STRING, NOT_DEFAULT_DATA_STRING); } @Test @@ -176,7 +176,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { // Verify that a SignalDrawable with a cut out is used to display data disabled. verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, 0, true, DEFAULT_QS_SIGNAL_STRENGTH, 0, false, - false, false, NOT_DEFAULT_DATA_STRING); + false, false, NOT_DEFAULT_DATA_STRING, NOT_DEFAULT_DATA_STRING); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java index f52c8c17bfa5..b922f0600427 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java @@ -369,8 +369,8 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { mNetworkController.onReceive(mContext, intent); String defaultNetworkName = mMobileSignalController - .getStringIfExists( - com.android.internal.R.string.lockscreen_carrier_default); + .getTextIfExists( + com.android.internal.R.string.lockscreen_carrier_default).toString(); assertNetworkNameEquals(defaultNetworkName); } @@ -383,8 +383,8 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { mNetworkController.onReceive(mContext, intent); - String defaultNetworkName = mMobileSignalController.getStringIfExists( - com.android.internal.R.string.lockscreen_carrier_default); + String defaultNetworkName = mMobileSignalController.getTextIfExists( + com.android.internal.R.string.lockscreen_carrier_default).toString(); assertNetworkNameEquals(defaultNetworkName); } @@ -401,8 +401,8 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { mNetworkController.onReceive(mContext, intent); assertNetworkNameEquals(plmn - + mMobileSignalController.getStringIfExists( - R.string.status_bar_network_name_separator) + + mMobileSignalController.getTextIfExists( + R.string.status_bar_network_name_separator).toString() + spn); } diff --git a/packages/services/PacProcessor/src/com/android/pacprocessor/LibpacInterface.java b/packages/services/PacProcessor/src/com/android/pacprocessor/LibpacInterface.java new file mode 100644 index 000000000000..103ef7811ff0 --- /dev/null +++ b/packages/services/PacProcessor/src/com/android/pacprocessor/LibpacInterface.java @@ -0,0 +1,34 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.pacprocessor; + +/** + * Common interface for both Android's and WebView's implementation of PAC processor. + * + * @hide + */ +interface LibpacInterface { + default boolean startPacSupport() { + return true; + } + + default boolean stopPacSupport() { + return true; + } + + boolean setCurrentProxyScript(String script); + String makeProxyRequest(String url, String host); +} diff --git a/packages/services/PacProcessor/src/com/android/pacprocessor/PacNative.java b/packages/services/PacProcessor/src/com/android/pacprocessor/PacNative.java index 1e8109cb393c..9c0cfc27bd14 100644 --- a/packages/services/PacProcessor/src/com/android/pacprocessor/PacNative.java +++ b/packages/services/PacProcessor/src/com/android/pacprocessor/PacNative.java @@ -20,7 +20,7 @@ import android.util.Log; /** * @hide */ -public class PacNative { +public class PacNative implements LibpacInterface { private static final String TAG = "PacProxy"; private static final PacNative sInstance = new PacNative(); @@ -49,34 +49,38 @@ public class PacNative { return sInstance; } + @Override public synchronized boolean startPacSupport() { if (createV8ParserNativeLocked()) { Log.e(TAG, "Unable to Create v8 Proxy Parser."); - return true; + return false; } mIsActive = true; - return false; + return true; } + @Override public synchronized boolean stopPacSupport() { if (mIsActive) { if (destroyV8ParserNativeLocked()) { Log.e(TAG, "Unable to Destroy v8 Proxy Parser."); - return true; + return false; } mIsActive = false; } - return false; + return true; } + @Override public synchronized boolean setCurrentProxyScript(String script) { if (setProxyScriptNativeLocked(script)) { Log.e(TAG, "Unable to parse proxy script."); - return true; + return false; } - return false; + return true; } + @Override public synchronized String makeProxyRequest(String url, String host) { String ret = makeProxyRequestNativeLocked(url, host); if ((ret == null) || (ret.length() == 0)) { diff --git a/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java b/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java index b006d6e1fa7b..7aea721617b9 100644 --- a/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java +++ b/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java @@ -17,6 +17,7 @@ package com.android.pacprocessor; import android.app.Service; import android.content.Intent; +import android.content.res.Resources; import android.os.Binder; import android.os.IBinder; import android.os.Process; @@ -30,19 +31,24 @@ import java.net.URL; public class PacService extends Service { private static final String TAG = "PacService"; + private static final boolean sUseWebViewPacProcessor = Resources.getSystem().getBoolean( + com.android.internal.R.bool.config_useWebViewPacProcessor); + + private final LibpacInterface mLibpac = sUseWebViewPacProcessor + ? PacWebView.getInstance() + : PacNative.getInstance(); - private PacNative mPacNative = PacNative.getInstance(); private ProxyServiceStub mStub = new ProxyServiceStub(); @Override public void onCreate() { super.onCreate(); - mPacNative.startPacSupport(); + mLibpac.startPacSupport(); } @Override public void onDestroy() { - mPacNative.stopPacSupport(); + mLibpac.stopPacSupport(); super.onDestroy(); } @@ -52,7 +58,6 @@ public class PacService extends Service { } private class ProxyServiceStub extends IProxyService.Stub { - @Override public String resolvePacFile(String host, String url) throws RemoteException { try { @@ -69,7 +74,7 @@ public class PacService extends Service { throw new IllegalArgumentException("Invalid host was passed"); } } - return mPacNative.makeProxyRequest(url, host); + return mLibpac.makeProxyRequest(url, host); } catch (MalformedURLException e) { throw new IllegalArgumentException("Invalid URL was passed"); } @@ -81,7 +86,7 @@ public class PacService extends Service { Log.e(TAG, "Only system user is allowed to call setPacFile"); throw new SecurityException(); } - mPacNative.setCurrentProxyScript(script); + mLibpac.setCurrentProxyScript(script); } @Override diff --git a/packages/services/PacProcessor/src/com/android/pacprocessor/PacWebView.java b/packages/services/PacProcessor/src/com/android/pacprocessor/PacWebView.java new file mode 100644 index 000000000000..89c466500fef --- /dev/null +++ b/packages/services/PacProcessor/src/com/android/pacprocessor/PacWebView.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.pacprocessor; + +import android.util.Log; +import android.webkit.PacProcessor; + +import com.android.internal.annotations.GuardedBy; + +/** + * @hide + */ +public class PacWebView implements LibpacInterface { + private static final String TAG = "PacWebView"; + + private static final PacWebView sInstance = new PacWebView(); + + private Object mLock = new Object(); + + @GuardedBy("mLock") + private PacProcessor mProcessor = PacProcessor.getInstance(); + + public static PacWebView getInstance() { + return sInstance; + } + + @Override + public boolean setCurrentProxyScript(String script) { + synchronized (mLock) { + if (!mProcessor.setProxyScript(script)) { + Log.e(TAG, "Unable to parse proxy script."); + return false; + } + return true; + } + } + + @Override + public String makeProxyRequest(String url, String host) { + synchronized (mLock) { + return mProcessor.findProxyForUrl(url); + } + } +} diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java index 5844f9873001..1c4db1214d3b 100644 --- a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java +++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java @@ -18,12 +18,14 @@ package com.android.server.appprediction; import static android.Manifest.permission.MANAGE_APP_PREDICTIONS; import static android.Manifest.permission.PACKAGE_USAGE_STATS; +import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; import static android.content.Context.APP_PREDICTION_SERVICE; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.ActivityManagerInternal; import android.app.prediction.AppPredictionContext; import android.app.prediction.AppPredictionSessionId; import android.app.prediction.AppTargetEvent; @@ -34,7 +36,6 @@ import android.content.pm.ParceledListSlice; import android.os.Binder; import android.os.ResultReceiver; import android.os.ShellCallback; -import android.os.UserHandle; import android.util.Slog; import com.android.server.LocalServices; @@ -108,21 +109,21 @@ public class AppPredictionManagerService extends @Override public void createPredictionSession(@NonNull AppPredictionContext context, @NonNull AppPredictionSessionId sessionId) { - runForUserLocked("createPredictionSession", + runForUserLocked("createPredictionSession", sessionId, (service) -> service.onCreatePredictionSessionLocked(context, sessionId)); } @Override public void notifyAppTargetEvent(@NonNull AppPredictionSessionId sessionId, @NonNull AppTargetEvent event) { - runForUserLocked("notifyAppTargetEvent", + runForUserLocked("notifyAppTargetEvent", sessionId, (service) -> service.notifyAppTargetEventLocked(sessionId, event)); } @Override public void notifyLaunchLocationShown(@NonNull AppPredictionSessionId sessionId, @NonNull String launchLocation, @NonNull ParceledListSlice targetIds) { - runForUserLocked("notifyLaunchLocationShown", (service) -> + runForUserLocked("notifyLaunchLocationShown", sessionId, (service) -> service.notifyLaunchLocationShownLocked(sessionId, launchLocation, targetIds)); } @@ -130,32 +131,32 @@ public class AppPredictionManagerService extends public void sortAppTargets(@NonNull AppPredictionSessionId sessionId, @NonNull ParceledListSlice targets, IPredictionCallback callback) { - runForUserLocked("sortAppTargets", + runForUserLocked("sortAppTargets", sessionId, (service) -> service.sortAppTargetsLocked(sessionId, targets, callback)); } @Override public void registerPredictionUpdates(@NonNull AppPredictionSessionId sessionId, @NonNull IPredictionCallback callback) { - runForUserLocked("registerPredictionUpdates", + runForUserLocked("registerPredictionUpdates", sessionId, (service) -> service.registerPredictionUpdatesLocked(sessionId, callback)); } public void unregisterPredictionUpdates(@NonNull AppPredictionSessionId sessionId, @NonNull IPredictionCallback callback) { - runForUserLocked("unregisterPredictionUpdates", + runForUserLocked("unregisterPredictionUpdates", sessionId, (service) -> service.unregisterPredictionUpdatesLocked(sessionId, callback)); } @Override public void requestPredictionUpdate(@NonNull AppPredictionSessionId sessionId) { - runForUserLocked("requestPredictionUpdate", + runForUserLocked("requestPredictionUpdate", sessionId, (service) -> service.requestPredictionUpdateLocked(sessionId)); } @Override public void onDestroyPredictionSession(@NonNull AppPredictionSessionId sessionId) { - runForUserLocked("onDestroyPredictionSession", + runForUserLocked("onDestroyPredictionSession", sessionId, (service) -> service.onDestroyPredictionSessionLocked(sessionId)); } @@ -167,9 +168,12 @@ public class AppPredictionManagerService extends .exec(this, in, out, err, args, callback, resultReceiver); } - private void runForUserLocked(@NonNull String func, - @NonNull Consumer<AppPredictionPerUserService> c) { - final int userId = UserHandle.getCallingUserId(); + private void runForUserLocked(@NonNull final String func, + @NonNull final AppPredictionSessionId sessionId, + @NonNull final Consumer<AppPredictionPerUserService> c) { + ActivityManagerInternal am = LocalServices.getService(ActivityManagerInternal.class); + final int userId = am.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), + sessionId.getUserId(), false, ALLOW_NON_FULL, null, null); Context ctx = getContext(); if (!(ctx.checkCallingPermission(PACKAGE_USAGE_STATS) == PERMISSION_GRANTED diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 53f85ea7b119..72029d178534 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -2436,65 +2436,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState forceRemoveSelfLocked(AutofillManager.STATE_UNKNOWN_COMPAT_MODE); return; } - if (!Objects.equals(value, viewState.getCurrentValue())) { - if ((value == null || value.isEmpty()) - && viewState.getCurrentValue() != null - && viewState.getCurrentValue().isText() - && viewState.getCurrentValue().getTextValue() != null - && getSaveInfoLocked() != null) { - final int length = viewState.getCurrentValue().getTextValue().length(); - if (sDebug) { - Slog.d(TAG, "updateLocked(" + id + "): resetting value that was " - + length + " chars long"); - } - final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_VALUE_RESET) - .addTaggedData(MetricsEvent.FIELD_AUTOFILL_PREVIOUS_LENGTH, length); - mMetricsLogger.write(log); - } - - // Always update the internal state. - viewState.setCurrentValue(value); - - // Must check if this update was caused by autofilling the view, in which - // case we just update the value, but not the UI. - final AutofillValue filledValue = viewState.getAutofilledValue(); - if (filledValue != null) { - if (filledValue.equals(value)) { - if (sVerbose) { - Slog.v(TAG, "ignoring autofilled change on id " + id); - } - viewState.resetState(ViewState.STATE_CHANGED); - return; - } - else { - if ((viewState.id.equals(this.mCurrentViewId)) && - (viewState.getState() & ViewState.STATE_AUTOFILLED) != 0) { - // Remove autofilled state once field is changed after autofilling. - if (sVerbose) { - Slog.v(TAG, "field changed after autofill on id " + id); - } - viewState.resetState(ViewState.STATE_AUTOFILLED); - final ViewState currentView = mViewStates.get(mCurrentViewId); - currentView.maybeCallOnFillReady(flags); - } - } - } - - // Update the internal state... - viewState.setState(ViewState.STATE_CHANGED); - - //..and the UI - final String filterText; - if (value == null || !value.isText()) { - filterText = null; - } else { - final CharSequence text = value.getTextValue(); - // Text should never be null, but it doesn't hurt to check to avoid a - // system crash... - filterText = (text == null) ? null : text.toString(); - } - getUiForShowing().filterFillUi(filterText, this); + logIfViewClearedLocked(id, value, viewState); + updateViewStateAndUiOnValueChangedLocked(id, value, viewState, flags); } break; case ACTION_VIEW_ENTERED: @@ -2573,6 +2517,68 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return ArrayUtils.contains(response.getIgnoredIds(), id); } + @GuardedBy("mLock") + private void logIfViewClearedLocked(AutofillId id, AutofillValue value, ViewState viewState) { + if ((value == null || value.isEmpty()) + && viewState.getCurrentValue() != null + && viewState.getCurrentValue().isText() + && viewState.getCurrentValue().getTextValue() != null + && getSaveInfoLocked() != null) { + final int length = viewState.getCurrentValue().getTextValue().length(); + if (sDebug) { + Slog.d(TAG, "updateLocked(" + id + "): resetting value that was " + + length + " chars long"); + } + final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_VALUE_RESET) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_PREVIOUS_LENGTH, length); + mMetricsLogger.write(log); + } + } + + @GuardedBy("mLock") + private void updateViewStateAndUiOnValueChangedLocked(AutofillId id, AutofillValue value, + ViewState viewState, int flags) { + viewState.setCurrentValue(value); + + final String filterText; + if (value == null || !value.isText()) { + filterText = null; + } else { + final CharSequence text = value.getTextValue(); + // Text should never be null, but it doesn't hurt to check to avoid a + // system crash... + filterText = (text == null) ? null : text.toString(); + } + + final AutofillValue filledValue = viewState.getAutofilledValue(); + if (filledValue != null) { + if (filledValue.equals(value)) { + // When the update is caused by autofilling the view, just update the + // value, not the UI. + if (sVerbose) { + Slog.v(TAG, "ignoring autofilled change on id " + id); + } + viewState.resetState(ViewState.STATE_CHANGED); + return; + } else if ((viewState.id.equals(this.mCurrentViewId)) + && (viewState.getState() & ViewState.STATE_AUTOFILLED) != 0) { + // Remove autofilled state once field is changed after autofilling. + if (sVerbose) { + Slog.v(TAG, "field changed after autofill on id " + id); + } + viewState.resetState(ViewState.STATE_AUTOFILLED); + final ViewState currentView = mViewStates.get(mCurrentViewId); + currentView.maybeCallOnFillReady(flags); + } + } else if (viewState.id.equals(this.mCurrentViewId) + && (viewState.getState() & ViewState.STATE_INLINE_SHOWN) != 0) { + requestShowInlineSuggestionsLocked(viewState.getResponse(), filterText); + } + + viewState.setState(ViewState.STATE_CHANGED); + getUiForShowing().filterFillUi(filterText, this); + } + @Override public void onFillReady(@NonNull FillResponse response, @NonNull AutofillId filledId, @Nullable AutofillValue value) { @@ -2602,7 +2608,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (response.supportsInlineSuggestions()) { synchronized (mLock) { - if (requestShowInlineSuggestionsLocked(response)) { + if (requestShowInlineSuggestionsLocked(response, filterText)) { + final ViewState currentView = mViewStates.get(mCurrentViewId); + currentView.setState(ViewState.STATE_INLINE_SHOWN); //TODO(b/137800469): Fix it to log showed only when IME asks for inflation, // rather than here where framework sends back the response. mService.logDatasetShown(id, mClientState); @@ -2645,7 +2653,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** * Returns whether we made a request to show inline suggestions. */ - private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response) { + private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response, + @Nullable String filterText) { final List<Dataset> datasets = response.getDatasets(); if (datasets == null) { Log.w(TAG, "response returned null datasets"); @@ -2663,7 +2672,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState InlineSuggestionsResponse inlineSuggestionsResponse = InlineSuggestionFactory.createInlineSuggestionsResponse(request, response.getRequestId(), - datasets.toArray(new Dataset[]{}), response.getInlineActions(), + datasets.toArray(new Dataset[]{}), filterText, response.getInlineActions(), mCurrentViewId, mContext, this, () -> { synchronized (mLock) { requestHideFillUi(mCurrentViewId); diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java index 84886f83027d..f7c24f080fa4 100644 --- a/services/autofill/java/com/android/server/autofill/ViewState.java +++ b/services/autofill/java/com/android/server/autofill/ViewState.java @@ -74,6 +74,8 @@ final class ViewState { public static final int STATE_AUTOFILLED_ONCE = 0x800; /** View triggered the latest augmented autofill request. */ public static final int STATE_TRIGGERED_AUGMENTED_AUTOFILL = 0x1000; + /** Inline suggestions were shown for this View. */ + public static final int STATE_INLINE_SHOWN = 0x2000; public final AutofillId id; diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java index 57961423061f..5dc43ef8ad56 100644 --- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java @@ -313,6 +313,8 @@ final class FillUi { Slog.e(TAG, "Error inflating remote views", e); continue; } + // TODO: Extract the shared filtering logic here and in FillUi to a common + // method. final DatasetFieldFilter filter = dataset.getFilter(index); Pattern filterPattern = null; String valueText = null; @@ -602,6 +604,7 @@ final class FillUi { * Returns whether this item matches the value input by the user so it can be included * in the filtered datasets. */ + // TODO: Extract the shared filtering logic here and in FillUi to a common method. public boolean matches(CharSequence filterText) { if (TextUtils.isEmpty(filterText)) { // Always show item when the user input is empty diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java index 95a4a191a52e..5f6e47b04113 100644 --- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java +++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java @@ -17,6 +17,7 @@ package com.android.server.autofill.ui; import static com.android.server.autofill.Helper.sDebug; +import static com.android.server.autofill.Helper.sVerbose; import android.annotation.NonNull; import android.annotation.Nullable; @@ -24,10 +25,12 @@ import android.content.Context; import android.os.RemoteException; import android.service.autofill.Dataset; import android.service.autofill.InlinePresentation; +import android.text.TextUtils; import android.util.Slog; import android.view.SurfaceControl; import android.view.View; import android.view.autofill.AutofillId; +import android.view.autofill.AutofillValue; import android.view.inline.InlinePresentationSpec; import android.view.inputmethod.InlineSuggestion; import android.view.inputmethod.InlineSuggestionInfo; @@ -42,6 +45,7 @@ import com.android.server.UiThread; import java.util.ArrayList; import java.util.List; import java.util.function.BiFunction; +import java.util.regex.Pattern; public final class InlineSuggestionFactory { private static final String TAG = "InlineSuggestionFactory"; @@ -69,17 +73,19 @@ public final class InlineSuggestionFactory { @NonNull Runnable onErrorCallback) { if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called"); return createInlineSuggestionsResponseInternal(/* isAugmented= */ true, request, - datasets, /* inlineActions= */ null, autofillId, context, onErrorCallback, + datasets, /* filterText= */ null, /* inlineActions= */ null, autofillId, context, + onErrorCallback, (dataset, filedIndex) -> (v -> inlineSuggestionUiCallback.autofill(dataset))); } /** * Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by the - * autofill service. + * autofill service, potentially filtering the datasets. */ public static InlineSuggestionsResponse createInlineSuggestionsResponse( @NonNull InlineSuggestionsRequest request, int requestId, @NonNull Dataset[] datasets, + @Nullable String filterText, @Nullable List<InlinePresentation> inlineActions, @NonNull AutofillId autofillId, @NonNull Context context, @@ -87,15 +93,15 @@ public final class InlineSuggestionFactory { @NonNull Runnable onErrorCallback) { if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called"); return createInlineSuggestionsResponseInternal(/* isAugmented= */ false, request, datasets, - inlineActions, autofillId, context, onErrorCallback, + filterText, inlineActions, autofillId, context, onErrorCallback, (dataset, filedIndex) -> (v -> client.fill(requestId, filedIndex, dataset))); } private static InlineSuggestionsResponse createInlineSuggestionsResponseInternal( boolean isAugmented, @NonNull InlineSuggestionsRequest request, - @NonNull Dataset[] datasets, @Nullable List<InlinePresentation> inlineActions, - @NonNull AutofillId autofillId, @NonNull Context context, - @NonNull Runnable onErrorCallback, + @NonNull Dataset[] datasets, @Nullable String filterText, + @Nullable List<InlinePresentation> inlineActions, @NonNull AutofillId autofillId, + @NonNull Context context, @NonNull Runnable onErrorCallback, @NonNull BiFunction<Dataset, Integer, View.OnClickListener> onClickListenerFactory) { final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>(); final InlineSuggestionUi inlineSuggestionUi = new InlineSuggestionUi(context, @@ -113,6 +119,9 @@ public final class InlineSuggestionFactory { Slog.w(TAG, "InlinePresentation not found in dataset"); return null; } + if (!includeDataset(dataset, fieldIndex, filterText)) { + continue; + } InlineSuggestion inlineSuggestion = createInlineSuggestion(isAugmented, dataset, fieldIndex, mergedInlinePresentation(request, i, inlinePresentation), inlineSuggestionUi, onClickListenerFactory); @@ -129,6 +138,38 @@ public final class InlineSuggestionFactory { return new InlineSuggestionsResponse(inlineSuggestions); } + // TODO: Extract the shared filtering logic here and in FillUi to a common method. + private static boolean includeDataset(Dataset dataset, int fieldIndex, + @Nullable String filterText) { + // Show everything when the user input is empty. + if (TextUtils.isEmpty(filterText)) { + return true; + } + + final String constraintLowerCase = filterText.toString().toLowerCase(); + + // Use the filter provided by the service, if available. + final Dataset.DatasetFieldFilter filter = dataset.getFilter(fieldIndex); + if (filter != null) { + Pattern filterPattern = filter.pattern; + if (filterPattern == null) { + if (sVerbose) { + Slog.v(TAG, "Explicitly disabling filter for dataset id" + dataset.getId()); + } + return true; + } + return filterPattern.matcher(constraintLowerCase).matches(); + } + + final AutofillValue value = dataset.getFieldValues().get(fieldIndex); + if (value == null || !value.isText()) { + return dataset.getAuthentication() == null; + } + final String valueText = value.getTextValue().toString().toLowerCase(); + return valueText.toLowerCase().startsWith(constraintLowerCase); + } + + private static InlineSuggestion createInlineAction(boolean isAugmented, @NonNull Context context, @NonNull InlinePresentation inlinePresentation, diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 2c91a11c358c..7aa466146ddd 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -27,9 +27,12 @@ import static android.location.LocationManager.NETWORK_PROVIDER; import static android.location.LocationManager.PASSIVE_PROVIDER; import static android.os.PowerManager.locationPowerSaveModeToString; +import static java.util.concurrent.TimeUnit.NANOSECONDS; + import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.PendingIntent; @@ -45,6 +48,7 @@ import android.location.Geofence; import android.location.GnssMeasurementCorrections; import android.location.GnssRequest; import android.location.IBatchedLocationCallback; +import android.location.IGnssAntennaInfoListener; import android.location.IGnssMeasurementsListener; import android.location.IGnssNavigationMessageListener; import android.location.IGnssStatusListener; @@ -75,7 +79,6 @@ import android.stats.location.LocationStatsEnums; import android.text.TextUtils; import android.util.EventLog; import android.util.Log; -import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; @@ -106,6 +109,7 @@ import com.android.server.location.MockableLocationProvider; import com.android.server.location.PassiveProvider; import com.android.server.location.SettingsHelper; import com.android.server.location.UserInfoHelper; +import com.android.server.location.UserInfoHelper.UserListener; import com.android.server.location.gnss.GnssManagerService; import com.android.server.pm.permission.PermissionManagerServiceInternal; @@ -124,7 +128,6 @@ import java.util.Objects; import java.util.Set; import java.util.TreeMap; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.TimeUnit; /** * The service class that manages LocationProviders and issues location @@ -154,7 +157,7 @@ public class LocationManagerService extends ILocationManager.Stub { if (phase == PHASE_SYSTEM_SERVICES_READY) { // the location service must be functioning after this boot phase mService.onSystemReady(); - } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { + } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { // some providers rely on third party code, so we wait to initialize // providers until third party code is allowed to run mService.onSystemThirdPartyAppsCanStart(); @@ -179,6 +182,9 @@ public class LocationManagerService extends ILocationManager.Stub { // The maximum interval a location request can have and still be considered "high power". private static final long HIGH_POWER_INTERVAL_MS = 5 * 60 * 1000; + // The fastest interval that applications can receive coarse locations + private static final long FASTEST_COARSE_INTERVAL_MS = 10 * 60 * 1000; + // maximum age of a location before it is no longer considered "current" private static final long MAX_CURRENT_LOCATION_AGE_MS = 10 * 1000; @@ -207,8 +213,12 @@ public class LocationManagerService extends ILocationManager.Stub { private PackageManager mPackageManager; private PowerManager mPowerManager; - private GeofenceManager mGeofenceManager; + // TODO: sharing a location fudger with mock providers can leak information as the mock provider + // can be used to retrieve offset information. the fudger should likely be reset whenever mock + // providers are added or removed private LocationFudger mLocationFudger; + + private GeofenceManager mGeofenceManager; private GeocoderProxy mGeocodeProvider; @GuardedBy("mLock") @@ -228,16 +238,6 @@ public class LocationManagerService extends ILocationManager.Stub { private final LocationRequestStatistics mRequestStatistics = new LocationRequestStatistics(); - // mapping from provider name to last known location - @GuardedBy("mLock") - private final HashMap<String, Location> mLastLocation = new HashMap<>(); - - // same as mLastLocation, but is not updated faster than LocationFudger.FASTEST_INTERVAL_MS. - // locations stored here are not fudged for coarse permissions. - @GuardedBy("mLock") - private final HashMap<String, Location> mLastLocationCoarseInterval = - new HashMap<>(); - @GuardedBy("mLock") @PowerManager.LocationPowerSaveMode private int mBatterySaverMode; @@ -254,7 +254,7 @@ public class LocationManagerService extends ILocationManager.Stub { mAppForegroundHelper = new AppForegroundHelper(mContext); mLocationUsageLogger = new LocationUsageLogger(); - // set up passive provider - we do this early because it has no dependencies on system + // set up passive provider - we do this early because it has no dependencies on system // services or external code that isn't ready yet, and because this allows the variable to // be final. other more complex providers are initialized later, when system services are // ready @@ -281,18 +281,12 @@ public class LocationManagerService extends ILocationManager.Stub { mSettingsHelper.onSystemReady(); mAppForegroundHelper.onSystemReady(); - if (GnssManagerService.isGnssSupported()) { - mGnssManagerService = new GnssManagerService(mContext, mSettingsHelper, - mAppForegroundHelper, mLocationUsageLogger); - mGnssManagerService.onSystemReady(); - } - synchronized (mLock) { mPackageManager = mContext.getPackageManager(); mAppOps = mContext.getSystemService(AppOpsManager.class); mPowerManager = mContext.getSystemService(PowerManager.class); - mLocationFudger = new LocationFudger(mContext, mHandler); + mLocationFudger = new LocationFudger(mSettingsHelper.getCoarseLocationAccuracyM()); mGeofenceManager = new GeofenceManager(mContext, mSettingsHelper); PowerManagerInternal localPowerManager = @@ -376,10 +370,11 @@ public class LocationManagerService extends ILocationManager.Stub { } }, UserHandle.ALL, intentFilter, null, mHandler); - // switching the user from null to current here performs the bulk of the initialization - // work. the user being changed will cause a reload of all user specific settings, which - // causes initialization, and propagates changes until a steady state is reached - onUserChanged(UserHandle.USER_NULL, mUserInfoHelper.getCurrentUserId()); + // initialize the current users. we would get the user started notifications for these + // users eventually anyways, but this takes care of it as early as possible. + for (int userId: mUserInfoHelper.getCurrentUserIds()) { + onUserChanged(userId, UserListener.USER_STARTED); + } } } @@ -427,7 +422,7 @@ public class LocationManagerService extends ILocationManager.Stub { } if (D) { - Slog.d(TAG, + Log.d(TAG, "Battery Saver location mode changed from " + locationPowerSaveModeToString(mBatterySaverMode) + " to " + locationPowerSaveModeToString(newLocationMode)); @@ -456,15 +451,15 @@ public class LocationManagerService extends ILocationManager.Stub { Log.d(TAG, "[u" + userId + "] location enabled = " + enabled); } - synchronized (mLock) { - Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION) - .putExtra(LocationManager.EXTRA_LOCATION_ENABLED, enabled) - .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY) - .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); - mContext.sendBroadcastAsUser(intent, UserHandle.of(userId)); + Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION) + .putExtra(LocationManager.EXTRA_LOCATION_ENABLED, enabled) + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + mContext.sendBroadcastAsUser(intent, UserHandle.of(userId)); + synchronized (mLock) { for (LocationProviderManager manager : mProviderManagers) { - manager.onEnabledChangedLocked(userId); + manager.onEnabledChangedLocked(userId); } } } @@ -538,12 +533,6 @@ public class LocationManagerService extends ILocationManager.Stub { @GuardedBy("mLock") private void initializeProvidersLocked() { - if (mGnssManagerService != null) { - LocationProviderManager gnssManager = new LocationProviderManager(GPS_PROVIDER); - mProviderManagers.add(gnssManager); - gnssManager.setRealProvider(mGnssManagerService.getGnssLocationProvider()); - } - LocationProviderProxy networkProvider = LocationProviderProxy.createAndRegister( mContext, NETWORK_LOCATION_SERVICE_ACTION, @@ -554,7 +543,7 @@ public class LocationManagerService extends ILocationManager.Stub { mProviderManagers.add(networkManager); networkManager.setRealProvider(networkProvider); } else { - Slog.w(TAG, "no network location provider found"); + Log.w(TAG, "no network location provider found"); } // ensure that a fused provider exists which will work in direct boot @@ -574,14 +563,13 @@ public class LocationManagerService extends ILocationManager.Stub { mProviderManagers.add(fusedManager); fusedManager.setRealProvider(fusedProvider); } else { - Slog.e(TAG, "no fused location provider found", - new IllegalStateException("Location service needs a fused location provider")); + Log.e(TAG, "no fused location provider found"); } // bind to geocoder provider mGeocodeProvider = GeocoderProxy.createAndRegister(mContext); if (mGeocodeProvider == null) { - Slog.e(TAG, "no geocoder provider found"); + Log.e(TAG, "no geocoder provider found"); } // bind to geofence proxy @@ -590,7 +578,7 @@ public class LocationManagerService extends ILocationManager.Stub { if (gpsGeofenceHardware != null) { GeofenceProxy provider = GeofenceProxy.createAndBind(mContext, gpsGeofenceHardware); if (provider == null) { - Slog.d(TAG, "unable to bind to GeofenceProxy"); + Log.e(TAG, "unable to bind to GeofenceProxy"); } } } @@ -619,22 +607,52 @@ public class LocationManagerService extends ILocationManager.Stub { Integer.parseInt(fragments[9]) /* accuracy */); addTestProvider(name, properties, mContext.getOpPackageName()); } - } - private void onUserChanged(int oldUserId, int newUserId) { - if (D) { - Log.d(TAG, "foreground user is changing to " + newUserId); - } + // initialize gnss last because it has no awareness of boot phases and blindly assumes that + // all other location providers are loaded at initialization + if (GnssManagerService.isGnssSupported()) { + mGnssManagerService = new GnssManagerService(mContext, mSettingsHelper, + mAppForegroundHelper, mLocationUsageLogger); + mGnssManagerService.onSystemReady(); - synchronized (mLock) { - for (LocationProviderManager manager : mProviderManagers) { - // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility - mSettingsHelper.setLocationProviderAllowed(manager.getName(), - manager.isEnabled(newUserId), newUserId); + LocationProviderManager gnssManager = new LocationProviderManager(GPS_PROVIDER); + mProviderManagers.add(gnssManager); + gnssManager.setRealProvider(mGnssManagerService.getGnssLocationProvider()); + } + } - manager.onEnabledChangedLocked(oldUserId); - manager.onEnabledChangedLocked(newUserId); - } + private void onUserChanged(@UserIdInt int userId, @UserListener.UserChange int change) { + switch (change) { + case UserListener.USER_SWITCHED: + if (D) { + Log.d(TAG, "user " + userId + " current status changed"); + } + synchronized (mLock) { + for (LocationProviderManager manager : mProviderManagers) { + manager.onEnabledChangedLocked(userId); + } + } + break; + case UserListener.USER_STARTED: + if (D) { + Log.d(TAG, "user " + userId + " started"); + } + synchronized (mLock) { + for (LocationProviderManager manager : mProviderManagers) { + manager.onUserStarted(userId); + } + } + break; + case UserListener.USER_STOPPED: + if (D) { + Log.d(TAG, "user " + userId + " stopped"); + } + synchronized (mLock) { + for (LocationProviderManager manager : mProviderManagers) { + manager.onUserStopped(userId); + } + } + break; } } @@ -645,25 +663,29 @@ public class LocationManagerService extends ILocationManager.Stub { private final String mName; - // acquiring mLock makes operations on mProvider atomic, but is otherwise unnecessary - protected final MockableLocationProvider mProvider; - - // enabled state for parent user ids, no entry implies false. location state is only kept - // for parent user ids, the location state for a profile user id is assumed to be the same - // as for the parent. if querying this structure, ensure that the user id being used is a - // parent id or the results may be incorrect. + // if the provider is enabled for a given user id - null or not present means unknown @GuardedBy("mLock") private final SparseArray<Boolean> mEnabled; + // last location for a given user + @GuardedBy("mLock") + private final SparseArray<Location> mLastLocation; + + // last coarse location for a given user + @GuardedBy("mLock") + private final SparseArray<Location> mLastCoarseLocation; + + // acquiring mLock makes operations on mProvider atomic, but is otherwise unnecessary + protected final MockableLocationProvider mProvider; + private LocationProviderManager(String name) { mName = name; - mEnabled = new SparseArray<>(1); + mEnabled = new SparseArray<>(2); + mLastLocation = new SparseArray<>(2); + mLastCoarseLocation = new SparseArray<>(2); // initialize last since this lets our reference escape mProvider = new MockableLocationProvider(mLock, this); - - // we can assume all users start with disabled location state since the initial state - // of all providers is disabled. no need to initialize mEnabled further. } public String getName() { @@ -679,7 +701,26 @@ public class LocationManagerService extends ILocationManager.Stub { } public void setMockProvider(@Nullable MockProvider provider) { - mProvider.setMockProvider(provider); + synchronized (mLock) { + mProvider.setMockProvider(provider); + + // when removing a mock provider, also clear any mock last locations + if (provider == null) { + for (int i = 0; i < mLastLocation.size(); i++) { + Location lastLocation = mLastLocation.valueAt(i); + if (lastLocation != null && lastLocation.isFromMockProvider()) { + mLastLocation.setValueAt(i, null); + } + } + + for (int i = 0; i < mLastCoarseLocation.size(); i++) { + Location lastCoarseLocation = mLastCoarseLocation.valueAt(i); + if (lastCoarseLocation != null && lastCoarseLocation.isFromMockProvider()) { + mLastCoarseLocation.setValueAt(i, null); + } + } + } + } } public Set<String> getPackages() { @@ -691,6 +732,45 @@ public class LocationManagerService extends ILocationManager.Stub { return mProvider.getState().properties; } + @Nullable + public Location getLastFineLocation(int userId) { + synchronized (mLock) { + return mLastLocation.get(userId); + } + } + + @Nullable + public Location getLastCoarseLocation(int userId) { + synchronized (mLock) { + return mLastCoarseLocation.get(userId); + } + } + + public void injectLastLocation(Location location, int userId) { + synchronized (mLock) { + if (mLastLocation.get(userId) == null) { + setLastLocation(location, userId); + } + } + } + + private void setLastLocation(Location location, int userId) { + synchronized (mLock) { + mLastLocation.put(userId, location); + + // update last coarse interval only if enough time has passed + long timeDeltaMs = Long.MAX_VALUE; + Location coarseLocation = mLastCoarseLocation.get(userId); + if (coarseLocation != null) { + timeDeltaMs = NANOSECONDS.toMillis(location.getElapsedRealtimeNanos()) + - NANOSECONDS.toMillis(coarseLocation.getElapsedRealtimeNanos()); + } + if (timeDeltaMs > FASTEST_COARSE_INTERVAL_MS) { + mLastCoarseLocation.put(userId, mLocationFudger.createCoarse(location)); + } + } + } + public void setMockProviderAllowed(boolean enabled) { synchronized (mLock) { if (!mProvider.isMock()) { @@ -743,23 +823,31 @@ public class LocationManagerService extends ILocationManager.Stub { // don't validate mock locations if (!location.isFromMockProvider()) { if (location.getLatitude() == 0 && location.getLongitude() == 0) { - Slog.w(TAG, "blocking 0,0 location from " + mName + " provider"); + Log.w(TAG, "blocking 0,0 location from " + mName + " provider"); return; } } - handleLocationChangedLocked(location, this); + if (!location.isComplete()) { + Log.w(TAG, "blocking incomplete location from " + mName + " provider"); + return; + } + + // update last location if the provider is enabled or if servicing a bypass request + boolean locationSettingsIgnored = mProvider.getCurrentRequest().locationSettingsIgnored; + for (int userId : mUserInfoHelper.getCurrentUserIds()) { + if (locationSettingsIgnored || isEnabled(userId)) { + setLastLocation(location, userId); + } + } + + handleLocationChangedLocked(this, location, mLocationFudger.createCoarse(location)); } @GuardedBy("mLock") @Override public void onReportLocation(List<Location> locations) { - if (mGnssManagerService == null) { - return; - } - - if (!GPS_PROVIDER.equals(mName) || !isEnabled()) { - Slog.w(TAG, "reportLocationBatch() called without user permission"); + if (mGnssManagerService == null || !GPS_PROVIDER.equals(mName)) { return; } @@ -770,10 +858,7 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void onStateChanged(State oldState, State newState) { if (oldState.allowed != newState.allowed) { - // it would be more correct to call this for all users, but we know this can - // only affect the current user since providers are disabled for non-current - // users - onEnabledChangedLocked(mUserInfoHelper.getCurrentUserId()); + onEnabledChangedLocked(UserHandle.USER_ALL); } } @@ -781,37 +866,74 @@ public class LocationManagerService extends ILocationManager.Stub { mProvider.requestSetAllowed(allowed); } - public boolean isEnabled() { - return isEnabled(mUserInfoHelper.getCurrentUserId()); + public void onUserStarted(int userId) { + synchronized (mLock) { + // clear the user's enabled state in order to force a reevalution of whether the + // provider is enabled or disabled for the given user. we clear the user's state + // first to ensure that a user starting never causes any change notifications. it's + // possible for us to observe a user before we observe it's been started (for + // example, another component gets a user started notification before us and + // registers a location request immediately), which would cause us to already have + // some state in place. when we eventually do get the user started notification + // ourselves we don't want to send a change notification based on the prior state + mEnabled.put(userId, null); + onEnabledChangedLocked(userId); + } + } + + public void onUserStopped(int userId) { + synchronized (mLock) { + mEnabled.remove(userId); + mLastLocation.remove(userId); + mLastCoarseLocation.remove(userId); + } } public boolean isEnabled(int userId) { + if (userId == UserHandle.USER_NULL) { + // used during initialization - ignore since many lower level operations (checking + // settings for instance) do not support the null user + return false; + } + synchronized (mLock) { - // normalize user id to always refer to parent since profile state is always the - // same as parent state - userId = mUserInfoHelper.getParentUserId(userId); - return mEnabled.get(userId, Boolean.FALSE); + Boolean enabled = mEnabled.get(userId); + if (enabled == null) { + // this generally shouldn't occur, but might be possible due to race conditions + // on when we are notified of new users + Log.w(TAG, mName + " provider saw user " + userId + " unexpectedly"); + onEnabledChangedLocked(userId); + enabled = Objects.requireNonNull(mEnabled.get(userId)); + } + + return enabled; } } @GuardedBy("mLock") public void onEnabledChangedLocked(int userId) { if (userId == UserHandle.USER_NULL) { - // only used during initialization - we don't care about the null user + // used during initialization - ignore since many lower level operations (checking + // settings for instance) do not support the null user + return; + } else if (userId == UserHandle.USER_ALL) { + // we know enabled changes can only happen for current users since providers are + // always disabled for all non-current users + for (int currentUserId : mUserInfoHelper.getCurrentUserIds()) { + onEnabledChangedLocked(currentUserId); + } return; } - // normalize user id to always refer to parent since profile state is always the same - // as parent state - userId = mUserInfoHelper.getParentUserId(userId); - // if any property that contributes to "enabled" here changes state, it MUST result // in a direct or indrect call to onEnabledChangedLocked. this allows the provider to // guarantee that it will always eventually reach the correct state. - boolean enabled = (userId == mUserInfoHelper.getCurrentUserId()) - && mSettingsHelper.isLocationEnabled(userId) && mProvider.getState().allowed; + boolean enabled = mProvider.getState().allowed + && mUserInfoHelper.isCurrentUserId(userId) + && mSettingsHelper.isLocationEnabled(userId); - if (enabled == isEnabled(userId)) { + Boolean wasEnabled = mEnabled.get(userId); + if (wasEnabled != null && wasEnabled == enabled) { return; } @@ -821,28 +943,29 @@ public class LocationManagerService extends ILocationManager.Stub { Log.d(TAG, "[u" + userId + "] " + mName + " provider enabled = " + enabled); } - // fused and passive provider never get public updates for legacy reasons - if (!FUSED_PROVIDER.equals(mName) && !PASSIVE_PROVIDER.equals(mName)) { - // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility - mSettingsHelper.setLocationProviderAllowed(mName, enabled, userId); - - Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION) - .putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName) - .putExtra(LocationManager.EXTRA_PROVIDER_ENABLED, enabled) - .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY) - .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); - mContext.sendBroadcastAsUser(intent, UserHandle.of(userId)); + // clear last locations if we become disabled and if not servicing a bypass request + if (!enabled && !mProvider.getCurrentRequest().locationSettingsIgnored) { + mLastLocation.put(userId, null); + mLastCoarseLocation.put(userId, null); } - if (!enabled) { - // If any provider has been disabled, clear all last locations for all - // providers. This is to be on the safe side in case a provider has location - // derived from this disabled provider. - mLastLocation.clear(); - mLastLocationCoarseInterval.clear(); + // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility + mSettingsHelper.setLocationProviderAllowed(mName, enabled, userId); + + // do not send change notifications if we just saw this user for the first time + if (wasEnabled != null) { + // fused and passive provider never get public updates for legacy reasons + if (!FUSED_PROVIDER.equals(mName) && !PASSIVE_PROVIDER.equals(mName)) { + Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION) + .putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName) + .putExtra(LocationManager.EXTRA_PROVIDER_ENABLED, enabled) + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + mContext.sendBroadcastAsUser(intent, UserHandle.of(userId)); + } } - updateProviderEnabledLocked(this); + updateProviderEnabledLocked(this, enabled); } public void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { @@ -855,7 +978,11 @@ public class LocationManagerService extends ILocationManager.Stub { pw.increaseIndent(); - pw.println("enabled=" + isEnabled()); + // for now we only dump for the parent user + int userId = mUserInfoHelper.getCurrentUserIds()[0]; + pw.println("last location=" + mLastLocation.get(userId)); + pw.println("last coarse location=" + mLastCoarseLocation.get(userId)); + pw.println("enabled=" + isEnabled(userId)); } mProvider.dump(fd, pw, args); @@ -1004,7 +1131,8 @@ public class LocationManagerService extends ILocationManager.Stub { if (manager == null) { continue; } - if (!manager.isEnabled() && !isSettingsExempt(updateRecord)) { + if (!manager.isEnabled(UserHandle.getUserId(mCallerIdentity.mUid)) + && !isSettingsExempt(updateRecord)) { continue; } @@ -1420,7 +1548,7 @@ public class LocationManagerService extends ILocationManager.Stub { if (FUSED_PROVIDER.equals(name)) { continue; } - if (enabledOnly && !manager.isEnabled()) { + if (enabledOnly && !manager.isEnabled(UserHandle.getCallingUserId())) { continue; } if (criteria != null @@ -1462,15 +1590,12 @@ public class LocationManagerService extends ILocationManager.Stub { } @GuardedBy("mLock") - private void updateProviderEnabledLocked(LocationProviderManager manager) { - boolean enabled = manager.isEnabled(); - + private void updateProviderEnabledLocked(LocationProviderManager manager, boolean enabled) { ArrayList<Receiver> deadReceivers = null; - ArrayList<UpdateRecord> records = mRecordsByProvider.get(manager.getName()); if (records != null) { for (UpdateRecord record : records) { - if (!mUserInfoHelper.isCurrentUserOrProfile( + if (!mUserInfoHelper.isCurrentUserId( UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) { continue; } @@ -1528,8 +1653,8 @@ public class LocationManagerService extends ILocationManager.Stub { // initialize the low power mode to true and set to false if any of the records requires providerRequest.setLowPowerMode(true); for (UpdateRecord record : records) { - if (!mUserInfoHelper.isCurrentUserOrProfile( - UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) { + int userId = UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid); + if (!mUserInfoHelper.isCurrentUserId(userId)) { continue; } if (!checkLocationAccess( @@ -1541,7 +1666,7 @@ public class LocationManagerService extends ILocationManager.Stub { } final boolean isBatterySaverDisablingLocation = shouldThrottleRequests || (isForegroundOnlyMode && !record.mIsForegroundUid); - if (!manager.isEnabled() || isBatterySaverDisablingLocation) { + if (!manager.isEnabled(userId) || isBatterySaverDisablingLocation) { if (isSettingsExempt(record)) { providerRequest.setLocationSettingsIgnored(true); providerRequest.setLowPowerMode(false); @@ -1587,7 +1712,7 @@ public class LocationManagerService extends ILocationManager.Stub { // TODO: overflow long thresholdInterval = (providerRequest.getInterval() + 1000) * 3 / 2; for (UpdateRecord record : records) { - if (mUserInfoHelper.isCurrentUserOrProfile( + if (mUserInfoHelper.isCurrentUserId( UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) { LocationRequest locationRequest = record.mRequest; @@ -1697,11 +1822,8 @@ public class LocationManagerService extends ILocationManager.Stub { mStackTrace = new Throwable(); } - ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider); - if (records == null) { - records = new ArrayList<>(); - mRecordsByProvider.put(provider, records); - } + ArrayList<UpdateRecord> records = mRecordsByProvider.computeIfAbsent(provider, + k -> new ArrayList<>()); if (!records.contains(this)) { records.add(this); } @@ -1837,11 +1959,11 @@ public class LocationManagerService extends ILocationManager.Stub { break; } // throttle - if (sanitizedRequest.getInterval() < LocationFudger.FASTEST_INTERVAL_MS) { - sanitizedRequest.setInterval(LocationFudger.FASTEST_INTERVAL_MS); + if (sanitizedRequest.getInterval() < FASTEST_COARSE_INTERVAL_MS) { + sanitizedRequest.setInterval(FASTEST_COARSE_INTERVAL_MS); } - if (sanitizedRequest.getFastestInterval() < LocationFudger.FASTEST_INTERVAL_MS) { - sanitizedRequest.setFastestInterval(LocationFudger.FASTEST_INTERVAL_MS); + if (sanitizedRequest.getFastestInterval() < FASTEST_COARSE_INTERVAL_MS) { + sanitizedRequest.setFastestInterval(FASTEST_COARSE_INTERVAL_MS); } } // make getFastestInterval() the minimum of interval and fastest interval @@ -1916,7 +2038,7 @@ public class LocationManagerService extends ILocationManager.Stub { receiver = getReceiverLocked(listener, pid, uid, packageName, featureId, workSource, hideFromAppOps, listenerIdentifier); } - requestLocationUpdatesLocked(sanitizedRequest, receiver, uid, packageName); + requestLocationUpdatesLocked(sanitizedRequest, receiver); } finally { Binder.restoreCallingIdentity(identity); } @@ -1924,8 +2046,7 @@ public class LocationManagerService extends ILocationManager.Stub { } @GuardedBy("mLock") - private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver, - int uid, String packageName) { + private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver) { // Figure out the provider. Either its explicitly request (legacy use cases), or // use the fused provider if (request == null) request = DEFAULT_LOCATION_REQUEST; @@ -1940,20 +2061,14 @@ public class LocationManagerService extends ILocationManager.Stub { } UpdateRecord record = new UpdateRecord(name, request, receiver); - if (D) { - Log.d(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver)) - + " " + name + " " + request + " from " + packageName + "(" + uid + " " - + (record.mIsForegroundUid ? "foreground" : "background") - + (isThrottlingExempt(receiver.mCallerIdentity) - ? " [whitelisted]" : "") + ")"); - } UpdateRecord oldRecord = receiver.mUpdateRecords.put(name, record); if (oldRecord != null) { oldRecord.disposeLocked(false); } - if (!manager.isEnabled() && !isSettingsExempt(record)) { + int userId = UserHandle.getUserId(receiver.mCallerIdentity.mUid); + if (!manager.isEnabled(userId) && !isSettingsExempt(record)) { // Notify the listener that updates are currently disabled - but only if the request // does not ignore location settings receiver.callProviderEnabledLocked(name, false); @@ -2030,91 +2145,65 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public Location getLastLocation(LocationRequest r, String packageName, String featureId) { + public Location getLastLocation(LocationRequest request, String packageName, String featureId) { + if (request == null) { + request = DEFAULT_LOCATION_REQUEST; + } + enforceCallingOrSelfLocationPermission(); enforceCallingOrSelfPackageName(packageName); - synchronized (mLock) { - LocationRequest request = r != null ? r : DEFAULT_LOCATION_REQUEST; - int allowedResolutionLevel = getCallerAllowedResolutionLevel(); - // no need to sanitize this request, as only the provider name is used + int allowedResolutionLevel = getCallerAllowedResolutionLevel(); + if (!reportLocationAccessNoThrow(Binder.getCallingPid(), Binder.getCallingUid(), + packageName, featureId, allowedResolutionLevel, null)) { + if (D) { + Log.d(TAG, "not returning last loc for no op app: " + packageName); + } + return null; + } - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final long identity = Binder.clearCallingIdentity(); - try { - if (mSettingsHelper.isLocationPackageBlacklisted(UserHandle.getUserId(uid), - packageName)) { - if (D) { - Log.d(TAG, "not returning last loc for blacklisted app: " - + packageName); - } - return null; - } + int userId = UserHandle.getCallingUserId(); - // Figure out the provider. Either its explicitly request (deprecated API's), - // or use the fused provider - String name = request.getProvider(); - if (name == null) name = LocationManager.FUSED_PROVIDER; - LocationProviderManager manager = getLocationProviderManager(name); - if (manager == null) return null; + if (mSettingsHelper.isLocationPackageBlacklisted(userId, packageName)) { + return null; + } - // only the current user or location providers may get location this way - if (!mUserInfoHelper.isCurrentUserOrProfile(UserHandle.getUserId(uid)) - && !mLocalService.isProviderPackage(packageName)) { - return null; - } + if (!mUserInfoHelper.isCurrentUserId(userId)) { + return null; + } - if (!manager.isEnabled()) { - return null; - } + synchronized (mLock) { + LocationProviderManager manager = getLocationProviderManager(request.getProvider()); + if (manager == null) { + return null; + } - Location location; - if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) { - // Make sure that an app with coarse permissions can't get frequent location - // updates by calling LocationManager.getLastKnownLocation repeatedly. - location = mLastLocationCoarseInterval.get(name); - } else { - location = mLastLocation.get(name); - } - if (location == null) { - return null; - } + if (!manager.isEnabled(userId) && !request.isLocationSettingsIgnored()) { + return null; + } - // Don't return stale location to apps with foreground-only location permission. - String op = resolutionLevelToOpStr(allowedResolutionLevel); - long locationAgeMs = TimeUnit.NANOSECONDS.toMillis( - SystemClock.elapsedRealtime() - location.getElapsedRealtimeNanos()); - if (locationAgeMs > mSettingsHelper.getMaxLastLocationAgeMs() - && (mAppOps.unsafeCheckOp(op, uid, packageName) - == AppOpsManager.MODE_FOREGROUND)) { - return null; - } + Location location; + if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) { + location = manager.getLastCoarseLocation(userId); + } else { + location = manager.getLastFineLocation(userId); + } + if (location == null) { + return null; + } - Location lastLocation = null; - if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) { - Location noGPSLocation = location.getExtraLocation( - Location.EXTRA_NO_GPS_LOCATION); - if (noGPSLocation != null) { - lastLocation = new Location(mLocationFudger.getOrCreate(noGPSLocation)); - } - } else { - lastLocation = new Location(location); - } - // Don't report location access if there is no last location to deliver. - if (lastLocation != null) { - if (!reportLocationAccessNoThrow(pid, uid, packageName, featureId, - allowedResolutionLevel, null)) { - if (D) { - Log.d(TAG, "not returning last loc for no op app: " + packageName); - } - lastLocation = null; - } - } - return lastLocation; - } finally { - Binder.restoreCallingIdentity(identity); + // Don't return stale location to apps with foreground-only location permission. + String op = resolutionLevelToOpStr(allowedResolutionLevel); + long locationAgeMs = NANOSECONDS.toMillis( + SystemClock.elapsedRealtime() - location.getElapsedRealtimeNanos()); + if (locationAgeMs > mSettingsHelper.getMaxLastLocationAgeMs() + && (mAppOps.unsafeCheckOp(op, Binder.getCallingUid(), packageName) + == AppOpsManager.MODE_FOREGROUND)) { + return null; } + + // make a defensive copy - the client could be in the same process as us + return new Location(location); } } @@ -2125,7 +2214,7 @@ public class LocationManagerService extends ILocationManager.Stub { // side effect of validating locationRequest and packageName Location lastLocation = getLastLocation(locationRequest, packageName, featureId); if (lastLocation != null) { - long locationAgeMs = TimeUnit.NANOSECONDS.toMillis( + long locationAgeMs = NANOSECONDS.toMillis( SystemClock.elapsedRealtimeNanos() - lastLocation.getElapsedRealtimeNanos()); if (locationAgeMs < MAX_CURRENT_LOCATION_AGE_MS) { @@ -2160,38 +2249,36 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public LocationTime getGnssTimeMillis() { synchronized (mLock) { - Location location = mLastLocation.get(LocationManager.GPS_PROVIDER); + LocationProviderManager gpsManager = getLocationProviderManager(GPS_PROVIDER); + if (gpsManager == null) { + return null; + } + + Location location = gpsManager.getLastFineLocation(UserHandle.getCallingUserId()); if (location == null) { return null; } + long currentNanos = SystemClock.elapsedRealtimeNanos(); - long deltaMs = (currentNanos - location.getElapsedRealtimeNanos()) / 1000000L; + long deltaMs = NANOSECONDS.toMillis( + currentNanos - location.getElapsedRealtimeNanos()); return new LocationTime(location.getTime() + deltaMs, currentNanos); } } @Override - public boolean injectLocation(Location location) { + public void injectLocation(Location location) { mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, null); mContext.enforceCallingPermission(ACCESS_FINE_LOCATION, null); Preconditions.checkArgument(location.isComplete()); + int userId = UserHandle.getCallingUserId(); synchronized (mLock) { LocationProviderManager manager = getLocationProviderManager(location.getProvider()); - if (manager == null || !manager.isEnabled()) { - return false; - } - - // NOTE: If last location is already available, location is not injected. If - // provider's normal source (like a GPS chipset) have already provided an output - // there is no need to inject this location. - if (mLastLocation.get(manager.getName()) != null) { - return false; + if (manager != null && manager.isEnabled(userId)) { + manager.injectLastLocation(Objects.requireNonNull(location), userId); } - - updateLastLocationLocked(location, manager.getName()); - return true; } } @@ -2322,6 +2409,22 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override + public boolean addGnssAntennaInfoListener(IGnssAntennaInfoListener listener, + String packageName, String featureId, String listenerIdentifier) { + Objects.requireNonNull(listenerIdentifier); + + return mGnssManagerService != null && mGnssManagerService.addGnssAntennaInfoListener( + listener, packageName, featureId, listenerIdentifier); + } + + @Override + public void removeGnssAntennaInfoListener(IGnssAntennaInfoListener listener) { + if (mGnssManagerService != null) { + mGnssManagerService.removeGnssAntennaInfoListener(listener); + } + } + + @Override public boolean addGnssNavigationMessageListener(IGnssNavigationMessageListener listener, String packageName, String featureId, String listenerIdentifier) { Objects.requireNonNull(listenerIdentifier); @@ -2440,18 +2543,15 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public boolean isProviderEnabledForUser(String providerName, int userId) { + public boolean isProviderEnabledForUser(String provider, int userId) { userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, false, false, "isProviderEnabledForUser", null); // Fused provider is accessed indirectly via criteria rather than the provider-based APIs, // so we discourage its use - if (FUSED_PROVIDER.equals(providerName)) return false; + if (FUSED_PROVIDER.equals(provider)) return false; - synchronized (mLock) { - LocationProviderManager manager = getLocationProviderManager(providerName); - return manager != null && manager.isEnabled(userId); - } + return mLocalService.isProviderEnabledForUser(provider, userId); } @GuardedBy("mLock") @@ -2464,7 +2564,7 @@ public class LocationManagerService extends ILocationManager.Stub { // Check whether sufficient time has passed long minTime = record.mRealRequest.getFastestInterval(); - long deltaMs = TimeUnit.NANOSECONDS.toMillis( + long deltaMs = NANOSECONDS.toMillis( loc.getElapsedRealtimeNanos() - lastLoc.getElapsedRealtimeNanos()); if (deltaMs < minTime - MAX_PROVIDER_SCHEDULING_JITTER_MS) { return false; @@ -2488,62 +2588,23 @@ public class LocationManagerService extends ILocationManager.Stub { } @GuardedBy("mLock") - private void handleLocationChangedLocked(Location location, LocationProviderManager manager) { + private void handleLocationChangedLocked(LocationProviderManager manager, Location location, + Location coarseLocation) { if (!mProviderManagers.contains(manager)) { Log.w(TAG, "received location from unknown provider: " + manager.getName()); return; } - if (!location.isComplete()) { - Log.w(TAG, "dropping incomplete location from " + manager.getName() + " provider: " - + location); - return; - } // notify passive provider if (manager != mPassiveManager) { - mPassiveManager.updateLocation(new Location(location)); + mPassiveManager.updateLocation(location); } - if (D) Log.d(TAG, "incoming location: " + location); long now = SystemClock.elapsedRealtime(); - - // only update last location for locations that come from enabled providers - if (manager.isEnabled()) { - updateLastLocationLocked(location, manager.getName()); - } - - // Update last known coarse interval location if enough time has passed. - Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get( - manager.getName()); - if (lastLocationCoarseInterval == null) { - lastLocationCoarseInterval = new Location(location); - - if (manager.isEnabled()) { - mLastLocationCoarseInterval.put(manager.getName(), lastLocationCoarseInterval); - } - } - long timeDeltaMs = TimeUnit.NANOSECONDS.toMillis(location.getElapsedRealtimeNanos() - - lastLocationCoarseInterval.getElapsedRealtimeNanos()); - if (timeDeltaMs > LocationFudger.FASTEST_INTERVAL_MS) { - lastLocationCoarseInterval.set(location); - } - // Don't ever return a coarse location that is more recent than the allowed update - // interval (i.e. don't allow an app to keep registering and unregistering for - // location updates to overcome the minimum interval). - Location noGPSLocation = - lastLocationCoarseInterval.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION); - - // Skip if there are no UpdateRecords for this provider. ArrayList<UpdateRecord> records = mRecordsByProvider.get(manager.getName()); if (records == null || records.size() == 0) return; - // Fetch coarse location - Location coarseLocation = null; - if (noGPSLocation != null) { - coarseLocation = mLocationFudger.getOrCreate(noGPSLocation); - } - ArrayList<Receiver> deadReceivers = null; ArrayList<UpdateRecord> deadUpdateRecords = null; @@ -2551,22 +2612,23 @@ public class LocationManagerService extends ILocationManager.Stub { for (UpdateRecord r : records) { Receiver receiver = r.mReceiver; boolean receiverDead = false; + int userId = UserHandle.getUserId(receiver.mCallerIdentity.mUid); - if (!manager.isEnabled() && !isSettingsExempt(r)) { + + if (!manager.isEnabled(userId) && !isSettingsExempt(r)) { continue; } - int receiverUserId = UserHandle.getUserId(receiver.mCallerIdentity.mUid); - if (!mUserInfoHelper.isCurrentUserOrProfile(receiverUserId) + if (!mUserInfoHelper.isCurrentUserId(userId) && !isProviderPackage(receiver.mCallerIdentity.mPackageName)) { if (D) { - Log.d(TAG, "skipping loc update for background user " + receiverUserId + - " (app: " + receiver.mCallerIdentity.mPackageName + ")"); + Log.d(TAG, "skipping loc update for background user " + userId + + " (app: " + receiver.mCallerIdentity.mPackageName + ")"); } continue; } - if (mSettingsHelper.isLocationPackageBlacklisted(receiverUserId, + if (mSettingsHelper.isLocationPackageBlacklisted(userId, receiver.mCallerIdentity.mPackageName)) { if (D) { Log.d(TAG, "skipping loc update for blacklisted app: " + @@ -2581,39 +2643,30 @@ public class LocationManagerService extends ILocationManager.Stub { } else { notifyLocation = location; // use fine location } - if (notifyLocation != null) { - Location lastLoc = r.mLastFixBroadcast; - if ((lastLoc == null) - || shouldBroadcastSafeLocked(notifyLocation, lastLoc, r, now)) { - if (lastLoc == null) { - lastLoc = new Location(notifyLocation); - r.mLastFixBroadcast = lastLoc; - } else { - lastLoc.set(notifyLocation); - } - // Report location access before delivering location to the client. This will - // note location delivery to appOps, so it should be called only when a - // location is really being delivered to the client. - if (!reportLocationAccessNoThrow( - receiver.mCallerIdentity.mPid, - receiver.mCallerIdentity.mUid, - receiver.mCallerIdentity.mPackageName, - receiver.mCallerIdentity.mFeatureId, - receiver.mAllowedResolutionLevel, - "Location sent to " + receiver.mCallerIdentity.mListenerIdentifier)) { - if (D) { - Log.d(TAG, "skipping loc update for no op app: " - + receiver.mCallerIdentity.mPackageName); - } - continue; - } - if (!receiver.callLocationChangedLocked(notifyLocation)) { - Slog.w(TAG, "RemoteException calling onLocationChanged on " - + receiver); - receiverDead = true; + if (shouldBroadcastSafeLocked(notifyLocation, r.mLastFixBroadcast, r, now)) { + r.mLastFixBroadcast = notifyLocation; + // Report location access before delivering location to the client. This will + // note location delivery to appOps, so it should be called only when a + // location is really being delivered to the client. + if (!reportLocationAccessNoThrow( + receiver.mCallerIdentity.mPid, + receiver.mCallerIdentity.mUid, + receiver.mCallerIdentity.mPackageName, + receiver.mCallerIdentity.mFeatureId, + receiver.mAllowedResolutionLevel, + "Location sent to " + receiver.mCallerIdentity.mListenerIdentifier)) { + if (D) { + Log.d(TAG, "skipping loc update for no op app: " + + receiver.mCallerIdentity.mPackageName); } - r.mRealRequest.decrementNumUpdates(); + continue; } + if (!receiver.callLocationChangedLocked(notifyLocation)) { + Log.w(TAG, "RemoteException calling onLocationChanged on " + + receiver); + receiverDead = true; + } + r.mRealRequest.decrementNumUpdates(); } // track expired records @@ -2650,30 +2703,6 @@ public class LocationManagerService extends ILocationManager.Stub { } } - @GuardedBy("mLock") - private void updateLastLocationLocked(Location location, String provider) { - Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION); - Location lastNoGPSLocation; - Location lastLocation = mLastLocation.get(provider); - if (lastLocation == null) { - lastLocation = new Location(provider); - mLastLocation.put(provider, lastLocation); - } else { - lastNoGPSLocation = lastLocation.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION); - if (noGPSLocation == null && lastNoGPSLocation != null) { - // New location has no no-GPS location: adopt last no-GPS location. This is set - // directly into location because we do not want to notify COARSE clients. - Bundle extras = location.getExtras(); - if (extras == null) { - extras = new Bundle(); - } - extras.putParcelable(Location.EXTRA_NO_GPS_LOCATION, lastNoGPSLocation); - location.setExtras(extras); - } - } - lastLocation.set(location); - } - // Geocoder @Override @@ -2743,8 +2772,6 @@ public class LocationManagerService extends ILocationManager.Stub { manager.setMockProvider(null); if (!manager.hasProvider()) { mProviderManagers.remove(manager); - mLastLocation.remove(manager.getName()); - mLastLocationCoarseInterval.remove(manager.getName()); } } } @@ -2854,8 +2881,8 @@ public class LocationManagerService extends ILocationManager.Stub { ipw.println("Historical Records by Provider:"); ipw.increaseIndent(); - TreeMap<PackageProviderKey, PackageStatistics> sorted = new TreeMap<>(); - sorted.putAll(mRequestStatistics.statistics); + TreeMap<PackageProviderKey, PackageStatistics> sorted = new TreeMap<>( + mRequestStatistics.statistics); for (Map.Entry<PackageProviderKey, PackageStatistics> entry : sorted.entrySet()) { PackageProviderKey key = entry.getKey(); @@ -2865,20 +2892,6 @@ public class LocationManagerService extends ILocationManager.Stub { mRequestStatistics.history.dump(ipw); - ipw.println("Last Known Locations:"); - ipw.increaseIndent(); - for (Map.Entry<String, Location> entry : mLastLocation.entrySet()) { - ipw.println(entry.getKey() + ": " + entry.getValue()); - } - ipw.decreaseIndent(); - - ipw.println("Last Known Coarse Locations:"); - ipw.increaseIndent(); - for (Map.Entry<String, Location> entry : mLastLocationCoarseInterval.entrySet()) { - ipw.println(entry.getKey() + ": " + entry.getValue()); - } - ipw.decreaseIndent(); - if (mGeofenceManager != null) { ipw.println("Geofences:"); ipw.increaseIndent(); @@ -2890,21 +2903,16 @@ public class LocationManagerService extends ILocationManager.Stub { ipw.println("Location Controller Extra Package: " + mExtraLocationControllerPackage + (mExtraLocationControllerPackageEnabled ? " [enabled]" : "[disabled]")); } + } - if (mLocationFudger != null) { - ipw.println("Location Fudger:"); - ipw.increaseIndent(); - mLocationFudger.dump(fd, ipw, args); - ipw.decreaseIndent(); - } - - ipw.println("Location Providers:"); - ipw.increaseIndent(); - for (LocationProviderManager manager : mProviderManagers) { - manager.dump(fd, ipw, args); - } - ipw.decreaseIndent(); + ipw.println("Location Providers:"); + ipw.increaseIndent(); + for (LocationProviderManager manager : mProviderManagers) { + manager.dump(fd, ipw, args); + } + ipw.decreaseIndent(); + synchronized (mLock) { if (mGnssManagerService != null) { ipw.println("GNSS:"); ipw.increaseIndent(); @@ -2929,6 +2937,18 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override + public boolean isProviderEnabledForUser(@NonNull String provider, int userId) { + synchronized (mLock) { + LocationProviderManager manager = getLocationProviderManager(provider); + if (manager == null) { + return false; + } + + return manager.isEnabled(userId); + } + } + + @Override public boolean isProviderPackage(String packageName) { for (LocationProviderManager manager : mProviderManagers) { if (manager.getPackages().contains(packageName)) { diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index a08bdb23dd22..7bb9adf31514 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -342,9 +342,43 @@ class StorageManagerService extends IStorageManager.Stub */ private final Object mPackagesLock = new Object(); + /** + * mLocalUnlockedUsers affects the return value of isUserUnlocked. If + * any value in the array changes, then the binder cache for + * isUserUnlocked must be invalidated. When adding mutating methods to + * WatchedLockedUsers, be sure to invalidate the cache in the new + * methods. + */ + private class WatchedLockedUsers { + private int[] users = EmptyArray.INT; + public WatchedLockedUsers() { + } + public void append(int userId) { + users = ArrayUtils.appendInt(users, userId); + invalidateIsUserUnlockedCache(); + } + public void remove(int userId) { + users = ArrayUtils.removeInt(users, userId); + invalidateIsUserUnlockedCache(); + } + public boolean contains(int userId) { + return ArrayUtils.contains(users, userId); + } + public int[] all() { + return users; + } + @Override + public String toString() { + return Arrays.toString(users); + } + private void invalidateIsUserUnlockedCache() { + UserManager.invalidateIsUserUnlockedCache(); + } + } + /** Set of users that we know are unlocked. */ @GuardedBy("mLock") - private int[] mLocalUnlockedUsers = EmptyArray.INT; + private WatchedLockedUsers mLocalUnlockedUsers = new WatchedLockedUsers(); /** Set of users that system knows are unlocked. */ @GuardedBy("mLock") private int[] mSystemUnlockedUsers = EmptyArray.INT; @@ -3042,7 +3076,7 @@ class StorageManagerService extends IStorageManager.Stub } synchronized (mLock) { - mLocalUnlockedUsers = ArrayUtils.appendInt(mLocalUnlockedUsers, userId); + mLocalUnlockedUsers.append(userId); } } @@ -3058,14 +3092,14 @@ class StorageManagerService extends IStorageManager.Stub } synchronized (mLock) { - mLocalUnlockedUsers = ArrayUtils.removeInt(mLocalUnlockedUsers, userId); + mLocalUnlockedUsers.remove(userId); } } @Override public boolean isUserKeyUnlocked(int userId) { synchronized (mLock) { - return ArrayUtils.contains(mLocalUnlockedUsers, userId); + return mLocalUnlockedUsers.contains(userId); } } @@ -4177,7 +4211,7 @@ class StorageManagerService extends IStorageManager.Stub } pw.println(); - pw.println("Local unlocked users: " + Arrays.toString(mLocalUnlockedUsers)); + pw.println("Local unlocked users: " + mLocalUnlockedUsers); pw.println("System unlocked users: " + Arrays.toString(mSystemUnlockedUsers)); final ContentResolver cr = mContext.getContentResolver(); diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index f7d7d6c5d15f..cdde67d8fc3a 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -42,11 +42,13 @@ import android.os.Handler; import android.os.IBinder; import android.os.IExternalVibratorService; import android.os.IVibratorService; +import android.os.IVibratorStateListener; import android.os.PowerManager; import android.os.PowerManager.ServiceType; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.Process; +import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; @@ -166,7 +168,11 @@ public class VibratorService extends IVibratorService.Stub private ExternalVibration mCurrentExternalVibration; private boolean mVibratorUnderExternalControl; private boolean mLowPowerMode; + @GuardedBy("mLock") private boolean mIsVibrating; + @GuardedBy("mLock") + private final RemoteCallbackList<IVibratorStateListener> mVibratorStateListeners = + new RemoteCallbackList<>(); private int mHapticFeedbackIntensity; private int mNotificationIntensity; private int mRingIntensity; @@ -522,6 +528,75 @@ public class VibratorService extends IVibratorService.Stub } @Override // Binder call + public boolean isVibrating() { + if (!hasPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)) { + throw new SecurityException("Requires ACCESS_VIBRATOR_STATE permission"); + } + synchronized (mLock) { + return mIsVibrating; + } + } + + @GuardedBy("mLock") + private void notifyStateListenerLocked(IVibratorStateListener listener) { + try { + listener.onVibrating(mIsVibrating); + } catch (RemoteException | RuntimeException e) { + Slog.e(TAG, "Vibrator callback failed to call", e); + } + } + + @GuardedBy("mLock") + private void notifyStateListenersLocked() { + final int length = mVibratorStateListeners.beginBroadcast(); + try { + for (int i = 0; i < length; i++) { + final IVibratorStateListener listener = + mVibratorStateListeners.getBroadcastItem(i); + notifyStateListenerLocked(listener); + } + } finally { + mVibratorStateListeners.finishBroadcast(); + } + } + + @Override // Binder call + public boolean registerVibratorStateListener(IVibratorStateListener listener) { + if (!hasPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)) { + throw new SecurityException("Requires ACCESS_VIBRATOR_STATE permission"); + } + synchronized (mLock) { + final long token = Binder.clearCallingIdentity(); + try { + if (!mVibratorStateListeners.register(listener)) { + return false; + } + // Notify its callback after new client registered. + notifyStateListenerLocked(listener); + return true; + } finally { + Binder.restoreCallingIdentity(token); + } + } + } + + @Override // Binder call + @GuardedBy("mLock") + public boolean unregisterVibratorStateListener(IVibratorStateListener listener) { + if (!hasPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)) { + throw new SecurityException("Requires ACCESS_VIBRATOR_STATE permission"); + } + synchronized (mLock) { + final long token = Binder.clearCallingIdentity(); + try { + return mVibratorStateListeners.unregister(listener); + } finally { + Binder.restoreCallingIdentity(token); + } + } + } + + @Override // Binder call public boolean hasAmplitudeControl() { synchronized (mInputDeviceVibrators) { // Input device vibrators don't support amplitude controls yet, but are still used over @@ -1373,7 +1448,10 @@ public class VibratorService extends IVibratorService.Stub FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED, uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__ON, millis); mCurVibUid = uid; - mIsVibrating = true; + if (!mIsVibrating) { + mIsVibrating = true; + notifyStateListenersLocked(); + } } catch (RemoteException e) { } } @@ -1387,7 +1465,10 @@ public class VibratorService extends IVibratorService.Stub } catch (RemoteException e) { } mCurVibUid = -1; } - mIsVibrating = false; + if (mIsVibrating) { + mIsVibrating = false; + notifyStateListenersLocked(); + } } private void setVibratorUnderExternalControl(boolean externalControl) { @@ -1414,6 +1495,8 @@ public class VibratorService extends IVibratorService.Stub pw.print(" mCurrentExternalVibration=" + mCurrentExternalVibration); pw.println(" mVibratorUnderExternalControl=" + mVibratorUnderExternalControl); pw.println(" mIsVibrating=" + mIsVibrating); + pw.println(" mVibratorStateListeners Count=" + + mVibratorStateListeners.getRegisteredCallbackCount()); pw.println(" mLowPowerMode=" + mLowPowerMode); pw.println(" mHapticFeedbackIntensity=" + mHapticFeedbackIntensity); pw.println(" mNotificationIntensity=" + mNotificationIntensity); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index b6dc3de908f0..c01766fe1f35 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -19398,6 +19398,13 @@ public class ActivityManagerService extends IActivityManager.Stub } /** + * Force the settings cache to be loaded + */ + void refreshSettingsCache() { + mCoreSettingsObserver.onChange(true); + } + + /** * Kill processes for the user with id userId and that depend on the package named packageName */ @Override diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 53a967b0ce50..73ca31e8d5ff 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -306,6 +306,8 @@ final class ActivityManagerShellCommand extends ShellCommand { return runWaitForBroadcastIdle(pw); case "compat": return runCompat(pw); + case "refresh-settings-cache": + return runRefreshSettingsCache(); default: return handleDefaultCommands(cmd); } @@ -2940,6 +2942,11 @@ final class ActivityManagerShellCommand extends ShellCommand { return 0; } + int runRefreshSettingsCache() throws RemoteException { + mInternal.refreshSettingsCache(); + return 0; + } + private int runCompat(PrintWriter pw) throws RemoteException { final PlatformCompat platformCompat = (PlatformCompat) ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE); diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index e17c1f8f8276..566b72d728e6 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -29,6 +29,7 @@ import android.media.AudioManager; import android.media.AudioRoutesInfo; import android.media.AudioSystem; import android.media.IAudioRoutesObserver; +import android.media.IStrategyPreferredDeviceDispatcher; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -410,6 +411,16 @@ import java.io.PrintWriter; return mDeviceInventory.removePreferredDeviceForStrategySync(strategy); } + /*package*/ void registerStrategyPreferredDeviceDispatcher( + @NonNull IStrategyPreferredDeviceDispatcher dispatcher) { + mDeviceInventory.registerStrategyPreferredDeviceDispatcher(dispatcher); + } + + /*package*/ void unregisterStrategyPreferredDeviceDispatcher( + @NonNull IStrategyPreferredDeviceDispatcher dispatcher) { + mDeviceInventory.unregisterStrategyPreferredDeviceDispatcher(dispatcher); + } + //--------------------------------------------------------------------- // Communication with (to) AudioService //TODO check whether the AudioService methods are candidates to move here diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index ef1bc835dea5..e170db07b398 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -16,6 +16,7 @@ package com.android.server.audio; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityManager; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; @@ -31,6 +32,7 @@ import android.media.AudioPort; import android.media.AudioRoutesInfo; import android.media.AudioSystem; import android.media.IAudioRoutesObserver; +import android.media.IStrategyPreferredDeviceDispatcher; import android.os.Binder; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -87,6 +89,10 @@ public class AudioDeviceInventory { final RemoteCallbackList<IAudioRoutesObserver> mRoutesObservers = new RemoteCallbackList<IAudioRoutesObserver>(); + // Monitoring of strategy-preferred device + final RemoteCallbackList<IStrategyPreferredDeviceDispatcher> mPrefDevDispatchers = + new RemoteCallbackList<IStrategyPreferredDeviceDispatcher>(); + /*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) { mDeviceBroker = broker; mAudioSystem = AudioSystemAdapter.getDefaultAdapter(); @@ -470,10 +476,12 @@ public class AudioDeviceInventory { /*package*/ void onSaveSetPreferredDevice(int strategy, @NonNull AudioDevice device) { mPreferredDevices.put(strategy, device); + dispatchPreferredDevice(strategy, device); } /*package*/ void onSaveRemovePreferredDevice(int strategy) { mPreferredDevices.remove(strategy); + dispatchPreferredDevice(strategy, null); } //------------------------------------------------------------ @@ -502,6 +510,16 @@ public class AudioDeviceInventory { return status; } + /*package*/ void registerStrategyPreferredDeviceDispatcher( + @NonNull IStrategyPreferredDeviceDispatcher dispatcher) { + mPrefDevDispatchers.register(dispatcher); + } + + /*package*/ void unregisterStrategyPreferredDeviceDispatcher( + @NonNull IStrategyPreferredDeviceDispatcher dispatcher) { + mPrefDevDispatchers.unregister(dispatcher); + } + /** * Implements the communication with AudioSystem to (dis)connect a device in the native layers * @param connect true if connection @@ -1094,6 +1112,17 @@ public class AudioDeviceInventory { } } + private void dispatchPreferredDevice(int strategy, @Nullable AudioDevice device) { + final int nbDispatchers = mPrefDevDispatchers.beginBroadcast(); + for (int i = 0; i < nbDispatchers; i++) { + try { + mPrefDevDispatchers.getBroadcastItem(i).dispatchPrefDeviceChanged(strategy, device); + } catch (RemoteException e) { + } + } + mPrefDevDispatchers.finishBroadcast(); + } + //---------------------------------------------------------- // For tests only diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 342ce22066b6..fea1c1077160 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -83,6 +83,7 @@ import android.media.IAudioService; import android.media.IPlaybackConfigDispatcher; import android.media.IRecordingConfigDispatcher; import android.media.IRingtonePlayer; +import android.media.IStrategyPreferredDeviceDispatcher; import android.media.IVolumeController; import android.media.MediaExtractor; import android.media.MediaFormat; @@ -1764,6 +1765,26 @@ public class AudioService extends IAudioService.Stub } } + /** @see AudioManager#addOnPreferredDeviceForStrategyChangedListener(Executor, AudioManager.OnPreferredDeviceForStrategyChangedListener) */ + public void registerStrategyPreferredDeviceDispatcher( + @Nullable IStrategyPreferredDeviceDispatcher dispatcher) { + if (dispatcher == null) { + return; + } + enforceModifyAudioRoutingPermission(); + mDeviceBroker.registerStrategyPreferredDeviceDispatcher(dispatcher); + } + + /** @see AudioManager#removeOnPreferredDeviceForStrategyChangedListener(AudioManager.OnPreferredDeviceForStrategyChangedListener) */ + public void unregisterStrategyPreferredDeviceDispatcher( + @Nullable IStrategyPreferredDeviceDispatcher dispatcher) { + if (dispatcher == null) { + return; + } + enforceModifyAudioRoutingPermission(); + mDeviceBroker.unregisterStrategyPreferredDeviceDispatcher(dispatcher); + } + /** @see AudioManager#getDevicesForAttributes(AudioAttributes) */ public @NonNull ArrayList<AudioDevice> getDevicesForAttributes( @NonNull AudioAttributes attributes) { diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index c99774a41f10..6178e6c1e094 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -21,6 +21,7 @@ import android.app.ActivityManager.StackInfo; import android.app.ActivityTaskManager; import android.app.IActivityTaskManager; import android.app.TaskStackListener; +import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.hardware.Sensor; @@ -41,6 +42,7 @@ import android.util.MathUtils; import android.util.Slog; import android.util.TimeUtils; +import com.android.internal.BrightnessSynchronizer; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import com.android.server.EventLogTags; @@ -89,6 +91,8 @@ class AutomaticBrightnessController { // The minimum and maximum screen brightnesses. private final int mScreenBrightnessRangeMinimum; private final int mScreenBrightnessRangeMaximum; + private final float mScreenBrightnessRangeMinimumFloat; + private final float mScreenBrightnessRangeMaximumFloat; // How much to scale doze brightness by (should be (0, 1.0]). private final float mDozeScaleFactor; @@ -174,7 +178,7 @@ class AutomaticBrightnessController { // that we can quickly revert to the previous auto-brightness level // while the light sensor warms up. // Use -1 if there is no current auto-brightness value available. - private int mScreenAutoBrightness = -1; + private int mScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID; // The current display policy. This is useful, for example, for knowing when we're dozing, // where the light sensor may not be available. @@ -204,39 +208,44 @@ class AutomaticBrightnessController { private TaskStackListenerImpl mTaskStackListener; private IActivityTaskManager mActivityTaskManager; private PackageManager mPackageManager; + private Context mContext; private final Injector mInjector; AutomaticBrightnessController(Callbacks callbacks, Looper looper, SensorManager sensorManager, Sensor lightSensor, BrightnessMappingStrategy mapper, - int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor, - int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig, - long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig, - HysteresisLevels ambientBrightnessThresholds, - HysteresisLevels screenBrightnessThresholds, - PackageManager packageManager) { + int lightSensorWarmUpTime, float brightnessMin, float brightnessMax, + float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, + long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, + boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds, + HysteresisLevels screenBrightnessThresholds, Context context) { this(new Injector(), callbacks, looper, sensorManager, lightSensor, mapper, lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor, lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig, darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig, - ambientBrightnessThresholds, screenBrightnessThresholds, packageManager); + ambientBrightnessThresholds, screenBrightnessThresholds, context); } @VisibleForTesting AutomaticBrightnessController(Injector injector, Callbacks callbacks, Looper looper, SensorManager sensorManager, Sensor lightSensor, BrightnessMappingStrategy mapper, - int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor, - int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig, - long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig, - HysteresisLevels ambientBrightnessThresholds, - HysteresisLevels screenBrightnessThresholds, - PackageManager packageManager) { + int lightSensorWarmUpTime, float brightnessMin, float brightnessMax, + float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, + long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, + boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds, + HysteresisLevels screenBrightnessThresholds, Context context) { mInjector = injector; + mContext = context; mCallbacks = callbacks; mSensorManager = sensorManager; mBrightnessMapper = mapper; - mScreenBrightnessRangeMinimum = brightnessMin; - mScreenBrightnessRangeMaximum = brightnessMax; + mScreenBrightnessRangeMinimum = + BrightnessSynchronizer.brightnessFloatToInt(mContext, brightnessMin); + mScreenBrightnessRangeMaximum = + com.android.internal.BrightnessSynchronizer.brightnessFloatToInt( + mContext, brightnessMax); + mScreenBrightnessRangeMinimumFloat = brightnessMin; + mScreenBrightnessRangeMaximumFloat = brightnessMax; mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime; mDozeScaleFactor = dozeScaleFactor; mNormalLightSensorRate = lightSensorRate; @@ -261,7 +270,7 @@ class AutomaticBrightnessController { } mActivityTaskManager = ActivityTaskManager.getService(); - mPackageManager = packageManager; + mPackageManager = mContext.getPackageManager(); mTaskStackListener = new TaskStackListenerImpl(); mForegroundAppPackageName = null; mPendingForegroundAppPackageName = null; @@ -291,7 +300,7 @@ class AutomaticBrightnessController { return -1; } if (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE) { - return (int) (mScreenAutoBrightness * mDozeScaleFactor); + return Math.round(mScreenAutoBrightness * mDozeScaleFactor); } return mScreenAutoBrightness; } @@ -473,7 +482,7 @@ class AutomaticBrightnessController { } else if (mLightSensorEnabled) { mLightSensorEnabled = false; mAmbientLuxValid = !mResetAmbientLuxAfterWarmUpConfig; - mScreenAutoBrightness = -1; + mScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID; mRecentLightSamples = 0; mAmbientLightRingBuffer.clear(); mCurrentLightSensorRate = -1; @@ -722,10 +731,8 @@ class AutomaticBrightnessController { float value = mBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName, mForegroundAppCategory); - - int newScreenAutoBrightness = Math.round(clampScreenBrightness( - value * PowerManager.BRIGHTNESS_ON)); - + int newScreenAutoBrightness = BrightnessSynchronizer.brightnessFloatToInt( + mContext, clampScreenBrightnessFloat(value)); // If screenAutoBrightness is set, we should have screen{Brightening,Darkening}Threshold, // in which case we ignore the new screen brightness if it doesn't differ enough from the // previous one. @@ -759,11 +766,19 @@ class AutomaticBrightnessController { } } + // Clamps values with float range [1.0-255.0] + // TODO(brightnessfloat): convert everything that uses this to float system private float clampScreenBrightness(float value) { return MathUtils.constrain(value, mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum); } + // Clamps values with float range [0.0-1.0] + private float clampScreenBrightnessFloat(float value) { + return MathUtils.constrain(value, + mScreenBrightnessRangeMinimumFloat, mScreenBrightnessRangeMaximumFloat); + } + private void prepareBrightnessAdjustmentSample() { if (!mBrightnessAdjustmentSamplePending) { mBrightnessAdjustmentSamplePending = true; diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java index 6ff276703443..a099606852d0 100644 --- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java +++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java @@ -28,6 +28,7 @@ import android.util.Pair; import android.util.Slog; import android.util.Spline; +import com.android.internal.BrightnessSynchronizer; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.server.display.utils.Plog; @@ -342,9 +343,9 @@ public abstract class BrightnessMappingStrategy { } protected static float normalizeAbsoluteBrightness(int brightness) { - brightness = MathUtils.constrain(brightness, - PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON); - return (float) brightness / PowerManager.BRIGHTNESS_ON; + return BrightnessSynchronizer.brightnessIntToFloat(brightness, + PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON, + PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX); } private Pair<float[], float[]> insertControlPoint( diff --git a/services/core/java/com/android/server/display/DisplayBlanker.java b/services/core/java/com/android/server/display/DisplayBlanker.java index 816dc1302bdc..d294898da8c4 100644 --- a/services/core/java/com/android/server/display/DisplayBlanker.java +++ b/services/core/java/com/android/server/display/DisplayBlanker.java @@ -20,5 +20,5 @@ package com.android.server.display; * Interface used to update the actual display state. */ public interface DisplayBlanker { - void requestDisplayState(int state, int brightness); + void requestDisplayState(int state, float brightness); } diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index e69a3b8ba4c1..63a8d7c92441 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -129,11 +129,11 @@ abstract class DisplayDevice { * Sets the display state, if supported. * * @param state The new display state. - * @param brightness The new display brightness. + * @param brightnessState The new display brightnessState. * @return A runnable containing work to be deferred until after we have * exited the critical section, or null if none. */ - public Runnable requestDisplayStateLocked(int state, int brightness) { + public Runnable requestDisplayStateLocked(int state, float brightnessState) { return null; } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 71ade6293216..91405890b076 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -232,7 +232,7 @@ public final class DisplayManagerService extends SystemService { // The overall display brightness. // For now, this only applies to the built-in display but we may split it up eventually. - private int mGlobalDisplayBrightness = PowerManager.BRIGHTNESS_DEFAULT; + private float mGlobalDisplayBrightness; // Set to true when there are pending display changes that have yet to be applied // to the surface flinger state. @@ -340,7 +340,8 @@ public final class DisplayManagerService extends SystemService { mMinimumBrightnessSpline = Spline.createSpline(lux, nits); PowerManager pm = mContext.getSystemService(PowerManager.class); - mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting(); + mGlobalDisplayBrightness = pm.getBrightnessConstraint( + PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT); mCurrentUserId = UserHandle.USER_SYSTEM; ColorSpace[] colorSpaces = SurfaceControl.getCompositionColorSpaces(); mWideColorSpace = colorSpaces[1]; @@ -539,16 +540,16 @@ public final class DisplayManagerService extends SystemService { } } - private void requestGlobalDisplayStateInternal(int state, int brightness) { + private void requestGlobalDisplayStateInternal(int state, float brightnessState) { if (state == Display.STATE_UNKNOWN) { state = Display.STATE_ON; } if (state == Display.STATE_OFF) { - brightness = PowerManager.BRIGHTNESS_OFF; - } else if (brightness < 0) { - brightness = PowerManager.BRIGHTNESS_DEFAULT; - } else if (brightness > PowerManager.BRIGHTNESS_ON) { - brightness = PowerManager.BRIGHTNESS_ON; + brightnessState = PowerManager.BRIGHTNESS_OFF_FLOAT; + } else if (brightnessState < PowerManager.BRIGHTNESS_MIN || Float.isNaN(brightnessState)) { + brightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT; + } else if (brightnessState > PowerManager.BRIGHTNESS_MAX) { + brightnessState = PowerManager.BRIGHTNESS_MAX; } synchronized (mTempDisplayStateWorkQueue) { @@ -558,15 +559,15 @@ public final class DisplayManagerService extends SystemService { // may happen as a side-effect of displays changing state. synchronized (mSyncRoot) { if (mGlobalDisplayState == state - && mGlobalDisplayBrightness == brightness) { + && mGlobalDisplayBrightness == brightnessState) { return; // no change } Trace.traceBegin(Trace.TRACE_TAG_POWER, "requestGlobalDisplayState(" + Display.stateToString(state) - + ", brightness=" + brightness + ")"); + + ", brightness=" + brightnessState + ")"); mGlobalDisplayState = state; - mGlobalDisplayBrightness = brightness; + mGlobalDisplayBrightness = brightnessState; applyGlobalDisplayStateLocked(mTempDisplayStateWorkQueue); } @@ -1023,7 +1024,8 @@ public final class DisplayManagerService extends SystemService { // by the display power controller (if known). DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) { - return device.requestDisplayStateLocked(mGlobalDisplayState, mGlobalDisplayBrightness); + return device.requestDisplayStateLocked( + mGlobalDisplayState, mGlobalDisplayBrightness); } return null; } @@ -2300,7 +2302,7 @@ public final class DisplayManagerService extends SystemService { } @Override // Binder call - public void setTemporaryBrightness(int brightness) { + public void setTemporaryBrightness(float brightness) { mContext.enforceCallingOrSelfPermission( Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS, "Permission required to set the display's brightness"); @@ -2419,7 +2421,7 @@ public final class DisplayManagerService extends SystemService { synchronized (mSyncRoot) { DisplayBlanker blanker = new DisplayBlanker() { @Override - public void requestDisplayState(int state, int brightness) { + public void requestDisplayState(int state, float brightness) { // The order of operations is important for legacy reasons. if (state == Display.STATE_OFF) { requestGlobalDisplayStateInternal(state, brightness); diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java index e87ad410f9a2..0c6c797b917a 100644 --- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java +++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java @@ -108,8 +108,8 @@ class DisplayManagerShellCommand extends ShellCommand { "Permission required to set the display's brightness"); final long token = Binder.clearCallingIdentity(); try { - Settings.System.putIntForUser(context.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS, (int) (brightness * 255), + Settings.System.putFloatForUser(context.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightness, UserHandle.USER_CURRENT); } finally { Binder.restoreCallingIdentity(token); diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 96532f4a21ac..8bbeabf779d2 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -946,9 +946,9 @@ public class DisplayModeDirector { * {@link R.array#config_ambientThresholdsOfPeakRefreshRate}. */ private class BrightnessObserver extends ContentObserver { + // TODO: brightnessfloat: change this to the float setting private final Uri mDisplayBrightnessSetting = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS); - private final static int LIGHT_SENSOR_RATE_MS = 250; private int[] mDisplayBrightnessThresholds; private int[] mAmbientBrightnessThresholds; @@ -1174,7 +1174,7 @@ public class DisplayModeDirector { return false; } - + // TODO: brightnessfloat: make it use float not int private void onBrightnessChangedLocked() { int brightness = Settings.System.getInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, -1); diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index f1655f0ed6e3..197842ed1c2d 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -51,6 +51,7 @@ import android.util.Slog; import android.util.TimeUtils; import android.view.Display; +import com.android.internal.BrightnessSynchronizer; import com.android.internal.app.IBatteryStats; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -102,7 +103,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static final boolean USE_COLOR_FADE_ON_ANIMATION = false; // The minimum reduction in brightness when dimmed. - private static final int SCREEN_DIM_MINIMUM_REDUCTION = 10; + private static final float SCREEN_DIM_MINIMUM_REDUCTION_FLOAT = 0.04f; + private static final float SCREEN_ANIMATION_RATE_MINIMUM = 0.0f; private static final int COLOR_FADE_ON_ANIMATION_DURATION_MILLIS = 250; private static final int COLOR_FADE_OFF_ANIMATION_DURATION_MILLIS = 400; @@ -169,28 +171,27 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private Sensor mProximitySensor; // The doze screen brightness. - private final int mScreenBrightnessDozeConfig; + private final float mScreenBrightnessDozeConfig; // The dim screen brightness. - private final int mScreenBrightnessDimConfig; + private final float mScreenBrightnessDimConfig; // The minimum allowed brightness. - private final int mScreenBrightnessRangeMinimum; + private final float mScreenBrightnessRangeMinimum; // The maximum allowed brightness. - private final int mScreenBrightnessRangeMaximum; + private final float mScreenBrightnessRangeMaximum; - // The default screen brightness. - private final int mScreenBrightnessDefault; + private final float mScreenBrightnessDefault; // The minimum allowed brightness while in VR. - private final int mScreenBrightnessForVrRangeMinimum; + private final float mScreenBrightnessForVrRangeMinimum; // The maximum allowed brightness while in VR. - private final int mScreenBrightnessForVrRangeMaximum; + private final float mScreenBrightnessForVrRangeMaximum; // The default screen brightness for VR. - private final int mScreenBrightnessForVrDefault; + private final float mScreenBrightnessForVrDefault; // True if auto-brightness should be used. private boolean mUseSoftwareAutoBrightnessConfig; @@ -317,8 +318,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private BrightnessReason mBrightnessReasonTemp = new BrightnessReason(); // Brightness animation ramp rates in brightness units per second - private final int mBrightnessRampRateFast; - private final int mBrightnessRampRateSlow; + private final float mBrightnessRampRateSlow = 0.2352941f; + private final float mBrightnessRampRateFast = 0.7058823f; + // Whether or not to skip the initial brightness ramps into STATE_ON. private final boolean mSkipScreenOnBrightnessRamp; @@ -333,7 +335,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private int mSkipRampState = RAMP_STATE_SKIP_NONE; // The first autobrightness value set when entering RAMP_STATE_SKIP_INITIAL. - private int mInitialAutoBrightness; + private float mInitialAutoBrightness; // The controller for the automatic brightness level. private AutomaticBrightnessController mAutomaticBrightnessController; @@ -348,24 +350,24 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // The last brightness that was set by the user and not temporary. Set to -1 when a brightness // has yet to be recorded. - private int mLastUserSetScreenBrightness; + private float mLastUserSetScreenBrightness; // The screen brightenss setting has changed but not taken effect yet. If this is different // from the current screen brightness setting then this is coming from something other than us // and should be considered a user interaction. - private int mPendingScreenBrightnessSetting; + private float mPendingScreenBrightnessSetting; // The last observed screen brightness setting, either set by us or by the settings app on // behalf of the user. - private int mCurrentScreenBrightnessSetting; + private float mCurrentScreenBrightnessSetting; // The temporary screen brightness. Typically set when a user is interacting with the - // brightness slider but hasn't settled on a choice yet. Set to -1 when there's no temporary - // brightness set. - private int mTemporaryScreenBrightness; + // brightness slider but hasn't settled on a choice yet. Set to + // PowerManager.BRIGHNTESS_INVALID_FLOAT when there's no temporary brightness set. + private float mTemporaryScreenBrightness; // The current screen brightness while in VR mode. - private int mScreenBrightnessForVr; + private float mScreenBrightnessForVr; // The last auto brightness adjustment that was set by the user and not temporary. Set to // Float.NaN when an auto-brightness adjustment hasn't been recorded yet. @@ -384,6 +386,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private ObjectAnimator mColorFadeOffAnimator; private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator; + private BrightnessSynchronizer mBrightnessSynchronizer; + /** * Creates the display power controller. */ @@ -394,37 +398,39 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mBrightnessTracker = new BrightnessTracker(context, null); mSettingsObserver = new SettingsObserver(mHandler); mCallbacks = callbacks; - + mBrightnessSynchronizer = new BrightnessSynchronizer(context); mBatteryStats = BatteryStatsService.getService(); mSensorManager = sensorManager; mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class); mBlanker = blanker; mContext = context; + PowerManager pm = context.getSystemService(PowerManager.class); final Resources resources = context.getResources(); - final int screenBrightnessSettingMinimum = clampAbsoluteBrightness(resources.getInteger( - com.android.internal.R.integer.config_screenBrightnessSettingMinimum)); + final float screenBrightnessSettingMinimumFloat = clampAbsoluteBrightness( + pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM)); - mScreenBrightnessDozeConfig = clampAbsoluteBrightness(resources.getInteger( - com.android.internal.R.integer.config_screenBrightnessDoze)); - - mScreenBrightnessDimConfig = clampAbsoluteBrightness(resources.getInteger( - com.android.internal.R.integer.config_screenBrightnessDim)); + // DOZE AND DIM SETTINGS + mScreenBrightnessDozeConfig = clampAbsoluteBrightness( + pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE)); + mScreenBrightnessDimConfig = clampAbsoluteBrightness( + pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM)); + // NORMAL SCREEN SETTINGS mScreenBrightnessRangeMinimum = - Math.min(screenBrightnessSettingMinimum, mScreenBrightnessDimConfig); - - mScreenBrightnessRangeMaximum = clampAbsoluteBrightness(resources.getInteger( - com.android.internal.R.integer.config_screenBrightnessSettingMaximum)); - mScreenBrightnessDefault = clampAbsoluteBrightness(resources.getInteger( - com.android.internal.R.integer.config_screenBrightnessSettingDefault)); - - mScreenBrightnessForVrRangeMinimum = clampAbsoluteBrightness(resources.getInteger( - com.android.internal.R.integer.config_screenBrightnessForVrSettingMinimum)); - mScreenBrightnessForVrRangeMaximum = clampAbsoluteBrightness(resources.getInteger( - com.android.internal.R.integer.config_screenBrightnessForVrSettingMaximum)); - mScreenBrightnessForVrDefault = clampAbsoluteBrightness(resources.getInteger( - com.android.internal.R.integer.config_screenBrightnessForVrSettingDefault)); + Math.min(screenBrightnessSettingMinimumFloat, mScreenBrightnessDimConfig); + mScreenBrightnessRangeMaximum = clampAbsoluteBrightness( + pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM)); + mScreenBrightnessDefault = clampAbsoluteBrightness( + pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT)); + + // VR SETTINGS + mScreenBrightnessForVrDefault = clampAbsoluteBrightness( + pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_VR)); + mScreenBrightnessForVrRangeMaximum = clampAbsoluteBrightness( + pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM_VR)); + mScreenBrightnessForVrRangeMinimum = clampAbsoluteBrightness( + pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR)); mUseSoftwareAutoBrightnessConfig = resources.getBoolean( com.android.internal.R.bool.config_automatic_brightness_available); @@ -432,10 +438,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAllowAutoBrightnessWhileDozingConfig = resources.getBoolean( com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing); - mBrightnessRampRateFast = resources.getInteger( - com.android.internal.R.integer.config_brightness_ramp_rate_fast); - mBrightnessRampRateSlow = resources.getInteger( - com.android.internal.R.integer.config_brightness_ramp_rate_slow); mSkipScreenOnBrightnessRamp = resources.getBoolean( com.android.internal.R.bool.config_skipScreenOnBrightnessRamp); @@ -496,7 +498,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate, initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds, - screenBrightnessThresholds, context.getPackageManager()); + screenBrightnessThresholds, context); } else { mUseSoftwareAutoBrightnessConfig = false; } @@ -519,14 +521,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call TYPICAL_PROXIMITY_THRESHOLD); } } - mCurrentScreenBrightnessSetting = getScreenBrightnessSetting(); mScreenBrightnessForVr = getScreenBrightnessForVrSetting(); mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting(); - mTemporaryScreenBrightness = -1; - mPendingScreenBrightnessSetting = -1; - mTemporaryAutoBrightnessAdjustment = Float.NaN; - mPendingAutoBrightnessAdjustment = Float.NaN; + mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; + mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT; + mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT; + mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT; DisplayWhiteBalanceSettings displayWhiteBalanceSettings = null; DisplayWhiteBalanceController displayWhiteBalanceController = null; @@ -681,28 +682,28 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } mScreenBrightnessRampAnimator = new RampAnimator<DisplayPowerState>( - mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS); + mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT); mScreenBrightnessRampAnimator.setListener(mRampAnimatorListener); // Initialize screen state for battery stats. try { mBatteryStats.noteScreenState(mPowerState.getScreenState()); - mBatteryStats.noteScreenBrightness(mPowerState.getScreenBrightness()); + mBatteryStats.noteScreenBrightness(BrightnessSynchronizer.brightnessFloatToInt(mContext, + mPowerState.getScreenBrightness())); } catch (RemoteException ex) { // same process } - // Initialize all of the brightness tracking state - final float brightness = convertToNits(mPowerState.getScreenBrightness()); + final float brightness = convertToNits(BrightnessSynchronizer.brightnessFloatToInt(mContext, + mPowerState.getScreenBrightness())); if (brightness >= 0.0f) { mBrightnessTracker.start(brightness); } - mContext.getContentResolver().registerContentObserver( - Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS), + Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FLOAT), false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL); mContext.getContentResolver().registerContentObserver( - Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR), + Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT), false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL); mContext.getContentResolver().registerContentObserver( Settings.System.getUriFor(Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ), @@ -776,8 +777,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Compute the basic display state using the policy. // We might override this below based on other factors. + // Initialise brightness as invalid. int state; - int brightness = PowerManager.BRIGHTNESS_DEFAULT; + float brightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT; boolean performScreenOffTransition = false; switch (mPowerRequest.policy) { case DisplayPowerRequest.POLICY_OFF: @@ -791,7 +793,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call state = Display.STATE_DOZE; } if (!mAllowAutoBrightnessWhileDozingConfig) { - brightness = mPowerRequest.dozeScreenBrightness; + brightnessState = mPowerRequest.dozeScreenBrightness; mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE); } break; @@ -843,20 +845,20 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call animateScreenStateChange(state, performScreenOffTransition); state = mPowerState.getScreenState(); - // Use zero brightness when screen is off. if (state == Display.STATE_OFF) { - brightness = PowerManager.BRIGHTNESS_OFF; + brightnessState = PowerManager.BRIGHTNESS_OFF_FLOAT; mBrightnessReasonTemp.setReason(BrightnessReason.REASON_SCREEN_OFF); } // Always use the VR brightness when in the VR state. if (state == Display.STATE_VR) { - brightness = mScreenBrightnessForVr; + brightnessState = mScreenBrightnessForVr; mBrightnessReasonTemp.setReason(BrightnessReason.REASON_VR); } - if (brightness < 0 && mPowerRequest.screenBrightnessOverride > 0) { - brightness = mPowerRequest.screenBrightnessOverride; + if ((Float.isNaN(brightnessState)) + && isValidBrightnessValue(mPowerRequest.screenBrightnessOverride)) { + brightnessState = mPowerRequest.screenBrightnessOverride; mBrightnessReasonTemp.setReason(BrightnessReason.REASON_OVERRIDE); mAppliedScreenBrightnessOverride = true; } else { @@ -867,15 +869,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state); final boolean autoBrightnessEnabled = mPowerRequest.useAutoBrightness && (state == Display.STATE_ON || autoBrightnessEnabledInDoze) - && brightness < 0 + && Float.isNaN(brightnessState) && mAutomaticBrightnessController != null; final boolean userSetBrightnessChanged = updateUserSetScreenBrightness(); // Use the temporary screen brightness if there isn't an override, either from // WindowManager or based on the display state. - if (mTemporaryScreenBrightness > 0) { - brightness = mTemporaryScreenBrightness; + if (isValidBrightnessValue(mTemporaryScreenBrightness)) { + brightnessState = mTemporaryScreenBrightness; mAppliedTemporaryBrightness = true; mBrightnessReasonTemp.setReason(BrightnessReason.REASON_TEMPORARY); } else { @@ -898,14 +900,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call brightnessAdjustmentFlags = BrightnessReason.ADJUSTMENT_AUTO; mAppliedTemporaryAutoBrightnessAdjustment = false; } - // Apply brightness boost. // We do this here after deciding whether auto-brightness is enabled so that we don't // disable the light sensor during this temporary state. That way when boost ends we will // be able to resume normal auto-brightness behavior without any delay. if (mPowerRequest.boostScreenBrightness - && brightness != PowerManager.BRIGHTNESS_OFF) { - brightness = PowerManager.BRIGHTNESS_ON; + && brightnessState != PowerManager.BRIGHTNESS_OFF_FLOAT) { + brightnessState = PowerManager.BRIGHTNESS_MAX; mBrightnessReasonTemp.setReason(BrightnessReason.REASON_BOOST); mAppliedBrightnessBoost = true; } else { @@ -914,16 +915,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // If the brightness is already set then it's been overridden by something other than the // user, or is a temporary adjustment. - boolean userInitiatedChange = brightness < 0 + boolean userInitiatedChange = (Float.isNaN(brightnessState)) && (autoBrightnessAdjustmentChanged || userSetBrightnessChanged); - boolean hadUserBrightnessPoint = false; // Configure auto-brightness. if (mAutomaticBrightnessController != null) { hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints(); mAutomaticBrightnessController.configure(autoBrightnessEnabled, mBrightnessConfiguration, - mLastUserSetScreenBrightness / (float) PowerManager.BRIGHTNESS_ON, + mLastUserSetScreenBrightness, userSetBrightnessChanged, autoBrightnessAdjustment, autoBrightnessAdjustmentChanged, mPowerRequest.policy); } @@ -934,17 +934,18 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Apply auto-brightness. boolean slowChange = false; - if (brightness < 0) { + if (Float.isNaN(brightnessState)) { float newAutoBrightnessAdjustment = autoBrightnessAdjustment; if (autoBrightnessEnabled) { - brightness = mAutomaticBrightnessController.getAutomaticScreenBrightness(); + brightnessState = BrightnessSynchronizer.brightnessIntToFloat( + mContext, mAutomaticBrightnessController.getAutomaticScreenBrightness()); newAutoBrightnessAdjustment = mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment(); } - - if (brightness >= 0) { + if (isValidBrightnessValue(brightnessState) + || brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT) { // Use current auto-brightness value and slowly adjust to changes. - brightness = clampScreenBrightness(brightness); + brightnessState = clampScreenBrightness(brightnessState); if (mAppliedAutoBrightness && !autoBrightnessAdjustmentChanged) { slowChange = true; // slowly adapt to auto-brightness } @@ -952,7 +953,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // before applying the low power or dim transformations so that the slider // accurately represents the full possible range, even if they range changes what // it means in absolute terms. - putScreenBrightnessSetting(brightness); + putScreenBrightnessSetting(brightnessState); mAppliedAutoBrightness = true; mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC); } else { @@ -970,24 +971,25 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAppliedAutoBrightness = false; brightnessAdjustmentFlags = 0; } - // Use default brightness when dozing unless overridden. - if (brightness < 0 && Display.isDozeState(state)) { - brightness = mScreenBrightnessDozeConfig; + if ((Float.isNaN(brightnessState)) + && Display.isDozeState(state)) { + brightnessState = mScreenBrightnessDozeConfig; mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT); } // Apply manual brightness. - if (brightness < 0) { - brightness = clampScreenBrightness(mCurrentScreenBrightnessSetting); + if (Float.isNaN(brightnessState)) { + brightnessState = clampScreenBrightness(mCurrentScreenBrightnessSetting); mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL); } // Apply dimming by at least some minimum amount when user activity // timeout is about to expire. if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) { - if (brightness > mScreenBrightnessRangeMinimum) { - brightness = Math.max(Math.min(brightness - SCREEN_DIM_MINIMUM_REDUCTION, + if (brightnessState > mScreenBrightnessRangeMinimum) { + brightnessState = Math.max(Math.min(brightnessState + - SCREEN_DIM_MINIMUM_REDUCTION_FLOAT, mScreenBrightnessDimConfig), mScreenBrightnessRangeMinimum); mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_DIMMED); } @@ -999,15 +1001,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call slowChange = false; mAppliedDimming = false; } - // If low power mode is enabled, scale brightness by screenLowPowerBrightnessFactor // as long as it is above the minimum threshold. if (mPowerRequest.lowPowerMode) { - if (brightness > mScreenBrightnessRangeMinimum) { + if (brightnessState > mScreenBrightnessRangeMinimum) { final float brightnessFactor = Math.min(mPowerRequest.screenLowPowerBrightnessFactor, 1); - final int lowPowerBrightness = (int) (brightness * brightnessFactor); - brightness = Math.max(lowPowerBrightness, mScreenBrightnessRangeMinimum); + final float lowPowerBrightnessFloat = (brightnessState * brightnessFactor); + brightnessState = Math.max(lowPowerBrightnessFloat, + mScreenBrightnessRangeMinimum); mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_LOW_POWER); } if (!mAppliedLowPower) { @@ -1025,11 +1027,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mSkipScreenOnBrightnessRamp) { if (state == Display.STATE_ON) { if (mSkipRampState == RAMP_STATE_SKIP_NONE && mDozing) { - mInitialAutoBrightness = brightness; + mInitialAutoBrightness = brightnessState; mSkipRampState = RAMP_STATE_SKIP_INITIAL; } else if (mSkipRampState == RAMP_STATE_SKIP_INITIAL && mUseSoftwareAutoBrightnessConfig - && brightness != mInitialAutoBrightness) { + && !BrightnessSynchronizer.floatEquals(brightnessState, + mInitialAutoBrightness)) { mSkipRampState = RAMP_STATE_SKIP_AUTOBRIGHT; } else if (mSkipRampState == RAMP_STATE_SKIP_AUTOBRIGHT) { mSkipRampState = RAMP_STATE_SKIP_NONE; @@ -1056,9 +1059,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAppliedTemporaryBrightness || mAppliedTemporaryAutoBrightnessAdjustment; if (initialRampSkip || hasBrightnessBuckets || wasOrWillBeInVr || !isDisplayContentVisible || brightnessIsTemporary) { - animateScreenBrightness(brightness, 0); + animateScreenBrightness(brightnessState, SCREEN_ANIMATION_RATE_MINIMUM); } else { - animateScreenBrightness(brightness, + animateScreenBrightness(brightnessState, slowChange ? mBrightnessRampRateSlow : mBrightnessRampRateFast); } @@ -1069,14 +1072,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // slider event so notify as if the system changed the brightness. userInitiatedChange = false; } - notifyBrightnessChanged(brightness, userInitiatedChange, hadUserBrightnessPoint); + notifyBrightnessChanged( + BrightnessSynchronizer.brightnessFloatToInt(mContext, brightnessState), + userInitiatedChange, hadUserBrightnessPoint); } } // Log any changes to what is currently driving the brightness setting. if (!mBrightnessReasonTemp.equals(mBrightnessReason) || brightnessAdjustmentFlags != 0) { - Slog.v(TAG, "Brightness [" + brightness + "] reason changing to: '" + Slog.v(TAG, "Brightness [" + brightnessState + "] reason changing to: '" + mBrightnessReasonTemp.toString(brightnessAdjustmentFlags) + "', previous reason: '" + mBrightnessReason + "'."); mBrightnessReason.set(mBrightnessReasonTemp); @@ -1161,9 +1166,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call msg.sendToTarget(); } - public void setTemporaryBrightness(int brightness) { + public void setTemporaryBrightness(float brightness) { Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_BRIGHTNESS, - brightness, 0 /*unused*/); + Float.floatToIntBits(brightness), 0 /*unused*/); msg.sendToTarget(); } @@ -1282,24 +1287,38 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mReportedScreenStateToPolicy = state; } - private int clampScreenBrightnessForVr(int value) { + private float clampScreenBrightnessForVr(float value) { return MathUtils.constrain( - value, mScreenBrightnessForVrRangeMinimum, mScreenBrightnessForVrRangeMaximum); + value, mScreenBrightnessForVrRangeMinimum, + mScreenBrightnessForVrRangeMaximum); } - private int clampScreenBrightness(int value) { + private float clampScreenBrightness(float value) { + if (Float.isNaN(value)) { + return mScreenBrightnessRangeMinimum; + } return MathUtils.constrain( value, mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum); } - private void animateScreenBrightness(int target, int rate) { + // Checks whether the brightness is within the valid brightness range, not including the off or + // invalid states. + private boolean isValidBrightnessValue(float brightnessState) { + return brightnessState >= mScreenBrightnessRangeMinimum + && brightnessState <= mScreenBrightnessRangeMaximum; + } + + private void animateScreenBrightness(float target, float rate) { if (DEBUG) { Slog.d(TAG, "Animating brightness: target=" + target +", rate=" + rate); } if (mScreenBrightnessRampAnimator.animateTo(target, rate)) { - Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", target); + Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", (int) target); try { - mBatteryStats.noteScreenBrightness(target); + // TODO(brightnessfloat): change BatteryStats to use float + mBatteryStats.noteScreenBrightness( + BrightnessSynchronizer.brightnessFloatToInt( + mContext, target)); } catch (RemoteException ex) { // same process } @@ -1578,6 +1597,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private void handleSettingsChange(boolean userSwitch) { mPendingScreenBrightnessSetting = getScreenBrightnessSetting(); + if (userSwitch) { // Don't treat user switches as user initiated change. mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting; @@ -1598,24 +1618,24 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call return Float.isNaN(adj) ? 0.0f : clampAutoBrightnessAdjustment(adj); } - private int getScreenBrightnessSetting() { - final int brightness = Settings.System.getIntForUser(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS, mScreenBrightnessDefault, + private float getScreenBrightnessSetting() { + final float brightness = Settings.System.getFloatForUser(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_FLOAT, mScreenBrightnessDefault, UserHandle.USER_CURRENT); return clampAbsoluteBrightness(brightness); } - private int getScreenBrightnessForVrSetting() { - final int brightness = Settings.System.getIntForUser(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_FOR_VR, mScreenBrightnessForVrDefault, + private float getScreenBrightnessForVrSetting() { + final float brightnessFloat = Settings.System.getFloatForUser(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT, mScreenBrightnessForVrDefault, UserHandle.USER_CURRENT); - return clampScreenBrightnessForVr(brightness); + return clampScreenBrightnessForVr(brightnessFloat); } - private void putScreenBrightnessSetting(int brightness) { - mCurrentScreenBrightnessSetting = brightness; - Settings.System.putIntForUser(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS, brightness, UserHandle.USER_CURRENT); + private void putScreenBrightnessSetting(float brightnessValue) { + mCurrentScreenBrightnessSetting = brightnessValue; + Settings.System.putFloatForUser(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightnessValue, UserHandle.USER_CURRENT); } private void putAutoBrightnessAdjustmentSetting(float adjustment) { @@ -1638,18 +1658,19 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } private boolean updateUserSetScreenBrightness() { - if (mPendingScreenBrightnessSetting < 0) { + if ((Float.isNaN(mPendingScreenBrightnessSetting) + || mPendingScreenBrightnessSetting < 0.0f)) { return false; } if (mCurrentScreenBrightnessSetting == mPendingScreenBrightnessSetting) { - mPendingScreenBrightnessSetting = -1; - mTemporaryScreenBrightness = -1; + mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT; + mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; return false; } mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting; mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting; - mPendingScreenBrightnessSetting = -1; - mTemporaryScreenBrightness = -1; + mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT; + mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; return true; } @@ -1728,8 +1749,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println("Display Power Controller Configuration:"); pw.println(" mScreenBrightnessDozeConfig=" + mScreenBrightnessDozeConfig); pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig); - pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum); - pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum); pw.println(" mScreenBrightnessDefault=" + mScreenBrightnessDefault); pw.println(" mScreenBrightnessForVrRangeMinimum=" + mScreenBrightnessForVrRangeMinimum); pw.println(" mScreenBrightnessForVrRangeMaximum=" + mScreenBrightnessForVrRangeMaximum); @@ -1737,8 +1756,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println(" mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig); pw.println(" mAllowAutoBrightnessWhileDozingConfig=" + mAllowAutoBrightnessWhileDozingConfig); - pw.println(" mBrightnessRampRateFast=" + mBrightnessRampRateFast); - pw.println(" mBrightnessRampRateSlow=" + mBrightnessRampRateSlow); pw.println(" mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp); pw.println(" mColorFadeFadesConfig=" + mColorFadeFadesConfig); pw.println(" mColorFadeEnabled=" + mColorFadeEnabled); @@ -1767,15 +1784,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println(" mPendingProximityDebounceTime=" + TimeUtils.formatUptime(mPendingProximityDebounceTime)); pw.println(" mScreenOffBecauseOfProximity=" + mScreenOffBecauseOfProximity); - pw.println(" mLastUserSetScreenBrightness=" + mLastUserSetScreenBrightness); - pw.println(" mCurrentScreenBrightnessSetting=" + mCurrentScreenBrightnessSetting); - pw.println(" mPendingScreenBrightnessSetting=" + mPendingScreenBrightnessSetting); - pw.println(" mTemporaryScreenBrightness=" + mTemporaryScreenBrightness); + pw.println(" mLastUserSetScreenBrightnessFloat=" + mLastUserSetScreenBrightness); + pw.println(" mPendingScreenBrightnessSettingFloat=" + + mPendingScreenBrightnessSetting); + pw.println(" mTemporaryScreenBrightnessFloat=" + mTemporaryScreenBrightness); pw.println(" mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment); pw.println(" mBrightnessReason=" + mBrightnessReason); pw.println(" mTemporaryAutoBrightnessAdjustment=" + mTemporaryAutoBrightnessAdjustment); pw.println(" mPendingAutoBrightnessAdjustment=" + mPendingAutoBrightnessAdjustment); - pw.println(" mScreenBrightnessForVr=" + mScreenBrightnessForVr); + pw.println(" mScreenBrightnessForVrFloat=" + mScreenBrightnessForVr); pw.println(" mAppliedAutoBrightness=" + mAppliedAutoBrightness); pw.println(" mAppliedDimming=" + mAppliedDimming); pw.println(" mAppliedLowPower=" + mAppliedLowPower); @@ -1783,7 +1800,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println(" mAppliedTemporaryBrightness=" + mAppliedTemporaryBrightness); pw.println(" mDozing=" + mDozing); pw.println(" mSkipRampState=" + skipRampStateToString(mSkipRampState)); - pw.println(" mInitialAutoBrightness=" + mInitialAutoBrightness); pw.println(" mScreenOnBlockStartRealTime=" + mScreenOnBlockStartRealTime); pw.println(" mScreenOffBlockStartRealTime=" + mScreenOffBlockStartRealTime); pw.println(" mPendingScreenOnUnblocker=" + mPendingScreenOnUnblocker); @@ -1869,6 +1885,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call return MathUtils.constrain(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON); } + private static float clampAbsoluteBrightness(float value) { + return MathUtils.constrain(value, PowerManager.BRIGHTNESS_MIN, + PowerManager.BRIGHTNESS_MAX); + } + private static float clampAutoBrightnessAdjustment(float value) { return MathUtils.constrain(value, -1.0f, 1.0f); } @@ -1908,7 +1929,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call case MSG_SET_TEMPORARY_BRIGHTNESS: // TODO: Should we have a a timeout for the temporary brightness? - mTemporaryScreenBrightness = msg.arg1; + mTemporaryScreenBrightness = Float.intBitsToFloat(msg.arg1); updatePowerState(); break; diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java index 763f56ff918f..24e1b4edc8a6 100644 --- a/services/core/java/com/android/server/display/DisplayPowerState.java +++ b/services/core/java/com/android/server/display/DisplayPowerState.java @@ -22,11 +22,12 @@ import android.os.Looper; import android.os.PowerManager; import android.os.Trace; import android.util.FloatProperty; -import android.util.IntProperty; import android.util.Slog; import android.view.Choreographer; import android.view.Display; +import com.android.internal.BrightnessSynchronizer; + import java.io.PrintWriter; /** @@ -59,7 +60,7 @@ final class DisplayPowerState { private final PhotonicModulator mPhotonicModulator; private int mScreenState; - private int mScreenBrightness; + private float mScreenBrightness; private boolean mScreenReady; private boolean mScreenUpdatePending; @@ -85,7 +86,7 @@ final class DisplayPowerState { // will reset the brightness to a new level immediately before the changes // actually have a chance to be applied. mScreenState = Display.STATE_ON; - mScreenBrightness = PowerManager.BRIGHTNESS_ON; + mScreenBrightness = PowerManager.BRIGHTNESS_MAX; scheduleScreenUpdate(); mColorFadePrepared = false; @@ -106,18 +107,19 @@ final class DisplayPowerState { } }; - public static final IntProperty<DisplayPowerState> SCREEN_BRIGHTNESS = - new IntProperty<DisplayPowerState>("screenBrightness") { - @Override - public void setValue(DisplayPowerState object, int value) { - object.setScreenBrightness(value); - } - @Override - public Integer get(DisplayPowerState object) { - return object.getScreenBrightness(); - } - }; + public static final FloatProperty<DisplayPowerState> SCREEN_BRIGHTNESS_FLOAT = + new FloatProperty<DisplayPowerState>("screenBrightnessFloat") { + @Override + public void setValue(DisplayPowerState object, float value) { + object.setScreenBrightness(value); + } + + @Override + public Float get(DisplayPowerState object) { + return object.getScreenBrightness(); + } + }; /** * Sets whether the screen is on, off, or dozing. @@ -146,7 +148,7 @@ final class DisplayPowerState { * * @param brightness The brightness, ranges from 0 (minimum / off) to 255 (brightest). */ - public void setScreenBrightness(int brightness) { + public void setScreenBrightness(float brightness) { if (mScreenBrightness != brightness) { if (DEBUG) { Slog.d(TAG, "setScreenBrightness: brightness=" + brightness); @@ -163,7 +165,7 @@ final class DisplayPowerState { /** * Gets the screen brightness. */ - public int getScreenBrightness() { + public float getScreenBrightness() { return mScreenBrightness; } @@ -308,9 +310,9 @@ final class DisplayPowerState { public void run() { mScreenUpdatePending = false; - int brightness = mScreenState != Display.STATE_OFF - && mColorFadeLevel > 0f ? mScreenBrightness : 0; - if (mPhotonicModulator.setState(mScreenState, brightness)) { + float brightnessState = mScreenState != Display.STATE_OFF + && mColorFadeLevel > 0f ? mScreenBrightness : PowerManager.BRIGHTNESS_OFF_FLOAT; + if (mPhotonicModulator.setState(mScreenState, brightnessState)) { if (DEBUG) { Slog.d(TAG, "Screen ready"); } @@ -345,14 +347,14 @@ final class DisplayPowerState { */ private final class PhotonicModulator extends Thread { private static final int INITIAL_SCREEN_STATE = Display.STATE_OFF; // unknown, assume off - private static final int INITIAL_BACKLIGHT = -1; // unknown + private static final float INITIAL_BACKLIGHT_FLOAT = PowerManager.BRIGHTNESS_INVALID_FLOAT; private final Object mLock = new Object(); private int mPendingState = INITIAL_SCREEN_STATE; - private int mPendingBacklight = INITIAL_BACKLIGHT; + private float mPendingBacklight = INITIAL_BACKLIGHT_FLOAT; private int mActualState = INITIAL_SCREEN_STATE; - private int mActualBacklight = INITIAL_BACKLIGHT; + private float mActualBacklight = INITIAL_BACKLIGHT_FLOAT; private boolean mStateChangeInProgress; private boolean mBacklightChangeInProgress; @@ -360,19 +362,19 @@ final class DisplayPowerState { super("PhotonicModulator"); } - public boolean setState(int state, int backlight) { + public boolean setState(int state, float brightnessState) { synchronized (mLock) { boolean stateChanged = state != mPendingState; - boolean backlightChanged = backlight != mPendingBacklight; + boolean backlightChanged = !BrightnessSynchronizer.floatEquals( + brightnessState, mPendingBacklight); if (stateChanged || backlightChanged) { if (DEBUG) { Slog.d(TAG, "Requesting new screen state: state=" - + Display.stateToString(state) + ", backlight=" + backlight); + + Display.stateToString(state) + ", backlight=" + brightnessState); } mPendingState = state; - mPendingBacklight = backlight; - + mPendingBacklight = brightnessState; boolean changeInProgress = mStateChangeInProgress || mBacklightChangeInProgress; mStateChangeInProgress = stateChanged || mStateChangeInProgress; mBacklightChangeInProgress = backlightChanged || mBacklightChangeInProgress; @@ -404,13 +406,14 @@ final class DisplayPowerState { // Get pending change. final int state; final boolean stateChanged; - final int backlight; + final float brightnessState; final boolean backlightChanged; synchronized (mLock) { state = mPendingState; stateChanged = (state != mActualState); - backlight = mPendingBacklight; - backlightChanged = (backlight != mActualBacklight); + brightnessState = mPendingBacklight; + backlightChanged = !BrightnessSynchronizer.floatEquals( + brightnessState, mActualBacklight); if (!stateChanged) { // State changed applied, notify outer class. postScreenUpdateThreadSafe(); @@ -426,15 +429,15 @@ final class DisplayPowerState { continue; } mActualState = state; - mActualBacklight = backlight; + mActualBacklight = brightnessState; } // Apply pending change. if (DEBUG) { Slog.d(TAG, "Updating screen state: state=" - + Display.stateToString(state) + ", backlight=" + backlight); + + Display.stateToString(state) + ", backlight=" + brightnessState); } - mBlanker.requestDisplayState(state, backlight); + mBlanker.requestDisplayState(state, brightnessState); } } } diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index fc9542a33a78..2b225e5f3ec6 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -38,6 +38,7 @@ import android.view.DisplayEventReceiver; import android.view.Surface; import android.view.SurfaceControl; +import com.android.internal.BrightnessSynchronizer; import com.android.internal.os.BackgroundThread; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalServices; @@ -179,7 +180,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { private DisplayDeviceInfo mInfo; private boolean mHavePendingChanges; private int mState = Display.STATE_UNKNOWN; - private int mBrightness = PowerManager.BRIGHTNESS_DEFAULT; + private float mBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT; private int mDefaultModeId; private int mActiveModeId; private boolean mActiveModeInvalid; @@ -574,12 +575,14 @@ final class LocalDisplayAdapter extends DisplayAdapter { } @Override - public Runnable requestDisplayStateLocked(final int state, final int brightness) { + public Runnable requestDisplayStateLocked(final int state, final float brightnessState) { // Assume that the brightness is off if the display is being turned off. - assert state != Display.STATE_OFF || brightness == PowerManager.BRIGHTNESS_OFF; - + assert state != Display.STATE_OFF || BrightnessSynchronizer.floatEquals( + brightnessState, PowerManager.BRIGHTNESS_OFF_FLOAT); final boolean stateChanged = (mState != state); - final boolean brightnessChanged = (mBrightness != brightness) && mBacklight != null; + final boolean brightnessChanged = (!BrightnessSynchronizer.floatEquals( + mBrightnessState, brightnessState)) + && mBacklight != null; if (stateChanged || brightnessChanged) { final long physicalDisplayId = mPhysicalDisplayId; final IBinder token = getDisplayTokenLocked(); @@ -591,7 +594,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { } if (brightnessChanged) { - mBrightness = brightness; + mBrightnessState = brightnessState; } // Defer actually setting the display state until after we have exited @@ -630,10 +633,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { vrModeChange = true; } - // Apply brightness changes given that we are in a non-suspended state. if (brightnessChanged || vrModeChange) { - setDisplayBrightness(brightness); + setDisplayBrightness(brightnessState); } // Enter the final desired state, possibly suspended. @@ -694,7 +696,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } - private void setDisplayBrightness(int brightness) { + private void setDisplayBrightness(float brightness) { if (DEBUG) { Slog.d(TAG, "setDisplayBrightness(" + "id=" + physicalDisplayId @@ -704,26 +706,33 @@ final class LocalDisplayAdapter extends DisplayAdapter { Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayBrightness(" + "id=" + physicalDisplayId + ", brightness=" + brightness + ")"); try { - if (mHalBrightnessSupport) { - mBacklight.setBrightnessFloat( - displayBrightnessToHalBrightness(brightness)); - } else { - mBacklight.setBrightness(brightness); + // TODO: make it float + if (isHalBrightnessRangeSpecified()) { + brightness = displayBrightnessToHalBrightness( + BrightnessSynchronizer.brightnessFloatToInt(getContext(), + brightness)); } + mBacklight.setBrightness(brightness); Trace.traceCounter(Trace.TRACE_TAG_POWER, - "ScreenBrightness", brightness); + "ScreenBrightness", + BrightnessSynchronizer.brightnessFloatToInt( + getContext(), brightness)); } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } } + private boolean isHalBrightnessRangeSpecified() { + return !(mSystemBrightnessToNits == null || mNitsToHalBrightness == null); + } + /** * Converts brightness range from the framework's brightness space to the * Hal brightness space if the HAL brightness space has been provided via * a display device configuration file. */ private float displayBrightnessToHalBrightness(int brightness) { - if (mSystemBrightnessToNits == null || mNitsToHalBrightness == null) { + if (!isHalBrightnessRangeSpecified()) { return PowerManager.BRIGHTNESS_INVALID_FLOAT; } @@ -887,7 +896,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { pw.println("mActiveColorMode=" + mActiveColorMode); pw.println("mDefaultModeId=" + mDefaultModeId); pw.println("mState=" + Display.stateToString(mState)); - pw.println("mBrightness=" + mBrightness); + pw.println("mBrightnessState=" + mBrightnessState); pw.println("mBacklight=" + mBacklight); pw.println("mAllmSupported=" + mAllmSupported); pw.println("mAllmRequested=" + mAllmRequested); diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java index d71269f9eaeb..7916d816dc9b 100644 --- a/services/core/java/com/android/server/display/RampAnimator.java +++ b/services/core/java/com/android/server/display/RampAnimator.java @@ -17,21 +17,23 @@ package com.android.server.display; import android.animation.ValueAnimator; -import android.util.IntProperty; +import android.util.FloatProperty; import android.view.Choreographer; +import com.android.internal.BrightnessSynchronizer; + /** * A custom animator that progressively updates a property value at * a given variable rate until it reaches a particular target value. */ final class RampAnimator<T> { private final T mObject; - private final IntProperty<T> mProperty; + private final FloatProperty<T> mProperty; private final Choreographer mChoreographer; - private int mCurrentValue; - private int mTargetValue; - private int mRate; + private float mCurrentValue; + private float mTargetValue; + private float mRate; private boolean mAnimating; private float mAnimatedValue; // higher precision copy of mCurrentValue @@ -41,7 +43,7 @@ final class RampAnimator<T> { private Listener mListener; - public RampAnimator(T object, IntProperty<T> property) { + public RampAnimator(T object, FloatProperty<T> property) { mObject = object; mProperty = property; mChoreographer = Choreographer.getInstance(); @@ -57,7 +59,8 @@ final class RampAnimator<T> { * @param rate The convergence rate in units per second, or 0 to set the value immediately. * @return True if the target differs from the previous target. */ - public boolean animateTo(int target, int rate) { + public boolean animateTo(float target, float rate) { + // Immediately jump to the target the first time. if (mFirstTime || rate <= 0) { if (mFirstTime || target != mCurrentValue) { @@ -152,14 +155,12 @@ final class RampAnimator<T> { mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetValue); } } - final int oldCurrentValue = mCurrentValue; - mCurrentValue = Math.round(mAnimatedValue); - - if (oldCurrentValue != mCurrentValue) { + final float oldCurrentValue = mCurrentValue; + mCurrentValue = mAnimatedValue; + if (!BrightnessSynchronizer.floatEquals(oldCurrentValue, mCurrentValue)) { mProperty.setValue(mObject, mCurrentValue); } - - if (mTargetValue != mCurrentValue) { + if (!BrightnessSynchronizer.floatEquals(mTargetValue, mCurrentValue)) { postAnimationCallback(); } else { mAnimating = false; diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java index 1ca8dd3af4e2..f4f2eadfaa8e 100644 --- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java +++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java @@ -265,7 +265,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter { } @Override - public Runnable requestDisplayStateLocked(int state, int brightness) { + public Runnable requestDisplayStateLocked(int state, float brightnessState) { if (state != mDisplayState) { mDisplayState = state; if (state == Display.STATE_OFF) { diff --git a/services/core/java/com/android/server/incremental/IncrementalManagerService.java b/services/core/java/com/android/server/incremental/IncrementalManagerService.java index 64f25dd6f9fd..a8121cc6924f 100644 --- a/services/core/java/com/android/server/incremental/IncrementalManagerService.java +++ b/services/core/java/com/android/server/incremental/IncrementalManagerService.java @@ -142,16 +142,6 @@ public class IncrementalManagerService extends IIncrementalManager.Stub { } } - // TODO: remove this - @Override - public void newFileForDataLoader(int mountId, byte[] fileId, byte[] metadata) { - IDataLoader dataLoader = mDataLoaderManager.getDataLoader(mountId); - if (dataLoader == null) { - Slog.e(TAG, "Failed to retrieve data loader for ID=" + mountId); - return; - } - } - @Override public void showHealthBlockedUI(int mountId) { // TODO(b/136132412): implement this diff --git a/services/core/java/com/android/server/integrity/model/ComponentBitSize.java b/services/core/java/com/android/server/integrity/model/ComponentBitSize.java index c3899638f40f..94e6708c3038 100644 --- a/services/core/java/com/android/server/integrity/model/ComponentBitSize.java +++ b/services/core/java/com/android/server/integrity/model/ComponentBitSize.java @@ -29,13 +29,14 @@ public final class ComponentBitSize { public static final int KEY_BITS = 4; public static final int OPERATOR_BITS = 3; public static final int CONNECTOR_BITS = 2; - public static final int SEPARATOR_BITS = 2; + public static final int SEPARATOR_BITS = 3; public static final int VALUE_SIZE_BITS = 8; public static final int IS_HASHED_BITS = 1; public static final int ATOMIC_FORMULA_START = 0; public static final int COMPOUND_FORMULA_START = 1; public static final int COMPOUND_FORMULA_END = 2; + public static final int INSTALLER_ALLOWED_BY_MANIFEST_START = 3; public static final int DEFAULT_FORMAT_VERSION = 1; public static final int SIGNAL_BIT = 1; diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java index 4b8efafcb6b0..11e8d91dde12 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java +++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java @@ -23,6 +23,7 @@ import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMU import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS; import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS; import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS; +import static com.android.server.integrity.model.ComponentBitSize.INSTALLER_ALLOWED_BY_MANIFEST_START; import static com.android.server.integrity.model.ComponentBitSize.IS_HASHED_BITS; import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS; import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS; @@ -35,6 +36,7 @@ import static com.android.server.integrity.parser.BinaryFileOperations.getString import android.content.integrity.AtomicFormula; import android.content.integrity.CompoundFormula; +import android.content.integrity.InstallerAllowedByManifestFormula; import android.content.integrity.IntegrityFormula; import android.content.integrity.Rule; @@ -140,6 +142,8 @@ public class RuleBinaryParser implements RuleParser { return parseCompoundFormula(bitInputStream); case COMPOUND_FORMULA_END: return null; + case INSTALLER_ALLOWED_BY_MANIFEST_START: + return new InstallerAllowedByManifestFormula(); default: throw new IllegalArgumentException( String.format("Unknown formula separator: %s", separator)); diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java index 00e054596cd7..8ba5870aef0f 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java @@ -23,6 +23,7 @@ import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS import static com.android.server.integrity.model.ComponentBitSize.DEFAULT_FORMAT_VERSION; import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS; import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS; +import static com.android.server.integrity.model.ComponentBitSize.INSTALLER_ALLOWED_BY_MANIFEST_START; import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS; import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS; import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS; @@ -36,6 +37,7 @@ import static com.android.server.integrity.serializer.RuleIndexingDetails.PACKAG import android.content.integrity.AtomicFormula; import android.content.integrity.CompoundFormula; +import android.content.integrity.InstallerAllowedByManifestFormula; import android.content.integrity.IntegrityFormula; import android.content.integrity.IntegrityUtils; import android.content.integrity.Rule; @@ -202,6 +204,8 @@ public class RuleBinarySerializer implements RuleSerializer { serializeAtomicFormula((AtomicFormula) formula, bitOutputStream); } else if (formula instanceof CompoundFormula) { serializeCompoundFormula((CompoundFormula) formula, bitOutputStream); + } else if (formula instanceof InstallerAllowedByManifestFormula) { + bitOutputStream.setNext(SEPARATOR_BITS, INSTALLER_ALLOWED_BY_MANIFEST_START); } else { throw new IllegalArgumentException( String.format("Invalid formula type: %s", formula.getClass())); diff --git a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java index 6f7d172aabcc..e7235591fb9b 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java @@ -84,6 +84,7 @@ class RuleIndexingDetailsIdentifier { return getIndexingDetailsForStringAtomicFormula( (AtomicFormula.StringAtomicFormula) formula); case IntegrityFormula.LONG_ATOMIC_FORMULA_TAG: + case IntegrityFormula.INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG: case IntegrityFormula.BOOLEAN_ATOMIC_FORMULA_TAG: // Package name and app certificate related formulas are string atomic formulas. return new RuleIndexingDetails(NOT_INDEXED); diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java index 5683e6901a31..a42dec8b575f 100644 --- a/services/core/java/com/android/server/lights/LightsService.java +++ b/services/core/java/com/android/server/lights/LightsService.java @@ -40,6 +40,7 @@ import android.view.SurfaceControl; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; +import com.android.internal.BrightnessSynchronizer; import com.android.server.SystemService; import java.util.ArrayList; @@ -249,28 +250,21 @@ public class LightsService extends SystemService { } @Override - public void setBrightnessFloat(float brightness) { - if (!Float.isNaN(brightness)) { - setBrightness(brightness, 0, BRIGHTNESS_MODE_USER); - } - } - - @Override - public void setBrightness(int brightness) { + public void setBrightness(float brightness) { setBrightness(brightness, BRIGHTNESS_MODE_USER); } @Override - public void setBrightness(int brightness, int brightnessMode) { - setBrightness(Float.NaN, brightness, brightnessMode); - } - - private void setBrightness(float brightnessFloat, int brightness, int brightnessMode) { + public void setBrightness(float brightness, int brightnessMode) { + if (Float.isNaN(brightness)) { + Slog.w(TAG, "Brightness is not valid: " + brightness); + return; + } synchronized (this) { // LOW_PERSISTENCE cannot be manually set if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) { Slog.w(TAG, "setBrightness with LOW_PERSISTENCE unexpected #" + mHwLight.id - + ": brightness=0x" + Integer.toHexString(brightness)); + + ": brightness=" + brightness); return; } // Ideally, we'd like to set the brightness mode through the SF/HWC as well, but @@ -278,6 +272,7 @@ public class LightsService extends SystemService { // anything but USER or the device shouldBeInLowPersistenceMode(). if (brightnessMode == BRIGHTNESS_MODE_USER && !shouldBeInLowPersistenceMode() && mSurfaceControlMaximumBrightness == 255) { + // New system // TODO: the last check should be mSurfaceControlMaximumBrightness != 0; the // reason we enforce 255 right now is to stay consistent with the old path. In // the future, the framework should be refactored so that brightness is a float @@ -286,17 +281,12 @@ public class LightsService extends SystemService { if (DEBUG) { Slog.d(TAG, "Using new setBrightness path!"); } - - if (!Float.isNaN(brightnessFloat)) { - SurfaceControl.setDisplayBrightness(mDisplayToken, brightnessFloat); - } else if (brightness == 0) { - SurfaceControl.setDisplayBrightness(mDisplayToken, -1.0f); - } else { - SurfaceControl.setDisplayBrightness(mDisplayToken, - (float) (brightness - 1) / (mSurfaceControlMaximumBrightness - 1)); - } + SurfaceControl.setDisplayBrightness(mDisplayToken, brightness); } else { - int color = brightness & 0x000000ff; + // Old system + int brightnessInt = BrightnessSynchronizer.brightnessFloatToInt( + getContext(), brightness); + int color = brightnessInt & 0x000000ff; color = 0xff000000 | (color << 16) | (color << 8) | color; setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode); } diff --git a/services/core/java/com/android/server/lights/LogicalLight.java b/services/core/java/com/android/server/lights/LogicalLight.java index 33dfbb4eea48..7491cecaefe9 100644 --- a/services/core/java/com/android/server/lights/LogicalLight.java +++ b/services/core/java/com/android/server/lights/LogicalLight.java @@ -57,18 +57,12 @@ public abstract class LogicalLight { /** * Set the brightness of a display. */ - public abstract void setBrightness(int brightness); + public abstract void setBrightness(float brightness); /** * Set the brightness and mode of a display. */ - public abstract void setBrightness(int brightness, int brightnessMode); - - /** - * Set the brightness of a display using the brightness range defines in a - * display-device-configuration file. - */ - public abstract void setBrightnessFloat(float brightness); + public abstract void setBrightness(float brightness, int brightnessMode); /** * Set the color of a light. diff --git a/services/core/java/com/android/server/location/GnssAntennaInfoProvider.java b/services/core/java/com/android/server/location/GnssAntennaInfoProvider.java new file mode 100644 index 000000000000..09af655e1735 --- /dev/null +++ b/services/core/java/com/android/server/location/GnssAntennaInfoProvider.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.location; + +import android.content.Context; +import android.location.GnssAntennaInfo; +import android.location.IGnssAntennaInfoListener; +import android.os.Handler; +import android.os.RemoteException; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.List; + +/** + * An base implementation for GNSS antenna info provider. It abstracts out the responsibility of + * handling listeners, while still allowing technology specific implementations to be built. + * + * @hide + */ +public abstract class GnssAntennaInfoProvider + extends RemoteListenerHelper<Void, IGnssAntennaInfoListener> { + private static final String TAG = "GnssAntennaInfoProvider"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private final GnssAntennaInfoProviderNative mNative; + + private boolean mIsListeningStarted; + + protected GnssAntennaInfoProvider(Context context, Handler handler) { + this(context, handler, new GnssAntennaInfoProviderNative()); + } + + @VisibleForTesting + public GnssAntennaInfoProvider( + Context context, Handler handler, GnssAntennaInfoProviderNative aNative) { + super(context, handler, TAG); + mNative = aNative; + } + + void resumeIfStarted() { + if (DEBUG) { + Log.d(TAG, "resumeIfStarted"); + } + if (mIsListeningStarted) { + mNative.startAntennaInfoListening(); + } + } + + + @Override + public boolean isAvailableInPlatform() { + return mNative.isAntennaInfoSupported(); + } + + @Override + protected int registerWithService() { + boolean started = mNative.startAntennaInfoListening(); + if (started) { + mIsListeningStarted = true; + return RemoteListenerHelper.RESULT_SUCCESS; + } + return RemoteListenerHelper.RESULT_INTERNAL_ERROR; + } + + @Override + protected void unregisterFromService() { + boolean stopped = mNative.stopAntennaInfoListening(); + if (stopped) { + mIsListeningStarted = false; + } + } + + /** Handle GNSS capabilities update from the GNSS HAL implementation. */ + public void onCapabilitiesUpdated(boolean isAntennaInfoSupported) { + setSupported(isAntennaInfoSupported); + updateResult(); + } + + /** Handle GNSS enabled changes.*/ + public void onGpsEnabledChanged() { + tryUpdateRegistrationWithService(); + updateResult(); + } + + @Override + protected ListenerOperation<IGnssAntennaInfoListener> getHandlerOperation(int result) { + int status; + switch (result) { + case RESULT_SUCCESS: + status = GnssAntennaInfo.Callback.STATUS_READY; + break; + case RESULT_NOT_AVAILABLE: + case RESULT_NOT_SUPPORTED: + case RESULT_INTERNAL_ERROR: + status = GnssAntennaInfo.Callback.STATUS_NOT_SUPPORTED; + break; + case RESULT_GPS_LOCATION_DISABLED: + status = GnssAntennaInfo.Callback.STATUS_LOCATION_DISABLED; + break; + case RESULT_UNKNOWN: + return null; + default: + Log.v(TAG, "Unhandled addListener result: " + result); + return null; + } + return new StatusChangedOperation(status); + } + + private static class StatusChangedOperation + implements ListenerOperation<IGnssAntennaInfoListener> { + private final int mStatus; + + StatusChangedOperation(int status) { + mStatus = status; + } + + @Override + public void execute(IGnssAntennaInfoListener listener, + CallerIdentity callerIdentity) throws RemoteException { + listener.onStatusChanged(mStatus); + } + } + + /** Handle Gnss Antenna Info report. */ + public void onGnssAntennaInfoAvailable(final List<GnssAntennaInfo> gnssAntennaInfos) { + foreach((IGnssAntennaInfoListener listener, CallerIdentity callerIdentity) -> { + if (!hasPermission(mContext, callerIdentity)) { + logPermissionDisabledEventNotReported( + TAG, callerIdentity.mPackageName, "GNSS antenna info"); + return; + } + listener.onGnssAntennaInfoReceived(gnssAntennaInfos); + }); + } + + /** + * Wrapper class for native methods. This is mocked for testing. + */ + @VisibleForTesting + public static class GnssAntennaInfoProviderNative { + + public boolean isAntennaInfoSupported() { + return native_is_antenna_info_supported(); + } + + /** Start antenna info listening. */ + public boolean startAntennaInfoListening() { + return native_start_antenna_info_listening(); + } + + /** Stop antenna info listening. */ + public boolean stopAntennaInfoListening() { + return native_stop_antenna_info_listening(); + } + } + + private static native boolean native_is_antenna_info_supported(); + + private static native boolean native_start_antenna_info_listening(); + + private static native boolean native_stop_antenna_info_listening(); +} diff --git a/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java b/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java index 88ff6e7d1366..5c8507f7fde0 100644 --- a/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java +++ b/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java @@ -77,6 +77,9 @@ public class GnssCapabilitiesProvider { if (hasCapability(topHalCapabilities, GnssLocationProvider.GPS_CAPABILITY_NAV_MESSAGES)) { gnssCapabilities |= GnssCapabilities.NAV_MESSAGES; } + if (hasCapability(topHalCapabilities, GnssLocationProvider.GPS_CAPABILITY_ANTENNA_INFO)) { + gnssCapabilities |= GnssCapabilities.ANTENNA_INFO; + } synchronized (this) { mGnssCapabilities &= ~GNSS_CAPABILITIES_TOP_HAL; diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index bcac4730cb8b..36136f49dc42 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -29,6 +29,7 @@ import android.hardware.location.GeofenceHardware; import android.hardware.location.GeofenceHardwareImpl; import android.location.Criteria; import android.location.FusedBatchOptions; +import android.location.GnssAntennaInfo; import android.location.GnssMeasurementsEvent; import android.location.GnssNavigationMessage; import android.location.GnssStatus; @@ -182,6 +183,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements public static final int GPS_CAPABILITY_LOW_POWER_MODE = 0x0000100; public static final int GPS_CAPABILITY_SATELLITE_BLACKLIST = 0x0000200; public static final int GPS_CAPABILITY_MEASUREMENT_CORRECTIONS = 0x0000400; + public static final int GPS_CAPABILITY_ANTENNA_INFO = 0x0000800; // The AGPS SUPL mode private static final int AGPS_SUPL_MODE_MSA = 0x02; @@ -397,6 +399,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private final GnssStatusListenerHelper mGnssStatusListenerHelper; private final GnssMeasurementsProvider mGnssMeasurementsProvider; private final GnssMeasurementCorrectionsProvider mGnssMeasurementCorrectionsProvider; + private final GnssAntennaInfoProvider mGnssAntennaInfoProvider; private final GnssNavigationMessageProvider mGnssNavigationMessageProvider; private final LocationChangeListener mNetworkLocationListener = new NetworkLocationListener(); private final LocationChangeListener mFusedLocationListener = new FusedLocationListener(); @@ -469,6 +472,10 @@ public class GnssLocationProvider extends AbstractLocationProvider implements return mGnssMeasurementCorrectionsProvider; } + public GnssAntennaInfoProvider getGnssAntennaInfoProvider() { + return mGnssAntennaInfoProvider; + } + public GnssNavigationMessageProvider getGnssNavigationMessageProvider() { return mGnssNavigationMessageProvider; } @@ -693,6 +700,13 @@ public class GnssLocationProvider extends AbstractLocationProvider implements mGnssMeasurementCorrectionsProvider = new GnssMeasurementCorrectionsProvider(mHandler); + mGnssAntennaInfoProvider = new GnssAntennaInfoProvider(mContext, mHandler) { + @Override + protected boolean isGpsEnabled() { + return GnssLocationProvider.this.isGpsEnabled(); + } + }; + mGnssNavigationMessageProvider = new GnssNavigationMessageProvider(mContext, mHandler) { @Override protected boolean isGpsEnabled() { @@ -992,6 +1006,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements mGnssMeasurementsProvider.onGpsEnabledChanged(); mGnssNavigationMessageProvider.onGpsEnabledChanged(); + mGnssAntennaInfoProvider.onGpsEnabledChanged(); mGnssBatchingProvider.enable(); if (mGnssVisibilityControl != null) { mGnssVisibilityControl.onGpsEnabledChanged(/* isEnabled= */ true); @@ -1018,6 +1033,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements // do this before releasing wakelock native_cleanup(); + mGnssAntennaInfoProvider.onGpsEnabledChanged(); mGnssMeasurementsProvider.onGpsEnabledChanged(); mGnssNavigationMessageProvider.onGpsEnabledChanged(); } @@ -1563,6 +1579,11 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } @NativeEntryPoint + private void reportAntennaInfo(List<GnssAntennaInfo> antennaInfos) { + mHandler.post(() -> mGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(antennaInfos)); + } + + @NativeEntryPoint private void reportNavigationMessage(GnssNavigationMessage event) { if (!mItarSpeedLimitExceeded) { // send to handler to allow native to return quickly @@ -1585,6 +1606,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements mGnssNavigationMessageProvider.onCapabilitiesUpdated( hasCapability(GPS_CAPABILITY_NAV_MESSAGES)); restartRequests(); + mGnssAntennaInfoProvider.onCapabilitiesUpdated( + hasCapability(GPS_CAPABILITY_ANTENNA_INFO)); mGnssCapabilitiesProvider.setTopHalCapabilities(mTopHalCapabilities); }); @@ -1606,6 +1629,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements Log.i(TAG, "restartRequests"); restartLocationRequest(); + mGnssAntennaInfoProvider.resumeIfStarted(); mGnssMeasurementsProvider.resumeIfStarted(); mGnssNavigationMessageProvider.resumeIfStarted(); mGnssBatchingProvider.resumeIfStarted(); @@ -2198,6 +2222,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements s.append(" ago)").append('\n'); s.append("mFixInterval=").append(mFixInterval).append('\n'); s.append("mLowPowerMode=").append(mLowPowerMode).append('\n'); + s.append("mGnssAntennaInfoProvider.isRegistered()=") + .append(mGnssAntennaInfoProvider.isRegistered()).append('\n'); s.append("mGnssMeasurementsProvider.isRegistered()=") .append(mGnssMeasurementsProvider.isRegistered()).append('\n'); s.append("mGnssNavigationMessageProvider.isRegistered()=") diff --git a/services/core/java/com/android/server/location/LocationFudger.java b/services/core/java/com/android/server/location/LocationFudger.java index 04c7714f07f7..a069e7ace636 100644 --- a/services/core/java/com/android/server/location/LocationFudger.java +++ b/services/core/java/com/android/server/location/LocationFudger.java @@ -16,297 +16,165 @@ package com.android.server.location; -import android.content.Context; -import android.database.ContentObserver; +import android.annotation.Nullable; import android.location.Location; -import android.os.Bundle; -import android.os.Handler; import android.os.SystemClock; -import android.provider.Settings; -import android.util.Log; -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.security.SecureRandom; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import java.security.SecureRandom; +import java.time.Clock; +import java.util.Random; /** - * Contains the logic to obfuscate (fudge) locations for coarse applications. - * - * <p>The goal is just to prevent applications with only - * the coarse location permission from receiving a fine location. + * Contains the logic to obfuscate (fudge) locations for coarse applications. The goal is just to + * prevent applications with only the coarse location permission from receiving a fine location. */ public class LocationFudger { - private static final boolean D = false; - private static final String TAG = "LocationFudge"; - - /** - * Default coarse accuracy in meters. - */ - private static final float DEFAULT_ACCURACY_IN_METERS = 2000.0f; - - /** - * Minimum coarse accuracy in meters. - */ - private static final float MINIMUM_ACCURACY_IN_METERS = 200.0f; - - /** - * Secure settings key for coarse accuracy. - */ - private static final String COARSE_ACCURACY_CONFIG_NAME = "locationCoarseAccuracy"; - /** - * This is the fastest interval that applications can receive coarse - * locations. - */ - public static final long FASTEST_INTERVAL_MS = 10 * 60 * 1000; // 10 minutes + // minimum accuracy a coarsened location can have + private static final float MIN_ACCURACY_M = 200.0f; - /** - * The duration until we change the random offset. - */ - private static final long CHANGE_INTERVAL_MS = 60 * 60 * 1000; // 1 hour + // how often random offsets are updated + @VisibleForTesting + static final long OFFSET_UPDATE_INTERVAL_MS = 60 * 60 * 1000; - /** - * The percentage that we change the random offset at every interval. - * - * <p>0.0 indicates the random offset doesn't change. 1.0 - * indicates the random offset is completely replaced every interval. - */ + // the percentage that we change the random offset at every interval. 0.0 indicates the random + // offset doesn't change. 1.0 indicates the random offset is completely replaced every interval private static final double CHANGE_PER_INTERVAL = 0.03; // 3% change - // Pre-calculated weights used to move the random offset. - // - // The goal is to iterate on the previous offset, but keep - // the resulting standard deviation the same. The variance of - // two gaussian distributions summed together is equal to the - // sum of the variance of each distribution. So some quick - // algebra results in the following sqrt calculation to - // weigh in a new offset while keeping the final standard - // deviation unchanged. + // weights used to move the random offset. the goal is to iterate on the previous offset, but + // keep the resulting standard deviation the same. the variance of two gaussian distributions + // summed together is equal to the sum of the variance of each distribution. so some quick + // algebra results in the following sqrt calculation to weight in a new offset while keeping the + // final standard deviation unchanged. private static final double NEW_WEIGHT = CHANGE_PER_INTERVAL; - private static final double PREVIOUS_WEIGHT = Math.sqrt(1 - NEW_WEIGHT * NEW_WEIGHT); - - /** - * This number actually varies because the earth is not round, but - * 111,000 meters is considered generally acceptable. - */ - private static final int APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR = 111000; - - /** - * Maximum latitude. - * - * <p>We pick a value 1 meter away from 90.0 degrees in order - * to keep cosine(MAX_LATITUDE) to a non-zero value, so that we avoid - * divide by zero fails. - */ - private static final double MAX_LATITUDE = 90.0 - - (1.0 / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR); - - private final Object mLock = new Object(); - private final SecureRandom mRandom = new SecureRandom(); - - /** - * Used to monitor coarse accuracy secure setting for changes. - */ - private final ContentObserver mSettingsObserver; - - /** - * Used to resolve coarse accuracy setting. - */ - private final Context mContext; - - // all fields below protected by mLock - private double mOffsetLatitudeMeters; - private double mOffsetLongitudeMeters; - private long mNextInterval; - - /** - * Best location accuracy allowed for coarse applications. - * This value should only be set by {@link #setAccuracyInMetersLocked(float)}. - */ - private float mAccuracyInMeters; + private static final double OLD_WEIGHT = Math.sqrt(1 - NEW_WEIGHT * NEW_WEIGHT); + + // this number actually varies because the earth is not round, but 111,000 meters is considered + // generally acceptable + private static final int APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR = 111_000; + + // we pick a value 1 meter away from 90.0 degrees in order to keep cosine(MAX_LATITUDE) to a + // non-zero value, so that we avoid divide by zero errors + private static final double MAX_LATITUDE = + 90.0 - (1.0 / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR); + + private final float mAccuracyM; + private final Clock mClock; + private final Random mRandom; + + @GuardedBy("this") + private double mLatitudeOffsetM; + @GuardedBy("this") + private double mLongitudeOffsetM; + @GuardedBy("this") + private long mNextUpdateRealtimeMs; + + @GuardedBy("this") + @Nullable private Location mCachedFineLocation; + @GuardedBy("this") + @Nullable private Location mCachedCoarseLocation; + + public LocationFudger(float accuracyM) { + this(accuracyM, SystemClock.elapsedRealtimeClock(), new SecureRandom()); + } - /** - * The distance between grids for snap-to-grid. See {@link #createCoarse}. - * This value should only be set by {@link #setAccuracyInMetersLocked(float)}. - */ - private double mGridSizeInMeters; + @VisibleForTesting + LocationFudger(float accuracyM, Clock clock, Random random) { + mClock = clock; + mRandom = random; + mAccuracyM = Math.max(accuracyM, MIN_ACCURACY_M); - /** - * Standard deviation of the (normally distributed) random offset applied - * to coarse locations. It does not need to be as large as - * {@link #COARSE_ACCURACY_METERS} because snap-to-grid is the primary obfuscation - * method. See further details in the implementation. - * This value should only be set by {@link #setAccuracyInMetersLocked(float)}. - */ - private double mStandardDeviationInMeters; - - public LocationFudger(Context context, Handler handler) { - mContext = context; - mSettingsObserver = new ContentObserver(handler) { - @Override - public void onChange(boolean selfChange) { - setAccuracyInMeters(loadCoarseAccuracy()); - } - }; - mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor( - COARSE_ACCURACY_CONFIG_NAME), false, mSettingsObserver); - - float accuracy = loadCoarseAccuracy(); - synchronized (mLock) { - setAccuracyInMetersLocked(accuracy); - mOffsetLatitudeMeters = nextOffsetLocked(); - mOffsetLongitudeMeters = nextOffsetLocked(); - mNextInterval = SystemClock.elapsedRealtime() + CHANGE_INTERVAL_MS; - } + mLatitudeOffsetM = nextRandomOffset(); + mLongitudeOffsetM = nextRandomOffset(); + mNextUpdateRealtimeMs = mClock.millis() + OFFSET_UPDATE_INTERVAL_MS; } /** - * Get the cached coarse location, or generate a new one and cache it. - */ - public Location getOrCreate(Location location) { - synchronized (mLock) { - Location coarse = location.getExtraLocation(Location.EXTRA_COARSE_LOCATION); - if (coarse == null) { - return addCoarseLocationExtraLocked(location); - } - if (coarse.getAccuracy() < mAccuracyInMeters) { - return addCoarseLocationExtraLocked(location); + * Create a coarse location using two technique, random offsets and snap-to-grid. + * + * First we add a random offset to mitigate against detecting grid transitions. Without a random + * offset it is possible to detect a user's position quite accurately when they cross a grid + * boundary. The random offset changes very slowly over time, to mitigate against taking many + * location samples and averaging them out. Second we snap-to-grid (quantize). This has the nice + * property of producing stable results, and mitigating against taking many samples to average + * out a random offset. + */ + public Location createCoarse(Location fine) { + synchronized (this) { + if (fine == mCachedFineLocation) { + return new Location(mCachedCoarseLocation); } - return coarse; } - } - private Location addCoarseLocationExtraLocked(Location location) { - Location coarse = createCoarseLocked(location); - Bundle extras = location.getExtras(); - if (extras == null) { - extras = new Bundle(); - } - extras.putParcelable(Location.EXTRA_COARSE_LOCATION, coarse); - location.setExtras(extras); - return coarse; - } + // update the offsets in use + updateOffsets(); - /** - * Create a coarse location. - * - * <p>Two techniques are used: random offsets and snap-to-grid. - * - * <p>First we add a random offset. This mitigates against detecting - * grid transitions. Without a random offset it is possible to detect - * a users position very accurately when they cross a grid boundary. - * The random offset changes very slowly over time, to mitigate against - * taking many location samples and averaging them out. - * - * <p>Second we snap-to-grid (quantize). This has the nice property of - * producing stable results, and mitigating against taking many samples - * to average out a random offset. - */ - private Location createCoarseLocked(Location fine) { Location coarse = new Location(fine); - // clean all the optional information off the location, because - // this can leak detailed location information + // clear any fields that could leak more detailed location information coarse.removeBearing(); coarse.removeSpeed(); coarse.removeAltitude(); coarse.setExtras(null); - double lat = coarse.getLatitude(); - double lon = coarse.getLongitude(); + double latitude = wrapLatitude(coarse.getLatitude()); + double longitude = wrapLongitude(coarse.getLongitude()); - // wrap - lat = wrapLatitude(lat); - lon = wrapLongitude(lon); + // add offsets - update longitude first using the non-offset latitude + longitude += wrapLongitude(metersToDegreesLongitude(mLongitudeOffsetM, latitude)); + latitude += wrapLatitude(metersToDegreesLatitude(mLatitudeOffsetM)); - // Step 1) apply a random offset - // - // The goal of the random offset is to prevent the application - // from determining that the device is on a grid boundary - // when it crosses from one grid to the next. - // - // We apply the offset even if the location already claims to be - // inaccurate, because it may be more accurate than claimed. - updateRandomOffsetLocked(); - // perform lon first whilst lat is still within bounds - lon += metersToDegreesLongitude(mOffsetLongitudeMeters, lat); - lat += metersToDegreesLatitude(mOffsetLatitudeMeters); - if (D) Log.d(TAG, String.format("applied offset of %.0f, %.0f (meters)", - mOffsetLongitudeMeters, mOffsetLatitudeMeters)); - - // wrap - lat = wrapLatitude(lat); - lon = wrapLongitude(lon); - - // Step 2) Snap-to-grid (quantize) + // quantize location by snapping to a grid. this is the primary means of obfuscation. it + // gives nice consistent results and is very effective at hiding the true location (as long + // as you are not sitting on a grid boundary, which the random offsets mitigate). // - // This is the primary means of obfuscation. It gives nice consistent - // results and is very effective at hiding the true location - // (as long as you are not sitting on a grid boundary, which - // step 1 mitigates). - // - // Note we quantize the latitude first, since the longitude - // quantization depends on the latitude value and so leaks information - // about the latitude - double latGranularity = metersToDegreesLatitude(mGridSizeInMeters); - lat = Math.round(lat / latGranularity) * latGranularity; - double lonGranularity = metersToDegreesLongitude(mGridSizeInMeters, lat); - lon = Math.round(lon / lonGranularity) * lonGranularity; - - // wrap again - lat = wrapLatitude(lat); - lon = wrapLongitude(lon); - - // apply - coarse.setLatitude(lat); - coarse.setLongitude(lon); - coarse.setAccuracy(Math.max(mAccuracyInMeters, coarse.getAccuracy())); - - if (D) Log.d(TAG, "fudged " + fine + " to " + coarse); - return coarse; + // note that we quantize the latitude first, since the longitude quantization depends on the + // latitude value and so leaks information about the latitude + double latGranularity = metersToDegreesLatitude(mAccuracyM); + latitude = wrapLatitude(Math.round(latitude / latGranularity) * latGranularity); + double lonGranularity = metersToDegreesLongitude(mAccuracyM, latitude); + longitude = wrapLongitude(Math.round(longitude / lonGranularity) * lonGranularity); + + coarse.setLatitude(latitude); + coarse.setLongitude(longitude); + coarse.setAccuracy(Math.max(mAccuracyM, coarse.getAccuracy())); + + synchronized (this) { + mCachedFineLocation = fine; + mCachedCoarseLocation = coarse; + } + + return new Location(mCachedCoarseLocation); } /** - * Update the random offset over time. + * Update the random offsets over time. * - * <p>If the random offset was new for every location - * fix then an application can more easily average location results - * over time, - * especially when the location is near a grid boundary. On the - * other hand if the random offset is constant then if an application - * found a way to reverse engineer the offset they would be able - * to detect location at grid boundaries very accurately. So - * we choose a random offset and then very slowly move it, to - * make both approaches very hard. - * - * <p>The random offset does not need to be large, because snap-to-grid - * is the primary obfuscation mechanism. It just needs to be large - * enough to stop information leakage as we cross grid boundaries. - */ - private void updateRandomOffsetLocked() { - long now = SystemClock.elapsedRealtime(); - if (now < mNextInterval) { + * If the random offset was reset for every location fix then an application could more easily + * average location results over time, especially when the location is near a grid boundary. On + * the other hand if the random offset is constant then if an application finds a way to reverse + * engineer the offset they would be able to detect location at grid boundaries very accurately. + * So we choose a random offset and then very slowly move it, to make both approaches very hard. + * The random offset does not need to be large, because snap-to-grid is the primary obfuscation + * mechanism. It just needs to be large enough to stop information leakage as we cross grid + * boundaries. + */ + private synchronized void updateOffsets() { + long now = mClock.millis(); + if (now < mNextUpdateRealtimeMs) { return; } - if (D) Log.d(TAG, String.format("old offset: %.0f, %.0f (meters)", - mOffsetLongitudeMeters, mOffsetLatitudeMeters)); - - // ok, need to update the random offset - mNextInterval = now + CHANGE_INTERVAL_MS; - - mOffsetLatitudeMeters *= PREVIOUS_WEIGHT; - mOffsetLatitudeMeters += NEW_WEIGHT * nextOffsetLocked(); - mOffsetLongitudeMeters *= PREVIOUS_WEIGHT; - mOffsetLongitudeMeters += NEW_WEIGHT * nextOffsetLocked(); - - if (D) Log.d(TAG, String.format("new offset: %.0f, %.0f (meters)", - mOffsetLongitudeMeters, mOffsetLatitudeMeters)); + mLatitudeOffsetM = (OLD_WEIGHT * mLatitudeOffsetM) + (NEW_WEIGHT * nextRandomOffset()); + mLongitudeOffsetM = (OLD_WEIGHT * mLongitudeOffsetM) + (NEW_WEIGHT * nextRandomOffset()); + mNextUpdateRealtimeMs = now + OFFSET_UPDATE_INTERVAL_MS; } - private double nextOffsetLocked() { - return mRandom.nextGaussian() * mStandardDeviationInMeters; + private double nextRandomOffset() { + return mRandom.nextGaussian() * (mAccuracyM / 4.0); } private static double wrapLatitude(double lat) { @@ -334,56 +202,8 @@ public class LocationFudger { return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR; } - /** - * Requires latitude since longitudinal distances change with distance from equator. - */ + // requires latitude since longitudinal distances change with distance from equator. private static double metersToDegreesLongitude(double distance, double lat) { return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR / Math.cos(Math.toRadians(lat)); } - - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println(String.format("offset: %.0f, %.0f (meters)", mOffsetLongitudeMeters, - mOffsetLatitudeMeters)); - } - - /** - * This is the main control: call this to set the best location accuracy - * allowed for coarse applications and all derived values. - */ - private void setAccuracyInMetersLocked(float accuracyInMeters) { - mAccuracyInMeters = Math.max(accuracyInMeters, MINIMUM_ACCURACY_IN_METERS); - if (D) { - Log.d(TAG, "setAccuracyInMetersLocked: new accuracy = " + mAccuracyInMeters); - } - mGridSizeInMeters = mAccuracyInMeters; - mStandardDeviationInMeters = mGridSizeInMeters / 4.0; - } - - /** - * Same as setAccuracyInMetersLocked without the pre-lock requirement. - */ - private void setAccuracyInMeters(float accuracyInMeters) { - synchronized (mLock) { - setAccuracyInMetersLocked(accuracyInMeters); - } - } - - /** - * Loads the coarse accuracy value from secure settings. - */ - private float loadCoarseAccuracy() { - String newSetting = Settings.Secure.getString(mContext.getContentResolver(), - COARSE_ACCURACY_CONFIG_NAME); - if (D) { - Log.d(TAG, "loadCoarseAccuracy: newSetting = \"" + newSetting + "\""); - } - if (newSetting == null) { - return DEFAULT_ACCURACY_IN_METERS; - } - try { - return Float.parseFloat(newSetting); - } catch (NumberFormatException e) { - return DEFAULT_ACCURACY_IN_METERS; - } - } } diff --git a/services/core/java/com/android/server/location/MockableLocationProvider.java b/services/core/java/com/android/server/location/MockableLocationProvider.java index b0e133061fc6..f43669e488d1 100644 --- a/services/core/java/com/android/server/location/MockableLocationProvider.java +++ b/services/core/java/com/android/server/location/MockableLocationProvider.java @@ -237,6 +237,10 @@ public class MockableLocationProvider extends AbstractLocationProvider { * Dumps the current provider implementation. */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + // holding the owner lock outside this could lead to deadlock since we don't run dump on the + // executor specified by the provider, we run it directly + Preconditions.checkState(!Thread.holdsLock(mOwnerLock)); + AbstractLocationProvider provider; synchronized (mOwnerLock) { provider = mProvider; diff --git a/services/core/java/com/android/server/location/SettingsHelper.java b/services/core/java/com/android/server/location/SettingsHelper.java index 6d1d1f901eb3..370a3f762d32 100644 --- a/services/core/java/com/android/server/location/SettingsHelper.java +++ b/services/core/java/com/android/server/location/SettingsHelper.java @@ -23,6 +23,7 @@ import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACK import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS; import static android.provider.Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST; import static android.provider.Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS; +import static android.provider.Settings.Secure.LOCATION_COARSE_ACCURACY_M; import static android.provider.Settings.Secure.LOCATION_MODE; import static android.provider.Settings.Secure.LOCATION_MODE_OFF; @@ -88,6 +89,7 @@ public class SettingsHelper { private static final long DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS = 30 * 60 * 1000; private static final long DEFAULT_MAX_LAST_LOCATION_AGE_MS = 20 * 60 * 1000; + private static final float DEFAULT_COARSE_LOCATION_ACCURACY_M = 2000.0f; private final Context mContext; @@ -268,19 +270,45 @@ public class SettingsHelper { * Retrieve the background throttling proximity alert interval. */ public long getBackgroundThrottleProximityAlertIntervalMs() { - return Settings.Global.getLong(mContext.getContentResolver(), - LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS, - DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS); + long identity = Binder.clearCallingIdentity(); + try { + return Settings.Global.getLong(mContext.getContentResolver(), + LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS, + DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS); + } finally { + Binder.restoreCallingIdentity(identity); + } } /** * Retrieve maximum age of the last location. */ public long getMaxLastLocationAgeMs() { - return Settings.Global.getLong( - mContext.getContentResolver(), - LOCATION_LAST_LOCATION_MAX_AGE_MILLIS, - DEFAULT_MAX_LAST_LOCATION_AGE_MS); + long identity = Binder.clearCallingIdentity(); + try { + return Settings.Global.getLong( + mContext.getContentResolver(), + LOCATION_LAST_LOCATION_MAX_AGE_MILLIS, + DEFAULT_MAX_LAST_LOCATION_AGE_MS); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + /** + * Retrieve the accuracy for coarsening location, ie, the grid size used for snap-to-grid + * coarsening. + */ + public float getCoarseLocationAccuracyM() { + long identity = Binder.clearCallingIdentity(); + try { + return Settings.Secure.getFloat( + mContext.getContentResolver(), + LOCATION_COARSE_ACCURACY_M, + DEFAULT_COARSE_LOCATION_ACCURACY_M); + } finally { + Binder.restoreCallingIdentity(identity); + } } /** diff --git a/services/core/java/com/android/server/location/UserInfoHelper.java b/services/core/java/com/android/server/location/UserInfoHelper.java index 94f3a88cb859..a33e2da4eb87 100644 --- a/services/core/java/com/android/server/location/UserInfoHelper.java +++ b/services/core/java/com/android/server/location/UserInfoHelper.java @@ -16,6 +16,9 @@ package com.android.server.location; +import static android.os.UserManager.DISALLOW_SHARE_LOCATION; + +import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; @@ -23,7 +26,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.UserInfo; import android.os.Binder; import android.os.Build; import android.os.UserHandle; @@ -36,6 +38,8 @@ import com.android.server.FgThread; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.concurrent.CopyOnWriteArrayList; @@ -47,15 +51,24 @@ public class UserInfoHelper { /** * Listener for current user changes. */ - public interface UserChangedListener { + public interface UserListener { + + int USER_SWITCHED = 1; + int USER_STARTED = 2; + int USER_STOPPED = 3; + + @IntDef({USER_SWITCHED, USER_STARTED, USER_STOPPED}) + @Retention(RetentionPolicy.SOURCE) + @interface UserChange {} + /** - * Called when the current user changes. + * Called when something has changed about the given user. */ - void onUserChanged(@UserIdInt int oldUserId, @UserIdInt int newUserId); + void onUserChanged(@UserIdInt int userId, @UserChange int change); } private final Context mContext; - private final CopyOnWriteArrayList<UserChangedListener> mListeners; + private final CopyOnWriteArrayList<UserListener> mListeners; @GuardedBy("this") @Nullable private UserManager mUserManager; @@ -86,6 +99,8 @@ public class UserInfoHelper { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_USER_SWITCHED); + intentFilter.addAction(Intent.ACTION_USER_STARTED); + intentFilter.addAction(Intent.ACTION_USER_STOPPED); intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); @@ -96,12 +111,24 @@ public class UserInfoHelper { if (action == null) { return; } + int userId; switch (action) { case Intent.ACTION_USER_SWITCHED: - int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, - UserHandle.USER_NULL); + userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); + if (userId != UserHandle.USER_NULL) { + onCurrentUserChanged(userId); + } + break; + case Intent.ACTION_USER_STARTED: + userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); + if (userId != UserHandle.USER_NULL) { + onUserChanged(userId, UserListener.USER_STARTED); + } + break; + case Intent.ACTION_USER_STOPPED: + userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); if (userId != UserHandle.USER_NULL) { - onUserChanged(userId); + onUserChanged(userId, UserListener.USER_STOPPED); } break; case Intent.ACTION_MANAGED_PROFILE_ADDED: @@ -118,18 +145,18 @@ public class UserInfoHelper { /** * Adds a listener for user changed events. Callbacks occur on an unspecified thread. */ - public void addListener(UserChangedListener listener) { + public void addListener(UserListener listener) { mListeners.add(listener); } /** * Removes a listener for user changed events. */ - public void removeListener(UserChangedListener listener) { + public void removeListener(UserListener listener) { mListeners.remove(listener); } - private void onUserChanged(@UserIdInt int newUserId) { + private void onCurrentUserChanged(@UserIdInt int newUserId) { if (newUserId == mCurrentUserId) { return; } @@ -137,8 +164,13 @@ public class UserInfoHelper { int oldUserId = mCurrentUserId; mCurrentUserId = newUserId; - for (UserChangedListener listener : mListeners) { - listener.onUserChanged(oldUserId, newUserId); + onUserChanged(oldUserId, UserListener.USER_SWITCHED); + onUserChanged(newUserId, UserListener.USER_SWITCHED); + } + + private void onUserChanged(@UserIdInt int userId, @UserListener.UserChange int change) { + for (UserListener listener : mListeners) { + listener.onUserChanged(userId, change); } } @@ -151,53 +183,23 @@ public class UserInfoHelper { } /** - * Returns the user id of the current user. + * Returns an array of current user ids. This will always include the current user, and will + * also include any profiles of the current user. */ - @UserIdInt - public int getCurrentUserId() { - return mCurrentUserId; + public int[] getCurrentUserIds() { + return getProfileUserIdsForParentUser(mCurrentUserId); } /** * Returns true if the given user id is either the current user or a profile of the current * user. */ - public boolean isCurrentUserOrProfile(@UserIdInt int userId) { + public boolean isCurrentUserId(@UserIdInt int userId) { int currentUserId = mCurrentUserId; return userId == currentUserId || ArrayUtils.contains( getProfileUserIdsForParentUser(currentUserId), userId); } - /** - * Returns the parent user id of the given user id, or the user id itself if the user id either - * is a parent or has no profiles. - */ - @UserIdInt - public int getParentUserId(@UserIdInt int userId) { - synchronized (this) { - if (userId == mCachedParentUserId || ArrayUtils.contains(mCachedProfileUserIds, - userId)) { - return mCachedParentUserId; - } - - Preconditions.checkState(mUserManager != null); - } - - int parentUserId; - - long identity = Binder.clearCallingIdentity(); - try { - UserInfo userInfo = mUserManager.getProfileParent(userId); - parentUserId = userInfo != null ? userInfo.id : userId; - } finally { - Binder.restoreCallingIdentity(identity); - } - - // force profiles into cache - getProfileUserIdsForParentUser(parentUserId); - return parentUserId; - } - @GuardedBy("this") private synchronized int[] getProfileUserIdsForParentUser(@UserIdInt int parentUserId) { if (parentUserId != mCachedParentUserId) { @@ -225,8 +227,22 @@ public class UserInfoHelper { * Dump info for debugging. */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - int currentUserId = mCurrentUserId; - pw.println("Current User: " + currentUserId + " " + Arrays.toString( - getProfileUserIdsForParentUser(currentUserId))); + boolean systemRunning; + synchronized (this) { + systemRunning = mUserManager != null; + } + + if (systemRunning) { + int[] currentUserIds = getProfileUserIdsForParentUser(mCurrentUserId); + pw.println("current users: " + Arrays.toString(currentUserIds)); + for (int userId : currentUserIds) { + if (mUserManager.hasUserRestrictionForUser(DISALLOW_SHARE_LOCATION, + UserHandle.of(userId))) { + pw.println(" u" + userId + " restricted"); + } + } + } else { + pw.println("current user: " + mCurrentUserId); + } } } diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java index 02c1bddd8c00..1039cf6694cc 100644 --- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java +++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java @@ -17,6 +17,7 @@ package com.android.server.location.gnss; import static android.app.AppOpsManager.OP_FINE_LOCATION; +import static android.location.LocationManager.GPS_PROVIDER; import android.Manifest; import android.annotation.NonNull; @@ -27,6 +28,7 @@ import android.location.GnssCapabilities; import android.location.GnssMeasurementCorrections; import android.location.GnssRequest; import android.location.IBatchedLocationCallback; +import android.location.IGnssAntennaInfoListener; import android.location.IGnssMeasurementsListener; import android.location.IGnssNavigationMessageListener; import android.location.IGnssStatusListener; @@ -39,6 +41,7 @@ import android.os.IBinder; import android.os.IInterface; import android.os.Process; import android.os.RemoteException; +import android.os.UserHandle; import android.stats.location.LocationStatsEnums; import android.util.ArrayMap; import android.util.Log; @@ -52,6 +55,7 @@ import com.android.server.LocationManagerServiceUtils.LinkedListener; import com.android.server.LocationManagerServiceUtils.LinkedListenerBase; import com.android.server.location.AppForegroundHelper; import com.android.server.location.CallerIdentity; +import com.android.server.location.GnssAntennaInfoProvider; import com.android.server.location.GnssBatchingProvider; import com.android.server.location.GnssCapabilitiesProvider; import com.android.server.location.GnssLocationProvider; @@ -88,6 +92,7 @@ public class GnssManagerService { private final GnssStatusListenerHelper mGnssStatusProvider; private final GnssMeasurementsProvider mGnssMeasurementsProvider; private final GnssMeasurementCorrectionsProvider mGnssMeasurementCorrectionsProvider; + private final GnssAntennaInfoProvider mGnssAntennaInfoProvider; private final GnssNavigationMessageProvider mGnssNavigationMessageProvider; private final GnssLocationProvider.GnssSystemInfoProvider mGnssSystemInfoProvider; private final GnssLocationProvider.GnssMetricsProvider mGnssMetricsProvider; @@ -100,6 +105,11 @@ public class GnssManagerService { private final ArrayMap<IBinder, LinkedListener<GnssRequest, IGnssMeasurementsListener>> mGnssMeasurementsListeners = new ArrayMap<>(); + @GuardedBy("mGnssAntennaInfoListeners") + private final ArrayMap<IBinder, + LinkedListener<Void, IGnssAntennaInfoListener>> + mGnssAntennaInfoListeners = new ArrayMap<>(); + @GuardedBy("mGnssNavigationMessageListeners") private final ArrayMap<IBinder, LinkedListener<Void, IGnssNavigationMessageListener>> mGnssNavigationMessageListeners = new ArrayMap<>(); @@ -149,6 +159,7 @@ public class GnssManagerService { mGnssLocationProvider = gnssLocationProvider; mGnssStatusProvider = mGnssLocationProvider.getGnssStatusProvider(); mGnssMeasurementsProvider = mGnssLocationProvider.getGnssMeasurementsProvider(); + mGnssAntennaInfoProvider = mGnssLocationProvider.getGnssAntennaInfoProvider(); mGnssMeasurementCorrectionsProvider = mGnssLocationProvider.getGnssMeasurementCorrectionsProvider(); mGnssNavigationMessageProvider = mGnssLocationProvider.getGnssNavigationMessageProvider(); @@ -357,6 +368,14 @@ public class GnssManagerService { uid, foreground); } + synchronized (mGnssAntennaInfoListeners) { + updateListenersOnForegroundChangedLocked( + mGnssAntennaInfoListeners, + mGnssAntennaInfoProvider, + IGnssAntennaInfoListener.Stub::asInterface, + uid, + foreground); + } } private <TRequest, TListener extends IInterface> void updateListenersOnForegroundChangedLocked( @@ -544,6 +563,41 @@ public class GnssManagerService { } /** + * Adds a GNSS Antenna Info listener. + * + * @param listener called when GNSS antenna info is received + * @param packageName name of requesting package + * @return true if listener is successfully added, false otherwise + */ + public boolean addGnssAntennaInfoListener( + IGnssAntennaInfoListener listener, String packageName, + @Nullable String featureId, @NonNull String listenerIdentifier) { + synchronized (mGnssAntennaInfoListeners) { + return addGnssDataListenerLocked( + /* request= */ null, + listener, + packageName, + featureId, + listenerIdentifier, + mGnssAntennaInfoProvider, + mGnssAntennaInfoListeners, + this::removeGnssAntennaInfoListener); + } + } + + /** + * Removes a GNSS Antenna Info listener. + * + * @param listener called when GNSS antenna info is received + */ + public void removeGnssAntennaInfoListener(IGnssAntennaInfoListener listener) { + synchronized (mGnssAntennaInfoListeners) { + removeGnssDataListenerLocked( + listener, mGnssAntennaInfoProvider, mGnssAntennaInfoListeners); + } + } + + /** * Adds a GNSS navigation message listener. */ public boolean addGnssNavigationMessageListener( @@ -588,18 +642,26 @@ public class GnssManagerService { */ public void onReportLocation(List<Location> locations) { IBatchedLocationCallback gnssBatchingCallback; + LinkedListener<Void, IBatchedLocationCallback> gnssBatchingDeathCallback; synchronized (mGnssBatchingLock) { gnssBatchingCallback = mGnssBatchingCallback; + gnssBatchingDeathCallback = mGnssBatchingDeathCallback; + } + + if (gnssBatchingCallback == null || gnssBatchingDeathCallback == null) { + return; } - if (gnssBatchingCallback == null) { + int userId = UserHandle.getUserId(gnssBatchingDeathCallback.getCallerIdentity().mUid); + if (!mLocationManagerInternal.isProviderEnabledForUser(GPS_PROVIDER, userId)) { + Log.w(TAG, "reportLocationBatch() called without user permission"); return; } try { gnssBatchingCallback.onLocationBatch(locations); } catch (RemoteException e) { - Log.e(TAG, "mGnssBatchingCallback.onLocationBatch failed", e); + Log.e(TAG, "reportLocationBatch() failed", e); } } diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java index b726e571cf0c..ac49fa293fe7 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java @@ -590,8 +590,8 @@ public class SyntheticPasswordManager { throw new IllegalStateException("Failed to create new SID for user", e); } if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) { - Slog.e(TAG, "Fail to create new SID for user " + userId); - return; + throw new IllegalStateException("Fail to create new SID for user " + userId + + " response: " + response.getResponseCode()); } saveSyntheticPasswordHandle(response.getPayload(), userId); } diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 1dcff07e36a2..bde9ee24c7e2 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -1610,6 +1610,22 @@ public class NetworkStatsService extends INetworkStatsService.Stub { pw.decreaseIndent(); pw.println(); + pw.println("Stats Providers:"); + pw.increaseIndent(); + invokeForAllStatsProviderCallbacks((cb) -> { + pw.println(cb.mTag + " Xt:"); + pw.increaseIndent(); + pw.print(cb.getCachedStats(STATS_PER_IFACE).toString()); + pw.decreaseIndent(); + if (includeUid) { + pw.println(cb.mTag + " Uid:"); + pw.increaseIndent(); + pw.print(cb.getCachedStats(STATS_PER_UID).toString()); + pw.decreaseIndent(); + } + }); + pw.decreaseIndent(); + pw.println("Dev stats:"); pw.increaseIndent(); mDevRecorder.dumpLocked(pw, fullHistory); diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java index 061cbd8a1a40..dc61fb01a246 100644 --- a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java +++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java @@ -231,6 +231,9 @@ public class NotificationHistoryDatabase { public void disableHistory() { synchronized (mLock) { + for (AtomicFile file : mHistoryFiles) { + file.delete(); + } mHistoryDir.delete(); mHistoryFiles.clear(); } @@ -249,6 +252,10 @@ public class NotificationHistoryDatabase { final AtomicFile currentOldestFile = mHistoryFiles.get(i); final long creationTime = mFileAttrProvider.getCreationTime(currentOldestFile.getBaseFile()); + if (DEBUG) { + Slog.d(TAG, "Pruning " + currentOldestFile.getBaseFile().getName() + + " created on " + creationTime); + } if (creationTime <= retentionBoundary.getTimeInMillis()) { if (DEBUG) { Slog.d(TAG, "Removed " + currentOldestFile.getBaseFile().getName()); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 7ffd899215de..c8bc1b0b2a75 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -185,6 +185,7 @@ import android.os.ServiceManager; import android.os.ShellCallback; import android.os.SystemClock; import android.os.SystemProperties; +import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.os.VibrationEffect; @@ -527,11 +528,11 @@ public class NotificationManagerService extends SystemService { private static class Archive { final int mBufferSize; - final ArrayDeque<StatusBarNotification> mBuffer; + final ArrayDeque<Pair<StatusBarNotification, Integer>> mBuffer; public Archive(int size) { mBufferSize = size; - mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize); + mBuffer = new ArrayDeque<>(mBufferSize); } public String toString() { @@ -544,7 +545,7 @@ public class NotificationManagerService extends SystemService { return sb.toString(); } - public void record(StatusBarNotification nr) { + public void record(StatusBarNotification nr, int reason) { if (mBuffer.size() == mBufferSize) { mBuffer.removeFirst(); } @@ -552,21 +553,24 @@ public class NotificationManagerService extends SystemService { // We don't want to store the heavy bits of the notification in the archive, // but other clients in the system process might be using the object, so we // store a (lightened) copy. - mBuffer.addLast(nr.cloneLight()); + mBuffer.addLast(new Pair<>(nr.cloneLight(), reason)); } - public Iterator<StatusBarNotification> descendingIterator() { + public Iterator<Pair<StatusBarNotification, Integer>> descendingIterator() { return mBuffer.descendingIterator(); } - public StatusBarNotification[] getArray(int count) { + public StatusBarNotification[] getArray(int count, boolean includeSnoozed) { if (count == 0) count = mBufferSize; final StatusBarNotification[] a = new StatusBarNotification[Math.min(count, mBuffer.size())]; - Iterator<StatusBarNotification> iter = descendingIterator(); + Iterator<Pair<StatusBarNotification, Integer>> iter = descendingIterator(); int i=0; while (iter.hasNext() && i < count) { - a[i++] = iter.next(); + Pair<StatusBarNotification, Integer> pair = iter.next(); + if (pair.second != REASON_SNOOZED || includeSnoozed) { + a[i++] = pair.first; + } } return a; } @@ -2260,12 +2264,26 @@ public class NotificationManagerService extends SystemService { @Override public void onUnlockUser(@NonNull UserInfo userInfo) { - mHandler.post(() -> mHistoryManager.onUserUnlocked(userInfo.id)); + mHandler.post(() -> { + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "notifHistoryUnlockUser"); + try { + mHistoryManager.onUserUnlocked(userInfo.id); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + } + }); } @Override public void onStopUser(@NonNull UserInfo userInfo) { - mHandler.post(() -> mHistoryManager.onUserStopped(userInfo.id)); + mHandler.post(() -> { + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "notifHistoryStopUser"); + try { + mHistoryManager.onUserStopped(userInfo.id); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + } + }); } @GuardedBy("mNotificationLock") @@ -2592,17 +2610,22 @@ public class NotificationManagerService extends SystemService { mAppUsageStats.reportInterruptiveNotification(r.getSbn().getPackageName(), r.getChannel().getId(), getRealUserId(r.getSbn().getUserId())); - mHistoryManager.addNotification(new HistoricalNotification.Builder() - .setPackage(r.getSbn().getPackageName()) - .setUid(r.getSbn().getUid()) - .setChannelId(r.getChannel().getId()) - .setChannelName(r.getChannel().getName().toString()) - .setPostedTimeMs(System.currentTimeMillis()) - .setTitle(getHistoryTitle(r.getNotification())) - .setText(getHistoryText( - r.getSbn().getPackageContext(getContext()), r.getNotification())) - .setIcon(r.getNotification().getSmallIcon()) - .build()); + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "notifHistoryAddItem"); + try { + mHistoryManager.addNotification(new HistoricalNotification.Builder() + .setPackage(r.getSbn().getPackageName()) + .setUid(r.getSbn().getUid()) + .setChannelId(r.getChannel().getId()) + .setChannelName(r.getChannel().getName().toString()) + .setPostedTimeMs(System.currentTimeMillis()) + .setTitle(getHistoryTitle(r.getNotification())) + .setText(getHistoryText( + r.getSbn().getPackageContext(getContext()), r.getNotification())) + .setIcon(r.getNotification().getSmallIcon()) + .build()); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + } r.setRecordedInterruption(true); } } @@ -3638,7 +3661,8 @@ public class NotificationManagerService extends SystemService { */ @Override @RequiresPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS) - public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) { + public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count, + boolean includeSnoozed) { // enforce() will ensure the calling uid has the correct permission getContext().enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NOTIFICATIONS, @@ -3651,7 +3675,7 @@ public class NotificationManagerService extends SystemService { if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) == AppOpsManager.MODE_ALLOWED) { synchronized (mArchive) { - tmp = mArchive.getArray(count); + tmp = mArchive.getArray(count, includeSnoozed); } } return tmp; @@ -3675,7 +3699,12 @@ public class NotificationManagerService extends SystemService { if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) == AppOpsManager.MODE_ALLOWED) { IntArray currentUserIds = mUserProfiles.getCurrentProfileIds(); - return mHistoryManager.readNotificationHistory(currentUserIds.toArray()); + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "notifHistoryReadHistory"); + try { + return mHistoryManager.readNotificationHistory(currentUserIds.toArray()); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + } } return new NotificationHistory(); } @@ -5199,10 +5228,10 @@ public class NotificationManagerService extends SystemService { pw.println(" mMaxPackageEnqueueRate=" + mMaxPackageEnqueueRate); } pw.println(" mArchive=" + mArchive.toString()); - Iterator<StatusBarNotification> iter = mArchive.descendingIterator(); + Iterator<Pair<StatusBarNotification, Integer>> iter = mArchive.descendingIterator(); int j=0; while (iter.hasNext()) { - final StatusBarNotification sbn = iter.next(); + final StatusBarNotification sbn = iter.next().first; if (filter != null && !filter.matches(sbn)) continue; pw.println(" " + sbn); if (++j >= 5) { @@ -7575,7 +7604,7 @@ public class NotificationManagerService extends SystemService { } // Save it for users of getHistoricalNotifications() - mArchive.record(r.getSbn()); + mArchive.record(r.getSbn(), reason); final long now = System.currentTimeMillis(); final LogMaker logMaker = r.getItemLogMaker() diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java index fc2d9e775149..2f7854226c5c 100644 --- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java +++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java @@ -27,6 +27,7 @@ import android.os.Bundle; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationStats; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; @@ -266,5 +267,39 @@ public interface NotificationRecordLogger { int getInstanceId() { return (r.getSbn().getInstanceId() == null ? 0 : r.getSbn().getInstanceId().getId()); } + + /** + * @return Small hash of the notification ID, and tag (if present). + */ + int getNotificationIdHash() { + return smallHash(Objects.hashCode(r.getSbn().getTag()) ^ r.getSbn().getId()); + } + + /** + * @return Small hash of the channel ID, if present, or 0 otherwise. + */ + int getChannelIdHash() { + return smallHash(Objects.hashCode(r.getSbn().getNotification().getChannelId())); + } + + /** + * @return Small hash of the group ID, respecting group override if present. 0 otherwise. + */ + int getGroupIdHash() { + return smallHash(Objects.hashCode(r.getSbn().getGroup())); + } + + // "Small" hashes will be in the range [0, MAX_HASH). + static final int MAX_HASH = (1 << 13); + + /** + * Maps in to the range [0, MAX_HASH), keeping similar values distinct. + * @param in An arbitrary integer. + * @return in mod MAX_HASH, signs chosen to stay in the range [0, MAX_HASH). + */ + @VisibleForTesting + static int smallHash(int in) { + return Math.floorMod(in, MAX_HASH); + } } } diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java index 015d280535e6..bb23d1e876dc 100644 --- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java +++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java @@ -40,28 +40,27 @@ public class NotificationRecordLoggerImpl implements NotificationRecordLogger { /* int32 uid = 2 */ r.getUid(), /* string package_name = 3 */ r.getSbn().getPackageName(), /* int32 instance_id = 4 */ p.getInstanceId(), - /* int32 notification_id = 5 */ r.getSbn().getId(), - /* string notification_tag = 6 */ r.getSbn().getTag(), - /* string channel_id = 7 */ r.getSbn().getChannelIdLogTag(), - /* string group_id = 8 */ r.getSbn().getGroupLogTag(), - /* int32 group_instance_id = 9 */ 0, // TODO generate and fill instance ids - /* bool is_group_summary = 10 */ r.getSbn().getNotification().isGroupSummary(), - /* string category = 11 */ r.getSbn().getNotification().category, - /* int32 style = 12 */ p.getStyle(), - /* int32 num_people = 13 */ p.getNumPeople(), - /* int32 position = 14 */ position, - /* android.stats.sysui.NotificationImportance importance = 15 */ r.getImportance(), - /* int32 alerting = 16 */ buzzBeepBlink, - /* NotificationImportanceExplanation importance_source = 17 */ + /* int32 notification_id_hash = 5 */ p.getNotificationIdHash(), + /* int32 channel_id_hash = 6 */ p.getChannelIdHash(), + /* string group_id_hash = 7 */ p.getGroupIdHash(), + /* int32 group_instance_id = 8 */ 0, // TODO generate and fill instance ids + /* bool is_group_summary = 9 */ r.getSbn().getNotification().isGroupSummary(), + /* string category = 10 */ r.getSbn().getNotification().category, + /* int32 style = 11 */ p.getStyle(), + /* int32 num_people = 12 */ p.getNumPeople(), + /* int32 position = 13 */ position, + /* android.stats.sysui.NotificationImportance importance = 14 */ r.getImportance(), + /* int32 alerting = 15 */ buzzBeepBlink, + /* NotificationImportanceExplanation importance_source = 16 */ r.getImportanceExplanationCode(), - /* android.stats.sysui.NotificationImportance importance_initial = 18 */ + /* android.stats.sysui.NotificationImportance importance_initial = 17 */ r.getInitialImportance(), - /* NotificationImportanceExplanation importance_initial_source = 19 */ + /* NotificationImportanceExplanation importance_initial_source = 18 */ r.getInitialImportanceExplanationCode(), - /* android.stats.sysui.NotificationImportance importance_asst = 20 */ + /* android.stats.sysui.NotificationImportance importance_asst = 19 */ r.getAssistantImportance(), - /* int32 assistant_hash = 21 */ p.getAssistantHash(), - /* float assistant_ranking_score = 22 */ 0 // TODO connect up ranking score + /* int32 assistant_hash = 20 */ p.getAssistantHash(), + /* float assistant_ranking_score = 21 */ 0 // TODO connect up ranking score ); } diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java index 74d2efeceb63..ad037bc09245 100644 --- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java @@ -56,6 +56,8 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IAppOpsService; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.FunctionalUtils.ThrowingRunnable; +import com.android.internal.util.FunctionalUtils.ThrowingSupplier; import com.android.server.LocalServices; import com.android.server.appop.AppOpsService; import com.android.server.wm.ActivityTaskManagerInternal; @@ -276,19 +278,14 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } private boolean isCrossProfilePackageWhitelisted(String packageName) { - final long ident = mInjector.clearCallingIdentity(); - try { - return mInjector.getDevicePolicyManagerInternal() - .getAllCrossProfilePackages().contains(packageName); - } finally { - mInjector.restoreCallingIdentity(ident); - } + return mInjector.withCleanCallingIdentity(() -> + mInjector.getDevicePolicyManagerInternal() + .getAllCrossProfilePackages().contains(packageName)); } private List<UserHandle> getTargetUserProfilesUnchecked( String packageName, @UserIdInt int userId) { - final long ident = mInjector.clearCallingIdentity(); - try { + return mInjector.withCleanCallingIdentity(() -> { final int[] enabledProfileIds = mInjector.getUserManager().getEnabledProfileIds(userId); @@ -303,15 +300,12 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { targetProfiles.add(UserHandle.of(profileId)); } return targetProfiles; - } finally { - mInjector.restoreCallingIdentity(ident); - } + }); } private boolean isPackageEnabled(String packageName, @UserIdInt int userId) { final int callingUid = mInjector.getCallingUid(); - final long ident = mInjector.clearCallingIdentity(); - try { + return mInjector.withCleanCallingIdentity(() -> { final PackageInfo info = mInjector.getPackageManagerInternal() .getPackageInfo( packageName, @@ -319,15 +313,12 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { callingUid, userId); return info != null && info.applicationInfo.enabled; - } finally { - mInjector.restoreCallingIdentity(ident); - } + }); } private void verifyActivityCanHandleIntent( Intent launchIntent, int callingUid, @UserIdInt int userId) { - final long ident = mInjector.clearCallingIdentity(); - try { + mInjector.withCleanCallingIdentity(() -> { final List<ResolveInfo> activities = mInjector.getPackageManagerInternal().queryIntentActivities( launchIntent, @@ -340,9 +331,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { return; } throw new SecurityException("Activity cannot handle intent"); - } finally { - mInjector.restoreCallingIdentity(ident); - } + }); } /** @@ -351,8 +340,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { */ private void verifyActivityCanHandleIntentAndExported( Intent launchIntent, ComponentName component, int callingUid, @UserIdInt int userId) { - final long ident = mInjector.clearCallingIdentity(); - try { + mInjector.withCleanCallingIdentity(() -> { final List<ResolveInfo> apps = mInjector.getPackageManagerInternal().queryIntentActivities( launchIntent, @@ -371,9 +359,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } throw new SecurityException("Attempt to launch activity without " + " category Intent.CATEGORY_LAUNCHER or activity is not exported" + component); - } finally { - mInjector.restoreCallingIdentity(ident); - } + }); } @Override @@ -385,7 +371,13 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { "INTERACT_ACROSS_USERS or INTERACT_ACROSS_USERS_FULL is required to set the" + " app-op for interacting across profiles."); } - final int callingUserId = mInjector.getCallingUserId(); + if (!isPermissionGranted(Manifest.permission.MANAGE_APP_OPS_MODES, callingUid) + && !isPermissionGranted( + Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, callingUid)) { + throw new SecurityException( + "MANAGE_APP_OPS_MODES or CONFIGURE_INTERACT_ACROSS_PROFILES is required to set" + + " the app-op for interacting across profiles."); + } if (newMode == AppOpsManager.MODE_ALLOWED && !canConfigureInteractAcrossProfiles(packageName)) { // The user should not be prompted for apps that cannot request to interact across @@ -395,7 +387,8 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { return; } final int[] profileIds = - mInjector.getUserManager().getProfileIds(callingUserId, /* enabledOnly= */ false); + mInjector.getUserManager() + .getProfileIds(mInjector.getCallingUserId(), /* enabledOnly= */ false); for (int profileId : profileIds) { if (!isPackageInstalled(packageName, profileId)) { continue; @@ -406,8 +399,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { private boolean isPackageInstalled(String packageName, @UserIdInt int userId) { final int callingUid = mInjector.getCallingUid(); - final long identity = mInjector.clearCallingIdentity(); - try { + return mInjector.withCleanCallingIdentity(() -> { final PackageInfo info = mInjector.getPackageManagerInternal() .getPackageInfo( @@ -416,9 +408,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { callingUid, userId); return info != null; - } finally { - mInjector.restoreCallingIdentity(identity); - } + }); } private void setInteractAcrossProfilesAppOpForUser( @@ -440,19 +430,31 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { + packageName + " on user ID " + userId); return; } - mInjector.getAppOpsManager() - .setMode(OP_INTERACT_ACROSS_PROFILES, - uid, - packageName, - newMode); + final int callingUid = mInjector.getCallingUid(); + if (isPermissionGranted( + Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, callingUid)) { + // Clear calling identity since the CONFIGURE_INTERACT_ACROSS_PROFILES permission allows + // this particular app-op to be modified without the broader app-op permissions. + mInjector.withCleanCallingIdentity(() -> + mInjector.getAppOpsManager() + .setMode(OP_INTERACT_ACROSS_PROFILES, uid, packageName, newMode)); + } else { + mInjector.getAppOpsManager() + .setMode(OP_INTERACT_ACROSS_PROFILES, uid, packageName, newMode); + } sendCanInteractAcrossProfilesChangedBroadcast(packageName, uid, UserHandle.of(userId)); } + /** + * Returns whether the given app-op mode is equivalent to the currently-set app-op of the given + * package name and UID. Clears identity to avoid permission checks, so ensure the caller does + * any necessary permission checks. + */ private boolean currentModeEquals(@Mode int otherMode, String packageName, int uid) { final String op = AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES); - return otherMode == - mInjector.getAppOpsManager().unsafeCheckOpNoThrow(op, uid, packageName); + return mInjector.withCleanCallingIdentity(() -> otherMode + == mInjector.getAppOpsManager().unsafeCheckOpNoThrow(op, uid, packageName)); } private void sendCanInteractAcrossProfilesChangedBroadcast( @@ -493,8 +495,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } private boolean hasOtherProfileWithPackageInstalled(String packageName, @UserIdInt int userId) { - final long ident = mInjector.clearCallingIdentity(); - try { + return mInjector.withCleanCallingIdentity(() -> { final int[] profileIds = mInjector.getUserManager().getProfileIds(userId, /* enabledOnly= */ false); for (int profileId : profileIds) { @@ -502,10 +503,8 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { return true; } } - } finally { - mInjector.restoreCallingIdentity(ident); - } - return false; + return false; + }); } @Override @@ -525,12 +524,8 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) { - final long ident = mInjector.clearCallingIdentity(); - try { - return mInjector.getUserManager().isSameProfileGroup(callerUserId, userId); - } finally { - mInjector.restoreCallingIdentity(ident); - } + return mInjector.withCleanCallingIdentity(() -> + mInjector.getUserManager().isSameProfileGroup(callerUserId, userId)); } /** @@ -560,42 +555,62 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { mContext = context; } + @Override public int getCallingUid() { return Binder.getCallingUid(); } + @Override public int getCallingPid() { return Binder.getCallingPid(); } + @Override public int getCallingUserId() { return UserHandle.getCallingUserId(); } + @Override public UserHandle getCallingUserHandle() { return Binder.getCallingUserHandle(); } + @Override public long clearCallingIdentity() { return Binder.clearCallingIdentity(); } + @Override public void restoreCallingIdentity(long token) { Binder.restoreCallingIdentity(token); } + @Override + public void withCleanCallingIdentity(ThrowingRunnable action) { + Binder.withCleanCallingIdentity(action); + } + + @Override + public final <T> T withCleanCallingIdentity(ThrowingSupplier<T> action) { + return Binder.withCleanCallingIdentity(action); + } + + @Override public UserManager getUserManager() { return mContext.getSystemService(UserManager.class); } + @Override public PackageManagerInternal getPackageManagerInternal() { return LocalServices.getService(PackageManagerInternal.class); } + @Override public PackageManager getPackageManager() { return mContext.getPackageManager(); } + @Override public AppOpsManager getAppOpsManager() { return mContext.getSystemService(AppOpsManager.class); } @@ -646,6 +661,10 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { void restoreCallingIdentity(long token); + void withCleanCallingIdentity(ThrowingRunnable action); + + <T> T withCleanCallingIdentity(ThrowingSupplier<T> action); + UserManager getUserManager(); PackageManagerInternal getPackageManagerInternal(); diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 97defcdd3bc7..4ed4de70264a 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -2450,17 +2450,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } - // TODO(b/136132412): update with new APIs - if (isIncrementalInstallation()) { - try { - mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, - stageDir, params.dataLoaderParams, addedFiles); - return true; - } catch (IOException e) { - throw new PackageManagerException(e); - } - } - final DataLoaderManager dataLoaderManager = mContext.getSystemService( DataLoaderManager.class); if (dataLoaderManager == null) { @@ -2468,6 +2457,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { "Failed to find data loader manager service"); } + final boolean manualStartAndDestroy = !isIncrementalInstallation(); IDataLoaderStatusListener listener = new IDataLoaderStatusListener.Stub() { @Override public void onStatusChanged(int dataLoaderId, int status) { @@ -2487,7 +2477,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { switch (status) { case IDataLoaderStatusListener.DATA_LOADER_CREATED: { - dataLoader.start(); + if (manualStartAndDestroy) { + // IncrementalFileStorages will call start after all files are + // created in IncFS. + dataLoader.start(); + } break; } case IDataLoaderStatusListener.DATA_LOADER_STARTED: { @@ -2502,7 +2496,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } else { dispatchStreamValidateAndCommit(); } - dataLoader.destroy(); + if (manualStartAndDestroy) { + dataLoader.destroy(); + } break; } case IDataLoaderStatusListener.DATA_LOADER_IMAGE_NOT_READY: { @@ -2510,7 +2506,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { onSessionVerificationFailure( new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, "Failed to prepare image.")); - dataLoader.destroy(); + if (manualStartAndDestroy) { + dataLoader.destroy(); + } break; } } @@ -2524,6 +2522,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } }; + if (!manualStartAndDestroy) { + try { + mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, + stageDir, params.dataLoaderParams, listener, addedFiles); + return false; + } catch (IOException e) { + throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(), + e.getCause()); + } + } + final FileSystemConnector connector = new FileSystemConnector(addedFiles); final FileSystemControlParcel control = new FileSystemControlParcel(); control.callback = connector; diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index c69a62d406aa..3909fdff21f0 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -181,6 +181,8 @@ class PackageManagerShellCommand extends ShellCommand { return runInstall(); case "install-streaming": return runStreamingInstall(); + case "install-incremental": + return runIncrementalInstall(); case "install-abandon": case "install-destroy": return runInstallAbandon(); @@ -1168,6 +1170,15 @@ class PackageManagerShellCommand extends ShellCommand { return doRunInstall(params); } + private int runIncrementalInstall() throws RemoteException { + final InstallParams params = makeInstallParams(); + if (params.sessionParams.dataLoaderParams == null) { + params.sessionParams.setDataLoaderParams( + PackageManagerShellCommandDataLoader.getIncrementalDataLoaderParams(this)); + } + return doRunInstall(params); + } + private int runInstall() throws RemoteException { return doRunInstall(makeInstallParams()); } @@ -3001,17 +3012,21 @@ class PackageManagerShellCommand extends ShellCommand { return 1; } - session.addFile(LOCATION_DATA_APP, name, sizeBytes, STDIN_PATH_BYTES, null); + // Incremental requires unique metadatas, let's add a name to the dash. + session.addFile(LOCATION_DATA_APP, name, sizeBytes, + ("-" + name).getBytes(StandardCharsets.UTF_8), null); continue; } // 3. Local file. final String inPath = arg; - String name = new File(inPath).getName(); + final File file = new File(inPath); + final String name = file.getName(); + final long size = file.length(); byte[] metadata = inPath.getBytes(StandardCharsets.UTF_8); - session.addFile(LOCATION_DATA_APP, name, -1, metadata, null); + session.addFile(LOCATION_DATA_APP, name, size, metadata, null); } return 0; } finally { diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java index 4170be4a913d..8f30e7da3204 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.content.ComponentName; import android.content.pm.DataLoaderParams; import android.content.pm.InstallationFile; +import android.content.pm.PackageInstaller; import android.os.ParcelFileDescriptor; import android.os.ShellCommand; import android.service.dataloader.DataLoaderService; @@ -55,6 +56,8 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService { private static final String STDIN_PATH = "-"; private static String getDataLoaderParamsArgs(ShellCommand shellCommand) { + nativeInitialize(); + int commandId; synchronized (sShellCommands) { // Clean up old references. @@ -86,6 +89,11 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService { getDataLoaderParamsArgs(shellCommand)); } + static DataLoaderParams getIncrementalDataLoaderParams(ShellCommand shellCommand) { + return DataLoaderParams.forIncremental(new ComponentName(PACKAGE, CLASS), + getDataLoaderParamsArgs(shellCommand), null); + } + private static int extractShellCommandId(String args) { int sessionIdIdx = args.indexOf(SHELL_COMMAND_ID_PREFIX); if (sessionIdIdx < 0) { @@ -106,7 +114,7 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService { } } - static class DataLoader implements DataLoaderService.DataLoader { + private static class DataLoader implements DataLoaderService.DataLoader { private DataLoaderParams mParams = null; private FileSystemConnector mConnector = null; @@ -121,17 +129,7 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService { @Override public boolean onPrepareImage(@NonNull Collection<InstallationFile> addedFiles, @NonNull Collection<String> removedFiles) { - final int commandId = extractShellCommandId(mParams.getArguments()); - if (commandId == INVALID_SHELL_COMMAND_ID) { - return false; - } - - final WeakReference<ShellCommand> shellCommandRef; - synchronized (sShellCommands) { - shellCommandRef = sShellCommands.get(commandId, null); - } - final ShellCommand shellCommand = - shellCommandRef != null ? shellCommandRef.get() : null; + ShellCommand shellCommand = lookupShellCommand(mParams.getArguments()); if (shellCommand == null) { Slog.e(TAG, "Missing shell command."); return false; @@ -139,14 +137,13 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService { try { for (InstallationFile file : addedFiles) { String filePath = new String(file.getMetadata(), StandardCharsets.UTF_8); - if (STDIN_PATH.equals(filePath) || TextUtils.isEmpty(filePath)) { - final ParcelFileDescriptor inFd = ParcelFileDescriptor.dup( - shellCommand.getInFileDescriptor()); + if (TextUtils.isEmpty(filePath) || filePath.startsWith(STDIN_PATH)) { + final ParcelFileDescriptor inFd = getStdInPFD(shellCommand); mConnector.writeData(file.getName(), 0, file.getLengthBytes(), inFd); } else { ParcelFileDescriptor incomingFd = null; try { - incomingFd = shellCommand.openFileForSystem(filePath, "r"); + incomingFd = getLocalFile(shellCommand, filePath); mConnector.writeData(file.getName(), 0, incomingFd.getStatSize(), incomingFd); } finally { @@ -162,9 +159,45 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService { } } + static ShellCommand lookupShellCommand(String args) { + final int commandId = extractShellCommandId(args); + if (commandId == INVALID_SHELL_COMMAND_ID) { + return null; + } + + final WeakReference<ShellCommand> shellCommandRef; + synchronized (sShellCommands) { + shellCommandRef = sShellCommands.get(commandId, null); + } + final ShellCommand shellCommand = + shellCommandRef != null ? shellCommandRef.get() : null; + + return shellCommand; + } + + static ParcelFileDescriptor getStdInPFD(ShellCommand shellCommand) { + try { + return ParcelFileDescriptor.dup(shellCommand.getInFileDescriptor()); + } catch (IOException e) { + Slog.e(TAG, "Exception while obtaining STDIN fd", e); + return null; + } + } + + static ParcelFileDescriptor getLocalFile(ShellCommand shellCommand, String filePath) { + return shellCommand.openFileForSystem(filePath, "r"); + } + @Override public DataLoaderService.DataLoader onCreateDataLoader( @NonNull DataLoaderParams dataLoaderParams) { - return new DataLoader(); + if (dataLoaderParams.getType() == PackageInstaller.DATA_LOADER_TYPE_STREAMING) { + // This DataLoader only supports streaming installations. + return new DataLoader(); + } + return null; } + + /* Native methods */ + private static native void nativeInitialize(); } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index fbea59570ac0..0fb4cb036282 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -4523,7 +4523,11 @@ public final class Settings { pw.print(prefix); pw.print(" privateFlags="); printFlags(pw, pkg.getPrivateFlags(), PRIVATE_FLAG_DUMP_SPEC); pw.println(); } - pw.print(prefix); pw.print(" forceQueryable="); pw.println(ps.pkg.isForceQueryable()); + pw.print(prefix); pw.print(" forceQueryable="); pw.print(ps.pkg.isForceQueryable()); + if (ps.forceQueryableOverride) { + pw.print(" (override=true)"); + } + pw.println(); if (ps.pkg.getQueriesPackages() != null) { pw.append(prefix).append(" queriesPackages=").println(ps.pkg.getQueriesPackages()); } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 1c02161c5b96..6e4ec7e4b7d0 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -462,8 +462,42 @@ public class UserManagerService extends IUserManager.Stub { @GuardedBy("mUsersLock") private boolean mForceEphemeralUsers; + /** + * The member mUserStates affects the return value of isUserUnlocked. + * If any value in mUserStates changes, then the binder cache for + * isUserUnlocked must be invalidated. When adding mutating methods to + * WatchedUserStates, be sure to invalidate the cache in the new + * methods. + */ + private class WatchedUserStates { + final SparseIntArray states; + public WatchedUserStates() { + states = new SparseIntArray(); + } + public int get(int userId) { + return states.get(userId); + } + public int get(int userId, int fallback) { + return states.indexOfKey(userId) >= 0 ? states.get(userId) : fallback; + } + public void put(int userId, int state) { + states.put(userId, state); + invalidateIsUserUnlockedCache(); + } + public void delete(int userId) { + states.delete(userId); + invalidateIsUserUnlockedCache(); + } + @Override + public String toString() { + return states.toString(); + } + private void invalidateIsUserUnlockedCache() { + UserManager.invalidateIsUserUnlockedCache(); + } + } @GuardedBy("mUserStates") - private final SparseIntArray mUserStates = new SparseIntArray(); + private final WatchedUserStates mUserStates = new WatchedUserStates(); private static UserManagerService sInstance; @@ -4801,6 +4835,11 @@ public class UserManagerService extends IUserManager.Stub { || (state == UserState.STATE_RUNNING_UNLOCKED); } + /** + * The return values of this method are cached in clients. If the + * logic in this function changes then the cache invalidation code + * may need to be revisited. + */ @Override public boolean isUserUnlocked(@UserIdInt int userId) { int state; diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index 91bd7aea067e..67b1008333e1 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -783,6 +783,7 @@ public class UserRestrictionsUtils { break; case android.provider.Settings.System.SCREEN_BRIGHTNESS: + case android.provider.Settings.System.SCREEN_BRIGHTNESS_FLOAT: case android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE: if (callingUid == Process.SYSTEM_UID) { return false; diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index ede04f3bbb14..61267878a409 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -2729,21 +2729,23 @@ public class PhoneWindowManager implements WindowManagerPolicy { Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT_OR_SELF); } - - int min = mPowerManager.getMinimumScreenBrightnessSetting(); - int max = mPowerManager.getMaximumScreenBrightnessSetting(); - int step = (max - min + BRIGHTNESS_STEPS - 1) / BRIGHTNESS_STEPS * direction; - int brightness = Settings.System.getIntForUser(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS, - mPowerManager.getDefaultScreenBrightnessSetting(), + float minFloat = mPowerManager.getBrightnessConstraint( + PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM); + float maxFloat = mPowerManager.getBrightnessConstraint( + PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM); + float stepFloat = (maxFloat - minFloat) / BRIGHTNESS_STEPS * direction; + float brightnessFloat = Settings.System.getFloatForUser( + mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_FLOAT, + mPowerManager.getBrightnessConstraint( + PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT), UserHandle.USER_CURRENT_OR_SELF); - brightness += step; + brightnessFloat += stepFloat; // Make sure we don't go beyond the limits. - brightness = Math.min(max, brightness); - brightness = Math.max(min, brightness); + brightnessFloat = Math.min(maxFloat, brightnessFloat); + brightnessFloat = Math.max(minFloat, brightnessFloat); - Settings.System.putIntForUser(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS, brightness, + Settings.System.putFloatForUser(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightnessFloat, UserHandle.USER_CURRENT_OR_SELF); startActivityAsUser(new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG), UserHandle.CURRENT_OR_SELF); diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index e81214e44aff..67a22d3e477c 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -75,7 +75,6 @@ import android.content.res.Configuration; import android.graphics.Rect; import android.os.Bundle; import android.os.IBinder; -import android.os.Looper; import android.os.RemoteException; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -83,7 +82,6 @@ import android.view.Display; import android.view.IApplicationToken; import android.view.IDisplayFoldListener; import android.view.IWindowManager; -import android.view.InputEventReceiver; import android.view.KeyEvent; import android.view.WindowManager; import android.view.WindowManagerGlobal; @@ -471,6 +469,10 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { * Remove the input consumer from the window manager. */ void dismiss(); + /** + * Dispose the input consumer and input receiver from UI thread. + */ + void dispose(); } /** @@ -505,12 +507,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { public static final int CAMERA_LENS_COVERED = 1; /** - * Add a input consumer which will consume all input events going to any window below it. - */ - public InputConsumer createInputConsumer(Looper looper, String name, - InputEventReceiver.Factory inputEventReceiverFactory, int displayId); - - /** * Returns a code that describes the current state of the lid switch. */ public int getLidState(); diff --git a/services/core/java/com/android/server/power/AttentionDetector.java b/services/core/java/com/android/server/power/AttentionDetector.java index cc72dd69a5fa..1ab6adee2320 100644 --- a/services/core/java/com/android/server/power/AttentionDetector.java +++ b/services/core/java/com/android/server/power/AttentionDetector.java @@ -17,7 +17,7 @@ package com.android.server.power; import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE; -import static android.provider.Settings.System.ADAPTIVE_SLEEP; +import static android.provider.Settings.Secure.ADAPTIVE_SLEEP; import android.Manifest; import android.app.ActivityManager; @@ -152,8 +152,8 @@ public class AttentionDetector { @VisibleForTesting void updateEnabledFromSettings(Context context) { - mIsSettingEnabled = Settings.System.getIntForUser(context.getContentResolver(), - Settings.System.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT) == 1; + mIsSettingEnabled = Settings.Secure.getIntForUser(context.getContentResolver(), + Settings.Secure.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT) == 1; } public void systemReady(Context context) { @@ -173,8 +173,8 @@ public class AttentionDetector { // Shouldn't happen since in-process. } - context.getContentResolver().registerContentObserver(Settings.System.getUriFor( - Settings.System.ADAPTIVE_SLEEP), + context.getContentResolver().registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.ADAPTIVE_SLEEP), false, new ContentObserver(new Handler(context.getMainLooper())) { @Override public void onChange(boolean selfChange) { @@ -194,7 +194,7 @@ public class AttentionDetector { if (!isAttentionServiceSupported() || !serviceHasSufficientPermissions()) { // Turns off adaptive sleep in settings for all users if attention service is not // available. The setting itself should also be grayed out in this case. - Settings.System.putInt(mContentResolver, ADAPTIVE_SLEEP, 0); + Settings.Secure.putInt(mContentResolver, ADAPTIVE_SLEEP, 0); return nextScreenDimming; } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 4d13658c85b7..002ab9ccc57c 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -83,6 +83,7 @@ import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import android.view.Display; +import com.android.internal.BrightnessSynchronizer; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IAppOpsService; import com.android.internal.app.IBatteryStats; @@ -192,6 +193,9 @@ public final class PowerManagerService extends SystemService // This should perhaps be a setting. private static final int SCREEN_BRIGHTNESS_BOOST_TIMEOUT = 5 * 1000; + // Float.NaN cannot be stored in config.xml so -2 is used instead + private static final float INVALID_BRIGHTNESS_IN_CONFIG = -2f; + // How long a partial wake lock must be held until we consider it a long wake lock. static final long MIN_LONG_WAKE_CHECK_INTERVAL = 60*1000; @@ -485,13 +489,17 @@ public final class PowerManagerService extends SystemService private boolean mProximityPositive; // Screen brightness setting limits. - private int mScreenBrightnessSettingMinimum; - private int mScreenBrightnessSettingMaximum; - private int mScreenBrightnessSettingDefault; - - // The screen brightness setting, from 0 to 255. - // Use -1 if no value has been set. - private int mScreenBrightnessSetting; + private float mScreenBrightnessSettingMinimum; + private float mScreenBrightnessSettingMaximum; + private float mScreenBrightnessSettingDefault; + public final float mScreenBrightnessMinimum; + public final float mScreenBrightnessMaximum; + public final float mScreenBrightnessDefault; + public final float mScreenBrightnessDoze; + public final float mScreenBrightnessDim; + public final float mScreenBrightnessMinimumVr; + public final float mScreenBrightnessMaximumVr; + public final float mScreenBrightnessDefaultVr; // The screen brightness mode. // One of the Settings.System.SCREEN_BRIGHTNESS_MODE_* constants. @@ -502,6 +510,9 @@ public final class PowerManagerService extends SystemService // Use -1 to disable. private int mScreenBrightnessOverrideFromWindowManager = -1; + private float mScreenBrightnessOverrideFromWindowManagerFloat = + PowerManager.BRIGHTNESS_INVALID_FLOAT; + // The window manager has determined the user to be inactive via other means. // Set this to false to disable. private boolean mUserInactiveOverrideFromWindowManager; @@ -521,6 +532,8 @@ public final class PowerManagerService extends SystemService // The screen brightness to use while dozing. private int mDozeScreenBrightnessOverrideFromDreamManager = PowerManager.BRIGHTNESS_DEFAULT; + private float mDozeScreenBrightnessOverrideFromDreamManagerFloat = + PowerManager.BRIGHTNESS_INVALID_FLOAT; // Keep display state when dozing. private boolean mDrawWakeLockOverrideFromSidekick; @@ -827,6 +840,91 @@ public final class PowerManagerService extends SystemService mInattentiveSleepWarningOverlayController = mInjector.createInattentiveSleepWarningController(); + // Save brightness values: + // Get float values from config. + // Store float if valid + // Otherwise, get int values and convert to float and then store. + final float min = mContext.getResources().getFloat(com.android.internal.R.dimen + .config_screenBrightnessSettingMinimumFloat); + final float max = mContext.getResources().getFloat(com.android.internal.R.dimen + .config_screenBrightnessSettingMaximumFloat); + final float def = mContext.getResources().getFloat(com.android.internal.R.dimen + .config_screenBrightnessSettingDefaultFloat); + final float doze = mContext.getResources().getFloat(com.android.internal.R.dimen + .config_screenBrightnessDozeFloat); + final float dim = mContext.getResources().getFloat(com.android.internal.R.dimen + .config_screenBrightnessDimFloat); + + if (min == INVALID_BRIGHTNESS_IN_CONFIG || max == INVALID_BRIGHTNESS_IN_CONFIG + || def == INVALID_BRIGHTNESS_IN_CONFIG) { + mScreenBrightnessMinimum = BrightnessSynchronizer.brightnessIntToFloat( + mContext.getResources().getInteger(com.android.internal.R.integer + .config_screenBrightnessSettingMinimum), + PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON, + PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX); + mScreenBrightnessMaximum = BrightnessSynchronizer.brightnessIntToFloat( + mContext.getResources().getInteger(com.android.internal.R.integer + .config_screenBrightnessSettingMaximum), + PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON, + PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX); + mScreenBrightnessDefault = BrightnessSynchronizer.brightnessIntToFloat( + mContext.getResources().getInteger(com.android.internal.R.integer + .config_screenBrightnessSettingDefault), + PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON, + PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX); + } else { + mScreenBrightnessMinimum = min; + mScreenBrightnessMaximum = max; + mScreenBrightnessDefault = def; + } + if (doze == INVALID_BRIGHTNESS_IN_CONFIG) { + mScreenBrightnessDoze = BrightnessSynchronizer.brightnessIntToFloat( + mContext.getResources().getInteger(com.android.internal.R.integer + .config_screenBrightnessDoze), PowerManager.BRIGHTNESS_OFF + 1, + PowerManager.BRIGHTNESS_ON, PowerManager.BRIGHTNESS_MIN, + PowerManager.BRIGHTNESS_MAX); + } else { + mScreenBrightnessDoze = doze; + } + if (dim == INVALID_BRIGHTNESS_IN_CONFIG) { + mScreenBrightnessDim = BrightnessSynchronizer.brightnessIntToFloat( + mContext.getResources().getInteger(com.android.internal.R.integer + .config_screenBrightnessDim), PowerManager.BRIGHTNESS_OFF + 1, + PowerManager.BRIGHTNESS_ON, PowerManager.BRIGHTNESS_MIN, + PowerManager.BRIGHTNESS_MAX); + } else { + mScreenBrightnessDim = dim; + } + + final float vrMin = mContext.getResources().getFloat(com.android.internal.R.dimen + .config_screenBrightnessSettingForVrMinimumFloat); + final float vrMax = mContext.getResources().getFloat(com.android.internal.R.dimen + .config_screenBrightnessSettingForVrMaximumFloat); + final float vrDef = mContext.getResources().getFloat(com.android.internal.R.dimen + .config_screenBrightnessSettingForVrDefaultFloat); + if (vrMin == INVALID_BRIGHTNESS_IN_CONFIG || vrMax == INVALID_BRIGHTNESS_IN_CONFIG + || vrDef == INVALID_BRIGHTNESS_IN_CONFIG) { + mScreenBrightnessMinimumVr = BrightnessSynchronizer.brightnessIntToFloat( + mContext.getResources().getInteger(com.android.internal.R.integer + .config_screenBrightnessForVrSettingMinimum), + PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON, + PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX); + mScreenBrightnessMaximumVr = BrightnessSynchronizer.brightnessIntToFloat( + mContext.getResources().getInteger(com.android.internal.R.integer + .config_screenBrightnessForVrSettingMaximum), + PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON, + PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX); + mScreenBrightnessDefaultVr = BrightnessSynchronizer.brightnessIntToFloat( + mContext.getResources().getInteger(com.android.internal.R.integer + .config_screenBrightnessForVrSettingDefault), + PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON, + PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX); + } else { + mScreenBrightnessMinimumVr = vrMin; + mScreenBrightnessMaximumVr = vrMax; + mScreenBrightnessDefaultVr = vrDef; + } + synchronized (mLock) { mWakeLockSuspendBlocker = mInjector.createSuspendBlocker(this, "PowerManagerService.WakeLocks"); @@ -895,9 +993,12 @@ public final class PowerManagerService extends SystemService mAttentionDetector.systemReady(mContext); PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting(); - mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting(); - mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting(); + mScreenBrightnessSettingMinimum = pm.getBrightnessConstraint( + PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM); + mScreenBrightnessSettingMaximum = pm.getBrightnessConstraint( + PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM); + mScreenBrightnessSettingDefault = pm.getBrightnessConstraint( + PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT); SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper()); @@ -2672,7 +2773,7 @@ public final class PowerManagerService extends SystemService // Determine appropriate screen brightness and auto-brightness adjustments. final boolean autoBrightness; - final int screenBrightnessOverride; + final float screenBrightnessOverride; if (!mBootCompleted) { // Keep the brightness steady during boot. This requires the // bootloader brightness and the default brightness to be identical. @@ -2680,11 +2781,11 @@ public final class PowerManagerService extends SystemService screenBrightnessOverride = mScreenBrightnessSettingDefault; } else if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) { autoBrightness = false; - screenBrightnessOverride = mScreenBrightnessOverrideFromWindowManager; + screenBrightnessOverride = mScreenBrightnessOverrideFromWindowManagerFloat; } else { autoBrightness = (mScreenBrightnessModeSetting == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - screenBrightnessOverride = -1; + screenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT; } // Update display power request. @@ -2707,10 +2808,11 @@ public final class PowerManagerService extends SystemService } } mDisplayPowerRequest.dozeScreenBrightness = - mDozeScreenBrightnessOverrideFromDreamManager; + mDozeScreenBrightnessOverrideFromDreamManagerFloat; } else { mDisplayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN; - mDisplayPowerRequest.dozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT; + mDisplayPowerRequest.dozeScreenBrightness = + PowerManager.BRIGHTNESS_INVALID_FLOAT; } mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest, @@ -3426,10 +3528,13 @@ public final class PowerManagerService extends SystemService } } + // TODO(brightnessfloat): change to float private void setScreenBrightnessOverrideFromWindowManagerInternal(int brightness) { synchronized (mLock) { if (mScreenBrightnessOverrideFromWindowManager != brightness) { mScreenBrightnessOverrideFromWindowManager = brightness; + mScreenBrightnessOverrideFromWindowManagerFloat = + BrightnessSynchronizer.brightnessIntToFloat(mContext, brightness); mDirty |= DIRTY_SETTINGS; updatePowerStateLocked(); } @@ -3462,6 +3567,9 @@ public final class PowerManagerService extends SystemService || mDozeScreenBrightnessOverrideFromDreamManager != screenBrightness) { mDozeScreenStateOverrideFromDreamManager = screenState; mDozeScreenBrightnessOverrideFromDreamManager = screenBrightness; + mDozeScreenBrightnessOverrideFromDreamManagerFloat = + BrightnessSynchronizer.brightnessIntToFloat(mContext, + mDozeScreenBrightnessOverrideFromDreamManager); mDirty |= DIRTY_SETTINGS; updatePowerStateLocked(); } @@ -3715,10 +3823,9 @@ public final class PowerManagerService extends SystemService + mMaximumScreenOffTimeoutFromDeviceAdmin + " (enforced=" + isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked() + ")"); pw.println(" mStayOnWhilePluggedInSetting=" + mStayOnWhilePluggedInSetting); - pw.println(" mScreenBrightnessSetting=" + mScreenBrightnessSetting); pw.println(" mScreenBrightnessModeSetting=" + mScreenBrightnessModeSetting); - pw.println(" mScreenBrightnessOverrideFromWindowManager=" - + mScreenBrightnessOverrideFromWindowManager); + pw.println(" mScreenBrightnessOverrideFromWindowManagerFloat=" + + mScreenBrightnessOverrideFromWindowManagerFloat); pw.println(" mUserActivityTimeoutOverrideFromWindowManager=" + mUserActivityTimeoutOverrideFromWindowManager); pw.println(" mUserInactiveOverrideFromWindowManager=" @@ -3728,9 +3835,9 @@ public final class PowerManagerService extends SystemService pw.println(" mDrawWakeLockOverrideFromSidekick=" + mDrawWakeLockOverrideFromSidekick); pw.println(" mDozeScreenBrightnessOverrideFromDreamManager=" + mDozeScreenBrightnessOverrideFromDreamManager); - pw.println(" mScreenBrightnessSettingMinimum=" + mScreenBrightnessSettingMinimum); - pw.println(" mScreenBrightnessSettingMaximum=" + mScreenBrightnessSettingMaximum); - pw.println(" mScreenBrightnessSettingDefault=" + mScreenBrightnessSettingDefault); + pw.println(" mScreenBrightnessSettingMinimumFloat=" + mScreenBrightnessSettingMinimum); + pw.println(" mScreenBrightnessSettingMaximumFloat=" + mScreenBrightnessSettingMaximum); + pw.println(" mScreenBrightnessSettingDefaultFloat=" + mScreenBrightnessSettingDefault); pw.println(" mDoubleTapWakeEnabled=" + mDoubleTapWakeEnabled); pw.println(" mIsVrModeEnabled=" + mIsVrModeEnabled); pw.println(" mForegroundProfile=" + mForegroundProfile); @@ -4056,7 +4163,7 @@ public final class PowerManagerService extends SystemService proto.write( PowerServiceSettingsAndConfigurationDumpProto .SCREEN_BRIGHTNESS_OVERRIDE_FROM_WINDOW_MANAGER, - mScreenBrightnessOverrideFromWindowManager); + mScreenBrightnessOverrideFromWindowManagerFloat); proto.write( PowerServiceSettingsAndConfigurationDumpProto .USER_ACTIVITY_TIMEOUT_OVERRIDE_FROM_WINDOW_MANAGER_MS, @@ -4738,6 +4845,29 @@ public final class PowerManagerService extends SystemService } } + public float getBrightnessConstraint(int constraint) { + switch (constraint) { + case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM: + return mScreenBrightnessMinimum; + case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM: + return mScreenBrightnessMaximum; + case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT: + return mScreenBrightnessDefault; + case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM: + return mScreenBrightnessDim; + case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE: + return mScreenBrightnessDoze; + case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR: + return mScreenBrightnessMinimumVr; + case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM_VR: + return mScreenBrightnessMaximumVr; + case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_VR: + return mScreenBrightnessDefaultVr; + default: + return PowerManager.BRIGHTNESS_INVALID_FLOAT; + } + } + @Override // Binder call public boolean isInteractive() { final long ident = Binder.clearCallingIdentity(); diff --git a/services/core/java/com/android/server/power/PreRebootLogger.java b/services/core/java/com/android/server/power/PreRebootLogger.java new file mode 100644 index 000000000000..cda00b404c0b --- /dev/null +++ b/services/core/java/com/android/server/power/PreRebootLogger.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.power; + +import android.annotation.NonNull; +import android.content.Context; +import android.os.Environment; +import android.os.IBinder; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.provider.Settings; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; + +/** + * Provides utils to dump/wipe pre-reboot information. + */ +final class PreRebootLogger { + private static final String TAG = "PreRebootLogger"; + private static final String PREREBOOT_DIR = "prereboot"; + + private static final String[] BUFFERS_TO_DUMP = {"system"}; + private static final String[] SERVICES_TO_DUMP = {Context.ROLLBACK_SERVICE, "package"}; + + private static final Object sLock = new Object(); + + /** + * Process pre-reboot information. Dump pre-reboot information to {@link #PREREBOOT_DIR} if + * enabled {@link Settings.Global#ADB_ENABLED}; wipe dumped information otherwise. + */ + static void log(Context context) { + log(context, getDumpDir()); + } + + @VisibleForTesting + static void log(Context context, @NonNull File dumpDir) { + if (Settings.Global.getInt( + context.getContentResolver(), Settings.Global.ADB_ENABLED, 0) == 1) { + Slog.d(TAG, "Dumping pre-reboot information..."); + dump(dumpDir); + } else { + Slog.d(TAG, "Wiping pre-reboot information..."); + wipe(dumpDir); + } + } + + private static void dump(@NonNull File dumpDir) { + synchronized (sLock) { + for (String buffer : BUFFERS_TO_DUMP) { + dumpLogsLocked(dumpDir, buffer); + } + for (String service : SERVICES_TO_DUMP) { + dumpServiceLocked(dumpDir, service); + } + } + } + + private static void wipe(@NonNull File dumpDir) { + synchronized (sLock) { + for (File file : dumpDir.listFiles()) { + file.delete(); + } + } + } + + private static File getDumpDir() { + final File dumpDir = new File(Environment.getDataMiscDirectory(), PREREBOOT_DIR); + if (!dumpDir.exists() || !dumpDir.isDirectory()) { + throw new UnsupportedOperationException("Pre-reboot dump directory not found"); + } + return dumpDir; + } + + @GuardedBy("sLock") + private static void dumpLogsLocked(@NonNull File dumpDir, @NonNull String buffer) { + try { + final File dumpFile = new File(dumpDir, buffer); + if (dumpFile.createNewFile()) { + dumpFile.setWritable(true /* writable */, true /* ownerOnly */); + } else { + // Wipes dumped information in existing file before recording new information. + new FileWriter(dumpFile, false).flush(); + } + + final String[] cmdline = + {"logcat", "-d", "-b", buffer, "-f", dumpFile.getAbsolutePath()}; + Runtime.getRuntime().exec(cmdline).waitFor(); + } catch (IOException | InterruptedException e) { + Slog.d(TAG, "Dump system log buffer before reboot fail", e); + } + } + + @GuardedBy("sLock") + private static void dumpServiceLocked(@NonNull File dumpDir, @NonNull String serviceName) { + final IBinder binder = ServiceManager.checkService(serviceName); + if (binder == null) { + return; + } + + try { + final File dumpFile = new File(dumpDir, serviceName); + final ParcelFileDescriptor fd = ParcelFileDescriptor.open(dumpFile, + ParcelFileDescriptor.MODE_CREATE | ParcelFileDescriptor.MODE_TRUNCATE + | ParcelFileDescriptor.MODE_WRITE_ONLY); + binder.dump(fd.getFileDescriptor(), ArrayUtils.emptyArray(String.class)); + } catch (FileNotFoundException | RemoteException e) { + Slog.d(TAG, String.format("Dump %s service before reboot fail", serviceName), e); + } + } +} diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java index cc1cddd3a111..bc722f181650 100644 --- a/services/core/java/com/android/server/power/ShutdownThread.java +++ b/services/core/java/com/android/server/power/ShutdownThread.java @@ -44,6 +44,7 @@ import android.os.Vibrator; import android.telephony.TelephonyManager; import android.util.ArrayMap; import android.util.Log; +import android.util.Slog; import android.util.TimingsTraceLog; import android.view.WindowManager; @@ -446,6 +447,15 @@ public final class ShutdownThread extends Thread { SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1"); } + shutdownTimingLog.traceBegin("DumpPreRebootInfo"); + try { + Slog.i(TAG, "Logging pre-reboot information..."); + PreRebootLogger.log(mContext); + } catch (Exception e) { + Slog.e(TAG, "Failed to log pre-reboot information", e); + } + shutdownTimingLog.traceEnd(); // DumpPreRebootInfo + metricStarted(METRIC_SEND_BROADCAST); shutdownTimingLog.traceBegin("SendShutdownBroadcast"); Log.i(TAG, "Sending shutdown broadcast..."); diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java index 5abd9f0698d3..7b96777b5987 100644 --- a/services/core/java/com/android/server/rollback/Rollback.java +++ b/services/core/java/com/android/server/rollback/Rollback.java @@ -181,15 +181,6 @@ class Rollback { private int mNumPackageSessionsWithSuccess; /** - * A temp flag to facilitate merging of the 2 rollback collections managed by - * RollbackManagerServiceImpl. True if this rollback is in the process of enabling and was - * originally managed by RollbackManagerServiceImpl#mNewRollbacks. - * TODO: remove this flag when merge is completed. - */ - @GuardedBy("mLock") - private boolean mIsNewRollback = false; - - /** * Constructs a new, empty Rollback instance. * * @param rollbackId the id of the rollback. @@ -837,18 +828,6 @@ class Rollback { } } - void setIsNewRollback(boolean newRollback) { - synchronized (mLock) { - mIsNewRollback = newRollback; - } - } - - boolean isNewRollback() { - synchronized (mLock) { - return mIsNewRollback; - } - } - static String rollbackStateToString(@RollbackState int state) { switch (state) { case Rollback.ROLLBACK_STATE_ENABLING: return "enabling"; diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index 1421258c12f6..91e7cc981b89 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -164,8 +164,16 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { // Load rollback data from device storage. synchronized (mLock) { mRollbacks = mRollbackStore.loadRollbacks(); - for (Rollback rollback : mRollbacks) { - mAllocatedRollbackIds.put(rollback.info.getRollbackId(), true); + if (!context.getPackageManager().isDeviceUpgrading()) { + for (Rollback rollback : mRollbacks) { + mAllocatedRollbackIds.put(rollback.info.getRollbackId(), true); + } + } else { + // Delete rollbacks when build fingerprint has changed. + for (Rollback rollback : mRollbacks) { + rollback.delete(mAppDataRollbackHelper); + } + mRollbacks.clear(); } } @@ -788,14 +796,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { Rollback newRollback; synchronized (mLock) { - // See if we already have a NewRollback that contains this package - // session. If not, create a NewRollback for the parent session + // See if we already have a Rollback that contains this package + // session. If not, create a new Rollback for the parent session // that we will use for all the packages in the session. - newRollback = getNewRollbackForPackageSessionLocked(packageSession.getSessionId()); + newRollback = getRollbackForSessionLocked(packageSession.getSessionId()); if (newRollback == null) { newRollback = createNewRollbackLocked(parentSession); mRollbacks.add(newRollback); - newRollback.setIsNewRollback(true); } } newRollback.addToken(token); @@ -1148,24 +1155,23 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } if (success) { - Rollback newRollback; + Rollback rollback; synchronized (mLock) { - newRollback = getNewRollbackForPackageSessionLocked(sessionId); - if (newRollback != null && newRollback.notifySessionWithSuccess()) { - mRollbacks.remove(newRollback); - newRollback.setIsNewRollback(false); - } else { - // Not all child sessions finished with success. - // Don't enable the rollback yet. - newRollback = null; + rollback = getRollbackForSessionLocked(sessionId); + if (rollback == null || rollback.isStaged() || !rollback.isEnabling() + || !rollback.notifySessionWithSuccess()) { + return; } + // All child sessions finished with success. We can enable this rollback now. + // TODO: refactor #completeEnableRollback so we won't remove 'rollback' from + // mRollbacks here and add it back in #completeEnableRollback later. + mRollbacks.remove(rollback); } - - if (newRollback != null) { - Rollback rollback = completeEnableRollback(newRollback); - if (rollback != null && !rollback.isStaged()) { - makeRollbackAvailable(rollback); - } + // TODO: Now #completeEnableRollback returns the same rollback object as the + // parameter on success. It would be more readable to return a boolean to indicate + // success or failure. + if (completeEnableRollback(rollback) != null) { + makeRollbackAvailable(rollback); } } else { synchronized (mLock) { @@ -1354,22 +1360,4 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } return null; } - - /** - * Returns the NewRollback associated with the given package session. - * Returns null if no NewRollback is found for the given package - * session. - */ - @WorkerThread - @GuardedBy("mLock") - Rollback getNewRollbackForPackageSessionLocked(int packageSessionId) { - // We expect mRollbacks to be a very small list; linear search - // should be plenty fast. - for (Rollback rollback: mRollbacks) { - if (rollback.isNewRollback() && rollback.containsSessionId(packageSessionId)) { - return rollback; - } - } - return null; - } } diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java index 58455ca2753b..816452679c45 100644 --- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java +++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java @@ -669,7 +669,8 @@ public final class TextClassificationManagerService extends ITextClassifierServi return mDefaultServiceState; } String textClassifierServicePackageOverride = - mSettings.getTextClassifierServicePackageOverride(); + Binder.withCleanCallingIdentity( + mSettings::getTextClassifierServicePackageOverride); if (!TextUtils.isEmpty(textClassifierServicePackageOverride)) { if (textClassifierServicePackageOverride.equals(mDefaultTextClassifierPackage)) { return mDefaultServiceState; diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index c78707a2204e..ca6bd2d1e3b4 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -57,8 +57,8 @@ import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATIO import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT; -import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR; @@ -855,7 +855,7 @@ public class DisplayPolicy { case TYPE_WALLPAPER: // Dreams and wallpapers don't have an app window token and can thus not be // letterboxed. Hence always let them extend under the cutout. - attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; break; case TYPE_NOTIFICATION_SHADE: // If the Keyguard is in a hidden state (occluded by another window), we force to @@ -1511,15 +1511,16 @@ public class DisplayPolicy { // nav bar and ensure the application doesn't see the event. if (navVisible || navAllowedHidden) { if (mInputConsumer != null) { + mInputConsumer.dismiss(); mHandler.sendMessage( mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer)); mInputConsumer = null; } } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) { - mInputConsumer = mService.createInputConsumer(mHandler.getLooper(), + mInputConsumer = mDisplayContent.getInputMonitor().createInputConsumer( + mHandler.getLooper(), INPUT_CONSUMER_NAVIGATION, - HideNavInputEventReceiver::new, - displayFrames.mDisplayId); + HideNavInputEventReceiver::new); // As long as mInputConsumer is active, hover events are not dispatched to the app // and the pointer icon is likely to become stale. Hide it to avoid confusion. InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL); @@ -2298,15 +2299,24 @@ public class DisplayPolicy { // cropped / shifted to the displayFrame in WindowState. final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen && type != TYPE_BASE_APPLICATION; - // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in // the cutout safe zone. - if (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT - || cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER) { + if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) { final Rect displayCutoutSafeExceptMaybeBars = sTmpDisplayCutoutSafeExceptMaybeBarsRect; displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe); + if (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES) { + if (displayFrames.mDisplayWidth < displayFrames.mDisplayHeight) { + displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE; + displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE; + } else { + displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE; + displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE; + } + } + if (layoutInScreen && layoutInsetDecor && !requestedFullscreen - && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) { + && (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT + || cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)) { // At the top we have the status bar, so apps that are // LAYOUT_IN_SCREEN | LAYOUT_INSET_DECOR but not FULLSCREEN // already expect that there's an inset there and we don't need to exclude @@ -2314,7 +2324,8 @@ public class DisplayPolicy { displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE; } if (layoutInScreen && layoutInsetDecor && !requestedHideNavigation - && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) { + && (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT + || cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)) { // Same for the navigation bar. switch (mNavigationBarPosition) { case NAV_BAR_BOTTOM: @@ -3173,7 +3184,7 @@ public class DisplayPolicy { private void disposeInputConsumer(InputConsumer inputConsumer) { if (inputConsumer != null) { - inputConsumer.dismiss(); + inputConsumer.dispose(); } } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index faa6e52da8bc..b638e4937b34 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -103,9 +103,17 @@ final class InputMonitor { @Override public void dismiss() { synchronized (mService.mGlobalLock) { - if (mInputMonitor.destroyInputConsumer(mWindowHandle.name)) { - mInputEventReceiver.dispose(); - } + mInputMonitor.mInputConsumers.remove(mName); + hide(mInputMonitor.mInputTransaction); + mInputMonitor.updateInputWindowsLw(true /* force */); + } + } + + @Override + public void dispose() { + synchronized (mService.mGlobalLock) { + disposeChannelsLw(); + mInputEventReceiver.dispose(); } } } @@ -415,18 +423,18 @@ final class InputMonitor { } private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> { - InputConsumerImpl navInputConsumer; - InputConsumerImpl pipInputConsumer; - InputConsumerImpl wallpaperInputConsumer; - InputConsumerImpl recentsAnimationInputConsumer; + InputConsumerImpl mNavInputConsumer; + InputConsumerImpl mPipInputConsumer; + InputConsumerImpl mWallpaperInputConsumer; + InputConsumerImpl mRecentsAnimationInputConsumer; - private boolean mAddInputConsumerHandle; + private boolean mAddNavInputConsumerHandle; private boolean mAddPipInputConsumerHandle; private boolean mAddWallpaperInputConsumerHandle; private boolean mAddRecentsAnimationInputConsumerHandle; - boolean inDrag; - WallpaperController wallpaperController; + boolean mInDrag; + WallpaperController mWallpaperController; // An invalid window handle that tells SurfaceFlinger not update the input info. final InputWindowHandle mInvalidInputWindow = new InputWindowHandle(null, mDisplayId); @@ -434,20 +442,20 @@ final class InputMonitor { private void updateInputWindows(boolean inDrag) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows"); - navInputConsumer = getInputConsumer(INPUT_CONSUMER_NAVIGATION); - pipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP); - wallpaperInputConsumer = getInputConsumer(INPUT_CONSUMER_WALLPAPER); - recentsAnimationInputConsumer = getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION); + mNavInputConsumer = getInputConsumer(INPUT_CONSUMER_NAVIGATION); + mPipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP); + mWallpaperInputConsumer = getInputConsumer(INPUT_CONSUMER_WALLPAPER); + mRecentsAnimationInputConsumer = getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION); - mAddInputConsumerHandle = navInputConsumer != null; - mAddPipInputConsumerHandle = pipInputConsumer != null; - mAddWallpaperInputConsumerHandle = wallpaperInputConsumer != null; - mAddRecentsAnimationInputConsumerHandle = recentsAnimationInputConsumer != null; + mAddNavInputConsumerHandle = mNavInputConsumer != null; + mAddPipInputConsumerHandle = mPipInputConsumer != null; + mAddWallpaperInputConsumerHandle = mWallpaperInputConsumer != null; + mAddRecentsAnimationInputConsumerHandle = mRecentsAnimationInputConsumer != null; mTmpRect.setEmpty(); mDisableWallpaperTouchEvents = false; - this.inDrag = inDrag; - wallpaperController = mDisplayContent.mWallpaperController; + mInDrag = inDrag; + mWallpaperController = mDisplayContent.mWallpaperController; resetInputConsumers(mInputTransaction); @@ -455,7 +463,7 @@ final class InputMonitor { true /* traverseTopToBottom */); if (mAddWallpaperInputConsumerHandle) { - wallpaperInputConsumer.show(mInputTransaction, 0); + mWallpaperInputConsumer.show(mInputTransaction, 0); } if (mApplyImmediately) { @@ -494,8 +502,8 @@ final class InputMonitor { if (recentsAnimationController != null && recentsAnimationController.shouldApplyInputConsumer(w.mActivityRecord)) { if (recentsAnimationController.updateInputConsumerForApp( - recentsAnimationInputConsumer.mWindowHandle, hasFocus)) { - recentsAnimationInputConsumer.show(mInputTransaction, w); + mRecentsAnimationInputConsumer.mWindowHandle, hasFocus)) { + mRecentsAnimationInputConsumer.show(mInputTransaction, w); mAddRecentsAnimationInputConsumerHandle = false; } } @@ -505,25 +513,25 @@ final class InputMonitor { if (mAddPipInputConsumerHandle) { // Update the bounds of the Pip input consumer to match the window bounds. w.getBounds(mTmpRect); - pipInputConsumer.layout(mInputTransaction, mTmpRect); + mPipInputConsumer.layout(mInputTransaction, mTmpRect); // The touchable region is relative to the surface top-left mTmpRect.offsetTo(0, 0); - pipInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect); - pipInputConsumer.show(mInputTransaction, w); + mPipInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect); + mPipInputConsumer.show(mInputTransaction, w); mAddPipInputConsumerHandle = false; } } - if (mAddInputConsumerHandle) { - navInputConsumer.show(mInputTransaction, w); - mAddInputConsumerHandle = false; + if (mAddNavInputConsumerHandle) { + mNavInputConsumer.show(mInputTransaction, w); + mAddNavInputConsumerHandle = false; } if (mAddWallpaperInputConsumerHandle) { if (w.mAttrs.type == TYPE_WALLPAPER && w.isVisibleLw()) { // Add the wallpaper input consumer above the first visible wallpaper. - wallpaperInputConsumer.show(mInputTransaction, w); + mWallpaperInputConsumer.show(mInputTransaction, w); mAddWallpaperInputConsumerHandle = false; } } @@ -531,13 +539,13 @@ final class InputMonitor { if ((privateFlags & PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS) != 0) { mDisableWallpaperTouchEvents = true; } - final boolean hasWallpaper = wallpaperController.isWallpaperTarget(w) + final boolean hasWallpaper = mWallpaperController.isWallpaperTarget(w) && !mService.mPolicy.isKeyguardShowing() && !mDisableWallpaperTouchEvents; // If there's a drag in progress and 'child' is a potential drop target, // make sure it's been told about the drag - if (inDrag && isVisible && w.getDisplayContent().isDefaultDisplay) { + if (mInDrag && isVisible && w.getDisplayContent().isDefaultDisplay) { mService.mDragDropController.sendDragStartedIfNeededLocked(w); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index d98c18c077b8..8f9caea26534 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -230,7 +230,6 @@ import android.view.InputApplicationHandle; import android.view.InputChannel; import android.view.InputDevice; import android.view.InputEvent; -import android.view.InputEventReceiver; import android.view.InputWindowHandle; import android.view.InsetsState; import android.view.KeyEvent; @@ -5763,20 +5762,6 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public WindowManagerPolicy.InputConsumer createInputConsumer(Looper looper, String name, - InputEventReceiver.Factory inputEventReceiverFactory, int displayId) { - synchronized (mGlobalLock) { - DisplayContent displayContent = mRoot.getDisplayContent(displayId); - if (displayContent != null) { - return displayContent.getInputMonitor().createInputConsumer(looper, name, - inputEventReceiverFactory); - } else { - return null; - } - } - } - - @Override public void createInputConsumer(IBinder token, String name, int displayId, InputChannel inputChannel) { synchronized (mGlobalLock) { diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 49c7e0a3b242..e888f2a4bae5 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -2,6 +2,7 @@ cc_library_static { name: "libservices.core", defaults: ["libservices.core-libs"], + cpp_std: "c++2a", cflags: [ "-Wall", "-Werror", @@ -55,6 +56,7 @@ cc_library_static { "com_android_server_am_CachedAppOptimizer.cpp", "com_android_server_am_LowMemDetector.cpp", "com_android_server_incremental_IncrementalManagerService.cpp", + "com_android_server_pm_PackageManagerShellCommandDataLoader.cpp", "onload.cpp", ":lib_networkStatsFactory_native", ], @@ -125,6 +127,8 @@ cc_defaults { "libnetdbpf", "libnetdutils", "libpsi", + "libdataloader", + "libincfs", "android.hardware.audio.common@2.0", "android.hardware.broadcastradio@1.0", "android.hardware.broadcastradio@1.1", diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index 1a8f1f9f0024..d0eaa48022e9 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -26,17 +26,18 @@ #include <android/hardware/gnss/1.0/IGnssMeasurement.h> #include <android/hardware/gnss/1.1/IGnssMeasurement.h> #include <android/hardware/gnss/2.0/IGnssMeasurement.h> +#include <android/hardware/gnss/2.1/IGnssAntennaInfo.h> #include <android/hardware/gnss/2.1/IGnssMeasurement.h> #include <android/hardware/gnss/measurement_corrections/1.0/IMeasurementCorrections.h> #include <android/hardware/gnss/measurement_corrections/1.1/IMeasurementCorrections.h> #include <android/hardware/gnss/visibility_control/1.0/IGnssVisibilityControl.h> #include <nativehelper/JNIHelp.h> -#include "jni.h" +#include "android_runtime/AndroidRuntime.h" +#include "android_runtime/Log.h" #include "hardware_legacy/power.h" +#include "jni.h" #include "utils/Log.h" #include "utils/misc.h" -#include "android_runtime/AndroidRuntime.h" -#include "android_runtime/Log.h" #include <arpa/inet.h> #include <cinttypes> @@ -54,6 +55,12 @@ static jclass class_location; static jclass class_gnssNavigationMessage; static jclass class_gnssClock; static jclass class_gnssConfiguration_halInterfaceVersion; +static jclass class_gnssAntennaInfo; +static jclass class_phaseCenterOffsetCoordinates; +static jclass class_phaseCenterVariationCorrections; +static jclass class_signalGainCorrections; +static jclass class_arrayList; +static jclass class_doubleArray; static jobject mCallbacksObj = nullptr; @@ -78,6 +85,7 @@ static jmethodID method_reportGeofenceRemoveStatus; static jmethodID method_reportGeofencePauseStatus; static jmethodID method_reportGeofenceResumeStatus; static jmethodID method_reportMeasurementData; +static jmethodID method_reportAntennaInfo; static jmethodID method_reportNavigationMessages; static jmethodID method_reportLocationBatch; static jmethodID method_reportGnssServiceDied; @@ -114,6 +122,12 @@ static jmethodID method_gnssNavigationMessageCtor; static jmethodID method_gnssClockCtor; static jmethodID method_gnssMeasurementCtor; static jmethodID method_halInterfaceVersionCtor; +static jmethodID method_gnssAntennaInfoCtor; +static jmethodID method_phaseCenterOffsetCoordinatesCtor; +static jmethodID method_phaseCenterVariationCorrectionsCtor; +static jmethodID method_signalGainCorrectionsCtor; +static jmethodID method_arrayListCtor; +static jmethodID method_arrayListAdd; /* * Save a pointer to JavaVm to attach/detach threads executing @@ -171,6 +185,8 @@ using IGnssConfiguration_V2_0 = android::hardware::gnss::V2_0::IGnssConfiguratio using IGnssConfiguration_V2_1 = android::hardware::gnss::V2_1::IGnssConfiguration; using IGnssDebug_V1_0 = android::hardware::gnss::V1_0::IGnssDebug; using IGnssDebug_V2_0 = android::hardware::gnss::V2_0::IGnssDebug; +using IGnssAntennaInfo = android::hardware::gnss::V2_1::IGnssAntennaInfo; +using IGnssAntennaInfoCallback = android::hardware::gnss::V2_1::IGnssAntennaInfoCallback; using IGnssMeasurement_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurement; using IGnssMeasurement_V1_1 = android::hardware::gnss::V1_1::IGnssMeasurement; using IGnssMeasurement_V2_0 = android::hardware::gnss::V2_0::IGnssMeasurement; @@ -241,6 +257,7 @@ sp<IGnssNavigationMessage> gnssNavigationMessageIface = nullptr; sp<IMeasurementCorrections_V1_0> gnssCorrectionsIface_V1_0 = nullptr; sp<IMeasurementCorrections_V1_1> gnssCorrectionsIface_V1_1 = nullptr; sp<IGnssVisibilityControl> gnssVisibilityControlIface = nullptr; +sp<IGnssAntennaInfo> gnssAntennaInfoIface = nullptr; #define WAKE_LOCK_NAME "GPS" @@ -1051,6 +1068,195 @@ Return<void> GnssNavigationMessageCallback::gnssNavigationMessageCb( } /* + * GnssAntennaInfoCallback implements the callback methods required for the + * GnssAntennaInfo interface. + */ +struct GnssAntennaInfoCallback : public IGnssAntennaInfoCallback { + // Methods from V2_1::GnssAntennaInfoCallback follow. + Return<void> gnssAntennaInfoCb( + const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos); + +private: + jobject translateAllGnssAntennaInfos( + JNIEnv* env, + const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos); + jobject translateSingleGnssAntennaInfo( + JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo); + jobject translatePhaseCenterOffsetCoordinates( + JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo); + jobject translatePhaseCenterVariationCorrections( + JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo); + jobject translateSignalGainCorrections( + JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo); + jobjectArray translate2dDoubleArray(JNIEnv* env, + const hidl_vec<IGnssAntennaInfoCallback::Row>& array); + void translateAndReportGnssAntennaInfo( + const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos); + void reportAntennaInfo(JNIEnv* env, const jobject antennaInfosArray); +}; + +Return<void> GnssAntennaInfoCallback::gnssAntennaInfoCb( + const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) { + translateAndReportGnssAntennaInfo(gnssAntennaInfos); + return Void(); +} + +jobjectArray GnssAntennaInfoCallback::translate2dDoubleArray( + JNIEnv* env, const hidl_vec<IGnssAntennaInfoCallback::Row>& array) { + jsize numRows = array.size(); + if (numRows == 0) { + // Empty array + return NULL; + } + jsize numCols = array[0].row.size(); + if (numCols <= 1) { + // phi angle separation is computed as 180.0 / (numColumns - 1), so can't be < 2. + return NULL; + } + + // Allocate array of double arrays + jobjectArray returnArray = env->NewObjectArray(numRows, class_doubleArray, NULL); + + // Create each double array + for (uint8_t i = 0; i < numRows; i++) { + jdoubleArray doubleArray = env->NewDoubleArray(numCols); + env->SetDoubleArrayRegion(doubleArray, (jsize)0, numCols, array[i].row.data()); + env->SetObjectArrayElement(returnArray, (jsize)i, doubleArray); + env->DeleteLocalRef(doubleArray); + } + return returnArray; +} + +jobject GnssAntennaInfoCallback::translateAllGnssAntennaInfos( + JNIEnv* env, const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) { + jobject arrayList = env->NewObject(class_arrayList, + method_arrayListCtor); // Create new ArrayList instance + + for (auto gnssAntennaInfo : gnssAntennaInfos) { + jobject gnssAntennaInfoObject = translateSingleGnssAntennaInfo(env, gnssAntennaInfo); + + env->CallBooleanMethod(arrayList, method_arrayListAdd, + gnssAntennaInfoObject); // Add the antennaInfo to the ArrayList + + // Delete Local Refs + env->DeleteLocalRef(gnssAntennaInfoObject); + } + return arrayList; +} + +jobject GnssAntennaInfoCallback::translatePhaseCenterOffsetCoordinates( + JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) { + jobject phaseCenterOffsetCoordinates = + env->NewObject(class_phaseCenterOffsetCoordinates, + method_phaseCenterOffsetCoordinatesCtor, + gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.x, + gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.xUncertainty, + gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.y, + gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.yUncertainty, + gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.z, + gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.zUncertainty); + + return phaseCenterOffsetCoordinates; +} + +jobject GnssAntennaInfoCallback::translatePhaseCenterVariationCorrections( + JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) { + if (gnssAntennaInfo.phaseCenterVariationCorrectionMillimeters == NULL || + gnssAntennaInfo.phaseCenterVariationCorrectionUncertaintyMillimeters == NULL) { + return NULL; + } + + jobjectArray phaseCenterVariationCorrectionsArray = + translate2dDoubleArray(env, gnssAntennaInfo.phaseCenterVariationCorrectionMillimeters); + jobjectArray phaseCenterVariationCorrectionsUncertaintiesArray = + translate2dDoubleArray(env, + gnssAntennaInfo + .phaseCenterVariationCorrectionUncertaintyMillimeters); + + if (phaseCenterVariationCorrectionsArray == NULL || + phaseCenterVariationCorrectionsUncertaintiesArray == NULL) { + return NULL; + } + + jobject phaseCenterVariationCorrections = + env->NewObject(class_phaseCenterVariationCorrections, + method_phaseCenterVariationCorrectionsCtor, + phaseCenterVariationCorrectionsArray, + phaseCenterVariationCorrectionsUncertaintiesArray); + + env->DeleteLocalRef(phaseCenterVariationCorrectionsArray); + env->DeleteLocalRef(phaseCenterVariationCorrectionsUncertaintiesArray); + + return phaseCenterVariationCorrections; +} + +jobject GnssAntennaInfoCallback::translateSignalGainCorrections( + JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) { + if (gnssAntennaInfo.signalGainCorrectionDbi == NULL || + gnssAntennaInfo.signalGainCorrectionUncertaintyDbi == NULL) { + return NULL; + } + jobjectArray signalGainCorrectionsArray = + translate2dDoubleArray(env, gnssAntennaInfo.signalGainCorrectionDbi); + jobjectArray signalGainCorrectionsUncertaintiesArray = + translate2dDoubleArray(env, gnssAntennaInfo.signalGainCorrectionUncertaintyDbi); + + if (signalGainCorrectionsArray == NULL || signalGainCorrectionsUncertaintiesArray == NULL) { + return NULL; + } + + jobject signalGainCorrections = + env->NewObject(class_signalGainCorrections, method_signalGainCorrectionsCtor, + signalGainCorrectionsArray, signalGainCorrectionsUncertaintiesArray); + + env->DeleteLocalRef(signalGainCorrectionsArray); + env->DeleteLocalRef(signalGainCorrectionsUncertaintiesArray); + + return signalGainCorrections; +} + +jobject GnssAntennaInfoCallback::translateSingleGnssAntennaInfo( + JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) { + jobject phaseCenterOffsetCoordinates = + translatePhaseCenterOffsetCoordinates(env, gnssAntennaInfo); + + // Nullable + jobject phaseCenterVariationCorrections = + translatePhaseCenterVariationCorrections(env, gnssAntennaInfo); + + // Nullable + jobject signalGainCorrections = translateSignalGainCorrections(env, gnssAntennaInfo); + + jobject gnssAntennaInfoObject = + env->NewObject(class_gnssAntennaInfo, method_gnssAntennaInfoCtor, + gnssAntennaInfo.carrierFrequencyMHz, phaseCenterOffsetCoordinates, + phaseCenterVariationCorrections, signalGainCorrections); + + // Delete Local Refs + env->DeleteLocalRef(phaseCenterOffsetCoordinates); + env->DeleteLocalRef(phaseCenterVariationCorrections); + env->DeleteLocalRef(signalGainCorrections); + + return gnssAntennaInfoObject; +} + +void GnssAntennaInfoCallback::translateAndReportGnssAntennaInfo( + const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) { + JNIEnv* env = getJniEnv(); + + jobject arrayList = translateAllGnssAntennaInfos(env, gnssAntennaInfos); + + reportAntennaInfo(env, arrayList); + + env->DeleteLocalRef(arrayList); +} + +void GnssAntennaInfoCallback::reportAntennaInfo(JNIEnv* env, const jobject antennaInfosArray) { + env->CallVoidMethod(mCallbacksObj, method_reportAntennaInfo, antennaInfosArray); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +} + +/* * GnssMeasurementCallback implements the callback methods required for the * GnssMeasurement interface. */ @@ -1708,6 +1914,7 @@ static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, "(II)V"); method_reportGeofencePauseStatus = env->GetMethodID(clazz, "reportGeofencePauseStatus", "(II)V"); + method_reportAntennaInfo = env->GetMethodID(clazz, "reportAntennaInfo", "(Ljava/util/List;)V"); method_reportMeasurementData = env->GetMethodID( clazz, "reportMeasurementData", @@ -1791,6 +1998,36 @@ static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, class_gnssMeasurement = (jclass) env->NewGlobalRef(gnssMeasurementClass); method_gnssMeasurementCtor = env->GetMethodID(class_gnssMeasurement, "<init>", "()V"); + jclass gnssAntennaInfoClass = env->FindClass("android/location/GnssAntennaInfo"); + class_gnssAntennaInfo = (jclass)env->NewGlobalRef(gnssAntennaInfoClass); + method_gnssAntennaInfoCtor = + env->GetMethodID(class_gnssAntennaInfo, "<init>", + "(D" + "Landroid/location/GnssAntennaInfo$PhaseCenterOffsetCoordinates;" + "Landroid/location/GnssAntennaInfo$PhaseCenterVariationCorrections;" + "Landroid/location/GnssAntennaInfo$SignalGainCorrections;" + ")V"); + + jclass phaseCenterOffsetCoordinatesClass = + env->FindClass("android/location/GnssAntennaInfo$PhaseCenterOffsetCoordinates"); + class_phaseCenterOffsetCoordinates = + (jclass)env->NewGlobalRef(phaseCenterOffsetCoordinatesClass); + method_phaseCenterOffsetCoordinatesCtor = + env->GetMethodID(class_phaseCenterOffsetCoordinates, "<init>", "(DDDDDD)V"); + + jclass phaseCenterVariationCorrectionsClass = + env->FindClass("android/location/GnssAntennaInfo$PhaseCenterVariationCorrections"); + class_phaseCenterVariationCorrections = + (jclass)env->NewGlobalRef(phaseCenterVariationCorrectionsClass); + method_phaseCenterVariationCorrectionsCtor = + env->GetMethodID(class_phaseCenterVariationCorrections, "<init>", "([[D[[D)V"); + + jclass signalGainCorrectionsClass = + env->FindClass("android/location/GnssAntennaInfo$SignalGainCorrections"); + class_signalGainCorrections = (jclass)env->NewGlobalRef(signalGainCorrectionsClass); + method_signalGainCorrectionsCtor = + env->GetMethodID(class_signalGainCorrections, "<init>", "([[D[[D)V"); + jclass locationClass = env->FindClass("android/location/Location"); class_location = (jclass) env->NewGlobalRef(locationClass); method_locationCtor = env->GetMethodID(class_location, "<init>", "(Ljava/lang/String;)V"); @@ -1809,6 +2046,14 @@ static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, (jclass) env->NewGlobalRef(gnssConfiguration_halInterfaceVersionClass); method_halInterfaceVersionCtor = env->GetMethodID(class_gnssConfiguration_halInterfaceVersion, "<init>", "(II)V"); + + jclass arrayListClass = env->FindClass("java/util/ArrayList"); + class_arrayList = (jclass)env->NewGlobalRef(arrayListClass); + method_arrayListCtor = env->GetMethodID(class_arrayList, "<init>", "()V"); + method_arrayListAdd = env->GetMethodID(class_arrayList, "add", "(Ljava/lang/Object;)Z"); + + jclass doubleArrayClass = env->FindClass("[D"); + class_doubleArray = (jclass)env->NewGlobalRef(doubleArrayClass); } /* Initialization needed at system boot and whenever GNSS service dies. */ @@ -1935,6 +2180,15 @@ static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass } if (gnssHal_V2_1 != nullptr) { + auto gnssAntennaInfo = gnssHal_V2_1->getExtensionGnssAntennaInfo(); + if (!gnssAntennaInfo.isOk()) { + ALOGD("Unable to get a handle to GnssAntennaInfo"); + } else { + gnssAntennaInfoIface = gnssAntennaInfo; + } + } + + if (gnssHal_V2_1 != nullptr) { auto gnssCorrections = gnssHal_V2_1->getExtensionMeasurementCorrections_1_1(); if (!gnssCorrections.isOk()) { ALOGD("Unable to get a handle to GnssMeasurementCorrections 1.1 interface"); @@ -2725,6 +2979,52 @@ static jboolean android_location_GnssGeofenceProvider_resume_geofence(JNIEnv* /* return checkHidlReturn(result, "IGnssGeofencing resumeGeofence() failed."); } +static jboolean android_location_GnssAntennaInfoProvider_is_antenna_info_supported(JNIEnv* env, + jclass clazz) { + if (gnssAntennaInfoIface != nullptr) { + return JNI_TRUE; + } + return JNI_FALSE; +} + +static jboolean android_location_GnssAntennaInfoProvider_start_antenna_info_listening( + JNIEnv* /* env */, jobject /* obj */) { + if (gnssAntennaInfoIface == nullptr) { + ALOGE("%s: IGnssAntennaInfo interface not available.", __func__); + return JNI_FALSE; + } + + sp<GnssAntennaInfoCallback> cbIface = new GnssAntennaInfoCallback(); + + auto result = gnssAntennaInfoIface->setCallback(cbIface); + + if (!checkHidlReturn(result, "IGnssAntennaInfo setCallback() failed.")) { + return JNI_FALSE; + } + + IGnssAntennaInfo::GnssAntennaInfoStatus initRet = result; + if (initRet != IGnssAntennaInfo::GnssAntennaInfoStatus::SUCCESS) { + ALOGE("An error has been found on GnssAntennaInfoInterface::init, status=%d", + static_cast<int32_t>(initRet)); + return JNI_FALSE; + } else { + ALOGD("gnss antenna info has been enabled"); + } + + return JNI_TRUE; +} + +static jboolean android_location_GnssAntennaInfoProvider_stop_antenna_info_listening( + JNIEnv* /* env */, jobject /* obj */) { + if (gnssAntennaInfoIface == nullptr) { + ALOGE("%s: IGnssAntennaInfo interface not available.", __func__); + return JNI_FALSE; + } + + auto result = gnssAntennaInfoIface->close(); + return checkHidlReturn(result, "IGnssAntennaInfo close() failed."); +} + static jboolean android_location_GnssMeasurementsProvider_is_measurement_supported( JNIEnv* env, jclass clazz) { if (gnssMeasurementIface != nullptr) { @@ -3286,6 +3586,19 @@ static const JNINativeMethod sMethodsBatching[] = { reinterpret_cast<void *>(android_location_GnssBatchingProvider_cleanup_batching)}, }; +static const JNINativeMethod sAntennaInfoMethods[] = { + /* name, signature, funcPtr */ + {"native_is_antenna_info_supported", "()Z", + reinterpret_cast<void*>( + android_location_GnssAntennaInfoProvider_is_antenna_info_supported)}, + {"native_start_antenna_info_listening", "()Z", + reinterpret_cast<void*>( + android_location_GnssAntennaInfoProvider_start_antenna_info_listening)}, + {"native_stop_antenna_info_listening", "()Z", + reinterpret_cast<void*>( + android_location_GnssAntennaInfoProvider_stop_antenna_info_listening)}, +}; + static const JNINativeMethod sGeofenceMethods[] = { /* name, signature, funcPtr */ {"native_is_geofence_supported", @@ -3407,6 +3720,8 @@ static const JNINativeMethod sVisibilityControlMethods[] = { }; int register_android_server_location_GnssLocationProvider(JNIEnv* env) { + jniRegisterNativeMethods(env, "com/android/server/location/GnssAntennaInfoProvider", + sAntennaInfoMethods, NELEM(sAntennaInfoMethods)); jniRegisterNativeMethods( env, "com/android/server/location/GnssBatchingProvider", diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp new file mode 100644 index 000000000000..d803c1dc9397 --- /dev/null +++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_ADB +#define LOG_TAG "PackageManagerShellCommandDataLoader-jni" +#include <android-base/logging.h> + +#include <android-base/unique_fd.h> +#include <nativehelper/JNIHelp.h> + +#include <core_jni_helpers.h> + +#include "dataloader.h" + +#include <chrono> +#include <thread> + +namespace android { + +namespace { + +using android::base::unique_fd; + +static constexpr int BUFFER_SIZE = 256 * 1024; +static constexpr int BLOCKS_COUNT = BUFFER_SIZE / INCFS_DATA_FILE_BLOCK_SIZE; + +struct JniIds { + jclass packageManagerShellCommandDataLoader; + jmethodID pmscdLookupShellCommand; + jmethodID pmscdGetStdInPFD; + jmethodID pmscdGetLocalFile; + + jmethodID parcelFileDescriptorGetFileDescriptor; + + jclass ioUtils; + jmethodID ioUtilsCloseQuietly; + + JniIds(JNIEnv* env) { + packageManagerShellCommandDataLoader = (jclass)env->NewGlobalRef( + FindClassOrDie(env, "com/android/server/pm/PackageManagerShellCommandDataLoader")); + pmscdLookupShellCommand = + GetStaticMethodIDOrDie(env, packageManagerShellCommandDataLoader, + "lookupShellCommand", + "(Ljava/lang/String;)Landroid/os/ShellCommand;"); + pmscdGetStdInPFD = + GetStaticMethodIDOrDie(env, packageManagerShellCommandDataLoader, "getStdInPFD", + "(Landroid/os/ShellCommand;)Landroid/os/" + "ParcelFileDescriptor;"); + pmscdGetLocalFile = + GetStaticMethodIDOrDie(env, packageManagerShellCommandDataLoader, "getLocalFile", + "(Landroid/os/ShellCommand;Ljava/lang/String;)Landroid/os/" + "ParcelFileDescriptor;"); + + auto parcelFileDescriptor = FindClassOrDie(env, "android/os/ParcelFileDescriptor"); + parcelFileDescriptorGetFileDescriptor = + GetMethodIDOrDie(env, parcelFileDescriptor, "getFileDescriptor", + "()Ljava/io/FileDescriptor;"); + + ioUtils = (jclass)env->NewGlobalRef(FindClassOrDie(env, "libcore/io/IoUtils")); + ioUtilsCloseQuietly = GetStaticMethodIDOrDie(env, ioUtils, "closeQuietly", + "(Ljava/lang/AutoCloseable;)V"); + } +}; + +const JniIds& jniIds(JNIEnv* env) { + static const JniIds ids(env); + return ids; +} + +static inline unique_fd convertPfdToFdAndDup(JNIEnv* env, const JniIds& jni, jobject pfd) { + if (!pfd) { + ALOGE("Missing In ParcelFileDescriptor."); + return {}; + } + auto managedFd = env->CallObjectMethod(pfd, jni.parcelFileDescriptorGetFileDescriptor); + if (!pfd) { + ALOGE("Missing In FileDescriptor."); + return {}; + } + return unique_fd{dup(jniGetFDFromFileDescriptor(env, managedFd))}; +} + +static inline std::pair<unique_fd, bool> openIncomingFile(JNIEnv* env, const JniIds& jni, + jobject shellCommand, + IncFsSpan metadata) { + jobject pfd = nullptr; + const bool stdin = (metadata.size == 0 || *metadata.data == '-'); + if (stdin) { + // stdin + pfd = env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader, + jni.pmscdGetStdInPFD, shellCommand); + } else { + // file + const std::string filePath(metadata.data, metadata.size); + pfd = env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader, + jni.pmscdGetLocalFile, shellCommand, + env->NewStringUTF(filePath.c_str())); + } + + auto result = convertPfdToFdAndDup(env, jni, pfd); + if (pfd) { + // Can be closed after dup. + env->CallStaticVoidMethod(jni.ioUtils, jni.ioUtilsCloseQuietly, pfd); + } + + return {std::move(result), stdin}; +} + +static inline JNIEnv* GetJNIEnvironment(JavaVM* vm) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + return 0; + } + return env; +} + +static inline JNIEnv* GetOrAttachJNIEnvironment(JavaVM* jvm) { + JNIEnv* env = GetJNIEnvironment(jvm); + if (!env) { + int result = jvm->AttachCurrentThread(&env, nullptr); + CHECK_EQ(result, JNI_OK) << "thread attach failed"; + struct VmDetacher { + VmDetacher(JavaVM* vm) : mVm(vm) {} + ~VmDetacher() { mVm->DetachCurrentThread(); } + + private: + JavaVM* const mVm; + }; + static thread_local VmDetacher detacher(jvm); + } + return env; +} + +class PackageManagerShellCommandDataLoaderDataLoader : public android::dataloader::DataLoader { +public: + PackageManagerShellCommandDataLoaderDataLoader(JavaVM* jvm) : mJvm(jvm) { CHECK(mJvm); } + +private: + // Lifecycle. + bool onCreate(const android::dataloader::DataLoaderParams& params, + android::dataloader::FilesystemConnectorPtr ifs, + android::dataloader::StatusListenerPtr statusListener, + android::dataloader::ServiceConnectorPtr, + android::dataloader::ServiceParamsPtr) final { + mArgs = params.arguments(); + mIfs = ifs; + return true; + } + bool onStart() final { return true; } + void onStop() final {} + void onDestroy() final {} + + // IFS callbacks. + void onPendingReads(const dataloader::PendingReads& pendingReads) final {} + void onPageReads(const dataloader::PageReads& pageReads) final {} + + // FS callbacks. + bool onPrepareImage(const dataloader::DataLoaderInstallationFiles& addedFiles) final { + JNIEnv* env = GetOrAttachJNIEnvironment(mJvm); + const auto& jni = jniIds(env); + + jobject shellCommand = env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader, + jni.pmscdLookupShellCommand, + env->NewStringUTF(mArgs.c_str())); + if (!shellCommand) { + ALOGE("Missing shell command."); + return false; + } + + std::vector<char> buffer; + buffer.reserve(BUFFER_SIZE); + + std::vector<IncFsDataBlock> blocks; + blocks.reserve(BLOCKS_COUNT); + + for (auto&& file : addedFiles) { + auto [incomingFd, stdin] = openIncomingFile(env, jni, shellCommand, file.metadata); + if (incomingFd < 0) { + ALOGE("Failed to open an IncFS file for metadata: %.*s, final file name is: %s. " + "Error %d", + int(file.metadata.size), file.metadata.data, file.name, errno); + return false; + } + + const auto fileId = IncFs_FileIdFromMetadata(file.metadata); + + const auto incfsFd(mIfs->openWrite(fileId)); + if (incfsFd < 0) { + ALOGE("Failed to open an IncFS file for metadata: %.*s, final file name is: %s. " + "Error %d", + int(file.metadata.size), file.metadata.data, file.name, errno); + return false; + } + + IncFsSize size = file.size; + IncFsSize remaining = size; + IncFsSize totalSize = 0; + IncFsBlockIndex blockIdx = 0; + while (remaining > 0) { + constexpr auto capacity = BUFFER_SIZE; + auto size = buffer.size(); + if (capacity - size < INCFS_DATA_FILE_BLOCK_SIZE) { + if (!flashToIncFs(incfsFd, false, &blocks, &blockIdx, &buffer)) { + return false; + } + continue; + } + + auto toRead = std::min<IncFsSize>(remaining, capacity - size); + buffer.resize(size + toRead); + auto read = ::read(incomingFd, buffer.data() + size, toRead); + if (read == 0) { + if (stdin) { + // eof of stdin, waiting... + ALOGE("eof of stdin, waiting...: %d, remaining: %d, block: %d, read: %d", + int(totalSize), int(remaining), int(blockIdx), int(read)); + using namespace std::chrono_literals; + std::this_thread::sleep_for(10ms); + continue; + } + break; + } + if (read < 0) { + ALOGE("Underlying file read error: %.*s: %d", int(file.metadata.size), + file.metadata.data, int(read)); + return false; + } + + buffer.resize(size + read); + remaining -= read; + totalSize += read; + } + if (!buffer.empty() && !flashToIncFs(incfsFd, true, &blocks, &blockIdx, &buffer)) { + return false; + } + } + + ALOGE("All done."); + return true; + } + + bool flashToIncFs(int incfsFd, bool eof, std::vector<IncFsDataBlock>* blocks, + IncFsBlockIndex* blockIdx, std::vector<char>* buffer) { + int consumed = 0; + const auto fullBlocks = buffer->size() / INCFS_DATA_FILE_BLOCK_SIZE; + for (int i = 0; i < fullBlocks; ++i) { + const auto inst = IncFsDataBlock{ + .fileFd = incfsFd, + .pageIndex = (*blockIdx)++, + .compression = INCFS_COMPRESSION_KIND_NONE, + .kind = INCFS_BLOCK_KIND_DATA, + .dataSize = INCFS_DATA_FILE_BLOCK_SIZE, + .data = buffer->data() + consumed, + }; + blocks->push_back(inst); + consumed += INCFS_DATA_FILE_BLOCK_SIZE; + } + const auto remain = buffer->size() - fullBlocks * INCFS_DATA_FILE_BLOCK_SIZE; + if (remain && eof) { + const auto inst = IncFsDataBlock{ + .fileFd = incfsFd, + .pageIndex = (*blockIdx)++, + .compression = INCFS_COMPRESSION_KIND_NONE, + .kind = INCFS_BLOCK_KIND_DATA, + .dataSize = static_cast<uint16_t>(remain), + .data = buffer->data() + consumed, + }; + blocks->push_back(inst); + consumed += remain; + } + + auto res = mIfs->writeBlocks({blocks->data(), blocks->data() + blocks->size()}); + + blocks->clear(); + buffer->erase(buffer->begin(), buffer->begin() + consumed); + + if (res < 0) { + ALOGE("Failed to write block to IncFS: %d", int(res)); + return false; + } + return true; + } + + JavaVM* const mJvm; + std::string mArgs; + android::dataloader::FilesystemConnectorPtr mIfs; +}; + +static void nativeInitialize(JNIEnv* env, jclass klass) { + jniIds(env); +} + +static const JNINativeMethod method_table[] = { + {"nativeInitialize", "()V", (void*)nativeInitialize}, +}; + +} // namespace + +int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader( + JNIEnv* env) { + android::dataloader::DataLoader::initialize( + [](auto jvm, const auto& params) -> android::dataloader::DataLoaderPtr { + if (params.type() == DATA_LOADER_TYPE_INCREMENTAL) { + // This DataLoader only supports incremental installations. + return std::make_unique<PackageManagerShellCommandDataLoaderDataLoader>(jvm); + } + return {}; + }); + return jniRegisterNativeMethods(env, + "com/android/server/pm/PackageManagerShellCommandDataLoader", + method_table, NELEM(method_table)); +} + +} // namespace android diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index c1864945f921..e57543203634 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -59,6 +59,7 @@ int register_android_server_am_LowMemDetector(JNIEnv* env); int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl( JNIEnv* env); int register_android_server_incremental_IncrementalManagerService(JNIEnv* env); +int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(JNIEnv* env); int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env); }; @@ -112,6 +113,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl( env); register_android_server_incremental_IncrementalManagerService(env); + register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(env); register_android_server_stats_pull_StatsPullAtomService(env); return JNI_VERSION_1_4; } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index cb687c9144c6..f700d7164d2a 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -490,6 +490,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { SYSTEM_SETTINGS_WHITELIST = new ArraySet<>(); SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_BRIGHTNESS); + SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_BRIGHTNESS_FLOAT); SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_BRIGHTNESS_MODE); SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_OFF_TIMEOUT); diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp index b2c316a25e7f..3fcb57a83cf5 100644 --- a/services/incremental/BinderIncrementalService.cpp +++ b/services/incremental/BinderIncrementalService.cpp @@ -117,10 +117,9 @@ binder::Status BinderIncrementalService::openStorage(const std::string& path, binder::Status BinderIncrementalService::createStorage(const std::string& path, const DataLoaderParamsParcel& params, + const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& listener, int32_t createMode, int32_t* _aidl_return) { - *_aidl_return = mImpl.createStorage(path, const_cast<DataLoaderParamsParcel&&>(params), - android::incremental::IncrementalService::CreateOptions( - createMode)); + *_aidl_return = mImpl.createStorage(path, const_cast<DataLoaderParamsParcel&&>(params), listener, android::incremental::IncrementalService::CreateOptions(createMode)); return ok(); } diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h index 51d7de3d9adf..4075da6d8159 100644 --- a/services/incremental/BinderIncrementalService.h +++ b/services/incremental/BinderIncrementalService.h @@ -38,9 +38,7 @@ public: void onInvalidStorage(int mountId); binder::Status openStorage(const std::string& path, int32_t* _aidl_return) final; - binder::Status createStorage(const std::string& path, - const ::android::content::pm::DataLoaderParamsParcel& params, - int32_t createMode, int32_t* _aidl_return) final; + binder::Status createStorage(const ::std::string& path, const ::android::content::pm::DataLoaderParamsParcel& params, const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& listener, int32_t createMode, int32_t* _aidl_return) final; binder::Status createLinkedStorage(const std::string& path, int32_t otherStorageId, int32_t createMode, int32_t* _aidl_return) final; binder::Status makeBindMount(int32_t storageId, const std::string& sourcePath, diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index 8012e74a6b3c..42dc1bacdadb 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -30,7 +30,6 @@ #include <binder/BinderService.h> #include <binder/ParcelFileDescriptor.h> #include <binder/Status.h> -#include <openssl/sha.h> #include <sys/stat.h> #include <uuid/uuid.h> #include <zlib.h> @@ -242,20 +241,7 @@ IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_v } FileId IncrementalService::idFromMetadata(std::span<const uint8_t> metadata) { - incfs::FileId id = {}; - if (size_t(metadata.size()) <= sizeof(id)) { - memcpy(&id, metadata.data(), metadata.size()); - } else { - uint8_t buffer[SHA_DIGEST_LENGTH]; - static_assert(sizeof(buffer) >= sizeof(id)); - - SHA_CTX ctx; - SHA1_Init(&ctx); - SHA1_Update(&ctx, metadata.data(), metadata.size()); - SHA1_Final(buffer, &ctx); - memcpy(&id, buffer, sizeof(id)); - } - return id; + return IncFs_FileIdFromMetadata({(const char*)metadata.data(), metadata.size()}); } IncrementalService::~IncrementalService() = default; @@ -344,7 +330,7 @@ std::optional<std::future<void>> IncrementalService::onSystemReady() { std::thread([this, mounts = std::move(mounts)]() { std::vector<IfsMountPtr> failedLoaderMounts; for (auto&& ifs : mounts) { - if (prepareDataLoader(*ifs, nullptr)) { + if (prepareDataLoader(*ifs)) { LOG(INFO) << "Successfully started data loader for mount " << ifs->mountId; } else { LOG(WARNING) << "Failed to start data loader for mount " << ifs->mountId; @@ -377,6 +363,7 @@ auto IncrementalService::getStorageSlotLocked() -> MountMap::iterator { StorageId IncrementalService::createStorage(std::string_view mountPoint, DataLoaderParamsParcel&& dataLoaderParams, + const DataLoaderStatusListener& dataLoaderStatusListener, CreateOptions options) { LOG(INFO) << "createStorage: " << mountPoint << " | " << int(options); if (!path::isAbsolute(mountPoint)) { @@ -509,7 +496,7 @@ StorageId IncrementalService::createStorage(std::string_view mountPoint, // Done here as well, all data structures are in good state. secondCleanupOnFailure.release(); - if (!prepareDataLoader(*ifs, &dataLoaderParams)) { + if (!prepareDataLoader(*ifs, &dataLoaderParams, &dataLoaderStatusListener)) { LOG(ERROR) << "prepareDataLoader() failed"; deleteStorageLocked(*ifs, std::move(l)); return kInvalidStorageId; @@ -767,7 +754,6 @@ int IncrementalService::makeFile(StorageId storage, std::string_view path, int m if (params.metadata.data && params.metadata.size > 0) { metadataBytes.assign(params.metadata.data, params.metadata.data + params.metadata.size); } - mIncrementalManager->newFileForDataLoader(ifs->mountId, id, metadataBytes); return 0; } return -EINVAL; @@ -1074,7 +1060,8 @@ bool IncrementalService::mountExistingImage(std::string_view root, std::string_v } bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs, - DataLoaderParamsParcel* params) { + DataLoaderParamsParcel* params, + const DataLoaderStatusListener* externalListener) { if (!mSystemReady.load(std::memory_order_relaxed)) { std::unique_lock l(ifs.lock); if (params) { @@ -1117,7 +1104,7 @@ bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs, fsControlParcel.incremental->pendingReads.reset( base::unique_fd(::dup(ifs.control.pendingReads))); fsControlParcel.incremental->log.reset(base::unique_fd(::dup(ifs.control.logs))); - sp<IncrementalDataLoaderListener> listener = new IncrementalDataLoaderListener(*this); + sp<IncrementalDataLoaderListener> listener = new IncrementalDataLoaderListener(*this, *externalListener); bool created = false; auto status = mIncrementalManager->prepareDataLoader(ifs.mountId, fsControlParcel, *dlp, listener, &created); @@ -1247,6 +1234,11 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_ binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChanged(MountId mountId, int newStatus) { + if (externalListener) { + // Give an external listener a chance to act before we destroy something. + externalListener->onStatusChanged(mountId, newStatus); + } + std::unique_lock l(incrementalService.mLock); const auto& ifs = incrementalService.getIfsLocked(mountId); if (!ifs) { @@ -1288,6 +1280,12 @@ binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChange case IDataLoaderStatusListener::DATA_LOADER_STOPPED: { break; } + case IDataLoaderStatusListener::DATA_LOADER_IMAGE_READY: { + break; + } + case IDataLoaderStatusListener::DATA_LOADER_IMAGE_NOT_READY: { + break; + } default: { LOG(WARNING) << "Unknown data loader status: " << newStatus << " for mount: " << mountId; diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h index 2e7ced37e91d..75d066b9444a 100644 --- a/services/incremental/IncrementalService.h +++ b/services/incremental/IncrementalService.h @@ -59,6 +59,8 @@ using Clock = std::chrono::steady_clock; using TimePoint = std::chrono::time_point<Clock>; using Seconds = std::chrono::seconds; +using DataLoaderStatusListener = ::android::sp<::android::content::pm::IDataLoaderStatusListener>; + class IncrementalService final { public: explicit IncrementalService(ServiceManagerWrapper&& sm, std::string_view rootDir); @@ -94,7 +96,9 @@ public: std::optional<std::future<void>> onSystemReady(); - StorageId createStorage(std::string_view mountPoint, DataLoaderParamsParcel&& dataLoaderParams, + StorageId createStorage(std::string_view mountPoint, + DataLoaderParamsParcel&& dataLoaderParams, + const DataLoaderStatusListener& dataLoaderStatusListener, CreateOptions options = CreateOptions::Default); StorageId createLinkedStorage(std::string_view mountPoint, StorageId linkedStorage, CreateOptions options = CreateOptions::Default); @@ -129,13 +133,14 @@ public: std::string_view libDirRelativePath, std::string_view abi); class IncrementalDataLoaderListener : public android::content::pm::BnDataLoaderStatusListener { public: - IncrementalDataLoaderListener(IncrementalService& incrementalService) - : incrementalService(incrementalService) {} + IncrementalDataLoaderListener(IncrementalService& incrementalService, DataLoaderStatusListener externalListener) + : incrementalService(incrementalService), externalListener(externalListener) {} // Callbacks interface binder::Status onStatusChanged(MountId mount, int newStatus) override; private: IncrementalService& incrementalService; + DataLoaderStatusListener externalListener; }; private: @@ -201,7 +206,7 @@ private: std::string&& source, std::string&& target, BindKind kind, std::unique_lock<std::mutex>& mainLock); - bool prepareDataLoader(IncFsMount& ifs, DataLoaderParamsParcel* params); + bool prepareDataLoader(IncFsMount& ifs, DataLoaderParamsParcel* params = nullptr, const DataLoaderStatusListener* externalListener = nullptr); BindPathMap::const_iterator findStorageLocked(std::string_view path) const; StorageId findStorageId(std::string_view path) const; diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h index f0b56729e8d7..642158322c7c 100644 --- a/services/incremental/ServiceWrappers.h +++ b/services/incremental/ServiceWrappers.h @@ -60,8 +60,6 @@ public: bool* _aidl_return) const = 0; virtual binder::Status startDataLoader(MountId mountId, bool* _aidl_return) const = 0; virtual binder::Status destroyDataLoader(MountId mountId) const = 0; - virtual binder::Status newFileForDataLoader(MountId mountId, FileId fileid, - const std::vector<uint8_t>& metadata) const = 0; virtual binder::Status showHealthBlockedUI(MountId mountId) const = 0; }; @@ -128,13 +126,6 @@ public: binder::Status destroyDataLoader(MountId mountId) const override { return mInterface->destroyDataLoader(mountId); } - binder::Status newFileForDataLoader(MountId mountId, FileId fileid, - const std::vector<uint8_t>& metadata) const override { - return mInterface->newFileForDataLoader(mountId, - {(const uint8_t*)fileid.data, - (const uint8_t*)fileid.data + sizeof(fileid.data)}, - metadata); - } binder::Status showHealthBlockedUI(MountId mountId) const override { return mInterface->showHealthBlockedUI(mountId); } diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp index 9cdc83e75055..f7598f769b2b 100644 --- a/services/incremental/test/IncrementalServiceTest.cpp +++ b/services/incremental/test/IncrementalServiceTest.cpp @@ -293,7 +293,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsFails) { EXPECT_CALL(*mIncrementalManager, prepareDataLoader(_, _, _, _, _)).Times(0); TemporaryDir tempDir; int storageId = - mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_LT(storageId, 0); } @@ -303,7 +303,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsInvalidControlParcel) EXPECT_CALL(*mIncrementalManager, prepareDataLoader(_, _, _, _, _)).Times(0); TemporaryDir tempDir; int storageId = - mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_LT(storageId, 0); } @@ -316,7 +316,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageMakeFileFails) { EXPECT_CALL(*mVold, unmountIncFs(_)); TemporaryDir tempDir; int storageId = - mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_LT(storageId, 0); } @@ -330,7 +330,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageBindMountFails) { EXPECT_CALL(*mVold, unmountIncFs(_)); TemporaryDir tempDir; int storageId = - mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_LT(storageId, 0); } @@ -344,7 +344,7 @@ TEST_F(IncrementalServiceTest, testCreateStoragePrepareDataLoaderFails) { EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); TemporaryDir tempDir; int storageId = - mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_LT(storageId, 0); } @@ -358,7 +358,7 @@ TEST_F(IncrementalServiceTest, testDeleteStorageSuccess) { EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); TemporaryDir tempDir; int storageId = - mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); mIncrementalService->deleteStorage(storageId); @@ -373,7 +373,7 @@ TEST_F(IncrementalServiceTest, testOnStatusNotReady) { EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); TemporaryDir tempDir; int storageId = - mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); mIncrementalManager->setDataLoaderStatusNotReady(); @@ -389,7 +389,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderSuccess) { EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); TemporaryDir tempDir; int storageId = - mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); mIncrementalManager->setDataLoaderStatusReady(); @@ -404,7 +404,7 @@ TEST_F(IncrementalServiceTest, testMakeDirectory) { mIncrementalManager->startDataLoaderSuccess(); TemporaryDir tempDir; int storageId = - mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); std::string dir_path("test"); @@ -428,7 +428,7 @@ TEST_F(IncrementalServiceTest, testMakeDirectories) { mIncrementalManager->startDataLoaderSuccess(); TemporaryDir tempDir; int storageId = - mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); auto first = "first"sv; auto second = "second"sv; diff --git a/services/robotests/src/com/android/server/location/GnssAntennaInfoProviderTest.java b/services/robotests/src/com/android/server/location/GnssAntennaInfoProviderTest.java new file mode 100644 index 000000000000..76f7ad646bb1 --- /dev/null +++ b/services/robotests/src/com/android/server/location/GnssAntennaInfoProviderTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.location; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.os.Handler; +import android.os.Looper; +import android.platform.test.annotations.Presubmit; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +/** + * Unit tests for {@link GnssAntennaInfoProvider}. + */ +@RunWith(RobolectricTestRunner.class) +@Presubmit +public class GnssAntennaInfoProviderTest { + @Mock + private GnssAntennaInfoProvider.GnssAntennaInfoProviderNative mMockNative; + private GnssAntennaInfoProvider mTestProvider; + + /** Setup. */ + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mMockNative.startAntennaInfoListening()).thenReturn(true); + when(mMockNative.stopAntennaInfoListening()).thenReturn(true); + + mTestProvider = new GnssAntennaInfoProvider(RuntimeEnvironment.application, + new Handler(Looper.myLooper()), mMockNative) { + @Override + public boolean isGpsEnabled() { + return true; + } + }; + } + + /** + * Test that registerWithService calls the native startAntennaInfoListening method. + */ + @Test + public void register_nativeStarted() { + mTestProvider.registerWithService(); + verify(mMockNative, times(1)).startAntennaInfoListening(); + } + + /** + * Test that unregisterFromService calls the native stopAntennaInfoListening method. + */ + @Test + public void unregister_nativeStopped() { + mTestProvider.registerWithService(); + mTestProvider.unregisterFromService(); + verify(mMockNative, times(1)).stopAntennaInfoListening(); + } + + /** + * Test that GnssAntennaInfoProvider.isAntennaInfoSupported() returns the result of the + * native isAntennaInfoSupported method. + */ + @Test + public void isSupported_nativeIsSupported() { + when(mMockNative.isAntennaInfoSupported()).thenReturn(true); + assertThat(mTestProvider.isAvailableInPlatform()).isTrue(); + + when(mMockNative.isAntennaInfoSupported()).thenReturn(false); + assertThat(mTestProvider.isAvailableInPlatform()).isFalse(); + } +} diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java index 1a7b1d3f6039..6190802d033d 100644 --- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java +++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java @@ -55,6 +55,8 @@ import android.platform.test.annotations.Presubmit; import androidx.test.core.app.ApplicationProvider; +import com.android.internal.util.FunctionalUtils.ThrowingRunnable; +import com.android.internal.util.FunctionalUtils.ThrowingSupplier; import com.android.server.LocalServices; import com.android.server.testing.shadows.ShadowApplicationPackageManager; import com.android.server.testing.shadows.ShadowUserManager; @@ -190,6 +192,8 @@ public class CrossProfileAppsServiceImplRoboTest { public void grantPermissions() { grantPermissions( Manifest.permission.MANAGE_APP_OPS_MODES, + Manifest.permission.UPDATE_APP_OPS_STATS, + Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, Manifest.permission.INTERACT_ACROSS_USERS, Manifest.permission.INTERACT_ACROSS_USERS_FULL); } @@ -213,9 +217,26 @@ public class CrossProfileAppsServiceImplRoboTest { } @Test + public void setInteractAcrossProfilesAppOp_noPermissions_throwsSecurityException() { + denyPermissions( + Manifest.permission.MANAGE_APP_OPS_MODES, + Manifest.permission.UPDATE_APP_OPS_STATS, + Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, + Manifest.permission.INTERACT_ACROSS_USERS, + Manifest.permission.INTERACT_ACROSS_USERS_FULL); + try { + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + fail(); + } catch (SecurityException expected) {} + } + + @Test public void setInteractAcrossProfilesAppOp_missingInteractAcrossUsersAndFull_throwsSecurityException() { - denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS); - denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL); + denyPermissions( + Manifest.permission.INTERACT_ACROSS_USERS, + Manifest.permission.INTERACT_ACROSS_USERS_FULL); + grantPermissions(Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES); try { mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); @@ -231,8 +252,38 @@ public class CrossProfileAppsServiceImplRoboTest { } @Test + public void setInteractAcrossProfilesAppOp_configureInteractAcrossProfilesPermissionWithoutAppOpsPermissions_setsAppOp() { + denyPermissions( + Manifest.permission.MANAGE_APP_OPS_MODES, + Manifest.permission.UPDATE_APP_OPS_STATS); + grantPermissions( + Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, + Manifest.permission.INTERACT_ACROSS_USERS); + + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + + assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); + } + + @Test + public void setInteractAcrossProfilesAppOp_appOpsPermissionsWithoutConfigureInteractAcrossProfilesPermission_setsAppOp() { + denyPermissions(Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES); + grantPermissions( + Manifest.permission.MANAGE_APP_OPS_MODES, + Manifest.permission.UPDATE_APP_OPS_STATS, + Manifest.permission.INTERACT_ACROSS_USERS); + + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + + assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); + } + + @Test public void setInteractAcrossProfilesAppOp_setsAppOpWithUsersAndWithoutFull() { denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL); + grantPermissions(Manifest.permission.INTERACT_ACROSS_USERS); mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); @@ -241,6 +292,7 @@ public class CrossProfileAppsServiceImplRoboTest { @Test public void setInteractAcrossProfilesAppOp_setsAppOpWithFullAndWithoutUsers() { denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS); + grantPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL); mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); @@ -479,6 +531,16 @@ public class CrossProfileAppsServiceImplRoboTest { public void restoreCallingIdentity(long token) {} @Override + public void withCleanCallingIdentity(ThrowingRunnable action) { + action.run(); + } + + @Override + public <T> T withCleanCallingIdentity(ThrowingSupplier<T> action) { + return action.get(); + } + + @Override public UserManager getUserManager() { return mUserManager; } diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index 339ff6b8b526..ff34ebd8aa9d 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -24,6 +24,7 @@ android_test { "service-permission", "service-blobstore", "androidx.test.runner", + "androidx.test.ext.truth", "mockito-target-extended-minus-junit4", "platform-test-annotations", "truth-prebuilt", diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java index e0e374b2a83a..00495f3ec02d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java @@ -683,8 +683,6 @@ public class JobSchedulerServiceTest { mService.new MaybeReadyJobQueueFunctor(); mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT = 5; mService.mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = HOUR_IN_MILLIS; - mService.mConstants.MIN_CONNECTIVITY_COUNT = 2; - mService.mConstants.MIN_READY_JOBS_COUNT = 1; JobStatus job = createJobStatus( "testRareJobBatching", diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java new file mode 100644 index 000000000000..f2246dac01ca --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.location; + +import static androidx.test.ext.truth.location.LocationSubject.assertThat; + +import static com.android.server.location.LocationUtils.createLocation; + +import static com.google.common.truth.Truth.assertThat; + +import android.location.Location; +import android.platform.test.annotations.Presubmit; +import android.util.Log; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Random; + +@Presubmit +@SmallTest +@RunWith(AndroidJUnit4.class) +public class LocationFudgerTest { + + private static final String TAG = "LocationFudgerTest"; + + private static final double APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR = 111_000; + private static final float ACCURACY_M = 2000; + private static final float MAX_COARSE_FUDGE_DISTANCE_M = + (float) Math.sqrt(2 * ACCURACY_M * ACCURACY_M); + + private Random mRandom; + + private LocationFudger mFudger; + + @Before + public void setUp() { + long seed = System.currentTimeMillis(); + Log.i(TAG, "location random seed: " + seed); + + mRandom = new Random(seed); + mFudger = new LocationFudger( + ACCURACY_M, + Clock.fixed(Instant.ofEpochMilli(0), ZoneId.systemDefault()), + mRandom); + } + + @Test + public void testCoarsen() { + // test that the coarsened location is not the same as the fine location and no leaks + for (int i = 0; i < 100; i++) { + Location fine = createLocation("test", mRandom); + fine.setBearing(1); + fine.setSpeed(1); + fine.setAltitude(1); + + Location coarse = mFudger.createCoarse(fine); + + assertThat(coarse).isNotNull(); + assertThat(coarse).isNotSameAs(fine); + assertThat(coarse.hasBearing()).isFalse(); + assertThat(coarse.hasSpeed()).isFalse(); + assertThat(coarse.hasAltitude()).isFalse(); + assertThat(coarse.getAccuracy()).isEqualTo(ACCURACY_M); + assertThat(coarse.distanceTo(fine)).isGreaterThan(1F); + assertThat(coarse).isNearby(fine, MAX_COARSE_FUDGE_DISTANCE_M); + } + } + + @Test + public void testCoarsen_Consistent() { + // test that coarsening the same location will always return the same coarse location + // (and thus that averaging to eliminate random noise won't work) + for (int i = 0; i < 100; i++) { + Location fine = createLocation("test", mRandom); + Location coarse = mFudger.createCoarse(fine); + assertThat(mFudger.createCoarse(new Location(fine))).isEqualTo(coarse); + assertThat(mFudger.createCoarse(new Location(fine))).isEqualTo(coarse); + } + } + + @Test + public void testCoarsen_AvgMany() { + // test that a set of locations normally distributed around the user's real location still + // cannot be easily average to reveal the user's real location + + int passed = 0; + int iterations = 100; + for (int j = 0; j < iterations; j++) { + Location fine = createLocation("test", mRandom); + + // generate a point cloud around a single location + ArrayList<Location> finePoints = new ArrayList<>(100); + for (int i = 0; i < 100; i++) { + finePoints.add(step(fine, mRandom.nextGaussian() * ACCURACY_M)); + } + + // generate the coarsened version of that point cloud + ArrayList<Location> coarsePoints = new ArrayList<>(100); + for (int i = 0; i < 100; i++) { + coarsePoints.add(mFudger.createCoarse(finePoints.get(i))); + } + + double avgFineLatitude = finePoints.stream().mapToDouble( + Location::getLatitude).average() + .orElseThrow(IllegalStateException::new); + double avgFineLongitude = finePoints.stream().mapToDouble( + Location::getLongitude).average() + .orElseThrow(IllegalStateException::new); + Location fineAvg = createLocation("test", avgFineLatitude, avgFineLongitude, 0); + + double avgCoarseLatitude = coarsePoints.stream().mapToDouble( + Location::getLatitude).average() + .orElseThrow(IllegalStateException::new); + double avgCoarseLongitude = coarsePoints.stream().mapToDouble( + Location::getLongitude).average() + .orElseThrow(IllegalStateException::new); + Location coarseAvg = createLocation("test", avgCoarseLatitude, avgCoarseLongitude, 0); + + if (coarseAvg.distanceTo(fine) > fineAvg.distanceTo(fine)) { + passed++; + } + } + + // very generally speaking, the closer the initial fine point is to a grid point, the more + // accurate the coarsened average will be. we use 70% as a lower bound by -very- roughly + // taking the area within a grid where we expect a reasonable percentage of points generated + // by step() to fall in another grid square. this likely doesn't have much mathematical + // validity, but it serves as a sanity test as least. + assertThat(passed / (double) iterations).isGreaterThan(.70); + } + + // step in a random direction by distance - assume cartesian + private Location step(Location input, double distanceM) { + double radians = mRandom.nextDouble() * 2 * Math.PI; + double deltaXM = Math.cos(radians) * distanceM; + double deltaYM = Math.sin(radians) * distanceM; + return createLocation("test", + input.getLatitude() + deltaXM / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR, + input.getLongitude() + deltaYM / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR, + 0); + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationUtils.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationUtils.java new file mode 100644 index 000000000000..decb3a6e334a --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationUtils.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.location; + +import android.location.Location; +import android.os.SystemClock; + +import java.util.Random; + +public final class LocationUtils { + + private static final double MIN_LATITUDE = -90D; + private static final double MAX_LATITUDE = 90D; + private static final double MIN_LONGITUDE = -180D; + private static final double MAX_LONGITUDE = 180D; + + private static final float MIN_ACCURACY = 1; + private static final float MAX_ACCURACY = 100; + + public static Location createLocation(String provider, Random random) { + return createLocation(provider, + MIN_LATITUDE + random.nextDouble() * (MAX_LATITUDE - MIN_LATITUDE), + MIN_LONGITUDE + random.nextDouble() * (MAX_LONGITUDE - MIN_LONGITUDE), + MIN_ACCURACY + random.nextFloat() * (MAX_ACCURACY - MIN_ACCURACY)); + } + + public static Location createLocation(String provider, double latitude, double longitude, + float accuracy) { + Location location = new Location(provider); + location.setLatitude(latitude); + location.setLongitude(longitude); + location.setAccuracy(accuracy); + location.setTime(System.currentTimeMillis()); + location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); + return location; + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/location/UserInfoHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/UserInfoHelperTest.java index 389fdf9b0abc..71e79b331cae 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/UserInfoHelperTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/UserInfoHelperTest.java @@ -26,6 +26,7 @@ import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.app.ActivityManager; @@ -43,6 +44,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.dx.mockito.inline.extended.StaticMockitoSession; +import com.android.server.location.UserInfoHelper.UserListener; import org.junit.After; import org.junit.Before; @@ -117,62 +119,88 @@ public class UserInfoHelperTest { } } + private void startUser(int userId) { + Intent intent = new Intent(Intent.ACTION_USER_STARTED).putExtra(Intent.EXTRA_USER_HANDLE, + userId); + for (BroadcastReceiver broadcastReceiver : mBroadcastReceivers) { + broadcastReceiver.onReceive(mContext, intent); + } + } + + private void stopUser(int userId) { + Intent intent = new Intent(Intent.ACTION_USER_STOPPED).putExtra(Intent.EXTRA_USER_HANDLE, + userId); + for (BroadcastReceiver broadcastReceiver : mBroadcastReceivers) { + broadcastReceiver.onReceive(mContext, intent); + } + } + @Test - public void testListeners() { - UserInfoHelper.UserChangedListener listener = mock( - UserInfoHelper.UserChangedListener.class); + public void testListener_SwitchUser() { + UserListener listener = mock(UserListener.class); mHelper.addListener(listener); switchUser(USER1_ID); verify(listener, never()).onUserChanged(anyInt(), anyInt()); switchUser(USER2_ID); - verify(listener).onUserChanged(USER1_ID, USER2_ID); + verify(listener, times(1)).onUserChanged(USER1_ID, UserListener.USER_SWITCHED); + verify(listener, times(1)).onUserChanged(USER2_ID, UserListener.USER_SWITCHED); switchUser(USER1_ID); - verify(listener).onUserChanged(USER2_ID, USER1_ID); + verify(listener, times(2)).onUserChanged(USER1_ID, UserListener.USER_SWITCHED); + verify(listener, times(2)).onUserChanged(USER2_ID, UserListener.USER_SWITCHED); } @Test - public void testCurrentUser() { - assertThat(mHelper.getCurrentUserId()).isEqualTo(USER1_ID); + public void testListener_StartUser() { + UserListener listener = mock(UserListener.class); + mHelper.addListener(listener); - switchUser(USER2_ID); + startUser(USER1_ID); + verify(listener).onUserChanged(USER1_ID, UserListener.USER_STARTED); - assertThat(mHelper.getCurrentUserId()).isEqualTo(USER2_ID); + startUser(USER2_ID); + verify(listener).onUserChanged(USER2_ID, UserListener.USER_STARTED); + } - switchUser(USER1_ID); + @Test + public void testListener_StopUser() { + UserListener listener = mock(UserListener.class); + mHelper.addListener(listener); + + stopUser(USER1_ID); + verify(listener).onUserChanged(USER1_ID, UserListener.USER_STOPPED); - assertThat(mHelper.getCurrentUserId()).isEqualTo(USER1_ID); + stopUser(USER2_ID); + verify(listener).onUserChanged(USER2_ID, UserListener.USER_STOPPED); } @Test - public void testIsCurrentUserOrProfile() { - assertThat(mHelper.isCurrentUserOrProfile(USER1_ID)).isTrue(); - assertThat(mHelper.isCurrentUserOrProfile(USER1_MANAGED_ID)).isTrue(); - assertThat(mHelper.isCurrentUserOrProfile(USER2_ID)).isFalse(); - assertThat(mHelper.isCurrentUserOrProfile(USER2_MANAGED_ID)).isFalse(); + public void testCurrentUserIds() { + assertThat(mHelper.getCurrentUserIds()).isEqualTo(USER1_PROFILES); switchUser(USER2_ID); - assertThat(mHelper.isCurrentUserOrProfile(USER1_ID)).isFalse(); - assertThat(mHelper.isCurrentUserOrProfile(USER2_ID)).isTrue(); - assertThat(mHelper.isCurrentUserOrProfile(USER1_MANAGED_ID)).isFalse(); - assertThat(mHelper.isCurrentUserOrProfile(USER2_MANAGED_ID)).isTrue(); + assertThat(mHelper.getCurrentUserIds()).isEqualTo(USER2_PROFILES); + + switchUser(USER1_ID); + + assertThat(mHelper.getCurrentUserIds()).isEqualTo(USER1_PROFILES); } @Test - public void testGetParentUserId() { - assertThat(mHelper.getParentUserId(USER1_ID)).isEqualTo(USER1_ID); - assertThat(mHelper.getParentUserId(USER1_MANAGED_ID)).isEqualTo(USER1_ID); - assertThat(mHelper.getParentUserId(USER2_ID)).isEqualTo(USER2_ID); - assertThat(mHelper.getParentUserId(USER2_MANAGED_ID)).isEqualTo(USER2_ID); + public void testIsCurrentUserId() { + assertThat(mHelper.isCurrentUserId(USER1_ID)).isTrue(); + assertThat(mHelper.isCurrentUserId(USER1_MANAGED_ID)).isTrue(); + assertThat(mHelper.isCurrentUserId(USER2_ID)).isFalse(); + assertThat(mHelper.isCurrentUserId(USER2_MANAGED_ID)).isFalse(); switchUser(USER2_ID); - assertThat(mHelper.getParentUserId(USER1_ID)).isEqualTo(USER1_ID); - assertThat(mHelper.getParentUserId(USER2_ID)).isEqualTo(USER2_ID); - assertThat(mHelper.getParentUserId(USER1_MANAGED_ID)).isEqualTo(USER1_ID); - assertThat(mHelper.getParentUserId(USER2_MANAGED_ID)).isEqualTo(USER2_ID); + assertThat(mHelper.isCurrentUserId(USER1_ID)).isFalse(); + assertThat(mHelper.isCurrentUserId(USER2_ID)).isTrue(); + assertThat(mHelper.isCurrentUserId(USER1_MANAGED_ID)).isFalse(); + assertThat(mHelper.isCurrentUserId(USER2_MANAGED_ID)).isTrue(); } } diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java index ca00116d7ead..234c987bd3d6 100644 --- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java @@ -25,7 +25,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; -import android.content.pm.PackageManager; import android.hardware.Sensor; import android.hardware.SensorEventListener; import android.hardware.SensorManager; @@ -46,9 +45,8 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidJUnit4.class) public class AutomaticBrightnessControllerTest { - - private static final int BRIGHTNESS_MIN = 1; - private static final int BRIGHTNESS_MAX = 255; + private static final float BRIGHTNESS_MIN_FLOAT = 0.0f; + private static final float BRIGHTNESS_MAX_FLOAT = 1.0f; private static final int LIGHT_SENSOR_RATE = 20; private static final int INITIAL_LIGHT_SENSOR_RATE = 20; private static final int BRIGHTENING_LIGHT_DEBOUNCE_CONFIG = 0; @@ -61,7 +59,6 @@ public class AutomaticBrightnessControllerTest { @Mock BrightnessMappingStrategy mBrightnessMappingStrategy; @Mock HysteresisLevels mAmbientBrightnessThresholds; @Mock HysteresisLevels mScreenBrightnessThresholds; - @Mock PackageManager mPackageManager; @Mock Handler mNoopHandler; private static final int LIGHT_SENSOR_WARMUP_TIME = 0; @@ -81,11 +78,11 @@ public class AutomaticBrightnessControllerTest { } }, () -> { }, mContext.getMainLooper(), mSensorManager, lightSensor, - mBrightnessMappingStrategy, LIGHT_SENSOR_WARMUP_TIME, BRIGHTNESS_MIN, - BRIGHTNESS_MAX, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE, INITIAL_LIGHT_SENSOR_RATE, - BRIGHTENING_LIGHT_DEBOUNCE_CONFIG, DARKENING_LIGHT_DEBOUNCE_CONFIG, - RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG, mAmbientBrightnessThresholds, - mScreenBrightnessThresholds, mPackageManager); + mBrightnessMappingStrategy, LIGHT_SENSOR_WARMUP_TIME, BRIGHTNESS_MIN_FLOAT, + BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE, + INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG, + DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG, + mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mContext); controller.setLoggingEnabled(true); // Configure the brightness controller and grab an instance of the sensor listener, @@ -110,7 +107,7 @@ public class AutomaticBrightnessControllerTest { // Set up system to return 5 as a brightness value float lux1 = 100.0f; - float normalizedBrightness1 = 0.02f; + float normalizedBrightness1 = 0.0158f; //float equivalent of 5 out of 255 when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux1)) .thenReturn(lux1); when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux1)) @@ -156,7 +153,7 @@ public class AutomaticBrightnessControllerTest { // Set up system to return 250 as a brightness value float lux1 = 100.0f; - float normalizedBrightness1 = 0.98f; + float normalizedBrightness1 = 0.981f; //float equivalent of 250 out of 255 when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux1)) .thenReturn(lux1); when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux1)) diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java index d40130a62fd9..8dae48cafd7b 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java @@ -211,7 +211,8 @@ public class AppIntegrityManagerServiceImplTest { IntentSender mockReceiver = mock(IntentSender.class); List<Rule> rules = Arrays.asList( - new Rule(IntegrityFormula.PACKAGE_NAME.equalTo(PACKAGE_NAME), Rule.DENY)); + new Rule(IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME), + Rule.DENY)); mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver); runJobInHandler(); @@ -230,7 +231,8 @@ public class AppIntegrityManagerServiceImplTest { IntentSender mockReceiver = mock(IntentSender.class); List<Rule> rules = Arrays.asList( - new Rule(IntegrityFormula.PACKAGE_NAME.equalTo(PACKAGE_NAME), Rule.DENY)); + new Rule(IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME), + Rule.DENY)); mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver); runJobInHandler(); @@ -390,7 +392,7 @@ public class AppIntegrityManagerServiceImplTest { public void getCurrentRules() throws Exception { whitelistUsAsRuleProvider(); makeUsSystemApp(); - Rule rule = new Rule(IntegrityFormula.PACKAGE_NAME.equalTo("package"), Rule.DENY); + Rule rule = new Rule(IntegrityFormula.Application.packageNameEquals("package"), Rule.DENY); when(mIntegrityFileManager.readRules(any())).thenReturn(Arrays.asList(rule)); assertThat(mService.getCurrentRules().getList()).containsExactly(rule); diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java index 38cf562f8c5b..3dc26afdb9af 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java @@ -58,7 +58,7 @@ public class RuleBinaryParserTest { getBits(COMPOUND_FORMULA_END, SEPARATOR_BITS); private static final String ATOMIC_FORMULA_START_BITS = getBits(ATOMIC_FORMULA_START, SEPARATOR_BITS); - private static final int INVALID_FORMULA_SEPARATOR_VALUE = 3; + private static final int INVALID_FORMULA_SEPARATOR_VALUE = (1 << SEPARATOR_BITS) - 1; private static final String INVALID_FORMULA_SEPARATOR_BITS = getBits(INVALID_FORMULA_SEPARATOR_VALUE, SEPARATOR_BITS); diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java index 913aff7daaa9..ea9e6ff86728 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java @@ -112,6 +112,7 @@ public class RuleIndexingDetailsIdentifierTest { ATOMIC_FORMULA_WITH_VERSION_CODE, ATOMIC_FORMULA_WITH_ISPREINSTALLED)), Rule.DENY); + public static final int INVALID_FORMULA_TAG = -1; @Test public void getIndexType_nullRule() { @@ -290,7 +291,7 @@ public class RuleIndexingDetailsIdentifierTest { return new AtomicFormula(0) { @Override public int getTag() { - return 4; + return INVALID_FORMULA_TAG; } @Override diff --git a/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java index 4ba584e67929..4d0ad96ac223 100644 --- a/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java @@ -35,6 +35,7 @@ import android.Manifest; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; +import android.location.GnssAntennaInfo; import android.location.GnssClock; import android.location.GnssMeasurementCorrections; import android.location.GnssMeasurementsEvent; @@ -42,6 +43,7 @@ import android.location.GnssNavigationMessage; import android.location.GnssRequest; import android.location.GnssSingleSatCorrection; import android.location.IBatchedLocationCallback; +import android.location.IGnssAntennaInfoListener; import android.location.IGnssMeasurementsListener; import android.location.IGnssNavigationMessageListener; import android.location.IGnssStatusListener; @@ -56,6 +58,8 @@ import android.os.RemoteException; import com.android.server.LocalServices; import com.android.server.location.AppForegroundHelper; +import com.android.server.location.GnssAntennaInfoProvider; +import com.android.server.location.GnssAntennaInfoProvider.GnssAntennaInfoProviderNative; import com.android.server.location.GnssBatchingProvider; import com.android.server.location.GnssCapabilitiesProvider; import com.android.server.location.GnssLocationProvider; @@ -102,6 +106,7 @@ public class GnssManagerServiceTest { private GnssMeasurementsProvider mTestGnssMeasurementsProvider; private GnssStatusListenerHelper mTestGnssStatusProvider; private GnssNavigationMessageProvider mTestGnssNavigationMessageProvider; + private GnssAntennaInfoProvider mTestGnssAntennaInfoProvider; // Managers and services @Mock @@ -152,6 +157,8 @@ public class GnssManagerServiceTest { mMockContext, mMockHandler); mTestGnssNavigationMessageProvider = createGnssNavigationMessageProvider( mMockContext, mMockHandler); + mTestGnssAntennaInfoProvider = createGnssAntennaInfoProvider( + mMockContext, mMockHandler); // Setup GnssLocationProvider to return providers when(mMockGnssLocationProvider.getGnssStatusProvider()).thenReturn( @@ -170,6 +177,8 @@ public class GnssManagerServiceTest { mTestGnssNavigationMessageProvider); when(mMockGnssLocationProvider.getNetInitiatedListener()).thenReturn( mNetInitiatedListener); + when(mMockGnssLocationProvider.getGnssAntennaInfoProvider()).thenReturn( + mTestGnssAntennaInfoProvider); // Setup GnssBatching provider when(mMockGnssBatchingProvider.start(anyLong(), anyBoolean())).thenReturn(true); @@ -205,6 +214,12 @@ public class GnssManagerServiceTest { return mockListener; } + private IGnssAntennaInfoListener createMockGnssAntennaInfoListener() { + IGnssAntennaInfoListener mockListener = mock(IGnssAntennaInfoListener.class); + overrideAsBinder(mockListener); + return mockListener; + } + private IBatchedLocationCallback createMockBatchedLocationCallback() { IBatchedLocationCallback mockedCallback = mock(IBatchedLocationCallback.class); overrideAsBinder(mockedCallback); @@ -225,6 +240,39 @@ public class GnssManagerServiceTest { Arrays.asList(gnssSingleSatCorrection)).build(); } + private static List<GnssAntennaInfo> createDummyGnssAntennaInfos() { + double carrierFrequencyMHz = 13758.0; + + GnssAntennaInfo.PhaseCenterOffsetCoordinates phaseCenterOffsetCoordinates = new + GnssAntennaInfo.PhaseCenterOffsetCoordinates( + 4.3d, + 1.4d, + 2.10d, + 2.1d, + 3.12d, + 0.5d); + + double[][] phaseCenterVariationCorrectionsMillimeters = new double[10][10]; + double[][] phaseCenterVariationCorrectionsUncertaintyMillimeters = new double[10][10]; + GnssAntennaInfo.PhaseCenterVariationCorrections + phaseCenterVariationCorrections = + new GnssAntennaInfo.PhaseCenterVariationCorrections( + phaseCenterVariationCorrectionsMillimeters, + phaseCenterVariationCorrectionsUncertaintyMillimeters); + + double[][] signalGainCorrectionsDbi = new double[10][10]; + double[][] signalGainCorrectionsUncertaintyDbi = new double[10][10]; + GnssAntennaInfo.SignalGainCorrections signalGainCorrections = new + GnssAntennaInfo.SignalGainCorrections( + signalGainCorrectionsDbi, + signalGainCorrectionsUncertaintyDbi); + + List<GnssAntennaInfo> gnssAntennaInfos = new ArrayList(); + gnssAntennaInfos.add(new GnssAntennaInfo(carrierFrequencyMHz, phaseCenterOffsetCoordinates, + phaseCenterVariationCorrections, signalGainCorrections)); + return gnssAntennaInfos; + } + private void enableLocationPermissions() { Mockito.doThrow(new SecurityException()).when( mMockContext).enforceCallingPermission( @@ -299,6 +347,18 @@ public class GnssManagerServiceTest { }; } + private GnssAntennaInfoProvider createGnssAntennaInfoProvider(Context context, + Handler handler) { + GnssAntennaInfoProviderNative mockGnssAntenaInfoProviderNative = mock( + GnssAntennaInfoProviderNative.class); + return new GnssAntennaInfoProvider(context, handler, mockGnssAntenaInfoProviderNative) { + @Override + protected boolean isGpsEnabled() { + return true; + } + }; + } + @Test public void getGnssYearOfHardwareTest() { final int gnssYearOfHardware = 2012; @@ -670,6 +730,82 @@ public class GnssManagerServiceTest { } @Test + public void addGnssAntennaInfoListenerWithoutPermissionsTest() throws RemoteException { + IGnssAntennaInfoListener mockGnssAntennaInfoListener = + createMockGnssAntennaInfoListener(); + List<GnssAntennaInfo> gnssAntennaInfos = createDummyGnssAntennaInfos(); + + disableLocationPermissions(); + + assertThrows(SecurityException.class, + () -> mGnssManagerService.addGnssAntennaInfoListener( + mockGnssAntennaInfoListener, + "com.android.server", "abcd123", "TestGnssAntennaInfoListener")); + + mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos); + verify(mockGnssAntennaInfoListener, times(0)) + .onGnssAntennaInfoReceived(gnssAntennaInfos); + } + + @Test + public void addGnssAntennaInfoListenerWithPermissionsTest() throws RemoteException { + IGnssAntennaInfoListener mockGnssAntennaInfoListener = + createMockGnssAntennaInfoListener(); + List<GnssAntennaInfo> gnssAntennaInfos = createDummyGnssAntennaInfos(); + + enableLocationPermissions(); + + assertThat(mGnssManagerService.addGnssAntennaInfoListener(mockGnssAntennaInfoListener, + "com.android.server", "abcd123", "TestGnssAntennaInfoListener")).isEqualTo(true); + + mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos); + verify(mockGnssAntennaInfoListener, times(1)) + .onGnssAntennaInfoReceived(gnssAntennaInfos); + } + + @Test + public void removeGnssAntennaInfoListenerWithoutPermissionsTest() throws RemoteException { + IGnssAntennaInfoListener mockGnssAntennaInfoListener = + createMockGnssAntennaInfoListener(); + List<GnssAntennaInfo> gnssAntennaInfos = createDummyGnssAntennaInfos(); + + enableLocationPermissions(); + + mGnssManagerService.addGnssAntennaInfoListener( + mockGnssAntennaInfoListener, + "com.android.server", "abcd123", "TestGnssAntennaInfoListener"); + + disableLocationPermissions(); + + mGnssManagerService.removeGnssAntennaInfoListener( + mockGnssAntennaInfoListener); + + mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos); + verify(mockGnssAntennaInfoListener, times(0)).onGnssAntennaInfoReceived( + gnssAntennaInfos); + } + + @Test + public void removeGnssAntennaInfoListenerWithPermissionsTest() throws RemoteException { + IGnssAntennaInfoListener mockGnssAntennaInfoListener = + createMockGnssAntennaInfoListener(); + List<GnssAntennaInfo> gnssAntennaInfos = createDummyGnssAntennaInfos(); + + enableLocationPermissions(); + + mGnssManagerService.addGnssAntennaInfoListener( + mockGnssAntennaInfoListener, + "com.android.server", "abcd123", "TestGnssAntennaInfoListener"); + + mGnssManagerService.removeGnssAntennaInfoListener( + mockGnssAntennaInfoListener); + + mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos); + verify(mockGnssAntennaInfoListener, times(0)).onGnssAntennaInfoReceived( + gnssAntennaInfos); + } + + @Test public void addGnssNavigationMessageListenerWithoutPermissionsTest() throws RemoteException { IGnssNavigationMessageListener mockGnssNavigationMessageListener = createMockGnssNavigationMessageListener(); diff --git a/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java index 4ae374abb7c2..9213e1fe5a25 100644 --- a/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java @@ -51,6 +51,7 @@ public final class PeopleServiceTest { private static final String APP_PREDICTION_SHARE_UI_SURFACE = "share"; private static final int APP_PREDICTION_TARGET_COUNT = 4; private static final String TEST_PACKAGE_NAME = "com.example"; + private static final int USER_ID = 0; private PeopleServiceInternal mServiceInternal; private PeopleService.LocalService mLocalService; @@ -73,7 +74,7 @@ public final class PeopleServiceTest { mServiceInternal = LocalServices.getService(PeopleServiceInternal.class); mLocalService = (PeopleService.LocalService) mServiceInternal; - mSessionId = new AppPredictionSessionId("abc"); + mSessionId = new AppPredictionSessionId("abc", USER_ID); mPredictionContext = new AppPredictionContext.Builder(mContext) .setUiSurface(APP_PREDICTION_SHARE_UI_SURFACE) .setPredictedTargetCount(APP_PREDICTION_TARGET_COUNT) diff --git a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java index 91cc9f35da1e..ef87e5574275 100644 --- a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java @@ -35,6 +35,8 @@ import android.os.UserManager; import android.platform.test.annotations.Presubmit; import android.util.SparseArray; +import com.android.internal.util.FunctionalUtils.ThrowingRunnable; +import com.android.internal.util.FunctionalUtils.ThrowingSupplier; import com.android.server.wm.ActivityTaskManagerInternal; import org.junit.Before; @@ -574,6 +576,16 @@ public class CrossProfileAppsServiceImplTest { } @Override + public void withCleanCallingIdentity(ThrowingRunnable action) { + action.run(); + } + + @Override + public <T> T withCleanCallingIdentity(ThrowingSupplier<T> action) { + return action.get(); + } + + @Override public UserManager getUserManager() { return mUserManager; } diff --git a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java index 642cedb4c852..c4289efe1839 100644 --- a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java +++ b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java @@ -92,18 +92,18 @@ public class AttentionDetectorTest extends AndroidTestCase { mNextDimming = SystemClock.uptimeMillis() + 3000L; // Save the existing state. - mIsSettingEnabled = Settings.System.getIntForUser(getContext().getContentResolver(), - Settings.System.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT); + mIsSettingEnabled = Settings.Secure.getIntForUser(getContext().getContentResolver(), + Settings.Secure.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT); - Settings.System.putIntForUser(getContext().getContentResolver(), - Settings.System.ADAPTIVE_SLEEP, 1, UserHandle.USER_CURRENT); + Settings.Secure.putIntForUser(getContext().getContentResolver(), + Settings.Secure.ADAPTIVE_SLEEP, 1, UserHandle.USER_CURRENT); mAttentionDetector.updateEnabledFromSettings(getContext()); } @After public void tearDown() { - Settings.System.putIntForUser(getContext().getContentResolver(), - Settings.System.ADAPTIVE_SLEEP, mIsSettingEnabled, UserHandle.USER_CURRENT); + Settings.Secure.putIntForUser(getContext().getContentResolver(), + Settings.Secure.ADAPTIVE_SLEEP, mIsSettingEnabled, UserHandle.USER_CURRENT); DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE, KEY_PRE_DIM_CHECK_DURATION_MILLIS, @@ -122,8 +122,8 @@ public class AttentionDetectorTest extends AndroidTestCase { @Test public void testOnUserActivity_doesntCheckIfNotEnabled() { - Settings.System.putIntForUser(getContext().getContentResolver(), - Settings.System.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT); + Settings.Secure.putIntForUser(getContext().getContentResolver(), + Settings.Secure.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT); mAttentionDetector.updateEnabledFromSettings(getContext()); long when = registerAttention(); verify(mAttentionManagerInternal, never()).checkAttention(anyLong(), any()); @@ -163,8 +163,8 @@ public class AttentionDetectorTest extends AndroidTestCase { PackageManager.PERMISSION_DENIED); registerAttention(); - boolean enabled = Settings.System.getIntForUser(getContext().getContentResolver(), - Settings.System.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT) == 1; + boolean enabled = Settings.Secure.getIntForUser(getContext().getContentResolver(), + Settings.Secure.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT) == 1; assertFalse(enabled); } diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index 6eef41aafc98..811089a45758 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -65,6 +65,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.platform.test.annotations.FlakyTest; import android.provider.Settings; +import android.service.dreams.DreamManagerInternal; import android.test.mock.MockContentResolver; import android.view.Display; @@ -111,6 +112,7 @@ public class PowerManagerServiceTest { @Mock private BatteryManagerInternal mBatteryManagerInternalMock; @Mock private ActivityManagerInternal mActivityManagerInternalMock; @Mock private AttentionManagerInternal mAttentionManagerInternalMock; + @Mock private DreamManagerInternal mDreamManagerInternalMock; @Mock private PowerManagerService.NativeWrapper mNativeWrapperMock; @Mock private Notifier mNotifierMock; @Mock private WirelessChargerDetector mWirelessChargerDetectorMock; @@ -171,6 +173,7 @@ public class PowerManagerServiceTest { addLocalServiceMock(BatteryManagerInternal.class, mBatteryManagerInternalMock); addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock); addLocalServiceMock(AttentionManagerInternal.class, mAttentionManagerInternalMock); + addLocalServiceMock(DreamManagerInternal.class, mDreamManagerInternalMock); mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); mResourcesSpy = spy(mContextSpy.getResources()); @@ -651,6 +654,7 @@ public class PowerManagerServiceTest { int flags = PowerManager.DOZE_WAKE_LOCK; mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, null /* workSource */, null /* historyTag */); + when(mDreamManagerInternalMock.isDreaming()).thenReturn(true); mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0); assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_DOZING); @@ -732,7 +736,7 @@ public class PowerManagerServiceTest { setAttentiveTimeout(5); createService(); startSystem(); - SystemClock.sleep(8); + SystemClock.sleep(20); assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP); } diff --git a/services/tests/servicestests/src/com/android/server/power/PreRebootLoggerTest.java b/services/tests/servicestests/src/com/android/server/power/PreRebootLoggerTest.java new file mode 100644 index 000000000000..a13823441665 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/power/PreRebootLoggerTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.power; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.provider.Settings; +import android.test.mock.MockContentResolver; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.util.test.FakeSettingsProvider; + +import com.google.common.io.Files; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.File; + +/** + * Tests for {@link PreRebootLogger} + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class PreRebootLoggerTest { + @Mock Context mContext; + private MockContentResolver mContentResolver; + private File mDumpDir; + + @BeforeClass + public static void setupOnce() { + FakeSettingsProvider.clearSettingsProvider(); + } + + @AfterClass + public static void tearDownOnce() { + FakeSettingsProvider.clearSettingsProvider(); + } + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mContentResolver = new MockContentResolver(getInstrumentation().getTargetContext()); + when(mContext.getContentResolver()).thenReturn(mContentResolver); + mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + + mDumpDir = Files.createTempDir(); + mDumpDir.mkdir(); + mDumpDir.deleteOnExit(); + } + + @Test + public void log_adbEnabled_dumpsInformationProperly() { + Settings.Global.putInt(mContentResolver, Settings.Global.ADB_ENABLED, 1); + + PreRebootLogger.log(mContext, mDumpDir); + + assertThat(mDumpDir.list()).asList().containsExactly("system", "package", "rollback"); + } + + @Test + public void log_adbDisabled_wipesDumpedInformation() { + Settings.Global.putInt(mContentResolver, Settings.Global.ADB_ENABLED, 1); + PreRebootLogger.log(mContext, mDumpDir); + Settings.Global.putInt(mContentResolver, Settings.Global.ADB_ENABLED, 0); + + PreRebootLogger.log(mContext, mDumpDir); + + assertThat(mDumpDir.listFiles()).isEmpty(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 03dc21370e24..e768205c2cf4 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -115,6 +115,7 @@ public class AppStandbyControllerTests { private static final long WORKING_SET_THRESHOLD = 12 * HOUR_MS; private static final long FREQUENT_THRESHOLD = 24 * HOUR_MS; private static final long RARE_THRESHOLD = 48 * HOUR_MS; + private static final long RESTRICTED_THRESHOLD = 96 * HOUR_MS; /** Mock variable used in {@link MyInjector#isPackageInstalled(String, int, int)} */ private static boolean isPackageInstalled = true; @@ -232,9 +233,8 @@ public class AppStandbyControllerTests { @Override String getAppIdleSettings() { return "screen_thresholds=0/0/0/" + HOUR_MS + ",elapsed_thresholds=0/" - + WORKING_SET_THRESHOLD + "/" - + FREQUENT_THRESHOLD + "/" - + RARE_THRESHOLD; + + WORKING_SET_THRESHOLD + "/" + FREQUENT_THRESHOLD + "/" + RARE_THRESHOLD + + "/" + RESTRICTED_THRESHOLD; } @Override @@ -372,12 +372,15 @@ public class AppStandbyControllerTests { // RARE bucket assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_RARE); + // RESTRICTED bucket + assertTimeout(mController, RESTRICTED_THRESHOLD + 1, STANDBY_BUCKET_RESTRICTED); + reportEvent(mController, USER_INTERACTION, RARE_THRESHOLD + 1, PACKAGE_1); assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_ACTIVE); - // RARE bucket - assertTimeout(mController, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE); + // RESTRICTED bucket + assertTimeout(mController, RESTRICTED_THRESHOLD * 2 + 2, STANDBY_BUCKET_RESTRICTED); } @Test @@ -437,7 +440,7 @@ public class AppStandbyControllerTests { assertNotEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1)); mInjector.setDisplayOn(true); - assertTimeout(mController, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE); + assertTimeout(mController, RARE_THRESHOLD + 2 * HOUR_MS + 1, STANDBY_BUCKET_RARE); } @Test @@ -642,7 +645,7 @@ public class AppStandbyControllerTests { assertBucket(STANDBY_BUCKET_FREQUENT); // Way past prediction timeout, use system thresholds - mInjector.mElapsedRealtime = RARE_THRESHOLD * 4; + mInjector.mElapsedRealtime = RARE_THRESHOLD; mController.checkIdleStates(USER_ID); assertBucket(STANDBY_BUCKET_RARE); } @@ -669,7 +672,7 @@ public class AppStandbyControllerTests { assertBucket(STANDBY_BUCKET_RESTRICTED); // Way past all timeouts. Make sure timeout processing doesn't raise bucket. - mInjector.mElapsedRealtime += RARE_THRESHOLD * 4; + mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4; mController.checkIdleStates(USER_ID); assertBucket(STANDBY_BUCKET_RESTRICTED); } @@ -682,7 +685,30 @@ public class AppStandbyControllerTests { reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1); assertBucket(STANDBY_BUCKET_ACTIVE); - mInjector.mElapsedRealtime += mInjector.getRestrictedBucketDelayMs() - 5000; + mInjector.mElapsedRealtime += mInjector.getAutoRestrictedBucketDelayMs() - 5000; + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_FORCED_BY_SYSTEM); + // Bucket shouldn't change + assertBucket(STANDBY_BUCKET_ACTIVE); + + // bucketing works after timeout + mInjector.mElapsedRealtime += 6000; + + Thread.sleep(6000); + // Enough time has passed. The app should automatically be put into the RESTRICTED bucket. + assertBucket(STANDBY_BUCKET_RESTRICTED); + } + + /** + * Test that an app is put into the RESTRICTED bucket after enough time has passed. + */ + @Test + public void testRestrictedDelay_DelayChange() throws Exception { + reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1); + assertBucket(STANDBY_BUCKET_ACTIVE); + + mInjector.mAutoRestrictedBucketDelayMs = 2 * HOUR_MS; + mInjector.mElapsedRealtime += 2 * HOUR_MS - 5000; mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, REASON_MAIN_FORCED_BY_SYSTEM); // Bucket shouldn't change @@ -697,6 +723,22 @@ public class AppStandbyControllerTests { } @Test + public void testPredictionRaiseFromRestrictedTimeout() { + reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); + + // Way past all timeouts. App times out into RESTRICTED bucket. + mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4; + mController.checkIdleStates(USER_ID); + assertBucket(STANDBY_BUCKET_RESTRICTED); + + // Since the app timed out into RESTRICTED, prediction should be able to remove from the + // bucket. + mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD; + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE, + REASON_MAIN_PREDICTED); + } + + @Test public void testCascadingTimeouts() throws Exception { reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1); assertBucket(STANDBY_BUCKET_ACTIVE); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java new file mode 100644 index 000000000000..f051fa4c2290 --- /dev/null +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.notification; + +import static android.app.NotificationManager.IMPORTANCE_DEFAULT; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.UiServiceTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; + + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class NotificationRecordLoggerTest extends UiServiceTestCase { + private static final int UID = 9999; + private static final String CHANNEL_ID = "NotificationRecordLoggerTestChannelId"; + + private NotificationRecord getNotification(int id, String tag) { + final String packageName = mContext.getPackageName(); + NotificationChannel channel = new NotificationChannel( + CHANNEL_ID, CHANNEL_ID, IMPORTANCE_DEFAULT); + Notification.Builder nb = new Notification.Builder(mContext, channel.getId()); + StatusBarNotification sbn = new StatusBarNotification(packageName, packageName, id, tag, + UID, 0, nb.build(), new UserHandle(UID), null, + 0); + return new NotificationRecord(mContext, sbn, channel); + } + + private NotificationRecordLogger.NotificationRecordPair getNotificationRecordPair(int id, + String tag) { + return new NotificationRecordLogger.NotificationRecordPair(getNotification(id, tag), + null); + } + + @Test + public void testSmallHash() { + assertEquals(0, NotificationRecordLogger.NotificationRecordPair.smallHash(0)); + final int maxHash = NotificationRecordLogger.NotificationRecordPair.MAX_HASH; + assertEquals(0, + NotificationRecordLogger.NotificationRecordPair.smallHash(maxHash)); + assertEquals(0, + NotificationRecordLogger.NotificationRecordPair.smallHash(17 * maxHash)); + assertEquals(maxHash - 1, + NotificationRecordLogger.NotificationRecordPair.smallHash(maxHash - 1)); + assertEquals(maxHash - 1, + NotificationRecordLogger.NotificationRecordPair.smallHash(-1)); + } + + @Test + public void testGetNotificationIdHash() { + assertEquals(0, + getNotificationRecordPair(0, null).getNotificationIdHash()); + assertEquals(1, + getNotificationRecordPair(1, null).getNotificationIdHash()); + assertEquals(NotificationRecordLogger.NotificationRecordPair.MAX_HASH - 1, + getNotificationRecordPair(-1, null).getNotificationIdHash()); + final String tag = "someTag"; + final int hash = NotificationRecordLogger.NotificationRecordPair.smallHash(tag.hashCode()); + assertEquals(hash, getNotificationRecordPair(0, tag).getNotificationIdHash()); + // We xor the tag and hashcode together before compressing the range. The order of + // operations doesn't matter if id is small. + assertEquals(1 ^ hash, + getNotificationRecordPair(1, tag).getNotificationIdHash()); + // But it does matter for an id with more 1 bits than fit in the small hash. + assertEquals( + NotificationRecordLogger.NotificationRecordPair.smallHash(-1 ^ tag.hashCode()), + getNotificationRecordPair(-1, tag).getNotificationIdHash()); + assertNotEquals(-1 ^ hash, + NotificationRecordLogger.NotificationRecordPair.smallHash(-1 ^ tag.hashCode())); + } + + @Test + public void testGetChannelIdHash() { + assertEquals( + NotificationRecordLogger.NotificationRecordPair.smallHash(CHANNEL_ID.hashCode()), + getNotificationRecordPair(0, null).getChannelIdHash()); + assertNotEquals( + NotificationRecordLogger.NotificationRecordPair.smallHash(CHANNEL_ID.hashCode()), + CHANNEL_ID.hashCode()); + } + + @Test + public void testGetGroupIdHash() { + NotificationRecordLogger.NotificationRecordPair p = getNotificationRecordPair( + 0, null); + assertEquals(0, p.getGroupIdHash()); + final String group = "someGroup"; + p.r.setOverrideGroupKey(group); + assertEquals( + NotificationRecordLogger.NotificationRecordPair.smallHash(group.hashCode()), + p.getGroupIdHash()); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java index f754c5956cd9..39cd76aeef9e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java @@ -174,6 +174,6 @@ public class DisplayPolicyInsetsTests extends DisplayPolicyTestsBase { } private static DisplayInfo displayInfoForRotation(int rotation, boolean withDisplayCutout) { - return displayInfoAndCutoutForRotation(rotation, withDisplayCutout).first; + return displayInfoAndCutoutForRotation(rotation, withDisplayCutout, false).first; } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index e6291a4c34ac..c19312debce3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -34,6 +34,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; @@ -91,6 +92,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { private WindowState mWindow; private int mRotation = ROTATION_0; private boolean mHasDisplayCutout; + private boolean mIsLongEdgeDisplayCutout; private static final int DECOR_WINDOW_INSET = 50; @Before @@ -124,6 +126,12 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { updateDisplayFrames(); } + public void addLongEdgeDisplayCutout() { + mHasDisplayCutout = true; + mIsLongEdgeDisplayCutout = true; + updateDisplayFrames(); + } + private void updateDisplayFrames() { mFrames = createDisplayFrames(); mDisplayContent.mDisplayFrames = mFrames; @@ -131,7 +139,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { private DisplayFrames createDisplayFrames() { final Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation, - mHasDisplayCutout); + mHasDisplayCutout, mIsLongEdgeDisplayCutout); return new DisplayFrames(mDisplayContent.getDisplayId(), info.first, info.second); } @@ -376,6 +384,46 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { } @Test + public void layoutWindowLw_withDisplayCutout_shortEdges() { + addDisplayCutout(); + + mWindow.mAttrs.flags = + FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + mWindow.mAttrs.setFitInsetsTypes(0 /* types */); + mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0); + assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); + assertInsetBy(mWindow.getContentFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); + assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); + assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_always() { + addDisplayCutout(); + + mWindow.mAttrs.flags = + FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + mWindow.mAttrs.setFitInsetsTypes(0 /* types */); + mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0); + assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); + assertInsetBy(mWindow.getContentFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); + assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); + assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0); + } + + @Test public void layoutWindowLw_withDisplayCutout_layoutFullscreen() { addDisplayCutout(); @@ -551,6 +599,88 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { } @Test + public void layoutWindowLw_withLongEdgeDisplayCutout() { + addLongEdgeDisplayCutout(); + + mWindow.mAttrs.flags = + FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); + assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); + assertInsetBy(mWindow.getContentFrameLw(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0, + NAV_BAR_HEIGHT); + assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); + assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); + } + + @Test + public void layoutWindowLw_withLongEdgeDisplayCutout_never() { + addLongEdgeDisplayCutout(); + + mWindow.mAttrs.flags = + FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); + assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, + NAV_BAR_HEIGHT); + assertInsetBy(mWindow.getContentFrameLw(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0, + NAV_BAR_HEIGHT); + assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); + assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); + } + + @Test + public void layoutWindowLw_withLongEdgeDisplayCutout_shortEdges() { + addLongEdgeDisplayCutout(); + + mWindow.mAttrs.flags = + FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + mWindow.mAttrs.setFitInsetsTypes(0 /* types */); + mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); + assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); + assertInsetBy(mWindow.getContentFrameLw(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0, + NAV_BAR_HEIGHT); + assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); + assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); + } + + @Test + public void layoutWindowLw_withLongEdgeDisplayCutout_always() { + addLongEdgeDisplayCutout(); + + mWindow.mAttrs.flags = + FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + mWindow.mAttrs.setFitInsetsTypes(0 /* types */); + mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0); + assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); + assertInsetBy(mWindow.getContentFrameLw(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0, + NAV_BAR_HEIGHT); + assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); + assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0); + } + + @Test public void layoutWindowLw_withForwardInset_SoftInputAdjustResize() { assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_NONE); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java index d0b335082ef5..2a20c2e959cc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java @@ -111,7 +111,7 @@ public class DisplayPolicyTestsBase extends WindowTestsBase { } static Pair<DisplayInfo, WmDisplayCutout> displayInfoAndCutoutForRotation(int rotation, - boolean withDisplayCutout) { + boolean withDisplayCutout, boolean isLongEdgeCutout) { final DisplayInfo info = new DisplayInfo(); WmDisplayCutout cutout = null; @@ -121,7 +121,7 @@ public class DisplayPolicyTestsBase extends WindowTestsBase { info.rotation = rotation; if (withDisplayCutout) { cutout = WmDisplayCutout.computeSafeInsets( - displayCutoutForRotation(rotation), info.logicalWidth, + displayCutoutForRotation(rotation, isLongEdgeCutout), info.logicalWidth, info.logicalHeight); info.displayCutout = cutout.getDisplayCutout(); } else { @@ -130,9 +130,13 @@ public class DisplayPolicyTestsBase extends WindowTestsBase { return Pair.create(info, cutout); } - private static DisplayCutout displayCutoutForRotation(int rotation) { - final RectF rectF = - new RectF(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT); + private static DisplayCutout displayCutoutForRotation(int rotation, boolean isLongEdgeCutout) { + RectF rectF = new RectF(); + if (isLongEdgeCutout) { + rectF.set(0, DISPLAY_HEIGHT / 4, DISPLAY_CUTOUT_HEIGHT, DISPLAY_HEIGHT * 3 / 4); + } else { + rectF.set(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT); + } final Matrix m = new Matrix(); transformPhysicalToLogicalCoordinates(rotation, DISPLAY_WIDTH, DISPLAY_HEIGHT, m); @@ -141,16 +145,16 @@ public class DisplayPolicyTestsBase extends WindowTestsBase { int pos = -1; switch (rotation) { case ROTATION_0: - pos = BOUNDS_POSITION_TOP; + pos = isLongEdgeCutout ? BOUNDS_POSITION_LEFT : BOUNDS_POSITION_TOP; break; case ROTATION_90: - pos = BOUNDS_POSITION_LEFT; + pos = isLongEdgeCutout ? BOUNDS_POSITION_BOTTOM : BOUNDS_POSITION_LEFT; break; case ROTATION_180: - pos = BOUNDS_POSITION_BOTTOM; + pos = isLongEdgeCutout ? BOUNDS_POSITION_RIGHT : BOUNDS_POSITION_BOTTOM; break; case ROTATION_270: - pos = BOUNDS_POSITION_RIGHT; + pos = isLongEdgeCutout ? BOUNDS_POSITION_TOP : BOUNDS_POSITION_RIGHT; break; } diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java index 7ffdd7cdceb6..f8117573efdd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java @@ -112,11 +112,44 @@ public class InsetsSourceProviderTest extends WindowTestsBase { final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); final WindowState target = createWindow(null, TYPE_APPLICATION, "target"); statusBar.getFrameLw().set(0, 0, 500, 100); + + // We must not have control or control target before we have the insets source window. + mProvider.updateControlForTarget(target, true /* force */); + assertNull(mProvider.getControl(target)); + assertNull(mProvider.getControlTarget()); + + // We can have the control or the control target after we have the insets source window. mProvider.setWindow(statusBar, null, null); mProvider.updateControlForTarget(target, false /* force */); assertNotNull(mProvider.getControl(target)); + assertNotNull(mProvider.getControlTarget()); + + // We must not have control or control target while we are performing seamless rotation. + // And the control and the control target must not be updated during that. + mProvider.startSeamlessRotation(); + assertNull(mProvider.getControl(target)); + assertNull(mProvider.getControlTarget()); + mProvider.updateControlForTarget(target, true /* force */); + assertNull(mProvider.getControl(target)); + assertNull(mProvider.getControlTarget()); + + // We can have the control and the control target after seamless rotation. + mProvider.finishSeamlessRotation(false /* timeout */); + mProvider.updateControlForTarget(target, false /* force */); + assertNotNull(mProvider.getControl(target)); + assertNotNull(mProvider.getControlTarget()); + + // We can clear the control and the control target. mProvider.updateControlForTarget(null, false /* force */); assertNull(mProvider.getControl(target)); + assertNull(mProvider.getControlTarget()); + + // We must not have control or control target if the insets source window doesn't have a + // surface. + statusBar.setSurfaceControl(null); + mProvider.updateControlForTarget(target, true /* force */); + assertNull(mProvider.getControl(target)); + assertNull(mProvider.getControlTarget()); } @Test diff --git a/startop/iorap/stress/Android.bp b/startop/iorap/stress/Android.bp new file mode 100644 index 000000000000..f9f251bdd889 --- /dev/null +++ b/startop/iorap/stress/Android.bp @@ -0,0 +1,33 @@ +// +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_binary { + name: "iorap.stress.memory", + srcs: ["main_memory.cc"], + + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + "-Wno-unused-parameter" + ], + + shared_libs: [ + "libbase" + ], + + host_supported: true, +} diff --git a/startop/iorap/stress/main_memory.cc b/startop/iorap/stress/main_memory.cc new file mode 100644 index 000000000000..1f268619e4d9 --- /dev/null +++ b/startop/iorap/stress/main_memory.cc @@ -0,0 +1,126 @@ +// +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include <chrono> +#include <fstream> +#include <iostream> +#include <random> +#include <string> + +#include <string.h> +#include <stdlib.h> +#include <sys/mman.h> + +#include <android-base/parseint.h> + +static constexpr size_t kBytesPerMb = 1048576; +const size_t kMemoryAllocationSize = 2 * 1024 * kBytesPerMb; + +#define USE_MLOCKALL 0 + +std::string GetProcessStatus(const char* key) { + // Build search pattern of key and separator. + std::string pattern(key); + pattern.push_back(':'); + + // Search for status lines starting with pattern. + std::ifstream fs("/proc/self/status"); + std::string line; + while (std::getline(fs, line)) { + if (strncmp(pattern.c_str(), line.c_str(), pattern.size()) == 0) { + // Skip whitespace in matching line (if any). + size_t pos = line.find_first_not_of(" \t", pattern.size()); + if (pos == std::string::npos) { + break; + } + return std::string(line, pos); + } + } + return "<unknown>"; +} + +int main(int argc, char** argv) { + size_t allocationSize = 0; + if (argc >= 2) { + if (!android::base::ParseUint(argv[1], /*out*/&allocationSize)) { + std::cerr << "Failed to parse the allocation size (must be 0,MAX_SIZE_T)" << std::endl; + return 1; + } + } else { + allocationSize = kMemoryAllocationSize; + } + + void* mem = malloc(allocationSize); + if (mem == nullptr) { + std::cerr << "Malloc failed" << std::endl; + return 1; + } + + volatile int* imem = static_cast<int *>(mem); // don't optimize out memory usage + + size_t imemCount = allocationSize / sizeof(int); + + std::cout << "Allocated " << allocationSize << " bytes" << std::endl; + + auto seed = std::chrono::high_resolution_clock::now().time_since_epoch().count(); + std::mt19937 mt_rand(seed); + + size_t randPrintCount = 10; + + // Write random numbers: + // * Ensures each page is resident + // * Avoids zeroed out pages (zRAM) + // * Avoids same-page merging + for (size_t i = 0; i < imemCount; ++i) { + imem[i] = mt_rand(); + + if (i < randPrintCount) { + std::cout << "Generated random value: " << imem[i] << std::endl; + } + } + +#if USE_MLOCKALL + /* + * Lock all pages from the address space of this process. + */ + if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0) { + std::cerr << "Mlockall failed" << std::endl; + return 1; + } +#else + // Use mlock because of the predictable VmLck size. + // Using mlockall tends to bring in anywhere from 2-2.5GB depending on the device. + if (mlock(mem, allocationSize) != 0) { + std::cerr << "Mlock failed" << std::endl; + return 1; + } +#endif + + // Validate memory is actually resident and locked with: + // $> cat /proc/$(pidof iorap.stress.memory)/status | grep VmLck + std::cout << "Locked memory (VmLck) = " << GetProcessStatus("VmLck") << std::endl; + + std::cout << "Press any key to terminate" << std::endl; + int any_input; + std::cin >> any_input; + + std::cout << "Terminating..." << std::endl; + + munlockall(); + free(mem); + + return 0; +} diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt index 460add897f2f..18c249136d05 100644 --- a/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt +++ b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt @@ -16,6 +16,7 @@ package com.google.android.startop.iorap import android.net.Uri import android.os.ServiceManager +import androidx.test.filters.FlakyTest import androidx.test.filters.MediumTest import org.junit.Test import org.mockito.Mockito.argThat @@ -26,6 +27,7 @@ import org.mockito.Mockito.timeout // @Ignore("Test is disabled until iorapd is added to init and there's selinux policies for it") @MediumTest +@FlakyTest(bugId = 149098310) // Failing on cuttlefish with SecurityException. class IIorapIntegrationTest { /** * @throws ServiceManager.ServiceNotFoundException if iorapd service could not be found @@ -55,6 +57,9 @@ class IIorapIntegrationTest { private fun testAnyMethod(func: (RequestId) -> Unit) { val taskListener = spy(DummyTaskListener())!! + // FIXME: b/149098310 + return + try { iorapService.setTaskListener(taskListener) // Note: Binder guarantees total order for oneway messages sent to the same binder diff --git a/startop/view_compiler/dex_layout_compiler.cc b/startop/view_compiler/dex_layout_compiler.cc index cb820f8f20fb..bddb8aa6716a 100644 --- a/startop/view_compiler/dex_layout_compiler.cc +++ b/startop/view_compiler/dex_layout_compiler.cc @@ -118,7 +118,7 @@ namespace { std::string ResolveName(const std::string& name) { if (name == "View") return "android.view.View"; if (name == "ViewGroup") return "android.view.ViewGroup"; - if (name.find(".") == std::string::npos) { + if (name.find('.') == std::string::npos) { return StringPrintf("android.widget.%s", name.c_str()); } return name; @@ -205,4 +205,4 @@ void DexViewBuilder::PopViewStack() { view_stack_.pop_back(); } -} // namespace startop
\ No newline at end of file +} // namespace startop diff --git a/startop/view_compiler/util.cc b/startop/view_compiler/util.cc index a0637e6da32f..c34d7b059cfc 100644 --- a/startop/view_compiler/util.cc +++ b/startop/view_compiler/util.cc @@ -23,13 +23,13 @@ namespace util { // TODO: see if we can borrow this from somewhere else, like aapt2. string FindLayoutNameFromFilename(const string& filename) { - size_t start = filename.rfind("/"); + size_t start = filename.rfind('/'); if (start == string::npos) { start = 0; } else { start++; // advance past '/' character } - size_t end = filename.find(".", start); + size_t end = filename.find('.', start); return filename.substr(start, end - start); } diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java index 6d6eaa4677f0..1cd45e93a52a 100644 --- a/telephony/java/android/telephony/CellSignalStrengthLte.java +++ b/telephony/java/android/telephony/CellSignalStrengthLte.java @@ -244,19 +244,30 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P } else { mParametersUseForLevel = cc.getInt( CarrierConfigManager.KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT); - Rlog.i(LOG_TAG, "Using signal strength level: " + mParametersUseForLevel); + if (DBG) { + Rlog.i(LOG_TAG, "Using signal strength level: " + mParametersUseForLevel); + } rsrpThresholds = cc.getIntArray( CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY); if (rsrpThresholds == null) rsrpThresholds = sRsrpThresholds; - Rlog.i(LOG_TAG, "Applying LTE RSRP Thresholds: " + Arrays.toString(rsrpThresholds)); + if (DBG) { + Rlog.i(LOG_TAG, "Applying LTE RSRP Thresholds: " + + Arrays.toString(rsrpThresholds)); + } rsrqThresholds = cc.getIntArray( CarrierConfigManager.KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY); if (rsrqThresholds == null) rsrqThresholds = sRsrqThresholds; - Rlog.i(LOG_TAG, "Applying LTE RSRQ Thresholds: " + Arrays.toString(rsrqThresholds)); + if (DBG) { + Rlog.i(LOG_TAG, "Applying LTE RSRQ Thresholds: " + + Arrays.toString(rsrqThresholds)); + } rssnrThresholds = cc.getIntArray( CarrierConfigManager.KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY); if (rssnrThresholds == null) rssnrThresholds = sRssnrThresholds; - Rlog.i(LOG_TAG, "Applying LTE RSSNR Thresholds: " + Arrays.toString(rssnrThresholds)); + if (DBG) { + Rlog.i(LOG_TAG, "Applying LTE RSSNR Thresholds: " + + Arrays.toString(rssnrThresholds)); + } rsrpOnly = cc.getBoolean( CarrierConfigManager.KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL, false); } @@ -283,15 +294,21 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P if (isLevelForParameter(USE_RSRP)) { rsrpLevel = updateLevelWithMeasure(rsrp, rsrpThresholds); - Rlog.i(LOG_TAG, "Updated 4G LTE RSRP Level: " + rsrpLevel); + if (DBG) { + Rlog.i(LOG_TAG, "Updated 4G LTE RSRP Level: " + rsrpLevel); + } } if (isLevelForParameter(USE_RSRQ)) { rsrqLevel = updateLevelWithMeasure(mRsrq, rsrqThresholds); - Rlog.i(LOG_TAG, "Updated 4G LTE RSRQ Level: " + rsrqLevel); + if (DBG) { + Rlog.i(LOG_TAG, "Updated 4G LTE RSRQ Level: " + rsrqLevel); + } } if (isLevelForParameter(USE_RSSNR)) { rssnrLevel = updateLevelWithMeasure(mRssnr, rssnrThresholds); - Rlog.i(LOG_TAG, "Updated 4G LTE RSSNR Level: " + rssnrLevel); + if (DBG) { + Rlog.i(LOG_TAG, "Updated 4G LTE RSSNR Level: " + rssnrLevel); + } } // Apply the smaller value among three levels of three measures. mLevel = Math.min(Math.min(rsrpLevel, rsrqLevel), rssnrLevel); diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java index e3d03a38b6b0..8562df1d015f 100644 --- a/telephony/java/android/telephony/CellSignalStrengthNr.java +++ b/telephony/java/android/telephony/CellSignalStrengthNr.java @@ -40,6 +40,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa */ public static final int UNKNOWN_ASU_LEVEL = 99; + private static final boolean VDBG = false; + private static final String TAG = "CellSignalStrengthNr"; // Lifted from Default carrier configs and max range of SSRSRP @@ -301,31 +303,45 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa } else { mParametersUseForLevel = cc.getInt( CarrierConfigManager.KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT, USE_SSRSRP); - Rlog.i(TAG, "Using SSRSRP for Level."); mSsRsrpThresholds = cc.getIntArray( CarrierConfigManager.KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY); - Rlog.i(TAG, "Applying 5G NR SSRSRP Thresholds: " + Arrays.toString(mSsRsrpThresholds)); + if (VDBG) { + Rlog.i(TAG, "Applying 5G NR SSRSRP Thresholds: " + + Arrays.toString(mSsRsrpThresholds)); + } mSsRsrqThresholds = cc.getIntArray( CarrierConfigManager.KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY); - Rlog.i(TAG, "Applying 5G NR SSRSRQ Thresholds: " + Arrays.toString(mSsRsrqThresholds)); + if (VDBG) { + Rlog.i(TAG, "Applying 5G NR SSRSRQ Thresholds: " + + Arrays.toString(mSsRsrqThresholds)); + } mSsSinrThresholds = cc.getIntArray( CarrierConfigManager.KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY); - Rlog.i(TAG, "Applying 5G NR SSSINR Thresholds: " + Arrays.toString(mSsSinrThresholds)); + if (VDBG) { + Rlog.i(TAG, "Applying 5G NR SSSINR Thresholds: " + + Arrays.toString(mSsSinrThresholds)); + } } int ssRsrpLevel = SignalStrength.INVALID; int ssRsrqLevel = SignalStrength.INVALID; int ssSinrLevel = SignalStrength.INVALID; if (isLevelForParameter(USE_SSRSRP)) { ssRsrpLevel = updateLevelWithMeasure(mSsRsrp, mSsRsrpThresholds); - Rlog.i(TAG, "Updated 5G NR SSRSRP Level: " + ssRsrpLevel); + if (VDBG) { + Rlog.i(TAG, "Updated 5G NR SSRSRP Level: " + ssRsrpLevel); + } } if (isLevelForParameter(USE_SSRSRQ)) { ssRsrqLevel = updateLevelWithMeasure(mSsRsrq, mSsRsrqThresholds); - Rlog.i(TAG, "Updated 5G NR SSRSRQ Level: " + ssRsrqLevel); + if (VDBG) { + Rlog.i(TAG, "Updated 5G NR SSRSRQ Level: " + ssRsrqLevel); + } } if (isLevelForParameter(USE_SSSINR)) { ssSinrLevel = updateLevelWithMeasure(mSsSinr, mSsSinrThresholds); - Rlog.i(TAG, "Updated 5G NR SSSINR Level: " + ssSinrLevel); + if (VDBG) { + Rlog.i(TAG, "Updated 5G NR SSSINR Level: " + ssSinrLevel); + } } // Apply the smaller value among three levels of three measures. mLevel = Math.min(Math.min(ssRsrpLevel, ssRsrqLevel), ssSinrLevel); diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index 8e83c4c11992..6c920f1950ac 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -801,11 +801,7 @@ public final class SmsManager { "Invalid pdu format. format must be either 3gpp or 3gpp2"); } try { - ISms iSms = ISms.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSmsServiceRegisterer() - .get()); + ISms iSms = TelephonyManager.getSmsService(); if (iSms != null) { iSms.injectSmsPduForSubscriber( getSubscriptionId(), pdu, format, receivedIntent); @@ -1642,7 +1638,7 @@ public final class SmsManager { * the service does not exist. */ private static ISms getISmsServiceOrThrow() { - ISms iSms = getISmsService(); + ISms iSms = TelephonyManager.getSmsService(); if (iSms == null) { throw new UnsupportedOperationException("Sms is not supported"); } @@ -1650,11 +1646,7 @@ public final class SmsManager { } private static ISms getISmsService() { - return ISms.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSmsServiceRegisterer() - .get()); + return TelephonyManager.getSmsService(); } /** @@ -2091,11 +2083,7 @@ public final class SmsManager { public boolean isSMSPromptEnabled() { ISms iSms = null; try { - iSms = ISms.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSmsServiceRegisterer() - .get()); + iSms = TelephonyManager.getSmsService(); return iSms.isSMSPromptEnabled(); } catch (RemoteException ex) { return false; diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index 6d82e51c14af..37d3d32efc34 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -627,7 +627,7 @@ public class SmsMessage { destinationAddress, message, statusReportRequested); } - return new SubmitPdu(spb); + return spb != null ? new SubmitPdu(spb) : null; } /** @@ -655,7 +655,7 @@ public class SmsMessage { destinationAddress, destinationPort, data, statusReportRequested); } - return new SubmitPdu(spb); + return spb != null ? new SubmitPdu(spb) : null; } // TODO: SubmitPdu class is used for SMS-DELIVER also now. Refactor for SubmitPdu and new diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 97f50fd314b8..b32e9d73a141 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1141,11 +1141,7 @@ public class SubscriptionManager { SubscriptionInfo subInfo = null; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { subInfo = iSub.getActiveSubscriptionInfo(subId, mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1179,11 +1175,7 @@ public class SubscriptionManager { SubscriptionInfo result = null; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getActiveSubscriptionInfoForIccId(iccId, mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1217,11 +1209,7 @@ public class SubscriptionManager { SubscriptionInfo result = null; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getActiveSubscriptionInfoForSimSlotIndex(slotIndex, mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1245,11 +1233,7 @@ public class SubscriptionManager { List<SubscriptionInfo> result = null; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getAllSubInfoList(mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1330,11 +1314,7 @@ public class SubscriptionManager { List<SubscriptionInfo> activeList = null; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1385,11 +1365,7 @@ public class SubscriptionManager { List<SubscriptionInfo> result = null; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getAvailableSubscriptionInfoList(mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1428,11 +1404,7 @@ public class SubscriptionManager { List<SubscriptionInfo> result = null; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getAccessibleSubscriptionInfoList(mContext.getOpPackageName()); } @@ -1461,11 +1433,7 @@ public class SubscriptionManager { public void requestEmbeddedSubscriptionInfoListRefresh() { int cardId = TelephonyManager.from(mContext).getCardIdForDefaultEuicc(); try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId); } @@ -1494,11 +1462,7 @@ public class SubscriptionManager { @SystemApi public void requestEmbeddedSubscriptionInfoListRefresh(int cardId) { try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId); } @@ -1519,11 +1483,7 @@ public class SubscriptionManager { int result = 0; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getAllSubInfoCount(mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1552,11 +1512,7 @@ public class SubscriptionManager { int result = 0; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getActiveSubInfoCount(mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1577,11 +1533,7 @@ public class SubscriptionManager { int result = 0; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getActiveSubInfoCountMax(); } @@ -1638,11 +1590,7 @@ public class SubscriptionManager { } try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub == null) { Log.e(LOG_TAG, "[addSubscriptionInfoRecord]- ISub service is null"); return; @@ -1676,11 +1624,7 @@ public class SubscriptionManager { } try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub == null) { Log.e(LOG_TAG, "[removeSubscriptionInfoRecord]- ISub service is null"); return; @@ -1783,11 +1727,7 @@ public class SubscriptionManager { int result = INVALID_SIM_SLOT_INDEX; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getSlotIndex(subscriptionId); } @@ -1821,11 +1761,7 @@ public class SubscriptionManager { int[] subId = null; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { subId = iSub.getSubId(slotIndex); } @@ -1849,11 +1785,7 @@ public class SubscriptionManager { int result = INVALID_PHONE_INDEX; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getPhoneId(subId); } @@ -1887,11 +1819,7 @@ public class SubscriptionManager { int subId = INVALID_SUBSCRIPTION_ID; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { subId = iSub.getDefaultSubId(); } @@ -1914,11 +1842,7 @@ public class SubscriptionManager { int subId = INVALID_SUBSCRIPTION_ID; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { subId = iSub.getDefaultVoiceSubId(); } @@ -1948,11 +1872,7 @@ public class SubscriptionManager { public void setDefaultVoiceSubscriptionId(int subscriptionId) { if (VDBG) logd("setDefaultVoiceSubId sub id = " + subscriptionId); try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { iSub.setDefaultVoiceSubId(subscriptionId); } @@ -2000,11 +1920,7 @@ public class SubscriptionManager { int subId = INVALID_SUBSCRIPTION_ID; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { subId = iSub.getDefaultSmsSubId(); } @@ -2030,11 +1946,7 @@ public class SubscriptionManager { public void setDefaultSmsSubId(int subscriptionId) { if (VDBG) logd("setDefaultSmsSubId sub id = " + subscriptionId); try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { iSub.setDefaultSmsSubId(subscriptionId); } @@ -2072,11 +1984,7 @@ public class SubscriptionManager { int subId = INVALID_SUBSCRIPTION_ID; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { subId = iSub.getDefaultDataSubId(); } @@ -2102,11 +2010,7 @@ public class SubscriptionManager { public void setDefaultDataSubId(int subscriptionId) { if (VDBG) logd("setDataSubscription sub id = " + subscriptionId); try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { iSub.setDefaultDataSubId(subscriptionId); } @@ -2137,11 +2041,7 @@ public class SubscriptionManager { /** @hide */ public void clearSubscriptionInfo() { try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { iSub.clearSubInfo(); } @@ -2277,11 +2177,7 @@ public class SubscriptionManager { */ public @NonNull int[] getActiveSubscriptionIdList(boolean visibleOnly) { try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { int[] subId = iSub.getActiveSubIdList(visibleOnly); if (subId != null) return subId; @@ -2332,11 +2228,7 @@ public class SubscriptionManager { int simState = TelephonyManager.SIM_STATE_UNKNOWN; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { simState = iSub.getSimStateForSlotIndex(slotIndex); } @@ -2355,11 +2247,7 @@ public class SubscriptionManager { */ public static void setSubscriptionProperty(int subId, String propKey, String propValue) { try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { iSub.setSubscriptionProperty(subId, propKey, propValue); } @@ -2379,11 +2267,7 @@ public class SubscriptionManager { Context context) { String resultValue = null; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { resultValue = iSub.getSubscriptionProperty(subId, propKey, context.getOpPackageName(), context.getFeatureId()); @@ -2547,11 +2431,7 @@ public class SubscriptionManager { @UnsupportedAppUsage public boolean isActiveSubId(int subId) { try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { return iSub.isActiveSubId(subId, mContext.getOpPackageName(), mContext.getFeatureId()); @@ -2747,11 +2627,7 @@ public class SubscriptionManager { @TelephonyManager.SetOpportunisticSubscriptionResult Consumer<Integer> callback) { if (VDBG) logd("[setPreferredDataSubscriptionId]+ subId:" + subId); try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub == null) return; ISetOpportunisticDataCallback callbackStub = new ISetOpportunisticDataCallback.Stub() { @@ -2794,11 +2670,7 @@ public class SubscriptionManager { public int getPreferredDataSubscriptionId() { int preferredSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { preferredSubId = iSub.getPreferredDataSubscriptionId(); } @@ -2829,11 +2701,7 @@ public class SubscriptionManager { List<SubscriptionInfo> subInfoList = null; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { subInfoList = iSub.getOpportunisticSubscriptions(contextPkg, contextFeature); } @@ -2934,11 +2802,7 @@ public class SubscriptionManager { ParcelUuid groupUuid = null; int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray(); try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { groupUuid = iSub.createSubscriptionGroup(subIdArray, pkgForDebug); } else { @@ -2988,11 +2852,7 @@ public class SubscriptionManager { int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray(); try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { iSub.addSubscriptionsIntoGroup(subIdArray, groupUuid, pkgForDebug); } else { @@ -3044,11 +2904,7 @@ public class SubscriptionManager { int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray(); try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { iSub.removeSubscriptionsFromGroup(subIdArray, groupUuid, pkgForDebug); } else { @@ -3093,11 +2949,7 @@ public class SubscriptionManager { List<SubscriptionInfo> result = null; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getSubscriptionsInGroup(groupUuid, contextPkg, contextFeature); } else { @@ -3210,11 +3062,7 @@ public class SubscriptionManager { logd("setSubscriptionActivated subId= " + subscriptionId + " enable " + enable); } try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { return iSub.setSubscriptionEnabled(enable, subscriptionId); } @@ -3303,11 +3151,7 @@ public class SubscriptionManager { @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSubscriptionEnabled(int subscriptionId) { try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { return iSub.isSubscriptionEnabled(subscriptionId); } @@ -3330,11 +3174,7 @@ public class SubscriptionManager { int subId = INVALID_SUBSCRIPTION_ID; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { subId = iSub.getEnabledSubscriptionId(slotIndex); } @@ -3360,11 +3200,7 @@ public class SubscriptionManager { int result = 0; try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = helper.callMethod(iSub); } @@ -3387,11 +3223,7 @@ public class SubscriptionManager { */ public static int getActiveDataSubscriptionId() { try { - ISub iSub = ISub.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getSubscriptionServiceRegisterer() - .get()); + ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { return iSub.getActiveDataSubscriptionId(); } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index e520a02c2721..15103bfb889d 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -54,6 +54,7 @@ import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.IBinder; import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; @@ -93,6 +94,7 @@ import android.util.Log; import android.util.Pair; import com.android.ims.internal.IImsServiceFeatureCallback; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.CellNetworkScanResult; import com.android.internal.telephony.IBooleanConsumer; @@ -100,6 +102,8 @@ import com.android.internal.telephony.INumberVerificationCallback; import com.android.internal.telephony.IOns; import com.android.internal.telephony.IPhoneSubInfo; import com.android.internal.telephony.ISetOpportunisticDataCallback; +import com.android.internal.telephony.ISms; +import com.android.internal.telephony.ISub; import com.android.internal.telephony.ITelephony; import com.android.internal.telephony.IUpdateAvailableNetworksCallback; import com.android.internal.telephony.OperatorInfo; @@ -295,6 +299,21 @@ public class TelephonyManager { private SubscriptionManager mSubscriptionManager; private TelephonyScanManager mTelephonyScanManager; + /** Cached service handles, cleared by resetServiceHandles() at death */ + private static final Object sCacheLock = new Object(); + + /** @hide */ + private static boolean sServiceHandleCacheEnabled = true; + + @GuardedBy("sCacheLock") + private static IPhoneSubInfo sIPhoneSubInfo; + @GuardedBy("sCacheLock") + private static ISub sISub; + @GuardedBy("sCacheLock") + private static ISms sISms; + @GuardedBy("sCacheLock") + private static final DeathRecipient sServiceDeath = new DeathRecipient(); + /** Enum indicating multisim variants * DSDS - Dual SIM Dual Standby * DSDA - Dual SIM Dual Active @@ -1991,7 +2010,7 @@ public class TelephonyManager { public String getDeviceId(int slotIndex) { // FIXME this assumes phoneId == slotIndex try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; return info.getDeviceIdForPhone(slotIndex, mContext.getOpPackageName(), @@ -2245,7 +2264,7 @@ public class TelephonyManager { private String getNaiBySubscriberId(int subId) { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; String nai = info.getNaiForSubscriber(subId, mContext.getOpPackageName(), @@ -3944,7 +3963,7 @@ public class TelephonyManager { @UnsupportedAppUsage public String getSimSerialNumber(int subId) { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; return info.getIccSerialNumberForSubscriber(subId, mContext.getOpPackageName(), @@ -4217,7 +4236,7 @@ public class TelephonyManager { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public String getSubscriberId(int subId) { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; return info.getSubscriberIdForSubscriber(subId, mContext.getOpPackageName(), @@ -4253,7 +4272,7 @@ public class TelephonyManager { @Nullable public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(@KeyType int keyType) { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) { Rlog.e(TAG,"IMSI error: Subscriber Info is null"); return null; @@ -4296,7 +4315,7 @@ public class TelephonyManager { @SystemApi public void resetCarrierKeysForImsiEncryption() { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) { Rlog.e(TAG, "IMSI error: Subscriber Info is null"); if (!isSystemProcess()) { @@ -4361,7 +4380,7 @@ public class TelephonyManager { */ public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo) { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return; info.setCarrierInfoForImsiEncryption(mSubId, mContext.getOpPackageName(), imsiEncryptionInfo); @@ -4385,7 +4404,7 @@ public class TelephonyManager { @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getGroupIdLevel1() { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; return info.getGroupIdLevel1ForSubscriber(getSubId(), mContext.getOpPackageName(), @@ -4409,7 +4428,7 @@ public class TelephonyManager { @UnsupportedAppUsage public String getGroupIdLevel1(int subId) { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; return info.getGroupIdLevel1ForSubscriber(subId, mContext.getOpPackageName(), @@ -4472,7 +4491,7 @@ public class TelephonyManager { return number; } try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; return info.getLine1NumberForSubscriber(subId, mContext.getOpPackageName(), @@ -4563,7 +4582,7 @@ public class TelephonyManager { return alphaTag; } try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; return info.getLine1AlphaTagForSubscriber(subId, getOpPackageName(), @@ -4651,7 +4670,7 @@ public class TelephonyManager { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public String getMsisdn(int subId) { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; return info.getMsisdnForSubscriber(subId, getOpPackageName(), getFeatureId()); @@ -4685,7 +4704,7 @@ public class TelephonyManager { @UnsupportedAppUsage public String getVoiceMailNumber(int subId) { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; return info.getVoiceMailNumberForSubscriber(subId, getOpPackageName(), @@ -5284,7 +5303,7 @@ public class TelephonyManager { @UnsupportedAppUsage public String getVoiceMailAlphaTag(int subId) { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; return info.getVoiceMailAlphaTagForSubscriber(subId, getOpPackageName(), @@ -5332,7 +5351,7 @@ public class TelephonyManager { @UnsupportedAppUsage public String getIsimImpi() { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; //get the Isim Impi based on subId @@ -5359,7 +5378,7 @@ public class TelephonyManager { @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain() { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; //get the Isim Domain based on subId @@ -5383,7 +5402,7 @@ public class TelephonyManager { @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String[] getIsimImpu() { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; //get the Isim Impu based on subId @@ -5396,19 +5415,6 @@ public class TelephonyManager { } } - /** - * @hide - */ - @UnsupportedAppUsage - private IPhoneSubInfo getSubscriberInfo() { - // get it each time because that process crashes a lot - return IPhoneSubInfo.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getPhoneSubServiceRegisterer() - .get()); - } - /** * Device call state: No activity. */ @@ -5465,6 +5471,14 @@ public class TelephonyManager { } /** + * @hide + */ + @UnsupportedAppUsage + private IPhoneSubInfo getSubscriberInfo() { + return getSubscriberInfoService(); + } + + /** * Returns the Telephony call state for calls on a specific SIM slot. * <p> * Note: This method considers ONLY telephony/mobile calls, where {@link #getCallState()} @@ -7124,7 +7138,7 @@ public class TelephonyManager { @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst() { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; //get the Isim Ist based on subId @@ -7146,7 +7160,7 @@ public class TelephonyManager { @UnsupportedAppUsage public String[] getIsimPcscf() { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; //get the Isim Pcscf based on subId @@ -7227,7 +7241,7 @@ public class TelephonyManager { @UnsupportedAppUsage public String getIccAuthentication(int subId, int appType, int authType, String data) { try { - IPhoneSubInfo info = getSubscriberInfo(); + IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) return null; return info.getIccSimChallengeResponse(subId, appType, authType, data); @@ -13086,4 +13100,153 @@ public class TelephonyManager { throw e.rethrowFromSystemServer(); } } + + private static class DeathRecipient implements IBinder.DeathRecipient { + @Override + public void binderDied() { + resetServiceCache(); + } + } + + /** + * Reset everything in the service cache; if one handle died then they are + * all probably broken. + * @hide + */ + private static void resetServiceCache() { + synchronized (sCacheLock) { + if (sISub != null) { + sISub.asBinder().unlinkToDeath(sServiceDeath, 0); + sISub = null; + } + if (sISms != null) { + sISms.asBinder().unlinkToDeath(sServiceDeath, 0); + sISms = null; + } + if (sIPhoneSubInfo != null) { + sIPhoneSubInfo.asBinder().unlinkToDeath(sServiceDeath, 0); + sIPhoneSubInfo = null; + } + } + } + + /** + * @hide + */ + static IPhoneSubInfo getSubscriberInfoService() { + // Keeps cache disabled until test fixes are checked into AOSP. + if (true) { + return IPhoneSubInfo.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getPhoneSubServiceRegisterer() + .get()); + } + + if (sIPhoneSubInfo == null) { + IPhoneSubInfo temp = IPhoneSubInfo.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getPhoneSubServiceRegisterer() + .get()); + synchronized (sCacheLock) { + if (sIPhoneSubInfo == null && temp != null) { + try { + sIPhoneSubInfo = temp; + sIPhoneSubInfo.asBinder().linkToDeath(sServiceDeath, 0); + } catch (Exception e) { + // something has gone horribly wrong + sIPhoneSubInfo = null; + } + } + } + } + return sIPhoneSubInfo; + } + + /** + * @hide + */ + static ISub getSubscriptionService() { + // Keeps cache disabled until test fixes are checked into AOSP. + if (true) { + return ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); + } + + if (sISub == null) { + ISub temp = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); + synchronized (sCacheLock) { + if (sISub == null && temp != null) { + try { + sISub = temp; + sISub.asBinder().linkToDeath(sServiceDeath, 0); + } catch (Exception e) { + // something has gone horribly wrong + sISub = null; + } + } + } + } + return sISub; + } + + /** + * @hide + */ + static ISms getSmsService() { + // Keeps cache disabled until test fixes are checked into AOSP. + if (true) { + return ISms.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSmsServiceRegisterer() + .get()); + } + + if (sISms == null) { + ISms temp = ISms.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSmsServiceRegisterer() + .get()); + synchronized (sCacheLock) { + if (sISms == null && temp != null) { + try { + sISms = temp; + sISms.asBinder().linkToDeath(sServiceDeath, 0); + } catch (Exception e) { + // something has gone horribly wrong + sISms = null; + } + } + } + } + return sISms; + } + + /** + * Disables service handle caching for tests that utilize mock services. + * @hide + */ + @VisibleForTesting + public static void disableServiceHandleCaching() { + sServiceHandleCacheEnabled = false; + } + + /** + * Reenables service handle caching. + * @hide + */ + @VisibleForTesting + public static void enableServiceHandleCaching() { + sServiceHandleCacheEnabled = true; + } } diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java index d4832917908e..c506cd5879d8 100644 --- a/telephony/java/android/telephony/ims/ImsRcsManager.java +++ b/telephony/java/android/telephony/ims/ImsRcsManager.java @@ -59,6 +59,7 @@ public class ImsRcsManager implements RegistrationManager { * @see #isAvailable(int) * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback) * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback) + * @hide */ public static class AvailabilityCallback { @@ -141,14 +142,16 @@ public class ImsRcsManager implements RegistrationManager { /** * @return A {@link RcsUceAdapter} used for User Capability Exchange (UCE) operations for * this subscription. - * @hide */ @NonNull public RcsUceAdapter getUceAdapter() { return new RcsUceAdapter(mSubId); } - /**{@inheritDoc}*/ + /** + * {@inheritDoc} + * @hide + */ @Override @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback( @@ -177,7 +180,10 @@ public class ImsRcsManager implements RegistrationManager { } } - /**{@inheritDoc}*/ + /** + * {@inheritDoc + * @hide + */ @Override @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback( @@ -199,7 +205,10 @@ public class ImsRcsManager implements RegistrationManager { } } - /**{@inheritDoc}*/ + /** + * {@inheritDoc} + * @hide + */ @Override @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull @CallbackExecutor Executor executor, @@ -229,7 +238,10 @@ public class ImsRcsManager implements RegistrationManager { } } - /**{@inheritDoc}*/ + /** + * {@inheritDoc} + * @hide + */ @Override @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor, @@ -280,6 +292,7 @@ public class ImsRcsManager implements RegistrationManager { * {@link ImsRcsManager} is valid, but the ImsService associated with the subscription is not * available. This can happen if the ImsService has crashed, for example, or if the subscription * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. + * @hide */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerRcsAvailabilityCallback(@NonNull @CallbackExecutor Executor executor, @@ -319,6 +332,7 @@ public class ImsRcsManager implements RegistrationManager { * @throws ImsException if the IMS service is not available when calling this method * {@link ImsRcsController#unregisterRcsAvailabilityCallback()}. * See {@link ImsException#getCode()} for more information on the error codes. + * @hide */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterRcsAvailabilityCallback(@NonNull AvailabilityCallback c) @@ -361,6 +375,7 @@ public class ImsRcsManager implements RegistrationManager { * @throws ImsException if the IMS service is not available when calling this method * {@link ImsRcsController#isCapable(int, int)}. * See {@link ImsException#getCode()} for more information on the error codes. + * @hide */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability, @@ -395,6 +410,7 @@ public class ImsRcsManager implements RegistrationManager { * @throws ImsException if the IMS service is not available when calling this method * {@link ImsRcsController#isAvailable(int, int)}. * See {@link ImsException#getCode()} for more information on the error codes. + * @hide */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java index 72a00ce052da..fc7c1ee99430 100644 --- a/telephony/java/android/telephony/ims/RcsUceAdapter.java +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -51,55 +51,67 @@ public class RcsUceAdapter { /** * An unknown error has caused the request to fail. + * @hide */ public static final int ERROR_GENERIC_FAILURE = 1; /** * The carrier network does not have UCE support enabled for this subscriber. + * @hide */ public static final int ERROR_NOT_ENABLED = 2; /** * The data network that the device is connected to does not support UCE currently (e.g. it is * 1x only currently). + * @hide */ public static final int ERROR_NOT_AVAILABLE = 3; /** * The network has responded with SIP 403 error and a reason "User not registered." + * @hide */ public static final int ERROR_NOT_REGISTERED = 4; /** * The network has responded to this request with a SIP 403 error and reason "not authorized for * presence" for this subscriber. + * @hide */ public static final int ERROR_NOT_AUTHORIZED = 5; /** * The network has responded to this request with a SIP 403 error and no reason. + * @hide */ public static final int ERROR_FORBIDDEN = 6; /** * The contact URI requested is not provisioned for VoLTE or it is not known as an IMS * subscriber to the carrier network. + * @hide */ public static final int ERROR_NOT_FOUND = 7; /** * The capabilities request contained too many URIs for the carrier network to handle. Retry * with a lower number of contact numbers. The number varies per carrier. + * @hide */ // TODO: Try to integrate this into the API so that the service will split based on carrier. public static final int ERROR_REQUEST_TOO_LARGE = 8; /** * The network did not respond to the capabilities request before the request timed out. + * @hide */ public static final int ERROR_REQUEST_TIMEOUT = 10; /** * The request failed due to the service having insufficient memory. + * @hide */ public static final int ERROR_INSUFFICIENT_MEMORY = 11; /** * The network was lost while trying to complete the request. + * @hide */ public static final int ERROR_LOST_NETWORK = 12; /** * The request has failed because the same request has already been added to the queue. + * @hide */ public static final int ERROR_ALREADY_IN_QUEUE = 13; @@ -124,28 +136,33 @@ public class RcsUceAdapter { /** * The last publish has resulted in a "200 OK" response or the device is using SIP OPTIONS for * UCE. + * @hide */ public static final int PUBLISH_STATE_200_OK = 1; /** * The hasn't published its capabilities since boot or hasn't gotten any publish response yet. + * @hide */ public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; /** * The device has tried to publish its capabilities, which has resulted in an error. This error * is related to the fact that the device is not VoLTE provisioned. + * @hide */ public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3; /** * The device has tried to publish its capabilities, which has resulted in an error. This error * is related to the fact that the device is not RCS or UCE provisioned. + * @hide */ public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; /** * The last publish resulted in a "408 Request Timeout" response. + * @hide */ public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; @@ -154,6 +171,7 @@ public class RcsUceAdapter { * or SIP 423 - "Interval too short". * <p> * Device shall retry with exponential back-off. + * @hide */ public static final int PUBLISH_STATE_OTHER_ERROR = 6; @@ -174,6 +192,7 @@ public class RcsUceAdapter { * Provides a one-time callback for the response to a UCE request. After this callback is called * by the framework, the reference to this callback will be discarded on the service side. * @see #requestCapabilities(Executor, List, CapabilitiesCallback) + * @hide */ public static class CapabilitiesCallback { @@ -225,6 +244,7 @@ public class RcsUceAdapter { * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not * available. This can happen if the ImsService has crashed, for example, or if the subscription * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. + * @hide */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void requestCapabilities(@NonNull @CallbackExecutor Executor executor, @@ -287,6 +307,7 @@ public class RcsUceAdapter { * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not * available. This can happen if the ImsService has crashed, for example, or if the subscription * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. + * @hide */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public @PublishState int getUcePublishState() throws ImsException { @@ -307,14 +328,13 @@ public class RcsUceAdapter { } /** - * The user’s setting for whether or not Presence and User Capability Exchange (UCE) is enabled - * for the associated subscription. + * The user’s setting for whether or not User Capability Exchange (UCE) is enabled for the + * associated subscription. + * <p> + * Note: This setting does not affect whether or not the device publishes its service + * capabilities if the subscription supports presence publication. * - * @return true if the user’s setting for UCE is enabled, false otherwise. If false, - * {@link ImsRcsManager#isCapable(int, int)} will return false for - * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} and - * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} - * @see #setUceSettingEnabled(boolean) + * @return true if the user’s setting for UCE is enabled, false otherwise. * @throws ImsException if the subscription associated with this instance of * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not * available. This can happen if the ImsService has crashed, for example, or if the subscription @@ -340,12 +360,12 @@ public class RcsUceAdapter { /** * Change the user’s setting for whether or not UCE is enabled for the associated subscription. - * @param isEnabled the user's setting for whether or not they wish for Presence and User - * Capability Exchange to be enabled. If false, - * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} and - * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} capability will be - * disabled, depending on which type of UCE the carrier supports. - * @see #isUceSettingEnabled() + * <p> + * Note: This setting does not affect whether or not the device publishes its service + * capabilities if the subscription supports presence publication. + * + * @param isEnabled the user's setting for whether or not they wish for User + * Capability Exchange to be enabled. * @throws ImsException if the subscription associated with this instance of * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not * available. This can happen if the ImsService has crashed, for example, or if the subscription diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java index 8e67621b2ea3..98b0bcf6075b 100644 --- a/telephony/java/android/telephony/ims/feature/RcsFeature.java +++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java @@ -193,6 +193,7 @@ public class RcsFeature extends ImsFeature { * of the capability and notify the capability status as true using * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the * framework that the capability is available for usage. + * @hide */ public static class RcsImsCapabilities extends Capabilities { /** @hide*/ @@ -286,6 +287,7 @@ public class RcsFeature extends ImsFeature { * set, the {@link RcsFeature} has brought up the capability and is ready for framework * requests. To change the status of the capabilities * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called. + * @hide */ @Override public @NonNull final RcsImsCapabilities queryCapabilityStatus() { @@ -296,6 +298,7 @@ public class RcsFeature extends ImsFeature { * Notify the framework that the capabilities status has changed. If a capability is enabled, * this signals to the framework that the capability has been initialized and is ready. * Call {@link #queryCapabilityStatus()} to return the current capability status. + * @hide */ public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities c) { if (c == null) { @@ -310,6 +313,7 @@ public class RcsFeature extends ImsFeature { * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} to * enable or disable capability A, this method should return the correct configuration for * capability A afterwards (until it has changed). + * @hide */ public boolean queryCapabilityConfiguration( @RcsImsCapabilities.RcsImsCapabilityFlag int capability, @@ -331,6 +335,7 @@ public class RcsFeature extends ImsFeature { * If for some reason one or more of these capabilities can not be enabled/disabled, * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError(int, int, int)} should * be called for each capability change that resulted in an error. + * @hide */ @Override public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request, @@ -349,6 +354,7 @@ public class RcsFeature extends ImsFeature { * * @return An instance of {@link RcsSipOptionsImplBase} that implements SIP options exchange if * it is supported by the device. + * @hide */ public @NonNull RcsSipOptionsImplBase getOptionsExchangeImpl() { // Base Implementation, override to implement functionality @@ -364,6 +370,7 @@ public class RcsFeature extends ImsFeature { * * @return An instance of {@link RcsPresenceExchangeImplBase} that implements presence * exchange if it is supported by the device. + * @hide */ public @NonNull RcsPresenceExchangeImplBase getPresenceExchangeImpl() { // Base Implementation, override to implement functionality. diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java index a24af2f74e27..fda295a27111 100644 --- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java +++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java @@ -17,8 +17,6 @@ package android.telephony.ims.stub; import android.annotation.IntDef; -import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.RemoteException; import android.telephony.ims.ImsException; import android.telephony.ims.aidl.IRcsFeatureListener; @@ -34,8 +32,6 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ -@SystemApi -@TestApi public class RcsCapabilityExchange { /** Service is unknown. */ diff --git a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java index f200ea2af2bc..bb034489a296 100644 --- a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java +++ b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java @@ -18,8 +18,6 @@ package android.telephony.ims.stub; import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.SystemApi; -import android.annotation.TestApi; import android.net.Uri; import android.os.RemoteException; import android.telephony.ims.ImsException; @@ -39,8 +37,6 @@ import java.util.List; * * @hide */ -@SystemApi -@TestApi public class RcsPresenceExchangeImplBase extends RcsCapabilityExchange { private static final String LOG_TAG = "RcsPresenceExchangeIB"; diff --git a/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java index 355c4dde75d8..2035fac4fae0 100644 --- a/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java +++ b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java @@ -19,8 +19,6 @@ package android.telephony.ims.stub; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.annotation.TestApi; import android.net.Uri; import android.os.RemoteException; import android.telephony.ims.ImsException; @@ -37,8 +35,6 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ -@SystemApi -@TestApi public class RcsSipOptionsImplBase extends RcsCapabilityExchange { private static final String LOG_TAG = "RcsSipOptionsImplBase"; diff --git a/tests/BootImageProfileTest/AndroidTest.xml b/tests/BootImageProfileTest/AndroidTest.xml index b4f2663585cc..d7f820411f27 100644 --- a/tests/BootImageProfileTest/AndroidTest.xml +++ b/tests/BootImageProfileTest/AndroidTest.xml @@ -19,18 +19,8 @@ --> <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> - <!-- we need this magic flag, otherwise it always reboots and breaks the selinux --> + <!-- we need this magic flag, otherwise it always reboots and breaks selinux --> <option name="force-skip-system-props" value="true" /> - - <option name="run-command" value="device_config put runtime_native_boot profilesystemserver true" /> - <option name="run-command" value="device_config put runtime_native_boot profilebootclasspath true" /> - - <!-- Profiling does not pick up the above changes we restart the shell --> - <option name="run-command" value="stop" /> - <option name="run-command" value="start" /> - - <!-- give it some time to restart the shell; otherwise the first unit test might fail --> - <option name="run-command" value="sleep 2" /> </target_preparer> <test class="com.android.tradefed.testtype.HostTest" > diff --git a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java index 10f3e54a5f96..1c8b6be49547 100644 --- a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java +++ b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java @@ -42,10 +42,9 @@ public class BootImageProfileTest implements IDeviceTest { } /** - * Test that the boot image profile properties are set. + * Validate that the boot image profile properties are set. */ - @Test - public void testProperties() throws Exception { + public void validateProperties() throws Exception { String res = mTestDevice.getProperty( "persist.device_config.runtime_native_boot.profilebootclasspath"); assertTrue("profile boot class path not enabled", res != null && res.equals("true")); @@ -67,13 +66,37 @@ public class BootImageProfileTest implements IDeviceTest { @Test public void testSystemServerProfile() throws Exception { + final int numIterations = 20; + for (int i = 1; i <= numIterations; ++i) { + String res; + res = mTestDevice.getProperty( + "persist.device_config.runtime_native_boot.profilebootclasspath"); + boolean profileBootClassPath = res != null && res.equals("true"); + res = mTestDevice.getProperty( + "persist.device_config.runtime_native_boot.profilesystemserver"); + boolean profileSystemServer = res != null && res.equals("true"); + if (profileBootClassPath && profileSystemServer) { + break; + } + if (i == numIterations) { + assertTrue("profile system server not enabled", profileSystemServer); + assertTrue("profile boot class path not enabled", profileSystemServer); + } + + res = mTestDevice.executeShellCommand( + "device_config put runtime_native_boot profilebootclasspath true"); + res = mTestDevice.executeShellCommand( + "device_config put runtime_native_boot profilesystemserver true"); + res = mTestDevice.executeShellCommand("stop"); + res = mTestDevice.executeShellCommand("start"); + Thread.sleep(5000); + } // Trunacte the profile before force it to be saved to prevent previous profiles // causing the test to pass. String res; res = mTestDevice.executeShellCommand("truncate -s 0 " + SYSTEM_SERVER_PROFILE).trim(); assertTrue(res, res.length() == 0); // Wait up to 20 seconds for the profile to be saved. - final int numIterations = 20; for (int i = 1; i <= numIterations; ++i) { // Force save the profile since we truncated it. if (forceSaveProfile("system_server")) { @@ -89,6 +112,9 @@ public class BootImageProfileTest implements IDeviceTest { // In case the profile is partially saved, wait an extra second. Thread.sleep(1000); + // Validate that properties are still set. + validateProperties(); + // Validate that the profile is non empty. res = mTestDevice.executeShellCommand("profman --dump-only --profile-file=" + SYSTEM_SERVER_PROFILE); diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp index 31aa2494052c..e7a82034c77a 100644 --- a/tools/aapt2/dump/DumpManifest.cpp +++ b/tools/aapt2/dump/DumpManifest.cpp @@ -1188,7 +1188,7 @@ class Activity : public ManifestExtractor::Element { } // Fully qualify the activity name - ssize_t idx = name.find("."); + ssize_t idx = name.find('.'); if (idx == 0) { name = package + name; } else if (idx < 0) { @@ -2138,7 +2138,7 @@ bool ManifestExtractor::Dump(text::Printer* printer, IDiagnostics* diag) { size_t pos = file_path.find("lib/"); if (pos != std::string::npos) { file_path = file_path.substr(pos + 4); - pos = file_path.find("/"); + pos = file_path.find('/'); if (pos != std::string::npos) { file_path = file_path.substr(0, pos); } diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp index 10e504ec0752..09ea03b23c9a 100644 --- a/tools/aapt2/java/ManifestClassGenerator.cpp +++ b/tools/aapt2/java/ManifestClassGenerator.cpp @@ -39,7 +39,7 @@ static Maybe<std::string> ExtractJavaIdentifier(IDiagnostics* diag, const Source } // Normalize only the java identifier, leave the original value unchanged. - if (result.find("-") != std::string::npos) { + if (result.find('-') != std::string::npos) { result = JavaClassGenerator::TransformToFieldName(result); } diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 0cce23d196cf..dc5ac1e6bd5a 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -2140,7 +2140,8 @@ public class WifiConfiguration implements Parcelable { public boolean isEnterprise() { return (allowedKeyManagement.get(KeyMgmt.WPA_EAP) || allowedKeyManagement.get(KeyMgmt.IEEE8021X) - || allowedKeyManagement.get(KeyMgmt.SUITE_B_192)) + || allowedKeyManagement.get(KeyMgmt.SUITE_B_192) + || allowedKeyManagement.get(KeyMgmt.WAPI_CERT)) && enterpriseConfig != null && enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE; } @@ -2416,6 +2417,9 @@ public class WifiConfiguration implements Parcelable { if (allowedKeyManagement.get(KeyMgmt.SUITE_B_192)) { keyMgmt += KeyMgmt.strings[KeyMgmt.SUITE_B_192]; } + if (allowedKeyManagement.get(KeyMgmt.WAPI_CERT)) { + keyMgmt += KeyMgmt.strings[KeyMgmt.WAPI_CERT]; + } if (TextUtils.isEmpty(keyMgmt)) { throw new IllegalStateException("Not an EAP network"); |