diff options
136 files changed, 3257 insertions, 3257 deletions
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index c9a4b3ba6368..43058d538552 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -51,11 +51,6 @@ cc_defaults { srcs: [ ":statsd_aidl", "src/active_config_list.proto", - "src/statsd_config.proto", - "src/uid_data.proto", - "src/FieldValue.cpp", - "src/hash.cpp", - "src/stats_log_util.cpp", "src/anomaly/AlarmMonitor.cpp", "src/anomaly/AlarmTracker.cpp", "src/anomaly/AnomalyTracker.cpp", @@ -63,51 +58,56 @@ cc_defaults { "src/anomaly/subscriber_util.cpp", "src/condition/CombinationConditionTracker.cpp", "src/condition/condition_util.cpp", - "src/condition/SimpleConditionTracker.cpp", "src/condition/ConditionWizard.cpp", + "src/condition/SimpleConditionTracker.cpp", "src/condition/StateConditionTracker.cpp", "src/config/ConfigKey.cpp", "src/config/ConfigListener.cpp", "src/config/ConfigManager.cpp", "src/external/GpuStatsPuller.cpp", "src/external/Perfetto.cpp", - "src/external/StatsPuller.cpp", + "src/external/PowerStatsPuller.cpp", + "src/external/puller_util.cpp", + "src/external/ResourceHealthManagerPuller.cpp", "src/external/StatsCallbackPuller.cpp", "src/external/StatsCompanionServicePuller.cpp", + "src/external/StatsPuller.cpp", + "src/external/StatsPullerManager.cpp", "src/external/SubsystemSleepStatePuller.cpp", - "src/external/PowerStatsPuller.cpp", - "src/external/ResourceHealthManagerPuller.cpp", "src/external/TrainInfoPuller.cpp", - "src/external/StatsPullerManager.cpp", - "src/external/puller_util.cpp", + "src/FieldValue.cpp", + "src/guardrail/StatsdStats.cpp", + "src/hash.cpp", + "src/HashableDimensionKey.cpp", "src/logd/LogEvent.cpp", "src/logd/LogEventQueue.cpp", "src/matchers/CombinationLogMatchingTracker.cpp", "src/matchers/EventMatcherWizard.cpp", "src/matchers/matcher_util.cpp", "src/matchers/SimpleLogMatchingTracker.cpp", - "src/metrics/MetricProducer.cpp", - "src/metrics/EventMetricProducer.cpp", "src/metrics/CountMetricProducer.cpp", - "src/metrics/DurationMetricProducer.cpp", - "src/metrics/duration_helper/OringDurationTracker.cpp", "src/metrics/duration_helper/MaxDurationTracker.cpp", - "src/metrics/ValueMetricProducer.cpp", + "src/metrics/duration_helper/OringDurationTracker.cpp", + "src/metrics/DurationMetricProducer.cpp", + "src/metrics/EventMetricProducer.cpp", "src/metrics/GaugeMetricProducer.cpp", - "src/metrics/MetricsManager.cpp", + "src/metrics/MetricProducer.cpp", "src/metrics/metrics_manager_util.cpp", + "src/metrics/MetricsManager.cpp", + "src/metrics/ValueMetricProducer.cpp", "src/packages/UidMap.cpp", - "src/storage/StorageManager.cpp", + "src/shell/shell_config.proto", + "src/shell/ShellSubscriber.cpp", + "src/socket/StatsSocketListener.cpp", + "src/stats_log_util.cpp", + "src/statscompanion_util.cpp", + "src/statsd_config.proto", "src/StatsLogProcessor.cpp", "src/StatsService.cpp", - "src/statscompanion_util.cpp", + "src/storage/StorageManager.cpp", "src/subscriber/IncidentdReporter.cpp", "src/subscriber/SubscriberReporter.cpp", - "src/HashableDimensionKey.cpp", - "src/guardrail/StatsdStats.cpp", - "src/socket/StatsSocketListener.cpp", - "src/shell/ShellSubscriber.cpp", - "src/shell/shell_config.proto", + "src/uid_data.proto", ], local_include_dirs: [ @@ -120,23 +120,23 @@ cc_defaults { ], shared_libs: [ + "android.frameworks.stats@1.0", + "android.hardware.health@2.0", + "android.hardware.power.stats@1.0", + "android.hardware.power@1.0", + "android.hardware.power@1.1", "libbase", "libbinder", + "libcutils", "libgraphicsenv", + "libhidlbase", "libincident", "liblog", - "libutils", - "libservices", "libprotoutil", + "libservices", "libstatslog", - "libhidlbase", - "android.frameworks.stats@1.0", - "android.hardware.health@2.0", - "android.hardware.power@1.0", - "android.hardware.power@1.1", - "android.hardware.power.stats@1.0", "libsysutils", - "libcutils", + "libutils", ], } @@ -210,54 +210,54 @@ cc_test { "src/atom_field_options.proto", "src/atoms.proto", - "src/stats_log.proto", "src/shell/shell_data.proto", + "src/stats_log.proto", "tests/AlarmMonitor_test.cpp", "tests/anomaly/AlarmTracker_test.cpp", "tests/anomaly/AnomalyTracker_test.cpp", + "tests/condition/CombinationConditionTracker_test.cpp", + "tests/condition/ConditionTimer_test.cpp", + "tests/condition/SimpleConditionTracker_test.cpp", + "tests/condition/StateConditionTracker_test.cpp", "tests/ConfigManager_test.cpp", - "tests/external/puller_util_test.cpp", + "tests/e2e/Alarm_e2e_test.cpp", + "tests/e2e/Anomaly_count_e2e_test.cpp", + "tests/e2e/Anomaly_duration_sum_e2e_test.cpp", + "tests/e2e/Attribution_e2e_test.cpp", + "tests/e2e/ConfigTtl_e2e_test.cpp", + "tests/e2e/DurationMetric_e2e_test.cpp", + "tests/e2e/GaugeMetric_e2e_pull_test.cpp", + "tests/e2e/GaugeMetric_e2e_push_test.cpp", + "tests/e2e/MetricActivation_e2e_test.cpp", + "tests/e2e/MetricConditionLink_e2e_test.cpp", + "tests/e2e/PartialBucket_e2e_test.cpp", + "tests/e2e/ValueMetric_pull_e2e_test.cpp", + "tests/e2e/WakelockDuration_e2e_test.cpp", "tests/external/GpuStatsPuller_test.cpp", "tests/external/IncidentReportArgs_test.cpp", + "tests/external/puller_util_test.cpp", "tests/external/StatsPuller_test.cpp", + "tests/FieldValue_test.cpp", + "tests/guardrail/StatsdStats_test.cpp", "tests/indexed_priority_queue_test.cpp", + "tests/log_event/LogEventQueue_test.cpp", "tests/LogEntryMatcher_test.cpp", "tests/LogEvent_test.cpp", - "tests/log_event/LogEventQueue_test.cpp", - "tests/MetricsManager_test.cpp", - "tests/StatsLogProcessor_test.cpp", - "tests/StatsService_test.cpp", - "tests/UidMap_test.cpp", - "tests/FieldValue_test.cpp", - "tests/condition/CombinationConditionTracker_test.cpp", - "tests/condition/SimpleConditionTracker_test.cpp", - "tests/condition/StateConditionTracker_test.cpp", - "tests/condition/ConditionTimer_test.cpp", - "tests/metrics/OringDurationTracker_test.cpp", - "tests/metrics/MaxDurationTracker_test.cpp", "tests/metrics/CountMetricProducer_test.cpp", "tests/metrics/DurationMetricProducer_test.cpp", "tests/metrics/EventMetricProducer_test.cpp", - "tests/metrics/ValueMetricProducer_test.cpp", "tests/metrics/GaugeMetricProducer_test.cpp", - "tests/guardrail/StatsdStats_test.cpp", + "tests/metrics/MaxDurationTracker_test.cpp", "tests/metrics/metrics_test_helper.cpp", + "tests/metrics/OringDurationTracker_test.cpp", + "tests/metrics/ValueMetricProducer_test.cpp", + "tests/MetricsManager_test.cpp", + "tests/shell/ShellSubscriber_test.cpp", "tests/statsd_test_util.cpp", + "tests/StatsLogProcessor_test.cpp", + "tests/StatsService_test.cpp", "tests/storage/StorageManager_test.cpp", - "tests/e2e/WakelockDuration_e2e_test.cpp", - "tests/e2e/MetricActivation_e2e_test.cpp", - "tests/e2e/MetricConditionLink_e2e_test.cpp", - "tests/e2e/Alarm_e2e_test.cpp", - "tests/e2e/Attribution_e2e_test.cpp", - "tests/e2e/GaugeMetric_e2e_push_test.cpp", - "tests/e2e/GaugeMetric_e2e_pull_test.cpp", - "tests/e2e/ValueMetric_pull_e2e_test.cpp", - "tests/e2e/Anomaly_count_e2e_test.cpp", - "tests/e2e/Anomaly_duration_sum_e2e_test.cpp", - "tests/e2e/ConfigTtl_e2e_test.cpp", - "tests/e2e/PartialBucket_e2e_test.cpp", - "tests/e2e/DurationMetric_e2e_test.cpp", - "tests/shell/ShellSubscriber_test.cpp", + "tests/UidMap_test.cpp", ], static_libs: [ @@ -287,17 +287,17 @@ cc_benchmark { // not included in libprotobuf-cpp-lite, so compile it here. ":libprotobuf-internal-protos", - "src/atom_field_options.proto", - "src/atoms.proto", - "src/stats_log.proto", - "benchmark/main.cpp", - "benchmark/hello_world_benchmark.cpp", - "benchmark/log_event_benchmark.cpp", - "benchmark/stats_write_benchmark.cpp", + "benchmark/duration_metric_benchmark.cpp", "benchmark/filter_value_benchmark.cpp", "benchmark/get_dimensions_for_condition_benchmark.cpp", + "benchmark/hello_world_benchmark.cpp", + "benchmark/log_event_benchmark.cpp", + "benchmark/main.cpp", "benchmark/metric_util.cpp", - "benchmark/duration_metric_benchmark.cpp", + "benchmark/stats_write_benchmark.cpp", + "src/atom_field_options.proto", + "src/atoms.proto", + "src/stats_log.proto", ], proto: { @@ -337,11 +337,11 @@ java_library { }, srcs: [ - "src/stats_log.proto", - "src/statsd_config.proto", "src/atoms.proto", "src/shell/shell_config.proto", "src/shell/shell_data.proto", + "src/stats_log.proto", + "src/statsd_config.proto", ], static_libs: [ diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index bb87d96b742c..9de42c3b57d5 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1218,6 +1218,9 @@ public class AppOpsManager { OP_REQUEST_INSTALL_PACKAGES, OP_START_FOREGROUND, OP_SMS_FINANCIAL_TRANSACTIONS, + OP_MANAGE_IPSEC_TUNNELS, + OP_GET_USAGE_STATS, + OP_INSTANT_APP_START_FOREGROUND }; /** @@ -1598,7 +1601,7 @@ public class AppOpsManager { Manifest.permission.REQUEST_DELETE_PACKAGES, Manifest.permission.BIND_ACCESSIBILITY_SERVICE, Manifest.permission.ACCEPT_HANDOVER, - null, // no permission for OP_MANAGE_IPSEC_TUNNELS + Manifest.permission.MANAGE_IPSEC_TUNNELS, Manifest.permission.FOREGROUND_SERVICE, null, // no permission for OP_BLUETOOTH_SCAN Manifest.permission.USE_BIOMETRIC, diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 4c970da36f45..0b157fa3bb1e 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -4070,32 +4070,54 @@ public class PackageParser { intentInfo, outError)) { return false; } - Intent intent = new Intent(); - if (intentInfo.countActions() != 1) { - outError[0] = "intent tags must contain exactly one action."; + + Uri data = null; + String dataType = null; + String host = ""; + final int numActions = intentInfo.countActions(); + final int numSchemes = intentInfo.countDataSchemes(); + final int numTypes = intentInfo.countDataTypes(); + final int numHosts = intentInfo.getHosts().length; + if ((numSchemes == 0 && numTypes == 0 && numActions == 0)) { + outError[0] = "intent tags must contain either an action or data."; return false; } - intent.setAction(intentInfo.getAction(0)); - for (int i = 0, max = intentInfo.countCategories(); i < max; i++) { - intent.addCategory(intentInfo.getCategory(i)); + if (numActions > 1) { + outError[0] = "intent tag may have at most one action."; + return false; } - Uri data = null; - String dataType = null; - if (intentInfo.countDataTypes() > 1) { + if (numTypes > 1) { outError[0] = "intent tag may have at most one data type."; return false; } - if (intentInfo.countDataSchemes() > 1) { + if (numSchemes > 1) { outError[0] = "intent tag may have at most one data scheme."; return false; } - if (intentInfo.countDataTypes() == 1) { - data = Uri.fromParts(intentInfo.getDataType(0), "", null); + if (numHosts > 1) { + outError[0] = "intent tag may have at most one data host."; + return false; + } + Intent intent = new Intent(); + for (int i = 0, max = intentInfo.countCategories(); i < max; i++) { + intent.addCategory(intentInfo.getCategory(i)); + } + if (numHosts == 1) { + host = intentInfo.getHosts()[0]; } - if (intentInfo.countDataSchemes() == 1) { - dataType = intentInfo.getDataScheme(0); + if (numSchemes == 1) { + data = new Uri.Builder() + .scheme(intentInfo.getDataScheme(0)) + .authority(host) + .build(); + } + if (numTypes == 1) { + dataType = intentInfo.getDataType(0); } intent.setDataAndType(data, dataType); + if (numActions == 1) { + intent.setAction(intentInfo.getAction(0)); + } owner.mQueriesIntents = ArrayUtils.add(owner.mQueriesIntents, intent); } else if (parser.getName().equals("package")) { final TypedArray sa = res.obtainAttributes(parser, diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 1654a5449e34..fb1ece29a369 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -1252,7 +1252,9 @@ public abstract class CameraDevice implements AutoCloseable { * * <p>The mute mode is a system-wide setting. When multiple CameraDevice objects * are setting different modes, the system will pick a the mode that's union of - * all modes set by CameraDevice.</p> + * all modes set by CameraDevice. Applications can also use + * {@link #getCameraAudioRestriction} to query current system-wide camera + * mute mode in effect.</p> * * <p>The mute settings from this CameraDevice will be automatically removed when the * CameraDevice is closed or the application is disconnected from the camera.</p> @@ -1278,7 +1280,7 @@ public abstract class CameraDevice implements AutoCloseable { * <p>Application can use this method to retrieve the system-wide camera audio restriction * settings described in {@link #setCameraAudioRestriction}.</p> * - * @return The system-wide mute mode setting resulting from this call + * @return The current system-wide mute mode setting in effect * * @throws CameraAccessException if the camera device is no longer connected or has * encountered a fatal error diff --git a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java index 160376bf6cba..4d71eebf69c7 100644 --- a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java +++ b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java @@ -58,8 +58,8 @@ public class KeyphraseEnrollmentInfo { */ private static final String VOICE_KEYPHRASE_META_DATA = "android.voice_enrollment"; /** - * Activity Action: Show activity for managing the keyphrases for hotword detection. - * This needs to be defined by an activity that supports enrolling users for hotword/keyphrase + * Intent Action: for managing the keyphrases for hotword detection. + * This needs to be defined by a service that supports enrolling users for hotword/keyphrase * detection. */ public static final String ACTION_MANAGE_VOICE_KEYPHRASES = @@ -101,7 +101,7 @@ public class KeyphraseEnrollmentInfo { // Find the apps that supports enrollment for hotword keyhphrases, // Pick a privileged app and obtain the information about the supported keyphrases // from its metadata. - List<ResolveInfo> ris = pm.queryIntentActivities( + List<ResolveInfo> ris = pm.queryIntentServices( new Intent(ACTION_MANAGE_VOICE_KEYPHRASES), PackageManager.MATCH_DEFAULT_ONLY); if (ris == null || ris.isEmpty()) { // No application capable of enrolling for voice keyphrases is present. @@ -116,11 +116,11 @@ public class KeyphraseEnrollmentInfo { for (ResolveInfo ri : ris) { try { ApplicationInfo ai = pm.getApplicationInfo( - ri.activityInfo.packageName, PackageManager.GET_META_DATA); + ri.serviceInfo.packageName, PackageManager.GET_META_DATA); if ((ai.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) == 0) { // The application isn't privileged (/system/priv-app). // The enrollment application needs to be a privileged system app. - Slog.w(TAG, ai.packageName + "is not a privileged system app"); + Slog.w(TAG, ai.packageName + " is not a privileged system app"); continue; } if (!Manifest.permission.MANAGE_VOICE_KEYPHRASES.equals(ai.permission)) { @@ -130,6 +130,8 @@ public class KeyphraseEnrollmentInfo { continue; } + Slog.i(TAG, ai.packageName + " added to keyphrase"); + KeyphraseMetadata metadata = getKeyphraseMetadataFromApplicationInfo(pm, ai, parseErrors); if (metadata != null) { @@ -137,7 +139,7 @@ public class KeyphraseEnrollmentInfo { } } catch (PackageManager.NameNotFoundException e) { String error = "error parsing voice enrollment meta-data for " - + ri.activityInfo.packageName; + + ri.serviceInfo.packageName; parseErrors.add(error + ": " + e); Slog.w(TAG, error, e); } @@ -290,7 +292,7 @@ public class KeyphraseEnrollmentInfo { } /** - * Returns an intent to launch an activity that manages the given keyphrase + * Returns an intent to launch an service that manages the given keyphrase * for the locale. * * @param action The enrollment related action that this intent is supposed to perform. diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl index 3cd9eb8c7e82..c8dbd16005ac 100644 --- a/core/java/android/hardware/usb/IUsbManager.aidl +++ b/core/java/android/hardware/usb/IUsbManager.aidl @@ -55,6 +55,18 @@ interface IUsbManager */ void setAccessoryPackage(in UsbAccessory accessory, String packageName, int userId); + /* Adds packages to the set of "denied and don't ask again" launch preferences for a device */ + void addDevicePackagesToPreferenceDenied(in UsbDevice device, in String[] packageNames, in UserHandle user); + + /* Adds packages to the set of "denied and don't ask again" launch preferences for an accessory */ + void addAccessoryPackagesToPreferenceDenied(in UsbAccessory accessory, in String[] packageNames, in UserHandle user); + + /* Removes packages from the set of "denied and don't ask again" launch preferences for a device */ + void removeDevicePackagesFromPreferenceDenied(in UsbDevice device, in String[] packageNames, in UserHandle user); + + /* Removes packages from the set of "denied and don't ask again" launch preferences for an accessory */ + void removeAccessoryPackagesFromPreferenceDenied(in UsbAccessory device, in String[] packageNames, in UserHandle user); + /* Sets the persistent permission granted state for USB device */ void setDevicePersistentPermission(in UsbDevice device, int uid, in UserHandle user, boolean shouldBeGranted); diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java index 23c54f450a67..1c78b081120a 100644 --- a/core/java/android/os/VintfObject.java +++ b/core/java/android/os/VintfObject.java @@ -72,6 +72,8 @@ public class VintfObject { * ["android.hidl.manager@1.0", "android.hardware.camera.device@1.0", * "android.hardware.camera.device@3.2"]. There are no duplicates. * + * For AIDL HALs, the version is stripped away + * (e.g. "android.hardware.light"). * @hide */ @TestApi diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 7115da2bb531..3b40c0021575 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -8811,13 +8811,6 @@ public final class Settings { "location_ignore_settings_package_whitelist"; /** - * Whether to disable location status callbacks in preparation for deprecation. - * @hide - */ - public static final String LOCATION_DISABLE_STATUS_CALLBACKS = - "location_disable_status_callbacks"; - - /** * Maximum staleness allowed for last location when returned to clients with only foreground * location permissions. * @hide diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index bd953cad2b75..cf56eae4a052 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -20,7 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; -import android.app.Activity; +import android.content.Context; import android.content.Intent; import android.hardware.soundtrigger.IRecognitionStatusCallback; import android.hardware.soundtrigger.KeyphraseEnrollmentInfo; @@ -32,7 +32,6 @@ import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra; import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel; import android.hardware.soundtrigger.SoundTrigger.ModuleProperties; import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig; -import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent; import android.media.AudioFormat; import android.os.AsyncTask; import android.os.Handler; @@ -447,7 +446,7 @@ public class AlwaysOnHotwordDetector { /** * Creates an intent to start the enrollment for the associated keyphrase. - * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}. + * This intent must be invoked using {@link Context#startForegroundService(Intent)}. * Starting re-enrollment is only valid if the keyphrase is un-enrolled, * i.e. {@link #STATE_KEYPHRASE_UNENROLLED}, * otherwise {@link #createReEnrollIntent()} should be preferred. @@ -469,7 +468,7 @@ public class AlwaysOnHotwordDetector { /** * Creates an intent to start the un-enrollment for the associated keyphrase. - * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}. + * This intent must be invoked using {@link Context#startForegroundService(Intent)}. * Starting re-enrollment is only valid if the keyphrase is already enrolled, * i.e. {@link #STATE_KEYPHRASE_ENROLLED}, otherwise invoking this may result in an error. * @@ -490,7 +489,7 @@ public class AlwaysOnHotwordDetector { /** * Creates an intent to start the re-enrollment for the associated keyphrase. - * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}. + * This intent must be invoked using {@link Context#startForegroundService(Intent)}. * Starting re-enrollment is only valid if the keyphrase is already enrolled, * i.e. {@link #STATE_KEYPHRASE_ENROLLED}, otherwise invoking this may result in an error. * diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index bb9867a298c3..1c3294858db8 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -640,4 +640,14 @@ interface IWindowManager * native InputManager before proceeding with tests. */ void syncInputTransactions(); + + /** + * Returns whether SurfaceFlinger layer tracing is enabled. + */ + boolean isLayerTracing(); + + /** + * Enables/disables SurfaceFlinger layer tracing. + */ + void setLayerTracing(boolean enabled); } diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java index 50ef91f90997..341c2147c64a 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -26,7 +26,6 @@ import android.annotation.Nullable; import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.Rect; -import android.os.UidProto.Sync; import android.util.ArraySet; import android.util.SparseArray; import android.util.SparseIntArray; @@ -40,7 +39,6 @@ import android.view.WindowManager.LayoutParams; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; -import java.util.function.Function; import java.util.function.Supplier; /** @@ -238,7 +236,12 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll addTranslationToMatrix(side, offset, mTmpMatrix, mTmpFrame); state.getSource(source.getType()).setFrame(mTmpFrame); - surfaceParams.add(new SurfaceParams(leash, 1f, mTmpMatrix, null, 0, 0f, inset != 0)); + + // If the system is controlling the insets source, the leash can be null. + if (leash != null) { + surfaceParams.add(new SurfaceParams(leash, 1f /* alpha */, mTmpMatrix, + null /* windowCrop */, 0 /* layer */, 0f /* cornerRadius*/, inset != 0)); + } } } diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index 9edccb3fb221..08d45a746dc4 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -167,7 +167,7 @@ public class InsetsSourceConsumer { } private void applyHiddenToControl() { - if (mSourceControl == null) { + if (mSourceControl == null || mSourceControl.getLeash() == null) { return; } diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java index 4940981748a8..4919074ec252 100644 --- a/core/java/android/view/InsetsSourceControl.java +++ b/core/java/android/view/InsetsSourceControl.java @@ -16,6 +16,7 @@ package android.view; +import android.annotation.Nullable; import android.graphics.Point; import android.os.Parcel; import android.os.Parcelable; @@ -28,10 +29,10 @@ import android.view.InsetsState.InternalInsetType; public class InsetsSourceControl implements Parcelable { private final @InternalInsetType int mType; - private final SurfaceControl mLeash; + private final @Nullable SurfaceControl mLeash; private final Point mSurfacePosition; - public InsetsSourceControl(@InternalInsetType int type, SurfaceControl leash, + public InsetsSourceControl(@InternalInsetType int type, @Nullable SurfaceControl leash, Point surfacePosition) { mType = type; mLeash = leash; @@ -42,7 +43,13 @@ public class InsetsSourceControl implements Parcelable { return mType; } - public SurfaceControl getLeash() { + /** + * Gets the leash for controlling insets source. If the system is controlling the insets source, + * for example, transient bars, the client will receive fake controls without leash in it. + * + * @return the leash. + */ + public @Nullable SurfaceControl getLeash() { return mLeash; } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 2639fcb2b1cf..cae1f3831b4a 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -24,6 +24,7 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityManager; @@ -120,6 +121,7 @@ import com.google.android.collect.Lists; import java.io.IOException; import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.text.Collator; import java.util.ArrayList; import java.util.Arrays; @@ -178,6 +180,20 @@ public class ChooserActivity extends ResolverActivity { private static final boolean USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS = true; private static final boolean USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS = true; + public static final int TARGET_TYPE_DEFAULT = 0; + public static final int TARGET_TYPE_CHOOSER_TARGET = 1; + public static final int TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER = 2; + public static final int TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE = 3; + + @IntDef(flag = false, prefix = { "TARGET_TYPE_" }, value = { + TARGET_TYPE_DEFAULT, + TARGET_TYPE_CHOOSER_TARGET, + TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER, + TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ShareTargetType {} + /** * The transition time between placeholders for direct share to a message * indicating that non are available. @@ -218,9 +234,9 @@ public class ChooserActivity extends ResolverActivity { private int mCurrAvailableWidth = 0; /** {@link ChooserActivity#getBaseScore} */ - private static final float CALLER_TARGET_SCORE_BOOST = 900.f; + public static final float CALLER_TARGET_SCORE_BOOST = 900.f; /** {@link ChooserActivity#getBaseScore} */ - private static final float SHORTCUT_TARGET_SCORE_BOOST = 90.f; + public static final float SHORTCUT_TARGET_SCORE_BOOST = 90.f; private static final String TARGET_DETAILS_FRAGMENT_TAG = "targetDetailsFragment"; // TODO: Update to handle landscape instead of using static value private static final int MAX_RANKED_TARGETS = 4; @@ -443,7 +459,7 @@ public class ChooserActivity extends ResolverActivity { } if (sri.resultTargets != null) { mChooserListAdapter.addServiceResults(sri.originalTarget, - sri.resultTargets, false); + sri.resultTargets, TARGET_TYPE_CHOOSER_TARGET); } unbindService(sri.connection); sri.connection.destroy(); @@ -474,7 +490,7 @@ public class ChooserActivity extends ResolverActivity { final ServiceResultInfo resultInfo = (ServiceResultInfo) msg.obj; if (resultInfo.resultTargets != null) { mChooserListAdapter.addServiceResults(resultInfo.originalTarget, - resultInfo.resultTargets, true); + resultInfo.resultTargets, msg.arg1); } break; @@ -714,7 +730,13 @@ public class ChooserActivity extends ResolverActivity { /** * Returns true if app prediction service is defined and the component exists on device. */ - private boolean isAppPredictionServiceAvailable() { + @VisibleForTesting + public boolean isAppPredictionServiceAvailable() { + if (getPackageManager().getAppPredictionServicePackageName() == null) { + // Default AppPredictionService is not defined. + return false; + } + final String appPredictionServiceName = getString(R.string.config_defaultAppPredictionService); if (appPredictionServiceName == null) { @@ -1214,7 +1236,7 @@ public class ChooserActivity extends ResolverActivity { mChooserListAdapter = (ChooserListAdapter) adapter; if (mCallerChooserTargets != null && mCallerChooserTargets.length > 0) { mChooserListAdapter.addServiceResults(null, Lists.newArrayList(mCallerChooserTargets), - false); + TARGET_TYPE_DEFAULT); } mChooserRowAdapter = new ChooserRowAdapter(mChooserListAdapter); if (listView != null) { @@ -1560,33 +1582,32 @@ public class ChooserActivity extends ResolverActivity { } } + // If |appTargets| is not null, results are from AppPredictionService and already sorted. + final int shortcutType = (appTargets == null ? TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER : + TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE); + // Match ShareShortcutInfos with DisplayResolveInfos to be able to use the old code path // for direct share targets. After ShareSheet is refactored we should use the // ShareShortcutInfos directly. boolean resultMessageSent = false; for (int i = 0; i < driList.size(); i++) { - List<ChooserTarget> chooserTargets = new ArrayList<>(); + List<ShortcutManager.ShareShortcutInfo> matchingShortcuts = new ArrayList<>(); for (int j = 0; j < resultList.size(); j++) { if (driList.get(i).getResolvedComponentName().equals( resultList.get(j).getTargetComponent())) { - ShortcutManager.ShareShortcutInfo shareShortcutInfo = resultList.get(j); - // Incoming results are ordered but without a score. Create a score - // based on the index in order to be sorted appropriately when joined - // with legacy direct share api results. - float score = Math.max(1.0f - (0.05f * j), 0.0f); - ChooserTarget chooserTarget = convertToChooserTarget(shareShortcutInfo, score); - chooserTargets.add(chooserTarget); - if (mDirectShareAppTargetCache != null && appTargets != null) { - mDirectShareAppTargetCache.put(chooserTarget, appTargets.get(j)); - } + matchingShortcuts.add(resultList.get(j)); } } - if (chooserTargets.isEmpty()) { + if (matchingShortcuts.isEmpty()) { continue; } + List<ChooserTarget> chooserTargets = convertToChooserTarget( + matchingShortcuts, resultList, appTargets, shortcutType); + final Message msg = Message.obtain(); msg.what = ChooserHandler.SHORTCUT_MANAGER_SHARE_TARGET_RESULT; msg.obj = new ServiceResultInfo(driList.get(i), chooserTargets, null); + msg.arg1 = shortcutType; mChooserHandler.sendMessage(msg); resultMessageSent = true; } @@ -1620,23 +1641,69 @@ public class ChooserActivity extends ResolverActivity { return false; } - private ChooserTarget convertToChooserTarget(ShortcutManager.ShareShortcutInfo shareShortcut, - float score) { - ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo(); - Bundle extras = new Bundle(); - extras.putString(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId()); - return new ChooserTarget( - // The name of this target. - shortcutInfo.getShortLabel(), - // Don't load the icon until it is selected to be shown - null, - // The ranking score for this target (0.0-1.0); the system will omit items with low - // scores when there are too many Direct Share items. - score, - // The name of the component to be launched if this target is chosen. - shareShortcut.getTargetComponent().clone(), - // The extra values here will be merged into the Intent when this target is chosen. - extras); + /** + * Converts a list of ShareShortcutInfos to ChooserTargets. + * @param matchingShortcuts List of shortcuts, all from the same package, that match the current + * share intent filter. + * @param allShortcuts List of all the shortcuts from all the packages on the device that are + * returned for the current sharing action. + * @param allAppTargets List of AppTargets. Null if the results are not from prediction service. + * @param shortcutType One of the values TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER or + * TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE + * @return A list of ChooserTargets sorted by score in descending order. + */ + @VisibleForTesting + @NonNull + public List<ChooserTarget> convertToChooserTarget( + @NonNull List<ShortcutManager.ShareShortcutInfo> matchingShortcuts, + @NonNull List<ShortcutManager.ShareShortcutInfo> allShortcuts, + @Nullable List<AppTarget> allAppTargets, @ShareTargetType int shortcutType) { + // A set of distinct scores for the matched shortcuts. We use index of a rank in the sorted + // list instead of the actual rank value when converting a rank to a score. + List<Integer> scoreList = new ArrayList<>(); + if (shortcutType == TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER) { + for (int i = 0; i < matchingShortcuts.size(); i++) { + int shortcutRank = matchingShortcuts.get(i).getShortcutInfo().getRank(); + if (!scoreList.contains(shortcutRank)) { + scoreList.add(shortcutRank); + } + } + Collections.sort(scoreList); + } + + List<ChooserTarget> chooserTargetList = new ArrayList<>(matchingShortcuts.size()); + for (int i = 0; i < matchingShortcuts.size(); i++) { + ShortcutInfo shortcutInfo = matchingShortcuts.get(i).getShortcutInfo(); + int indexInAllShortcuts = allShortcuts.indexOf(matchingShortcuts.get(i)); + + float score; + if (shortcutType == TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE) { + // Incoming results are ordered. Create a score based on index in the original list. + score = Math.max(1.0f - (0.01f * indexInAllShortcuts), 0.0f); + } else { + // Create a score based on the rank of the shortcut. + int rankIndex = scoreList.indexOf(shortcutInfo.getRank()); + score = Math.max(1.0f - (0.01f * rankIndex), 0.0f); + } + + Bundle extras = new Bundle(); + extras.putString(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId()); + ChooserTarget chooserTarget = new ChooserTarget(shortcutInfo.getShortLabel(), + null, // Icon will be loaded later if this target is selected to be shown. + score, matchingShortcuts.get(i).getTargetComponent().clone(), extras); + + chooserTargetList.add(chooserTarget); + if (mDirectShareAppTargetCache != null && allAppTargets != null) { + mDirectShareAppTargetCache.put(chooserTarget, + allAppTargets.get(indexInAllShortcuts)); + } + } + + // Sort ChooserTargets by score in descending order + Comparator<ChooserTarget> byScore = + (ChooserTarget a, ChooserTarget b) -> -Float.compare(a.getScore(), b.getScore()); + Collections.sort(chooserTargetList, byScore); + return chooserTargetList; } private String convertServiceName(String packageName, String serviceName) { @@ -1728,8 +1795,7 @@ public class ChooserActivity extends ResolverActivity { if (!mIsAppPredictorComponentAvailable) { return null; } - if (mAppPredictor == null - && getPackageManager().getAppPredictionServicePackageName() != null) { + if (mAppPredictor == null) { final IntentFilter filter = getTargetIntentFilter(); Bundle extras = new Bundle(); extras.putParcelable(APP_PREDICTION_INTENT_FILTER_KEY, filter); @@ -2677,7 +2743,7 @@ public class ChooserActivity extends ResolverActivity { * if score is too low. */ public void addServiceResults(DisplayResolveInfo origTarget, List<ChooserTarget> targets, - boolean isShortcutResult) { + @ShareTargetType int targetType) { if (DEBUG) { Log.d(TAG, "addServiceResults " + origTarget + ", " + targets.size() + " targets"); @@ -2687,9 +2753,12 @@ public class ChooserActivity extends ResolverActivity { return; } - final float baseScore = getBaseScore(origTarget, isShortcutResult); + final float baseScore = getBaseScore(origTarget, targetType); Collections.sort(targets, mBaseTargetComparator); + final boolean isShortcutResult = + (targetType == TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER + || targetType == TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE); final int maxTargets = isShortcutResult ? mMaxShortcutTargetsPerApp : MAX_CHOOSER_TARGETS_PER_APP; float lastScore = 0; @@ -2740,17 +2809,17 @@ public class ChooserActivity extends ResolverActivity { * <li>Legacy direct share targets * </ol> */ - private float getBaseScore(DisplayResolveInfo target, boolean isShortcutResult) { + public float getBaseScore(DisplayResolveInfo target, @ShareTargetType int targetType) { if (target == null) { return CALLER_TARGET_SCORE_BOOST; } - if (isShortcutResult && getAppPredictorForDirectShareIfEnabled() != null) { + if (targetType == TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE) { return SHORTCUT_TARGET_SCORE_BOOST; } float score = super.getScore(target); - if (isShortcutResult) { + if (targetType == TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER) { return score * SHORTCUT_TARGET_SCORE_BOOST; } diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 981d0bb1cd69..b02563a67503 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -804,7 +804,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind updateElevation(); mAllowUpdateElevation = true; - if (changed && mResizeMode == RESIZE_MODE_DOCKED_DIVIDER) { + if (changed + && (mResizeMode == RESIZE_MODE_DOCKED_DIVIDER + || mDrawLegacyNavigationBarBackground)) { getViewRootImpl().requestInvalidateRootRenderNode(); } } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 7779f55518f0..5a0f16e589ce 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -42,10 +42,6 @@ cc_library_shared { ], include_dirs: [ - // we need to access the private Bionic header - // <bionic_tls.h> in com_google_android_gles_jni_GLImpl.cpp - "bionic/libc/private", - "external/skia/include/private", "frameworks/base/media/jni", "system/media/camera/include", @@ -277,6 +273,7 @@ cc_library_shared { "libnativewindow", ], generated_sources: ["android_util_StatsLogInternal.cpp"], + header_libs: ["bionic_libc_platform_headers"], }, host: { cflags: [ diff --git a/core/jni/android_app_ActivityThread.cpp b/core/jni/android_app_ActivityThread.cpp index 5f830382bd01..ca8b8de8d3ad 100644 --- a/core/jni/android_app_ActivityThread.cpp +++ b/core/jni/android_app_ActivityThread.cpp @@ -23,7 +23,7 @@ #include "core_jni_helpers.h" #include <unistd.h> -#include <bionic_malloc.h> +#include <bionic/malloc.h> namespace android { diff --git a/core/jni/android_ddm_DdmHandleNativeHeap.cpp b/core/jni/android_ddm_DdmHandleNativeHeap.cpp index 076e99dd1fba..2ca4500991fa 100644 --- a/core/jni/android_ddm_DdmHandleNativeHeap.cpp +++ b/core/jni/android_ddm_DdmHandleNativeHeap.cpp @@ -23,7 +23,7 @@ #include "core_jni_helpers.h" #include <android-base/logging.h> -#include <bionic_malloc.h> +#include <bionic/malloc.h> #include <utils/Log.h> #include <utils/String8.h> diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp index 03057dc5e602..0002f8b4048a 100644 --- a/core/jni/android_hardware_SoundTrigger.cpp +++ b/core/jni/android_hardware_SoundTrigger.cpp @@ -606,12 +606,12 @@ android_hardware_SoundTrigger_loadSoundModel(JNIEnv *env, jobject thiz, goto exit; } memory = memoryDealer->allocate(offset + size); - if (memory == 0 || memory->pointer() == NULL) { + if (memory == 0 || memory->unsecurePointer() == NULL) { status = SOUNDTRIGGER_STATUS_ERROR; goto exit; } - nSoundModel = (struct sound_trigger_sound_model *)memory->pointer(); + nSoundModel = (struct sound_trigger_sound_model *)memory->unsecurePointer(); nSoundModel->type = type; nSoundModel->uuid = nUuid; @@ -737,18 +737,18 @@ android_hardware_SoundTrigger_startRecognition(JNIEnv *env, jobject thiz, return SOUNDTRIGGER_STATUS_ERROR; } sp<IMemory> memory = memoryDealer->allocate(totalSize); - if (memory == 0 || memory->pointer() == NULL) { + if (memory == 0 || memory->unsecurePointer() == NULL) { return SOUNDTRIGGER_STATUS_ERROR; } if (dataSize != 0) { - memcpy((char *)memory->pointer() + sizeof(struct sound_trigger_recognition_config), + memcpy((char *)memory->unsecurePointer() + sizeof(struct sound_trigger_recognition_config), nData, dataSize); env->ReleaseByteArrayElements(jData, nData, 0); } env->DeleteLocalRef(jData); struct sound_trigger_recognition_config *config = - (struct sound_trigger_recognition_config *)memory->pointer(); + (struct sound_trigger_recognition_config *)memory->unsecurePointer(); config->data_size = dataSize; config->data_offset = sizeof(struct sound_trigger_recognition_config); config->capture_requested = env->GetBooleanField(jConfig, diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index daa63475f706..c5049ecd3784 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -649,7 +649,7 @@ static jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const T if ((size_t)sizeInBytes > track->sharedBuffer()->size()) { sizeInBytes = track->sharedBuffer()->size(); } - memcpy(track->sharedBuffer()->pointer(), data + offsetInSamples, sizeInBytes); + memcpy(track->sharedBuffer()->unsecurePointer(), data + offsetInSamples, sizeInBytes); written = sizeInBytes; } if (written >= 0) { diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index cf8df287a6db..9c52a6433360 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -34,7 +34,7 @@ #include <vector> #include <android-base/logging.h> -#include <bionic_malloc.h> +#include <bionic/malloc.h> #include <debuggerd/client.h> #include <log/log.h> #include <utils/misc.h> diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp index 8553a2c24826..af34e7b7a7ff 100644 --- a/core/jni/android_view_InputChannel.cpp +++ b/core/jni/android_view_InputChannel.cpp @@ -16,6 +16,7 @@ #define LOG_TAG "InputChannel-JNI" +#include "android-base/stringprintf.h" #include <nativehelper/JNIHelp.h> #include "nativehelper/scoped_utf_chars.h" #include <android_runtime/AndroidRuntime.h> @@ -60,7 +61,7 @@ private: // ---------------------------------------------------------------------------- NativeInputChannel::NativeInputChannel(const sp<InputChannel>& inputChannel) : - mInputChannel(inputChannel), mDisposeCallback(NULL) { + mInputChannel(inputChannel), mDisposeCallback(nullptr) { } NativeInputChannel::~NativeInputChannel() { @@ -74,8 +75,8 @@ void NativeInputChannel::setDisposeCallback(InputChannelObjDisposeCallback callb void NativeInputChannel::invokeAndRemoveDisposeCallback(JNIEnv* env, jobject obj) { if (mDisposeCallback) { mDisposeCallback(env, obj, mInputChannel, mDisposeData); - mDisposeCallback = NULL; - mDisposeData = NULL; + mDisposeCallback = nullptr; + mDisposeData = nullptr; } } @@ -96,14 +97,14 @@ static void android_view_InputChannel_setNativeInputChannel(JNIEnv* env, jobject sp<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env, jobject inputChannelObj) { NativeInputChannel* nativeInputChannel = android_view_InputChannel_getNativeInputChannel(env, inputChannelObj); - return nativeInputChannel != NULL ? nativeInputChannel->getInputChannel() : NULL; + return nativeInputChannel != nullptr ? nativeInputChannel->getInputChannel() : nullptr; } void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChannelObj, InputChannelObjDisposeCallback callback, void* data) { NativeInputChannel* nativeInputChannel = android_view_InputChannel_getNativeInputChannel(env, inputChannelObj); - if (nativeInputChannel == NULL) { + if (nativeInputChannel == nullptr) { ALOGW("Cannot set dispose callback because input channel object has not been initialized."); } else { nativeInputChannel->setDisposeCallback(callback, data); @@ -131,27 +132,27 @@ static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel); if (result) { - String8 message; - message.appendFormat("Could not open input channel pair. status=%d", result); - jniThrowRuntimeException(env, message.string()); - return NULL; + std::string message = android::base::StringPrintf( + "Could not open input channel pair : %s", strerror(-result)); + jniThrowRuntimeException(env, message.c_str()); + return nullptr; } - jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL); + jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, nullptr); if (env->ExceptionCheck()) { - return NULL; + return nullptr; } jobject serverChannelObj = android_view_InputChannel_createInputChannel(env, std::make_unique<NativeInputChannel>(serverChannel)); if (env->ExceptionCheck()) { - return NULL; + return nullptr; } jobject clientChannelObj = android_view_InputChannel_createInputChannel(env, std::make_unique<NativeInputChannel>(clientChannel)); if (env->ExceptionCheck()) { - return NULL; + return nullptr; } env->SetObjectArrayElement(channelPair, 0, serverChannelObj); @@ -170,7 +171,7 @@ static void android_view_InputChannel_nativeDispose(JNIEnv* env, jobject obj, jb nativeInputChannel->invokeAndRemoveDisposeCallback(env, obj); - android_view_InputChannel_setNativeInputChannel(env, obj, NULL); + android_view_InputChannel_setNativeInputChannel(env, obj, nullptr); delete nativeInputChannel; } } @@ -179,14 +180,14 @@ static void android_view_InputChannel_nativeRelease(JNIEnv* env, jobject obj, jb NativeInputChannel* nativeInputChannel = android_view_InputChannel_getNativeInputChannel(env, obj); if (nativeInputChannel) { - android_view_InputChannel_setNativeInputChannel(env, obj, NULL); + android_view_InputChannel_setNativeInputChannel(env, obj, nullptr); delete nativeInputChannel; } } static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj, jobject otherObj) { - if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) { + if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != nullptr) { jniThrowException(env, "java/lang/IllegalStateException", "Other object already has a native input channel."); return; @@ -195,12 +196,12 @@ static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj, NativeInputChannel* nativeInputChannel = android_view_InputChannel_getNativeInputChannel(env, obj); android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel); - android_view_InputChannel_setNativeInputChannel(env, obj, NULL); + android_view_InputChannel_setNativeInputChannel(env, obj, nullptr); } static void android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject obj, jobject parcelObj) { - if (android_view_InputChannel_getNativeInputChannel(env, obj) != NULL) { + if (android_view_InputChannel_getNativeInputChannel(env, obj) != nullptr) { jniThrowException(env, "java/lang/IllegalStateException", "This object already has a native input channel."); return; @@ -222,25 +223,26 @@ static void android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject static void android_view_InputChannel_nativeWriteToParcel(JNIEnv* env, jobject obj, jobject parcelObj) { Parcel* parcel = parcelForJavaObject(env, parcelObj); - if (parcel) { - NativeInputChannel* nativeInputChannel = - android_view_InputChannel_getNativeInputChannel(env, obj); - if (nativeInputChannel) { - sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel(); - - parcel->writeInt32(1); - inputChannel->write(*parcel); - } else { - parcel->writeInt32(0); - } + if (parcel == nullptr) { + ALOGE("Could not obtain parcel for Java object"); + return; + } + + NativeInputChannel* nativeInputChannel = + android_view_InputChannel_getNativeInputChannel(env, obj); + if (!nativeInputChannel) { + parcel->writeInt32(0); // not initialized + return; } + parcel->writeInt32(1); // initialized + nativeInputChannel->getInputChannel()->write(*parcel); } static jstring android_view_InputChannel_nativeGetName(JNIEnv* env, jobject obj) { NativeInputChannel* nativeInputChannel = android_view_InputChannel_getNativeInputChannel(env, obj); if (! nativeInputChannel) { - return NULL; + return nullptr; } jstring name = env->NewStringUTF(nativeInputChannel->getInputChannel()->getName().c_str()); @@ -250,10 +252,24 @@ static jstring android_view_InputChannel_nativeGetName(JNIEnv* env, jobject obj) static void android_view_InputChannel_nativeDup(JNIEnv* env, jobject obj, jobject otherObj) { NativeInputChannel* nativeInputChannel = android_view_InputChannel_getNativeInputChannel(env, obj); - if (nativeInputChannel) { - android_view_InputChannel_setNativeInputChannel(env, otherObj, - new NativeInputChannel(nativeInputChannel->getInputChannel()->dup())); + if (nativeInputChannel == nullptr) { + jniThrowRuntimeException(env, "InputChannel has no valid NativeInputChannel"); + return; + } + + sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel(); + if (inputChannel == nullptr) { + jniThrowRuntimeException(env, "NativeInputChannel has no corresponding InputChannel"); + return; + } + sp<InputChannel> dupInputChannel = inputChannel->dup(); + if (dupInputChannel == nullptr) { + std::string message = android::base::StringPrintf( + "Could not duplicate input channel %s", inputChannel->getName().c_str()); + jniThrowRuntimeException(env, message.c_str()); } + android_view_InputChannel_setNativeInputChannel(env, otherObj, + new NativeInputChannel(dupInputChannel)); } static jobject android_view_InputChannel_nativeGetToken(JNIEnv* env, jobject obj) { diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index d5b875b85d7d..d42a48a1f899 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -73,7 +73,7 @@ #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> -#include <bionic_malloc.h> +#include <bionic/malloc.h> #include <cutils/ashmem.h> #include <cutils/fs.h> #include <cutils/multiuser.h> diff --git a/core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java index e1ccd7523eba..8891d3fd2dca 100644 --- a/core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java @@ -65,8 +65,10 @@ public class LegacyIntentClassificationFactoryTest { null, null, null, - 0, - 0); + null, + 0L, + 0L, + 0d); List<LabeledIntent> intents = mLegacyIntentClassificationFactory.create( InstrumentationRegistry.getContext(), @@ -101,8 +103,10 @@ public class LegacyIntentClassificationFactoryTest { null, null, null, - 0, - 0); + null, + 0L, + 0L, + 0d); List<LabeledIntent> intents = mLegacyIntentClassificationFactory.create( InstrumentationRegistry.getContext(), diff --git a/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java index b9a1a8cc4e42..bcea5fea6a13 100644 --- a/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java @@ -83,9 +83,11 @@ public class TemplateClassificationIntentFactoryTest { null, null, null, + null, createRemoteActionTemplates(), - 0, - 0); + 0L, + 0L, + 0d); List<LabeledIntent> intents = mTemplateClassificationIntentFactory.create( @@ -124,9 +126,11 @@ public class TemplateClassificationIntentFactoryTest { null, null, null, + null, createRemoteActionTemplates(), - 0, - 0); + 0L, + 0L, + 0d); List<LabeledIntent> intents = mTemplateClassificationIntentFactory.create( @@ -162,8 +166,10 @@ public class TemplateClassificationIntentFactoryTest { null, null, null, - 0, - 0); + null, + 0L, + 0L, + 0d); mTemplateClassificationIntentFactory.create( InstrumentationRegistry.getContext(), @@ -196,9 +202,11 @@ public class TemplateClassificationIntentFactoryTest { null, null, null, + null, new RemoteActionTemplate[0], - 0, - 0); + 0L, + 0L, + 0d); mTemplateClassificationIntentFactory.create( InstrumentationRegistry.getContext(), diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java index 767ec0e38a86..c44b7d81868d 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -24,12 +24,19 @@ import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; import static androidx.test.espresso.matcher.ViewMatchers.withId; import static androidx.test.espresso.matcher.ViewMatchers.withText; +import static com.android.internal.app.ChooserActivity.CALLER_TARGET_SCORE_BOOST; +import static com.android.internal.app.ChooserActivity.SHORTCUT_TARGET_SCORE_BOOST; +import static com.android.internal.app.ChooserActivity.TARGET_TYPE_CHOOSER_TARGET; +import static com.android.internal.app.ChooserActivity.TARGET_TYPE_DEFAULT; +import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE; +import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER; import static com.android.internal.app.ChooserWrapperActivity.sOverrides; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -45,6 +52,8 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutManager.ShareShortcutInfo; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -770,6 +779,139 @@ public class ChooserActivityTest { onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed())); } + @Test + public void testGetBaseScore() { + final float testBaseScore = 0.89f; + + Intent sendIntent = createSendTextIntent(); + List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); + + when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); + when(sOverrides.resolverListController.getScore(Mockito.isA( + ResolverActivity.DisplayResolveInfo.class))).thenReturn(testBaseScore); + + final ChooserWrapperActivity activity = mActivityRule + .launchActivity(Intent.createChooser(sendIntent, null)); + waitForIdle(); + + final ResolverActivity.DisplayResolveInfo testDri = + activity.createTestDisplayResolveInfo(sendIntent, + ResolverDataProvider.createResolveInfo(3, 0), "testLabel", "testInfo", sendIntent); + final ChooserActivity.ChooserListAdapter adapter = activity.getAdapter(); + + assertThat(adapter.getBaseScore(null, 0), is(CALLER_TARGET_SCORE_BOOST)); + assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_DEFAULT), is(testBaseScore)); + assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_CHOOSER_TARGET), is(testBaseScore)); + assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE), + is(SHORTCUT_TARGET_SCORE_BOOST)); + assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER), + is(testBaseScore * SHORTCUT_TARGET_SCORE_BOOST)); + } + + /** + * The case when AppPrediction service is not defined in PackageManager is already covered + * as a test parameter {@link ChooserActivityTest#packageManagers}. This test is checking the + * case when the prediction service is defined but the component is not available on the device. + */ + @Test + public void testIsAppPredictionServiceAvailable() { + Intent sendIntent = createSendTextIntent(); + List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); + when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); + + final ChooserWrapperActivity activity = mActivityRule + .launchActivity(Intent.createChooser(sendIntent, null)); + waitForIdle(); + + if (activity.getPackageManager().getAppPredictionServicePackageName() == null) { + assertThat(activity.isAppPredictionServiceAvailable(), is(false)); + } else { + assertThat(activity.isAppPredictionServiceAvailable(), is(true)); + + sOverrides.resources = Mockito.spy(activity.getResources()); + when(sOverrides.resources.getString(R.string.config_defaultAppPredictionService)) + .thenReturn("ComponentNameThatDoesNotExist"); + + assertThat(activity.isAppPredictionServiceAvailable(), is(false)); + } + } + + @Test + public void testConvertToChooserTarget_predictionService() { + Intent sendIntent = createSendTextIntent(); + List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); + when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); + + final ChooserWrapperActivity activity = mActivityRule + .launchActivity(Intent.createChooser(sendIntent, null)); + waitForIdle(); + + List<ShareShortcutInfo> shortcuts = createShortcuts(activity); + + int[] expectedOrderAllShortcuts = {0, 1, 2, 3}; + float[] expectedScoreAllShortcuts = {1.0f, 0.99f, 0.98f, 0.97f}; + + List<ChooserTarget> chooserTargets = activity.convertToChooserTarget(shortcuts, shortcuts, + null, TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE); + assertCorrectShortcutToChooserTargetConversion(shortcuts, chooserTargets, + expectedOrderAllShortcuts, expectedScoreAllShortcuts); + + List<ShareShortcutInfo> subset = new ArrayList<>(); + subset.add(shortcuts.get(1)); + subset.add(shortcuts.get(2)); + subset.add(shortcuts.get(3)); + + int[] expectedOrderSubset = {1, 2, 3}; + float[] expectedScoreSubset = {0.99f, 0.98f, 0.97f}; + + chooserTargets = activity.convertToChooserTarget(subset, shortcuts, null, + TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE); + assertCorrectShortcutToChooserTargetConversion(shortcuts, chooserTargets, + expectedOrderSubset, expectedScoreSubset); + } + + @Test + public void testConvertToChooserTarget_shortcutManager() { + Intent sendIntent = createSendTextIntent(); + List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); + when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); + + final ChooserWrapperActivity activity = mActivityRule + .launchActivity(Intent.createChooser(sendIntent, null)); + waitForIdle(); + + List<ShareShortcutInfo> shortcuts = createShortcuts(activity); + + int[] expectedOrderAllShortcuts = {2, 0, 3, 1}; + float[] expectedScoreAllShortcuts = {1.0f, 0.99f, 0.99f, 0.98f}; + + List<ChooserTarget> chooserTargets = activity.convertToChooserTarget(shortcuts, shortcuts, + null, TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER); + assertCorrectShortcutToChooserTargetConversion(shortcuts, chooserTargets, + expectedOrderAllShortcuts, expectedScoreAllShortcuts); + + List<ShareShortcutInfo> subset = new ArrayList<>(); + subset.add(shortcuts.get(1)); + subset.add(shortcuts.get(2)); + subset.add(shortcuts.get(3)); + + int[] expectedOrderSubset = {2, 3, 1}; + float[] expectedScoreSubset = {1.0f, 0.99f, 0.98f}; + + chooserTargets = activity.convertToChooserTarget(subset, shortcuts, null, + TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER); + assertCorrectShortcutToChooserTargetConversion(shortcuts, chooserTargets, + expectedOrderSubset, expectedScoreSubset); + } + // This test is too long and too slow and should not be taken as an example for future tests. @Test public void testDirectTargetSelectionLogging() throws InterruptedException { @@ -800,7 +942,7 @@ public class ChooserActivityTest { "testInfo", sendIntent), serviceTargets, - false) + TARGET_TYPE_CHOOSER_TARGET) ); // Thread.sleep shouldn't be a thing in an integration test but it's // necessary here because of the way the code is structured @@ -866,7 +1008,7 @@ public class ChooserActivityTest { "testInfo", sendIntent), serviceTargets, - false) + TARGET_TYPE_CHOOSER_TARGET) ); // Thread.sleep shouldn't be a thing in an integration test but it's // necessary here because of the way the code is structured @@ -927,7 +1069,7 @@ public class ChooserActivityTest { "testInfo", sendIntent), serviceTargets, - false) + TARGET_TYPE_CHOOSER_TARGET) ); // Thread.sleep shouldn't be a thing in an integration test but it's // necessary here because of the way the code is structured @@ -1066,4 +1208,43 @@ public class ChooserActivityTest { return bitmap; } + + private List<ShareShortcutInfo> createShortcuts(Context context) { + Intent testIntent = new Intent("TestIntent"); + + List<ShareShortcutInfo> shortcuts = new ArrayList<>(); + shortcuts.add(new ShareShortcutInfo( + new ShortcutInfo.Builder(context, "shortcut1") + .setIntent(testIntent).setShortLabel("label1").setRank(3).build(), // 0 2 + new ComponentName("package1", "class1"))); + shortcuts.add(new ShareShortcutInfo( + new ShortcutInfo.Builder(context, "shortcut2") + .setIntent(testIntent).setShortLabel("label2").setRank(7).build(), // 1 3 + new ComponentName("package2", "class2"))); + shortcuts.add(new ShareShortcutInfo( + new ShortcutInfo.Builder(context, "shortcut3") + .setIntent(testIntent).setShortLabel("label3").setRank(1).build(), // 2 0 + new ComponentName("package3", "class3"))); + shortcuts.add(new ShareShortcutInfo( + new ShortcutInfo.Builder(context, "shortcut4") + .setIntent(testIntent).setShortLabel("label4").setRank(3).build(), // 3 2 + new ComponentName("package4", "class4"))); + + return shortcuts; + } + + private void assertCorrectShortcutToChooserTargetConversion(List<ShareShortcutInfo> shortcuts, + List<ChooserTarget> chooserTargets, int[] expectedOrder, float[] expectedScores) { + assertEquals(expectedOrder.length, chooserTargets.size()); + for (int i = 0; i < chooserTargets.size(); i++) { + ChooserTarget ct = chooserTargets.get(i); + ShortcutInfo si = shortcuts.get(expectedOrder[i]).getShortcutInfo(); + ComponentName cn = shortcuts.get(expectedOrder[i]).getTargetComponent(); + + assertEquals(si.getId(), ct.getIntentExtras().getString(Intent.EXTRA_SHORTCUT_ID)); + assertEquals(si.getShortLabel(), ct.getTitle()); + assertThat(Math.abs(expectedScores[i] - ct.getScore()) < 0.000001, is(true)); + assertEquals(cn.flattenToString(), ct.getComponentName().flattenToString()); + } + } } diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java index 44e56eaf0593..1d567c73f376 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java @@ -24,6 +24,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; import android.net.Uri; @@ -85,6 +86,14 @@ public class ChooserWrapperActivity extends ChooserActivity { } @Override + public Resources getResources() { + if (sOverrides.resources != null) { + return sOverrides.resources; + } + return super.getResources(); + } + + @Override protected Bitmap loadThumbnail(Uri uri, Size size) { if (sOverrides.previewThumbnail != null) { return sOverrides.previewThumbnail; @@ -145,6 +154,7 @@ public class ChooserWrapperActivity extends ChooserActivity { public Bitmap previewThumbnail; public MetricsLogger metricsLogger; public int alternateProfileSetting; + public Resources resources; public void reset() { onSafelyStartCallback = null; @@ -157,6 +167,7 @@ public class ChooserWrapperActivity extends ChooserActivity { resolverListController = mock(ResolverListController.class); metricsLogger = mock(MetricsLogger.class); alternateProfileSetting = 0; + resources = null; } } } diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index 97bc4042b86f..d06ba12f5e2a 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -107,10 +107,6 @@ interface ILocationManager List<LocationRequest> getTestProviderCurrentRequests(String provider, String opPackageName); LocationTime getGnssTimeMillis(); - // --- deprecated --- - void setTestProviderStatus(String provider, int status, in Bundle extras, long updateTime, - String opPackageName); - boolean sendExtraCommand(String provider, String command, inout Bundle extras); // --- internal --- diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 1cc246b51765..5be4770440dc 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -1543,44 +1543,19 @@ public class LocationManager { /** * This method has no effect as provider status has been deprecated and is no longer supported. * - * @param provider the provider name - * @param status the mock status - * @param extras a Bundle containing mock extras - * @param updateTime the mock update time - * - * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION - * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED - * allowed} for your app. - * @throws IllegalArgumentException if no provider with the given name exists - * * @deprecated This method has no effect. */ @Deprecated public void setTestProviderStatus( - @NonNull String provider, int status, @Nullable Bundle extras, long updateTime) { - try { - mService.setTestProviderStatus(provider, status, extras, updateTime, - mContext.getOpPackageName()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } + @NonNull String provider, int status, @Nullable Bundle extras, long updateTime) {} /** * This method has no effect as provider status has been deprecated and is no longer supported. * - * @param provider the provider name - * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION - * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED - * allowed} for your app. - * @throws IllegalArgumentException if no provider with the given name exists - * * @deprecated This method has no effect. */ @Deprecated - public void clearTestProviderStatus(@NonNull String provider) { - setTestProviderStatus(provider, LocationProvider.AVAILABLE, null, 0L); - } + public void clearTestProviderStatus(@NonNull String provider) {} /** * Get the last list of {@link LocationRequest}s sent to the provider. diff --git a/location/java/com/android/internal/location/ILocationProvider.aidl b/location/java/com/android/internal/location/ILocationProvider.aidl index 8ae972bde4f9..4246c6cd1004 100644 --- a/location/java/com/android/internal/location/ILocationProvider.aidl +++ b/location/java/com/android/internal/location/ILocationProvider.aidl @@ -37,10 +37,4 @@ interface ILocationProvider { @UnsupportedAppUsage oneway void sendExtraCommand(String command, in Bundle extras); - - // --- deprecated and will be removed the future --- - @UnsupportedAppUsage - int getStatus(out Bundle extras); - @UnsupportedAppUsage - long getStatusUpdateTime(); } diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java index 6bde3a884c30..fc7bff3c6dab 100644 --- a/location/lib/java/com/android/location/provider/LocationProviderBase.java +++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java @@ -256,17 +256,6 @@ public abstract class LocationProviderBase { /** * This method will no longer be invoked. * - * Returns a information on the status of this provider. - * <p>{@link android.location.LocationProvider#OUT_OF_SERVICE} is returned if the provider is - * out of service, and this is not expected to change in the near - * future; {@link android.location.LocationProvider#TEMPORARILY_UNAVAILABLE} is returned if - * the provider is temporarily unavailable but is expected to be - * available shortly; and {@link android.location.LocationProvider#AVAILABLE} is returned - * if the provider is currently available. - * - * <p>If extras is non-null, additional status information may be - * added to it in the form of provider-specific key/value pairs. - * * @deprecated This callback will be never be invoked on Android Q and above. This method should * only be implemented in location providers that need to support SDKs below Android Q. This * method may be removed in the future. @@ -279,15 +268,6 @@ public abstract class LocationProviderBase { /** * This method will no longer be invoked. * - * Returns the time at which the status was last updated. It is the - * responsibility of the provider to appropriately set this value using - * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}. - * there is a status update that it wishes to broadcast to all its - * listeners. The provider should be careful not to broadcast - * the same status again. - * - * @return time of last status update in millis since last reboot - * * @deprecated This callback will be never be invoked on Android Q and above. This method should * only be implemented in location providers that need to support SDKs below Android Q. This * method may be removed in the future. @@ -332,16 +312,6 @@ public abstract class LocationProviderBase { } @Override - public int getStatus(Bundle extras) { - return onGetStatus(extras); - } - - @Override - public long getStatusUpdateTime() { - return onGetStatusUpdateTime(); - } - - @Override public void sendExtraCommand(String command, Bundle extras) { onSendExtraCommand(command, extras); } diff --git a/media/jni/android_media_MediaDataSource.cpp b/media/jni/android_media_MediaDataSource.cpp index 8c38d887f82b..9705b91dd52a 100644 --- a/media/jni/android_media_MediaDataSource.cpp +++ b/media/jni/android_media_MediaDataSource.cpp @@ -106,7 +106,8 @@ ssize_t JMediaDataSource::readAt(off64_t offset, size_t size) { } ALOGV("readAt %lld / %zu => %d.", (long long)offset, size, numread); - env->GetByteArrayRegion(mByteArrayObj, 0, numread, (jbyte*)mMemory->pointer()); + env->GetByteArrayRegion(mByteArrayObj, 0, numread, + (jbyte*)mMemory->unsecurePointer()); return numread; } diff --git a/media/jni/android_media_MediaDescrambler.cpp b/media/jni/android_media_MediaDescrambler.cpp index aa79ce0a44ab..c61365a448d3 100644 --- a/media/jni/android_media_MediaDescrambler.cpp +++ b/media/jni/android_media_MediaDescrambler.cpp @@ -220,7 +220,7 @@ status_t JDescrambler::descramble( return NO_MEMORY; } - memcpy(mMem->pointer(), + memcpy(mMem->unsecurePointer(), (const void*)((const uint8_t*)srcPtr + srcOffset), totalLength); DestinationBuffer dstBuffer; @@ -248,7 +248,8 @@ status_t JDescrambler::descramble( if (*status == Status::OK) { if (*bytesWritten > 0 && (ssize_t) *bytesWritten <= totalLength) { - memcpy((void*)((uint8_t*)dstPtr + dstOffset), mMem->pointer(), *bytesWritten); + memcpy((void*)((uint8_t*)dstPtr + dstOffset), mMem->unsecurePointer(), + *bytesWritten); } else { // status seems OK but bytesWritten is invalid, we really // have no idea what is wrong. diff --git a/media/jni/android_media_MediaHTTPConnection.cpp b/media/jni/android_media_MediaHTTPConnection.cpp index 365e045689f0..53adff3e251e 100644 --- a/media/jni/android_media_MediaHTTPConnection.cpp +++ b/media/jni/android_media_MediaHTTPConnection.cpp @@ -148,7 +148,7 @@ static jint android_media_MediaHTTPConnection_native_readAt( byteArrayObj, 0, n, - (jbyte *)conn->getIMemory()->pointer()); + (jbyte *)conn->getIMemory()->unsecurePointer()); } return n; diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp index 3809bc4752a8..18fd1a01cfd6 100644 --- a/media/jni/android_media_MediaMetadataRetriever.cpp +++ b/media/jni/android_media_MediaMetadataRetriever.cpp @@ -396,8 +396,12 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtTime( // Call native method to retrieve a video frame VideoFrame *videoFrame = NULL; sp<IMemory> frameMemory = retriever->getFrameAtTime(timeUs, option); + // TODO: Using unsecurePointer() has some associated security pitfalls + // (see declaration for details). + // Either document why it is safe in this case or address the + // issue (e.g. by copying). if (frameMemory != 0) { // cast the shared structure to a VideoFrame object - videoFrame = static_cast<VideoFrame *>(frameMemory->pointer()); + videoFrame = static_cast<VideoFrame *>(frameMemory->unsecurePointer()); } if (videoFrame == NULL) { ALOGE("getFrameAtTime: videoFrame is a NULL pointer"); @@ -423,7 +427,11 @@ static jobject android_media_MediaMetadataRetriever_getImageAtIndex( VideoFrame *videoFrame = NULL; sp<IMemory> frameMemory = retriever->getImageAtIndex(index, colorFormat); if (frameMemory != 0) { // cast the shared structure to a VideoFrame object - videoFrame = static_cast<VideoFrame *>(frameMemory->pointer()); + // TODO: Using unsecurePointer() has some associated security pitfalls + // (see declaration for details). + // Either document why it is safe in this case or address the + // issue (e.g. by copying). + videoFrame = static_cast<VideoFrame *>(frameMemory->unsecurePointer()); } if (videoFrame == NULL) { ALOGE("getImageAtIndex: videoFrame is a NULL pointer"); @@ -454,7 +462,11 @@ static jobject android_media_MediaMetadataRetriever_getThumbnailImageAtIndex( sp<IMemory> frameMemory = retriever->getImageAtIndex( index, colorFormat, true /*metaOnly*/, true /*thumbnail*/); if (frameMemory != 0) { - videoFrame = static_cast<VideoFrame *>(frameMemory->pointer()); + // TODO: Using unsecurePointer() has some associated security pitfalls + // (see declaration for details). + // Either document why it is safe in this case or address the + // issue (e.g. by copying). + videoFrame = static_cast<VideoFrame *>(frameMemory->unsecurePointer()); int32_t thumbWidth = videoFrame->mWidth; int32_t thumbHeight = videoFrame->mHeight; videoFrame = NULL; @@ -467,7 +479,11 @@ static jobject android_media_MediaMetadataRetriever_getThumbnailImageAtIndex( || thumbPixels * 6 >= maxPixels) { frameMemory = retriever->getImageAtIndex( index, colorFormat, false /*metaOnly*/, true /*thumbnail*/); - videoFrame = static_cast<VideoFrame *>(frameMemory->pointer()); + // TODO: Using unsecurePointer() has some associated security pitfalls + // (see declaration for details). + // Either document why it is safe in this case or address the + // issue (e.g. by copying). + videoFrame = static_cast<VideoFrame *>(frameMemory->unsecurePointer()); if (thumbPixels > maxPixels) { int downscale = ceil(sqrt(thumbPixels / (float)maxPixels)); @@ -514,11 +530,15 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtIndex( size_t i = 0; for (; i < numFrames; i++) { sp<IMemory> frame = retriever->getFrameAtIndex(frameIndex + i, colorFormat); - if (frame == NULL || frame->pointer() == NULL) { + if (frame == NULL || frame->unsecurePointer() == NULL) { ALOGE("video frame at index %zu is a NULL pointer", frameIndex + i); break; } - VideoFrame *videoFrame = static_cast<VideoFrame *>(frame->pointer()); + // TODO: Using unsecurePointer() has some associated security pitfalls + // (see declaration for details). + // Either document why it is safe in this case or address the + // issue (e.g. by copying). + VideoFrame *videoFrame = static_cast<VideoFrame *>(frame->unsecurePointer()); jobject bitmapObj = getBitmapFromVideoFrame(env, videoFrame, -1, -1, outColorType); env->CallBooleanMethod(arrayList, fields.arrayListAdd, bitmapObj); env->DeleteLocalRef(bitmapObj); @@ -551,7 +571,11 @@ static jbyteArray android_media_MediaMetadataRetriever_getEmbeddedPicture( // the method name to getEmbeddedPicture(). sp<IMemory> albumArtMemory = retriever->extractAlbumArt(); if (albumArtMemory != 0) { // cast the shared structure to a MediaAlbumArt object - mediaAlbumArt = static_cast<MediaAlbumArt *>(albumArtMemory->pointer()); + // TODO: Using unsecurePointer() has some associated security pitfalls + // (see declaration for details). + // Either document why it is safe in this case or address the + // issue (e.g. by copying). + mediaAlbumArt = static_cast<MediaAlbumArt *>(albumArtMemory->unsecurePointer()); } if (mediaAlbumArt == NULL) { ALOGE("getEmbeddedPicture: Call to getEmbeddedPicture failed."); diff --git a/media/jni/soundpool/SoundPool.h b/media/jni/soundpool/SoundPool.h index 9d7410305c2c..01e4faae6f6c 100644 --- a/media/jni/soundpool/SoundPool.h +++ b/media/jni/soundpool/SoundPool.h @@ -61,7 +61,7 @@ public: audio_channel_mask_t channelMask() { return mChannelMask; } size_t size() { return mSize; } int state() { return mState; } - uint8_t* data() { return static_cast<uint8_t*>(mData->pointer()); } + uint8_t* data() { return static_cast<uint8_t*>(mData->unsecurePointer()); } status_t doLoad(); void startLoad() { mState = LOADING; } sp<IMemory> getIMemory() { return mData; } diff --git a/media/tests/audiotests/shared_mem_test.cpp b/media/tests/audiotests/shared_mem_test.cpp index 2f5749933b54..d586b6a6da17 100644 --- a/media/tests/audiotests/shared_mem_test.cpp +++ b/media/tests/audiotests/shared_mem_test.cpp @@ -92,7 +92,7 @@ int AudioTrackTest::Test01() { iMem = heap->allocate(BUF_SZ*sizeof(short)); - p = static_cast<uint8_t*>(iMem->pointer()); + p = static_cast<uint8_t*>(iMem->unsecurePointer()); memcpy(p, smpBuf, BUF_SZ*sizeof(short)); sp<AudioTrack> track = new AudioTrack(AUDIO_STREAM_MUSIC,// stream type diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java index 2010620f76ed..033f1b10118c 100644 --- a/packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java +++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java @@ -30,6 +30,7 @@ import com.android.internal.annotations.VisibleForTesting; import java.security.KeyStoreException; import java.util.Optional; +import java.util.concurrent.TimeUnit; /** * State about encrypted backups that needs to be remembered. @@ -51,6 +52,9 @@ public class CryptoSettings { SECONDARY_KEY_LAST_ROTATED_AT }; + private static final long DEFAULT_SECONDARY_KEY_ROTATION_PERIOD = + TimeUnit.MILLISECONDS.convert(31, TimeUnit.DAYS); + private static final String KEY_ANCESTRAL_SECONDARY_KEY_VERSION = "ancestral_secondary_key_version"; @@ -202,6 +206,11 @@ public class CryptoSettings { .apply(); } + /** The number of milliseconds between secondary key rotation */ + public long backupSecondaryKeyRotationIntervalMs() { + return DEFAULT_SECONDARY_KEY_ROTATION_PERIOD; + } + /** Deletes all crypto settings related to backup (as opposed to restore). */ public void clearAllSettingsForBackup() { Editor sharedPrefsEditor = mSharedPreferences.edit(); diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationScheduler.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationScheduler.java new file mode 100644 index 000000000000..91b57cf69795 --- /dev/null +++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationScheduler.java @@ -0,0 +1,116 @@ +/* + * 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.server.backup.encryption.keys; + +import android.content.Context; +import android.util.Slog; + +import com.android.server.backup.encryption.CryptoSettings; +import com.android.server.backup.encryption.tasks.StartSecondaryKeyRotationTask; + +import java.io.File; +import java.time.Clock; +import java.util.Optional; + +/** + * Helps schedule rotations of secondary keys. + * + * <p>TODO(b/72028016) Replace with a job. + */ +public class SecondaryKeyRotationScheduler { + + private static final String TAG = "SecondaryKeyRotationScheduler"; + private static final String SENTINEL_FILE_PATH = "force_secondary_key_rotation"; + + private final Context mContext; + private final RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager; + private final CryptoSettings mCryptoSettings; + private final Clock mClock; + + public SecondaryKeyRotationScheduler( + Context context, + RecoverableKeyStoreSecondaryKeyManager secondaryKeyManager, + CryptoSettings cryptoSettings, + Clock clock) { + mContext = context; + mCryptoSettings = cryptoSettings; + mClock = clock; + mSecondaryKeyManager = secondaryKeyManager; + } + + /** + * Returns {@code true} if a sentinel file for forcing secondary key rotation is present. This + * is only for testing purposes. + */ + private boolean isForceRotationTestSentinelPresent() { + File file = new File(mContext.getFilesDir(), SENTINEL_FILE_PATH); + if (file.exists()) { + file.delete(); + return true; + } + return false; + } + + /** Start the key rotation task if it's time to do so */ + public void startRotationIfScheduled() { + if (isForceRotationTestSentinelPresent()) { + Slog.i(TAG, "Found force flag for secondary rotation. Starting now."); + startRotation(); + return; + } + + Optional<Long> maybeLastRotated = mCryptoSettings.getSecondaryLastRotated(); + if (!maybeLastRotated.isPresent()) { + Slog.v(TAG, "No previous rotation, scheduling from now."); + scheduleRotationFromNow(); + return; + } + + long lastRotated = maybeLastRotated.get(); + long now = mClock.millis(); + + if (lastRotated > now) { + Slog.i(TAG, "Last rotation was in the future. Clock must have changed. Rotate now."); + startRotation(); + return; + } + + long millisSinceLastRotation = now - lastRotated; + long rotationInterval = mCryptoSettings.backupSecondaryKeyRotationIntervalMs(); + if (millisSinceLastRotation >= rotationInterval) { + Slog.i( + TAG, + "Last rotation was more than " + + rotationInterval + + "ms (" + + millisSinceLastRotation + + "ms) in the past. Rotate now."); + startRotation(); + } + + Slog.v(TAG, "No rotation required, last " + lastRotated + "."); + } + + private void startRotation() { + scheduleRotationFromNow(); + new StartSecondaryKeyRotationTask(mCryptoSettings, mSecondaryKeyManager).run(); + } + + private void scheduleRotationFromNow() { + mCryptoSettings.setSecondaryLastRotated(mClock.millis()); + } +} diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTask.java new file mode 100644 index 000000000000..77cfded32173 --- /dev/null +++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTask.java @@ -0,0 +1,104 @@ +/* + * 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.server.backup.encryption.tasks; + +import android.security.keystore.recovery.InternalRecoveryServiceException; +import android.security.keystore.recovery.LockScreenRequiredException; +import android.util.Slog; + +import com.android.internal.util.Preconditions; +import com.android.server.backup.encryption.CryptoSettings; +import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey; +import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager; + +import java.security.UnrecoverableKeyException; +import java.util.Optional; + +/** + * Starts rotating to a new secondary key. Cannot complete until the screen is unlocked and the new + * key is synced. + */ +public class StartSecondaryKeyRotationTask { + private static final String TAG = "BE-StSecondaryKeyRotTsk"; + + private final CryptoSettings mCryptoSettings; + private final RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager; + + public StartSecondaryKeyRotationTask( + CryptoSettings cryptoSettings, + RecoverableKeyStoreSecondaryKeyManager secondaryKeyManager) { + mCryptoSettings = Preconditions.checkNotNull(cryptoSettings); + mSecondaryKeyManager = Preconditions.checkNotNull(secondaryKeyManager); + } + + /** Begin the key rotation */ + public void run() { + Slog.i(TAG, "Attempting to initiate a secondary key rotation."); + + Optional<String> maybeCurrentAlias = mCryptoSettings.getActiveSecondaryKeyAlias(); + if (!maybeCurrentAlias.isPresent()) { + Slog.w(TAG, "No active current alias. Cannot trigger a secondary rotation."); + return; + } + String currentAlias = maybeCurrentAlias.get(); + + Optional<String> maybeNextAlias = mCryptoSettings.getNextSecondaryKeyAlias(); + if (maybeNextAlias.isPresent()) { + String nextAlias = maybeNextAlias.get(); + if (nextAlias.equals(currentAlias)) { + // Shouldn't be possible, but guard against accidentally deleting the active key. + Slog.e(TAG, "Was already trying to rotate to what is already the active key."); + } else { + Slog.w(TAG, "Was already rotating to another key. Cancelling that."); + try { + mSecondaryKeyManager.remove(nextAlias); + } catch (Exception e) { + Slog.wtf(TAG, "Could not remove old key", e); + } + } + mCryptoSettings.removeNextSecondaryKeyAlias(); + } + + RecoverableKeyStoreSecondaryKey newSecondaryKey; + try { + newSecondaryKey = mSecondaryKeyManager.generate(); + } catch (LockScreenRequiredException e) { + Slog.e(TAG, "No lock screen is set - cannot generate a new key to rotate to.", e); + return; + } catch (InternalRecoveryServiceException e) { + Slog.e(TAG, "Internal error in Recovery Controller, failed to rotate key.", e); + return; + } catch (UnrecoverableKeyException e) { + Slog.e(TAG, "Failed to get key after generating, failed to rotate", e); + return; + } + + String alias = newSecondaryKey.getAlias(); + Slog.i(TAG, "Generated a new secondary key with alias '" + alias + "'."); + try { + mCryptoSettings.setNextSecondaryAlias(alias); + Slog.i(TAG, "Successfully set '" + alias + "' as next key to rotate to"); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Unexpected error setting next alias", e); + try { + mSecondaryKeyManager.remove(alias); + } catch (Exception err) { + Slog.wtf(TAG, "Failed to remove generated key after encountering error", err); + } + } + } +} diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationSchedulerTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationSchedulerTest.java new file mode 100644 index 000000000000..c31d19d8568c --- /dev/null +++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationSchedulerTest.java @@ -0,0 +1,179 @@ +/* + * 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.server.backup.encryption.keys; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.platform.test.annotations.Presubmit; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.server.backup.encryption.CryptoSettings; +import com.android.server.backup.encryption.tasks.StartSecondaryKeyRotationTask; + +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; +import org.robolectric.annotation.Config; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.annotation.Resetter; + +import java.io.File; +import java.time.Clock; + +@Config(shadows = SecondaryKeyRotationSchedulerTest.ShadowStartSecondaryKeyRotationTask.class) +@RunWith(RobolectricTestRunner.class) +@Presubmit +public class SecondaryKeyRotationSchedulerTest { + private static final String SENTINEL_FILE_PATH = "force_secondary_key_rotation"; + + @Mock private RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager; + @Mock private Clock mClock; + + private CryptoSettings mCryptoSettings; + private SecondaryKeyRotationScheduler mScheduler; + private long mRotationIntervalMillis; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + Context application = ApplicationProvider.getApplicationContext(); + + mCryptoSettings = CryptoSettings.getInstanceForTesting(application); + mRotationIntervalMillis = mCryptoSettings.backupSecondaryKeyRotationIntervalMs(); + + mScheduler = + new SecondaryKeyRotationScheduler( + application, mSecondaryKeyManager, mCryptoSettings, mClock); + ShadowStartSecondaryKeyRotationTask.reset(); + } + + @Test + public void startRotationIfScheduled_rotatesIfRotationWasFarEnoughInThePast() { + long lastRotated = 100009; + mCryptoSettings.setSecondaryLastRotated(lastRotated); + setNow(lastRotated + mRotationIntervalMillis); + + mScheduler.startRotationIfScheduled(); + + assertThat(ShadowStartSecondaryKeyRotationTask.sRan).isTrue(); + } + + @Test + public void startRotationIfScheduled_setsNewRotationTimeIfRotationWasFarEnoughInThePast() { + long lastRotated = 100009; + long now = lastRotated + mRotationIntervalMillis; + mCryptoSettings.setSecondaryLastRotated(lastRotated); + setNow(now); + + mScheduler.startRotationIfScheduled(); + + assertThat(mCryptoSettings.getSecondaryLastRotated().get()).isEqualTo(now); + } + + @Test + public void startRotationIfScheduled_rotatesIfClockHasChanged() { + long lastRotated = 100009; + mCryptoSettings.setSecondaryLastRotated(lastRotated); + setNow(lastRotated - 1); + + mScheduler.startRotationIfScheduled(); + + assertThat(ShadowStartSecondaryKeyRotationTask.sRan).isTrue(); + } + + @Test + public void startRotationIfScheduled_rotatesIfSentinelFileIsPresent() throws Exception { + File file = new File(RuntimeEnvironment.application.getFilesDir(), SENTINEL_FILE_PATH); + file.createNewFile(); + + mScheduler.startRotationIfScheduled(); + + assertThat(ShadowStartSecondaryKeyRotationTask.sRan).isTrue(); + } + + @Test + public void startRotationIfScheduled_setsNextRotationIfClockHasChanged() { + long lastRotated = 100009; + long now = lastRotated - 1; + mCryptoSettings.setSecondaryLastRotated(lastRotated); + setNow(now); + + mScheduler.startRotationIfScheduled(); + + assertThat(mCryptoSettings.getSecondaryLastRotated().get()).isEqualTo(now); + } + + @Test + public void startRotationIfScheduled_doesNothingIfRotationWasRecentEnough() { + long lastRotated = 100009; + mCryptoSettings.setSecondaryLastRotated(lastRotated); + setNow(lastRotated + mRotationIntervalMillis - 1); + + mScheduler.startRotationIfScheduled(); + + assertThat(ShadowStartSecondaryKeyRotationTask.sRan).isFalse(); + } + + @Test + public void startRotationIfScheduled_doesNotSetRotationTimeIfRotationWasRecentEnough() { + long lastRotated = 100009; + mCryptoSettings.setSecondaryLastRotated(lastRotated); + setNow(lastRotated + mRotationIntervalMillis - 1); + + mScheduler.startRotationIfScheduled(); + + assertThat(mCryptoSettings.getSecondaryLastRotated().get()).isEqualTo(lastRotated); + } + + @Test + public void startRotationIfScheduled_setsLastRotatedToNowIfNeverRotated() { + long now = 13295436; + setNow(now); + + mScheduler.startRotationIfScheduled(); + + assertThat(mCryptoSettings.getSecondaryLastRotated().get()).isEqualTo(now); + } + + private void setNow(long timestamp) { + when(mClock.millis()).thenReturn(timestamp); + } + + @Implements(StartSecondaryKeyRotationTask.class) + public static class ShadowStartSecondaryKeyRotationTask { + private static boolean sRan = false; + + @Implementation + public void run() { + sRan = true; + } + + @Resetter + public static void reset() { + sRan = false; + } + } +} diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTaskTest.java new file mode 100644 index 000000000000..4ac4fa8d06c9 --- /dev/null +++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTaskTest.java @@ -0,0 +1,113 @@ +/* + * 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.server.backup.encryption.tasks; + +import static com.google.common.truth.Truth.assertThat; + +import android.platform.test.annotations.Presubmit; +import android.security.keystore.recovery.RecoveryController; + +import com.android.server.backup.encryption.CryptoSettings; +import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey; +import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager; +import com.android.server.testing.shadows.ShadowRecoveryController; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +import java.security.SecureRandom; + +@RunWith(RobolectricTestRunner.class) +@Config(shadows = {ShadowRecoveryController.class}) +@Presubmit +public class StartSecondaryKeyRotationTaskTest { + + private CryptoSettings mCryptoSettings; + private RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager; + private StartSecondaryKeyRotationTask mStartSecondaryKeyRotationTask; + + @Before + public void setUp() throws Exception { + mSecondaryKeyManager = + new RecoverableKeyStoreSecondaryKeyManager( + RecoveryController.getInstance(RuntimeEnvironment.application), + new SecureRandom()); + mCryptoSettings = CryptoSettings.getInstanceForTesting(RuntimeEnvironment.application); + mStartSecondaryKeyRotationTask = + new StartSecondaryKeyRotationTask(mCryptoSettings, mSecondaryKeyManager); + + ShadowRecoveryController.reset(); + } + + @Test + public void run_doesNothingIfNoActiveSecondaryExists() { + mStartSecondaryKeyRotationTask.run(); + + assertThat(mCryptoSettings.getNextSecondaryKeyAlias().isPresent()).isFalse(); + } + + @Test + public void run_doesNotRemoveExistingNextSecondaryKeyIfItIsAlreadyActive() throws Exception { + generateAnActiveKey(); + String activeAlias = mCryptoSettings.getActiveSecondaryKeyAlias().get(); + mCryptoSettings.setNextSecondaryAlias(activeAlias); + + mStartSecondaryKeyRotationTask.run(); + + assertThat(mSecondaryKeyManager.get(activeAlias).isPresent()).isTrue(); + } + + @Test + public void run_doesRemoveExistingNextSecondaryKeyIfItIsNotYetActive() throws Exception { + generateAnActiveKey(); + RecoverableKeyStoreSecondaryKey nextKey = mSecondaryKeyManager.generate(); + String nextAlias = nextKey.getAlias(); + mCryptoSettings.setNextSecondaryAlias(nextAlias); + + mStartSecondaryKeyRotationTask.run(); + + assertThat(mSecondaryKeyManager.get(nextAlias).isPresent()).isFalse(); + } + + @Test + public void run_generatesANewNextSecondaryKey() throws Exception { + generateAnActiveKey(); + + mStartSecondaryKeyRotationTask.run(); + + assertThat(mCryptoSettings.getNextSecondaryKeyAlias().isPresent()).isTrue(); + } + + @Test + public void run_generatesANewKeyThatExistsInKeyStore() throws Exception { + generateAnActiveKey(); + + mStartSecondaryKeyRotationTask.run(); + + String nextAlias = mCryptoSettings.getNextSecondaryKeyAlias().get(); + assertThat(mSecondaryKeyManager.get(nextAlias).isPresent()).isTrue(); + } + + private void generateAnActiveKey() throws Exception { + RecoverableKeyStoreSecondaryKey secondaryKey = mSecondaryKeyManager.generate(); + mCryptoSettings.setActiveSecondaryKeyAlias(secondaryKey.getAlias()); + } +} diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index d7eb7e955d8c..6ea3db366f66 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -308,7 +308,6 @@ public class SettingsBackupTest { Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS, Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST, Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST, - Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS, Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS, Settings.Global.LOCATION_GLOBAL_KILL_SWITCH, Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED, diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java index 4ba5146b8d39..d2f168eb5e3e 100644 --- a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java +++ b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java @@ -52,7 +52,6 @@ import com.android.internal.app.AlertActivity; import com.android.internal.app.AlertController; import java.io.IOException; -import java.util.Objects; import java.util.regex.Pattern; /** @@ -154,10 +153,7 @@ public final class RingtonePickerActivity extends AlertActivity implements if (which == mCursor.getCount() + mStaticItemCount) { // The "Add new ringtone" item was clicked. Start a file picker intent to select // only audio files (MIME type "audio/*") - final Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT); - chooseFile.setType("audio/*"); - chooseFile.putExtra(Intent.EXTRA_MIME_TYPES, - new String[] { "audio/*", "application/ogg" }); + final Intent chooseFile = getMediaFilePickerIntent(); startActivityForResult(chooseFile, ADD_FILE_REQUEST_CODE); return; } @@ -375,7 +371,8 @@ public final class RingtonePickerActivity extends AlertActivity implements setSuccessResultWithRingtone(getCurrentlySelectedRingtoneUri()); } // If external storage is available, add a button to install sounds from storage. - if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + if (resolvesMediaFilePicker() + && Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { addNewSoundItem(listView); } @@ -633,6 +630,18 @@ public final class RingtonePickerActivity extends AlertActivity implements return ringtoneManagerPos + mStaticItemCount; } + private Intent getMediaFilePickerIntent() { + final Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT); + chooseFile.setType("audio/*"); + chooseFile.putExtra(Intent.EXTRA_MIME_TYPES, + new String[] { "audio/*", "application/ogg" }); + return chooseFile; + } + + private boolean resolvesMediaFilePicker() { + return getMediaFilePickerIntent().resolveActivity(getPackageManager()) != null; + } + private static class LocalizedCursor extends CursorWrapper { final int mTitleIndex; diff --git a/packages/SystemUI/res/drawable/biometric_dialog_bg.xml b/packages/SystemUI/res/drawable/biometric_dialog_bg.xml deleted file mode 100644 index 0c6d57dd6183..000000000000 --- a/packages/SystemUI/res/drawable/biometric_dialog_bg.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<!-- - ~ Copyright (C) 2018 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License - --> - -<shape xmlns:android="http://schemas.android.com/apk/res/android"> - <solid android:color="?android:attr/colorBackgroundFloating" /> - <corners - android:topLeftRadius="@dimen/biometric_dialog_corner_size" - android:topRightRadius="@dimen/biometric_dialog_corner_size" - android:bottomLeftRadius="@dimen/biometric_dialog_corner_size" - android:bottomRightRadius="@dimen/biometric_dialog_corner_size"/> -</shape> diff --git a/packages/SystemUI/res/layout/biometric_dialog.xml b/packages/SystemUI/res/layout/biometric_dialog.xml deleted file mode 100644 index e687cdfac356..000000000000 --- a/packages/SystemUI/res/layout/biometric_dialog.xml +++ /dev/null @@ -1,190 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2018 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License - --> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/layout" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <ImageView - android:id="@+id/background" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:scaleType="center" /> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="match_parent" - android:gravity="bottom" - android:background="@color/biometric_dialog_dim_color" - android:orientation="vertical"> - - <!-- This is not a Space since Spaces cannot be clicked --> - <View - android:id="@+id/space" - android:layout_width="match_parent" - android:layout_height="0dp" - android:layout_weight="1" - android:contentDescription="@string/biometric_dialog_empty_space_description"/> - - <ScrollView - android:layout_width="match_parent" - android:layout_height="wrap_content"> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal"> - - <!-- This is not a Space since Spaces cannot be clicked. The width of this changes - depending on horizontal/portrait orientation --> - <View - android:id="@+id/left_space" - android:layout_weight="1" - android:layout_width="0dp" - android:layout_height="match_parent" - android:contentDescription="@string/biometric_dialog_empty_space_description"/> - - <LinearLayout - android:id="@+id/dialog" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:background="@drawable/biometric_dialog_bg" - android:layout_marginBottom="@dimen/biometric_dialog_border_padding" - android:layout_marginLeft="@dimen/biometric_dialog_border_padding" - android:layout_marginRight="@dimen/biometric_dialog_border_padding"> - - <TextView - android:id="@+id/title" - android:fontFamily="@*android:string/config_headlineFontFamilyMedium" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginEnd="24dp" - android:layout_marginStart="24dp" - android:layout_marginTop="24dp" - android:gravity="@integer/biometric_dialog_text_gravity" - android:textSize="20sp" - android:textColor="?android:attr/textColorPrimary"/> - - <TextView - android:id="@+id/subtitle" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:layout_marginStart="24dp" - android:layout_marginEnd="24dp" - android:gravity="@integer/biometric_dialog_text_gravity" - android:textSize="16sp" - android:textColor="?android:attr/textColorPrimary"/> - - <TextView - android:id="@+id/description" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginEnd="24dp" - android:layout_marginStart="24dp" - android:gravity="@integer/biometric_dialog_text_gravity" - android:paddingTop="8dp" - android:textSize="16sp" - android:textColor="?android:attr/textColorPrimary"/> - - <ImageView - android:id="@+id/biometric_icon" - android:layout_width="@dimen/biometric_dialog_biometric_icon_size" - android:layout_height="@dimen/biometric_dialog_biometric_icon_size" - android:layout_gravity="center_horizontal" - android:layout_marginTop="48dp" - android:scaleType="fitXY" /> - - <TextView - android:id="@+id/error" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginEnd="24dp" - android:layout_marginStart="24dp" - android:paddingTop="16dp" - android:paddingBottom="24dp" - android:textSize="12sp" - android:gravity="center_horizontal" - android:accessibilityLiveRegion="polite" - android:textColor="@color/biometric_dialog_gray"/> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="72dip" - android:paddingTop="24dp" - android:layout_gravity="center_vertical" - style="?android:attr/buttonBarStyle" - android:orientation="horizontal" - android:measureWithLargestChild="true"> - <Space android:id="@+id/leftSpacer" - android:layout_width="12dp" - android:layout_height="match_parent" - android:visibility="visible" /> - <!-- Negative Button --> - <Button android:id="@+id/button2" - android:layout_width="wrap_content" - android:layout_height="match_parent" - style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored" - android:gravity="center" - android:maxLines="2" /> - <Space android:id="@+id/middleSpacer" - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="1" - android:visibility="visible" /> - <!-- Positive Button --> - <Button android:id="@+id/button1" - android:layout_width="wrap_content" - android:layout_height="match_parent" - style="@*android:style/Widget.DeviceDefault.Button.Colored" - android:gravity="center" - android:maxLines="2" - android:text="@string/biometric_dialog_confirm" - android:visibility="gone"/> - <!-- Try Again Button --> - <Button android:id="@+id/button_try_again" - android:layout_width="wrap_content" - android:layout_height="match_parent" - style="@*android:style/Widget.DeviceDefault.Button.Colored" - android:gravity="center" - android:maxLines="2" - android:text="@string/biometric_dialog_try_again" - android:visibility="gone"/> - <Space android:id="@+id/rightSpacer" - android:layout_width="12dip" - android:layout_height="match_parent" - android:visibility="visible" /> - </LinearLayout> - </LinearLayout> - - <!-- This is not a Space since Spaces cannot be clicked. The width of this changes - depending on horizontal/portrait orientation --> - <View - android:id="@+id/right_space" - android:layout_weight="1" - android:layout_width="0dp" - android:layout_height="match_parent" - android:contentDescription="@string/biometric_dialog_empty_space_description"/> - - </LinearLayout> - - </ScrollView> - - </LinearLayout> - -</FrameLayout> diff --git a/packages/SystemUI/res/layout/keyguard_indication_text_view.xml b/packages/SystemUI/res/layout/keyguard_indication_text_view.xml index 2b2100c850d8..9376b1f04cc8 100644 --- a/packages/SystemUI/res/layout/keyguard_indication_text_view.xml +++ b/packages/SystemUI/res/layout/keyguard_indication_text_view.xml @@ -1,17 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android"> - - <com.android.systemui.statusbar.phone.KeyguardIndicationTextView - android:id="@+id/keyguard_indication_enterprise_disclosure" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:paddingStart="@dimen/keyguard_indication_text_padding" - android:paddingEnd="@dimen/keyguard_indication_text_padding" - android:textAppearance="@style/TextAppearance.Keyguard.BottomArea" - android:visibility="gone"/> - <com.android.systemui.statusbar.phone.KeyguardIndicationTextView android:id="@+id/keyguard_indication_text" android:layout_width="match_parent" diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 7c6792ce5d48..d10a3fede412 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -29,7 +29,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; -import android.provider.Settings; import android.util.Log; import android.view.WindowManager; @@ -46,8 +45,6 @@ import java.util.List; */ public class AuthController extends SystemUI implements CommandQueue.Callbacks, AuthDialogCallback { - private static final String DISABLE_NEW_DIALOG = - "com.android.systemui.biometrics.AuthController.DISABLE_NEW_DIALOG"; private static final String TAG = "BiometricPrompt/AuthController"; private static final boolean DEBUG = true; @@ -316,25 +313,13 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, protected AuthDialog buildDialog(Bundle biometricPromptBundle, boolean requireConfirmation, int userId, int type, String opPackageName, boolean skipIntro) { - if (Settings.Secure.getIntForUser( - mContext.getContentResolver(), DISABLE_NEW_DIALOG, userId, 0) == 0) { - return new AuthContainerView.Builder(mContext) - .setCallback(this) - .setBiometricPromptBundle(biometricPromptBundle) - .setRequireConfirmation(requireConfirmation) - .setUserId(userId) - .setOpPackageName(opPackageName) - .setSkipIntro(skipIntro) - .build(type); - } else { - return new BiometricDialogView.Builder(mContext) - .setCallback(this) - .setBiometricPromptBundle(biometricPromptBundle) - .setRequireConfirmation(requireConfirmation) - .setUserId(userId) - .setOpPackageName(opPackageName) - .setSkipIntro(skipIntro) - .build(type); - } + return new AuthContainerView.Builder(mContext) + .setCallback(this) + .setBiometricPromptBundle(biometricPromptBundle) + .setRequireConfirmation(requireConfirmation) + .setUserId(userId) + .setOpPackageName(opPackageName) + .setSkipIntro(skipIntro) + .build(type); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java deleted file mode 100644 index b985e1c2a4d4..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java +++ /dev/null @@ -1,963 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics; - -import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ValueAnimator; -import android.annotation.IntDef; -import android.annotation.Nullable; -import android.app.admin.DevicePolicyManager; -import android.content.Context; -import android.graphics.Outline; -import android.graphics.PixelFormat; -import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; -import android.hardware.biometrics.BiometricAuthenticator; -import android.hardware.biometrics.BiometricPrompt; -import android.os.Binder; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.Message; -import android.os.UserManager; -import android.text.TextUtils; -import android.util.DisplayMetrics; -import android.util.Log; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewOutlineProvider; -import android.view.WindowManager; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; -import android.view.animation.Interpolator; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.Dependency; -import com.android.systemui.Interpolators; -import com.android.systemui.R; -import com.android.systemui.keyguard.WakefulnessLifecycle; -import com.android.systemui.util.leak.RotationUtils; - -/** - * Abstract base class. Shows a dialog for BiometricPrompt. - */ -public abstract class BiometricDialogView extends LinearLayout implements AuthDialog { - - private static final String TAG = "BiometricPrompt/DialogView"; - - public static final String KEY_TRY_AGAIN_VISIBILITY = "key_try_again_visibility"; - public static final String KEY_CONFIRM_VISIBILITY = "key_confirm_visibility"; - public static final String KEY_CONFIRM_ENABLED = "key_confirm_enabled"; - public static final String KEY_STATE = "key_state"; - public static final String KEY_ERROR_TEXT_VISIBILITY = "key_error_text_visibility"; - public static final String KEY_ERROR_TEXT_STRING = "key_error_text_string"; - public static final String KEY_ERROR_TEXT_IS_TEMPORARY = "key_error_text_is_temporary"; - public static final String KEY_ERROR_TEXT_COLOR = "key_error_text_color"; - public static final String KEY_DIALOG_SIZE = "key_dialog_size"; - - private static final int ANIMATION_DURATION_SHOW = 250; // ms - private static final int ANIMATION_DURATION_AWAY = 350; // ms - - protected static final int MSG_RESET_MESSAGE = 1; - - protected static final int STATE_IDLE = 0; - protected static final int STATE_AUTHENTICATING = 1; - protected static final int STATE_ERROR = 2; - protected static final int STATE_PENDING_CONFIRMATION = 3; - protected static final int STATE_AUTHENTICATED = 4; - - // Dialog layout/animation - private static final int IMPLICIT_Y_PADDING = 16; // dp - private static final int GROW_DURATION = 150; // ms - private static final int TEXT_ANIMATE_DISTANCE = 32; // dp - @VisibleForTesting static final int SIZE_UNKNOWN = 0; - @VisibleForTesting static final int SIZE_SMALL = 1; - @VisibleForTesting static final int SIZE_GROWING = 2; - @VisibleForTesting static final int SIZE_BIG = 3; - @IntDef({SIZE_UNKNOWN, SIZE_SMALL, SIZE_GROWING, SIZE_BIG}) - @interface DialogSize {} - - @VisibleForTesting final WakefulnessLifecycle mWakefulnessLifecycle; - private final AccessibilityManager mAccessibilityManager; - private final IBinder mWindowToken = new Binder(); - private final Interpolator mLinearOutSlowIn; - private final WindowManager mWindowManager; - private final UserManager mUserManager; - private final DevicePolicyManager mDevicePolicyManager; - private final float mAnimationTranslationOffset; - private final int mErrorColor; - private final float mDialogWidth; - protected final AuthDialogCallback mCallback; - private final DialogOutlineProvider mOutlineProvider = new DialogOutlineProvider(); - - protected final ViewGroup mLayout; - protected final LinearLayout mDialog; - @VisibleForTesting final TextView mTitleText; - @VisibleForTesting final TextView mSubtitleText; - @VisibleForTesting final TextView mDescriptionText; - @VisibleForTesting final ImageView mBiometricIcon; - @VisibleForTesting final TextView mErrorText; - @VisibleForTesting final Button mPositiveButton; - @VisibleForTesting final Button mNegativeButton; - @VisibleForTesting final Button mTryAgainButton; - - protected final int mTextColor; - - private Bundle mBundle; - private Bundle mRestoredState; - private String mOpPackageName; - - private int mState = STATE_IDLE; - private boolean mWasForceRemoved; - private boolean mSkipIntro; - protected boolean mRequireConfirmation; - private int mUserId; // used to determine if we should show work background - private @DialogSize int mSize; - private float mIconOriginalY; - - private boolean mCompletedAnimatingIn; - private boolean mPendingDismissDialog; - - protected abstract int getHintStringResourceId(); - protected abstract int getAuthenticatedAccessibilityResourceId(); - protected abstract int getIconDescriptionResourceId(); - protected abstract int getDelayAfterAuthenticatedDurationMs(); - protected abstract boolean shouldGrayAreaDismissDialog(); - protected abstract void handleResetMessage(); - protected abstract void updateIcon(int oldState, int newState); - protected abstract boolean supportsSmallDialog(); - - private final Runnable mShowAnimationRunnable = new Runnable() { - @Override - public void run() { - mLayout.animate() - .alpha(1f) - .setDuration(ANIMATION_DURATION_SHOW) - .setInterpolator(mLinearOutSlowIn) - .withLayer() - .start(); - mDialog.animate() - .translationY(0) - .setDuration(ANIMATION_DURATION_SHOW) - .setInterpolator(mLinearOutSlowIn) - .withLayer() - .withEndAction(() -> onDialogAnimatedIn()) - .start(); - } - }; - - @VisibleForTesting - final WakefulnessLifecycle.Observer mWakefulnessObserver = - new WakefulnessLifecycle.Observer() { - @Override - public void onStartedGoingToSleep() { - animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); - } - }; - - private final class DialogOutlineProvider extends ViewOutlineProvider { - - float mY; - - @Override - public void getOutline(View view, Outline outline) { - outline.setRoundRect( - 0 /* left */, - (int) mY, /* top */ - mDialog.getWidth() /* right */, - mDialog.getBottom(), /* bottom */ - getResources().getDimension(R.dimen.biometric_dialog_corner_size)); - } - - int calculateSmall() { - final float padding = Utils.dpToPixels(mContext, IMPLICIT_Y_PADDING); - return mDialog.getHeight() - mBiometricIcon.getHeight() - 2 * (int) padding; - } - - void setOutlineY(float y) { - mY = y; - } - } - - protected Handler mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch(msg.what) { - case MSG_RESET_MESSAGE: - handleResetMessage(); - break; - default: - Log.e(TAG, "Unhandled message: " + msg.what); - break; - } - } - }; - - /** - * Builds the dialog with specified parameters. - */ - public static class Builder { - public static final int TYPE_FINGERPRINT = BiometricAuthenticator.TYPE_FINGERPRINT; - public static final int TYPE_FACE = BiometricAuthenticator.TYPE_FACE; - - private Context mContext; - private AuthDialogCallback mCallback; - private Bundle mBundle; - private boolean mRequireConfirmation; - private int mUserId; - private String mOpPackageName; - private boolean mSkipIntro; - - public Builder(Context context) { - mContext = context; - } - - public Builder setCallback(AuthDialogCallback callback) { - mCallback = callback; - return this; - } - - public Builder setBiometricPromptBundle(Bundle bundle) { - mBundle = bundle; - return this; - } - - public Builder setRequireConfirmation(boolean requireConfirmation) { - mRequireConfirmation = requireConfirmation; - return this; - } - - public Builder setUserId(int userId) { - mUserId = userId; - return this; - } - - public Builder setOpPackageName(String opPackageName) { - mOpPackageName = opPackageName; - return this; - } - - public Builder setSkipIntro(boolean skipIntro) { - mSkipIntro = skipIntro; - return this; - } - - public BiometricDialogView build(int type) { - return build(type, new Injector()); - } - - public BiometricDialogView build(int type, Injector injector) { - BiometricDialogView dialog; - if (type == TYPE_FINGERPRINT) { - dialog = new FingerprintDialogView(mContext, mCallback, injector); - } else if (type == TYPE_FACE) { - dialog = new FaceDialogView(mContext, mCallback, injector); - } else { - return null; - } - dialog.setBundle(mBundle); - dialog.setRequireConfirmation(mRequireConfirmation); - dialog.setUserId(mUserId); - dialog.setOpPackageName(mOpPackageName); - dialog.setSkipIntro(mSkipIntro); - return dialog; - } - } - - public static class Injector { - public WakefulnessLifecycle getWakefulnessLifecycle() { - return Dependency.get(WakefulnessLifecycle.class); - } - } - - protected BiometricDialogView(Context context, AuthDialogCallback callback, Injector injector) { - super(context); - mWakefulnessLifecycle = injector.getWakefulnessLifecycle(); - - mCallback = callback; - mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN; - mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class); - mWindowManager = mContext.getSystemService(WindowManager.class); - mUserManager = mContext.getSystemService(UserManager.class); - mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class); - mAnimationTranslationOffset = getResources() - .getDimension(R.dimen.biometric_dialog_animation_translation_offset); - mErrorColor = getResources().getColor(R.color.biometric_dialog_error); - mTextColor = getResources().getColor(R.color.biometric_dialog_gray); - - DisplayMetrics metrics = new DisplayMetrics(); - mWindowManager.getDefaultDisplay().getMetrics(metrics); - mDialogWidth = Math.min(metrics.widthPixels, metrics.heightPixels); - - // Create the dialog - LayoutInflater factory = LayoutInflater.from(getContext()); - mLayout = (ViewGroup) factory.inflate(R.layout.biometric_dialog, this, false); - addView(mLayout); - - mLayout.setOnKeyListener(new View.OnKeyListener() { - @Override - public boolean onKey(View v, int keyCode, KeyEvent event) { - if (keyCode != KeyEvent.KEYCODE_BACK) { - return false; - } - if (event.getAction() == KeyEvent.ACTION_UP) { - animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); - } - return true; - } - }); - - final View space = mLayout.findViewById(R.id.space); - final View leftSpace = mLayout.findViewById(R.id.left_space); - final View rightSpace = mLayout.findViewById(R.id.right_space); - - mDialog = mLayout.findViewById(R.id.dialog); - mTitleText = mLayout.findViewById(R.id.title); - mSubtitleText = mLayout.findViewById(R.id.subtitle); - mDescriptionText = mLayout.findViewById(R.id.description); - mBiometricIcon = mLayout.findViewById(R.id.biometric_icon); - mErrorText = mLayout.findViewById(R.id.error); - mNegativeButton = mLayout.findViewById(R.id.button2); - mPositiveButton = mLayout.findViewById(R.id.button1); - mTryAgainButton = mLayout.findViewById(R.id.button_try_again); - - mBiometricIcon.setContentDescription( - getResources().getString(getIconDescriptionResourceId())); - - setDismissesDialog(space); - setDismissesDialog(leftSpace); - setDismissesDialog(rightSpace); - - mNegativeButton.setOnClickListener((View v) -> { - if (mState == STATE_PENDING_CONFIRMATION || mState == STATE_AUTHENTICATED) { - animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); - } else { - animateAway(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE); - } - }); - - mPositiveButton.setOnClickListener((View v) -> { - updateState(STATE_AUTHENTICATED); - mHandler.postDelayed(() -> { - animateAway(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE); - }, getDelayAfterAuthenticatedDurationMs()); - }); - - mTryAgainButton.setOnClickListener((View v) -> { - handleResetMessage(); - updateState(STATE_AUTHENTICATING); - showTryAgainButton(false /* show */); - - mPositiveButton.setVisibility(View.VISIBLE); - mPositiveButton.setEnabled(false); - - mCallback.onTryAgainPressed(); - }); - - // Must set these in order for the back button events to be received. - mLayout.setFocusableInTouchMode(true); - mLayout.requestFocus(); - } - - @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - - mWakefulnessLifecycle.addObserver(mWakefulnessObserver); - - final ImageView backgroundView = mLayout.findViewById(R.id.background); - if (mUserManager.isManagedProfile(mUserId)) { - final Drawable image = getResources().getDrawable(R.drawable.work_challenge_background, - mContext.getTheme()); - image.setColorFilter(mDevicePolicyManager.getOrganizationColorForUser(mUserId), - PorterDuff.Mode.DARKEN); - backgroundView.setScaleType(ImageView.ScaleType.CENTER_CROP); - backgroundView.setImageDrawable(image); - } else { - backgroundView.setImageDrawable(null); - backgroundView.setBackgroundColor(R.color.biometric_dialog_dim_color); - } - - mNegativeButton.setVisibility(View.VISIBLE); - mNegativeButton.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT)); - - if (RotationUtils.getRotation(mContext) != RotationUtils.ROTATION_NONE) { - mDialog.getLayoutParams().width = (int) mDialogWidth; - } - - if (mRestoredState == null) { - updateState(STATE_AUTHENTICATING); - final int hint = getHintStringResourceId(); - if (hint != 0) { - mErrorText.setText(hint); - mErrorText.setContentDescription(mContext.getString(hint)); - mErrorText.setVisibility(View.VISIBLE); - } else { - mErrorText.setVisibility(View.INVISIBLE); - } - announceAccessibilityEvent(); - } else { - updateState(mState); - } - - CharSequence titleText = mBundle.getCharSequence(BiometricPrompt.KEY_TITLE); - - mTitleText.setVisibility(View.VISIBLE); - mTitleText.setText(titleText); - - final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE); - if (TextUtils.isEmpty(subtitleText)) { - mSubtitleText.setVisibility(View.GONE); - announceAccessibilityEvent(); - } else { - mSubtitleText.setVisibility(View.VISIBLE); - mSubtitleText.setText(subtitleText); - } - - final CharSequence descriptionText = - mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION); - if (TextUtils.isEmpty(descriptionText)) { - mDescriptionText.setVisibility(View.GONE); - announceAccessibilityEvent(); - } else { - mDescriptionText.setVisibility(View.VISIBLE); - mDescriptionText.setText(descriptionText); - } - - if (requiresConfirmation() && mRestoredState == null) { - mPositiveButton.setVisibility(View.VISIBLE); - mPositiveButton.setEnabled(false); - } - - if (mWasForceRemoved || mSkipIntro) { - // Show the dialog immediately - mLayout.animate().cancel(); - mDialog.animate().cancel(); - mDialog.setAlpha(1.0f); - mDialog.setTranslationY(0); - mLayout.setAlpha(1.0f); - mCompletedAnimatingIn = true; - } else { - // Dim the background and slide the dialog up - mDialog.setTranslationY(mAnimationTranslationOffset); - mLayout.setAlpha(0f); - postOnAnimation(mShowAnimationRunnable); - } - mWasForceRemoved = false; - mSkipIntro = false; - } - - /** - * Do small/big layout here instead of onAttachedToWindow, since: - * 1) We need the big layout to be measured, etc for small -> big animation - * 2) We need the dialog measurements to know where to move the biometric icon to - * - * BiometricDialogView already sets the views to their default big state, so here we only - * need to hide the ones that are unnecessary. - */ - @Override - public void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - - if (mIconOriginalY == 0) { - mIconOriginalY = mBiometricIcon.getY(); - } - - // UNKNOWN means size hasn't been set yet. First time we create the dialog. - // onLayout can happen when visibility of views change (during animation, etc). - if (getSize() != SIZE_UNKNOWN) { - // Probably not the cleanest way to do this, but since dialog is big by default, - // and small dialogs can persist across orientation changes, we need to set it to - // small size here again. - if (getSize() == SIZE_SMALL) { - updateSize(SIZE_SMALL); - } - return; - } - - // If we don't require confirmation, show the small dialog first (until errors occur). - if (!requiresConfirmation() && supportsSmallDialog()) { - updateSize(SIZE_SMALL); - } else { - updateSize(SIZE_BIG); - } - } - - @Override - public void onDetachedFromWindow() { - super.onDetachedFromWindow(); - - mWakefulnessLifecycle.removeObserver(mWakefulnessObserver); - } - - @VisibleForTesting - void updateSize(@DialogSize int newSize) { - final float padding = Utils.dpToPixels(mContext, IMPLICIT_Y_PADDING); - final float iconSmallPositionY = mDialog.getHeight() - mBiometricIcon.getHeight() - padding; - - if (newSize == SIZE_SMALL) { - if (!supportsSmallDialog()) { - Log.e(TAG, "Small dialog unsupported"); - return; - } - - // These fields are required and/or always hold a spot on the UI, so should be set to - // INVISIBLE so they keep their position - mTitleText.setVisibility(View.INVISIBLE); - mErrorText.setVisibility(View.INVISIBLE); - mNegativeButton.setVisibility(View.INVISIBLE); - - // These fields are optional, so set them to gone or invisible depending on their - // usage. If they're empty, they're already set to GONE in BiometricDialogView. - if (!TextUtils.isEmpty(mSubtitleText.getText())) { - mSubtitleText.setVisibility(View.INVISIBLE); - } - if (!TextUtils.isEmpty(mDescriptionText.getText())) { - mDescriptionText.setVisibility(View.INVISIBLE); - } - - // Move the biometric icon to the small spot - mBiometricIcon.setY(iconSmallPositionY); - - // Clip the dialog to the small size - mDialog.setOutlineProvider(mOutlineProvider); - mOutlineProvider.setOutlineY(mOutlineProvider.calculateSmall()); - - mDialog.setClipToOutline(true); - mDialog.invalidateOutline(); - - mSize = newSize; - announceAccessibilityEvent(); - } else if (mSize == SIZE_SMALL && newSize == SIZE_BIG) { - mSize = SIZE_GROWING; - - // Animate the outline - final ValueAnimator outlineAnimator = - ValueAnimator.ofFloat(mOutlineProvider.calculateSmall(), 0); - outlineAnimator.addUpdateListener((animation) -> { - final float y = (float) animation.getAnimatedValue(); - mOutlineProvider.setOutlineY(y); - mDialog.invalidateOutline(); - }); - - // Animate the icon back to original big position - final ValueAnimator iconAnimator = - ValueAnimator.ofFloat(iconSmallPositionY, mIconOriginalY); - iconAnimator.addUpdateListener((animation) -> { - final float y = (float) animation.getAnimatedValue(); - mBiometricIcon.setY(y); - }); - - // Animate the error text so it slides up with the icon - final ValueAnimator textSlideAnimator = - ValueAnimator.ofFloat(Utils.dpToPixels(mContext, TEXT_ANIMATE_DISTANCE), 0); - textSlideAnimator.addUpdateListener((animation) -> { - final float y = (float) animation.getAnimatedValue(); - mErrorText.setTranslationY(y); - }); - - // Opacity animator for things that should fade in (title, subtitle, details, negative - // button) - final ValueAnimator opacityAnimator = ValueAnimator.ofFloat(0, 1); - opacityAnimator.addUpdateListener((animation) -> { - final float opacity = (float) animation.getAnimatedValue(); - - // These fields are required and/or always hold a spot on the UI - mTitleText.setAlpha(opacity); - mErrorText.setAlpha(opacity); - mNegativeButton.setAlpha(opacity); - mTryAgainButton.setAlpha(opacity); - - // These fields are optional, so only animate them if they're supposed to be showing - if (!TextUtils.isEmpty(mSubtitleText.getText())) { - mSubtitleText.setAlpha(opacity); - } - if (!TextUtils.isEmpty(mDescriptionText.getText())) { - mDescriptionText.setAlpha(opacity); - } - }); - - // Choreograph together - final AnimatorSet as = new AnimatorSet(); - as.setDuration(GROW_DURATION); - as.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - super.onAnimationStart(animation); - // Set the visibility of opacity-animating views back to VISIBLE - mTitleText.setVisibility(View.VISIBLE); - mErrorText.setVisibility(View.VISIBLE); - mNegativeButton.setVisibility(View.VISIBLE); - mTryAgainButton.setVisibility(View.VISIBLE); - - if (!TextUtils.isEmpty(mSubtitleText.getText())) { - mSubtitleText.setVisibility(View.VISIBLE); - } - if (!TextUtils.isEmpty(mDescriptionText.getText())) { - mDescriptionText.setVisibility(View.VISIBLE); - } - } - - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - mSize = SIZE_BIG; - } - }); - as.play(outlineAnimator).with(iconAnimator).with(opacityAnimator) - .with(textSlideAnimator); - as.start(); - } else if (mSize == SIZE_BIG) { - mDialog.setClipToOutline(false); - mDialog.invalidateOutline(); - - mBiometricIcon.setY(mIconOriginalY); - - mSize = newSize; - } - } - - private void setDismissesDialog(View v) { - v.setClickable(true); - v.setOnClickListener(v1 -> { - if (mState != STATE_AUTHENTICATED && shouldGrayAreaDismissDialog()) { - animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); - } - }); - } - - private void animateAway(@AuthDialogCallback.DismissedReason int reason) { - animateAway(true /* sendReason */, reason); - } - - /** - * Animate the dialog away - * @param reason one of the {@link AuthDialogCallback} codes - */ - private void animateAway(boolean sendReason, @AuthDialogCallback.DismissedReason int reason) { - if (!mCompletedAnimatingIn) { - Log.w(TAG, "startDismiss(): waiting for onDialogAnimatedIn"); - mPendingDismissDialog = true; - return; - } - - // This is where final cleanup should occur. - final Runnable endActionRunnable = new Runnable() { - @Override - public void run() { - mWindowManager.removeView(BiometricDialogView.this); - // Set the icons / text back to normal state - handleResetMessage(); - showTryAgainButton(false /* show */); - updateState(STATE_IDLE); - if (sendReason) { - mCallback.onDismissed(reason); - } - } - }; - - postOnAnimation(new Runnable() { - @Override - public void run() { - mLayout.animate() - .alpha(0f) - .setDuration(ANIMATION_DURATION_AWAY) - .setInterpolator(mLinearOutSlowIn) - .withLayer() - .start(); - mDialog.animate() - .translationY(mAnimationTranslationOffset) - .setDuration(ANIMATION_DURATION_AWAY) - .setInterpolator(mLinearOutSlowIn) - .withLayer() - .withEndAction(endActionRunnable) - .start(); - } - }); - } - - /** - * Skip the intro animation - */ - private void setSkipIntro(boolean skip) { - mSkipIntro = skip; - } - - private void setBundle(Bundle bundle) { - mBundle = bundle; - } - - private void setRequireConfirmation(boolean requireConfirmation) { - mRequireConfirmation = requireConfirmation; - } - - protected boolean requiresConfirmation() { - return mRequireConfirmation; - } - - private void setUserId(int userId) { - mUserId = userId; - } - - private void setOpPackageName(String opPackageName) { - mOpPackageName = opPackageName; - } - - // Shows an error/help message - protected void showTemporaryMessage(String message) { - mHandler.removeMessages(MSG_RESET_MESSAGE); - mErrorText.setText(message); - mErrorText.setTextColor(mErrorColor); - mErrorText.setContentDescription(message); - mErrorText.setVisibility(View.VISIBLE); - mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RESET_MESSAGE), - BiometricPrompt.HIDE_DIALOG_DELAY); - } - - @Override - public void show(WindowManager wm, @Nullable Bundle savedState) { - if (savedState != null) { - restoreState(savedState); - } - wm.addView(this, getLayoutParams(mWindowToken)); - } - - /** - * Force remove the window, cancelling any animation that's happening. This should only be - * called if we want to quickly show the dialog again (e.g. on rotation). Calling this method - * will cause the dialog to show without an animation the next time it's attached. - */ - @Override - public void dismissWithoutCallback(boolean animate) { - if (animate) { - animateAway(false /* sendReason */, 0 /* reason */); - } else { - mLayout.animate().cancel(); - mDialog.animate().cancel(); - mWindowManager.removeView(BiometricDialogView.this); - mWasForceRemoved = true; - } - } - - @Override - public void dismissFromSystemServer() { - animateAway(AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER); - } - - @Override - public void onAuthenticationSucceeded() { - announceForAccessibility(getResources().getText(getAuthenticatedAccessibilityResourceId())); - - if (requiresConfirmation()) { - updateState(STATE_PENDING_CONFIRMATION); - } else { - mHandler.postDelayed(() -> { - animateAway(AuthDialogCallback.DISMISSED_AUTHENTICATED); - }, getDelayAfterAuthenticatedDurationMs()); - - updateState(STATE_AUTHENTICATED); - } - } - - - @Override - public void onAuthenticationFailed(String message) { - updateState(STATE_ERROR); - showTemporaryMessage(message); - } - - /** - * Transient help message (acquire) is received, dialog stays showing. Sensor stays in - * "authenticating" state. - * @param message - */ - @Override - public void onHelp(String message) { - updateState(STATE_ERROR); - showTemporaryMessage(message); - } - - /** - * Hard error is received, dialog will be dismissed soon. - * @param error - */ - @Override - public void onError(String error) { - // All error messages will cause the dialog to go from small -> big. Error messages - // are messages such as lockout, auth failed, etc. - if (mSize == SIZE_SMALL) { - updateSize(SIZE_BIG); - } - - updateState(STATE_ERROR); - showTemporaryMessage(error); - showTryAgainButton(false /* show */); - - mHandler.postDelayed(() -> { - animateAway(AuthDialogCallback.DISMISSED_ERROR); - }, BiometricPrompt.HIDE_DIALOG_DELAY); - } - - - @Override - public void onSaveState(Bundle bundle) { - bundle.putInt(KEY_TRY_AGAIN_VISIBILITY, mTryAgainButton.getVisibility()); - bundle.putInt(KEY_CONFIRM_VISIBILITY, mPositiveButton.getVisibility()); - bundle.putBoolean(KEY_CONFIRM_ENABLED, mPositiveButton.isEnabled()); - bundle.putInt(KEY_STATE, mState); - bundle.putInt(KEY_ERROR_TEXT_VISIBILITY, mErrorText.getVisibility()); - bundle.putCharSequence(KEY_ERROR_TEXT_STRING, mErrorText.getText()); - bundle.putBoolean(KEY_ERROR_TEXT_IS_TEMPORARY, mHandler.hasMessages(MSG_RESET_MESSAGE)); - bundle.putInt(KEY_ERROR_TEXT_COLOR, mErrorText.getCurrentTextColor()); - bundle.putInt(KEY_DIALOG_SIZE, mSize); - } - - public void restoreState(Bundle bundle) { - mRestoredState = bundle; - - // Keep in mind that this happens before onAttachedToWindow() - mSize = bundle.getInt(KEY_DIALOG_SIZE); - - final int tryAgainVisibility = bundle.getInt(KEY_TRY_AGAIN_VISIBILITY); - mTryAgainButton.setVisibility(tryAgainVisibility); - final int confirmVisibility = bundle.getInt(KEY_CONFIRM_VISIBILITY); - mPositiveButton.setVisibility(confirmVisibility); - final boolean confirmEnabled = bundle.getBoolean(KEY_CONFIRM_ENABLED); - mPositiveButton.setEnabled(confirmEnabled); - mState = bundle.getInt(KEY_STATE); - mErrorText.setText(bundle.getCharSequence(KEY_ERROR_TEXT_STRING)); - mErrorText.setContentDescription(bundle.getCharSequence(KEY_ERROR_TEXT_STRING)); - final int errorTextVisibility = bundle.getInt(KEY_ERROR_TEXT_VISIBILITY); - mErrorText.setVisibility(errorTextVisibility); - if (errorTextVisibility == View.INVISIBLE || tryAgainVisibility == View.INVISIBLE - || confirmVisibility == View.INVISIBLE) { - announceAccessibilityEvent(); - } - mErrorText.setTextColor(bundle.getInt(KEY_ERROR_TEXT_COLOR)); - if (bundle.getBoolean(KEY_ERROR_TEXT_IS_TEMPORARY)) { - mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RESET_MESSAGE), - BiometricPrompt.HIDE_DIALOG_DELAY); - } - } - - @Override - public String getOpPackageName() { - return mOpPackageName; - } - - protected void updateState(int newState) { - if (newState == STATE_PENDING_CONFIRMATION) { - mHandler.removeMessages(MSG_RESET_MESSAGE); - mErrorText.setTextColor(mTextColor); - mErrorText.setText(R.string.biometric_dialog_tap_confirm); - mErrorText.setContentDescription( - getResources().getString(R.string.biometric_dialog_tap_confirm)); - mErrorText.setVisibility(View.VISIBLE); - announceAccessibilityEvent(); - mPositiveButton.setVisibility(View.VISIBLE); - mPositiveButton.setEnabled(true); - } else if (newState == STATE_AUTHENTICATED) { - mPositiveButton.setVisibility(View.GONE); - mNegativeButton.setVisibility(View.GONE); - mErrorText.setVisibility(View.INVISIBLE); - announceAccessibilityEvent(); - } - - if (newState == STATE_PENDING_CONFIRMATION || newState == STATE_AUTHENTICATED) { - mNegativeButton.setText(R.string.cancel); - mNegativeButton.setContentDescription(getResources().getString(R.string.cancel)); - } - - updateIcon(mState, newState); - mState = newState; - } - - protected @DialogSize int getSize() { - return mSize; - } - - protected void showTryAgainButton(boolean show) { - if (show && getSize() == SIZE_SMALL) { - // Do not call super, we will nicely animate the alpha together with the rest - // of the elements in here. - updateSize(SIZE_BIG); - } else { - if (show) { - mTryAgainButton.setVisibility(View.VISIBLE); - } else { - mTryAgainButton.setVisibility(View.GONE); - announceAccessibilityEvent(); - } - } - - if (show) { - mPositiveButton.setVisibility(View.GONE); - announceAccessibilityEvent(); - } - } - - protected void onDialogAnimatedIn() { - mCompletedAnimatingIn = true; - - if (mPendingDismissDialog) { - Log.d(TAG, "onDialogAnimatedIn(): mPendingDismissDialog=true, dismissing now"); - animateAway(false /* sendReason */, 0); - mPendingDismissDialog = false; - } - } - - /** - * @param windowToken token for the window - * @return - */ - public static WindowManager.LayoutParams getLayoutParams(IBinder windowToken) { - final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, - WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, - PixelFormat.TRANSLUCENT); - lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; - lp.setTitle("BiometricDialogView"); - lp.token = windowToken; - return lp; - } - - // Every time a view becomes invisible we need to announce an accessibility event. - // This is due to an issue in the framework, b/132298701 recommended this workaround. - protected void announceAccessibilityEvent() { - if (!mAccessibilityManager.isEnabled()) { - return; - } - AccessibilityEvent event = AccessibilityEvent.obtain(); - event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); - event.setContentChangeTypes(CONTENT_CHANGE_TYPE_SUBTREE); - mDialog.sendAccessibilityEventUnchecked(event); - mDialog.notifySubtreeAccessibilityStateChanged(mDialog, mDialog, - CONTENT_CHANGE_TYPE_SUBTREE); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java deleted file mode 100644 index d5dcbf126b63..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics; - -import android.content.Context; -import android.graphics.drawable.Animatable2; -import android.graphics.drawable.AnimatedVectorDrawable; -import android.graphics.drawable.Drawable; -import android.hardware.biometrics.BiometricPrompt; -import android.os.Bundle; -import android.util.Log; -import android.view.View; - -import com.android.systemui.R; - -/** - * This class loads the view for the system-provided dialog. The view consists of: - * Application Icon, Title, Subtitle, Description, Biometric Icon, Error/Help message area, - * and positive/negative buttons. - */ -public class FaceDialogView extends BiometricDialogView { - - private static final String TAG = "BiometricPrompt/FaceDialogView"; - - private static final String KEY_DIALOG_ANIMATED_IN = "key_dialog_animated_in"; - - private static final int HIDE_DIALOG_DELAY = 500; // ms - - private IconController mIconController; - private boolean mDialogAnimatedIn; - - /** - * Class that handles the biometric icon animations. - */ - private final class IconController extends Animatable2.AnimationCallback { - - private boolean mLastPulseDirection; // false = dark to light, true = light to dark - - int mState; - - IconController() { - mState = STATE_IDLE; - } - - public void animateOnce(int iconRes) { - animateIcon(iconRes, false); - } - - public void showStatic(int iconRes) { - mBiometricIcon.setImageDrawable(mContext.getDrawable(iconRes)); - } - - public void startPulsing() { - mLastPulseDirection = false; - animateIcon(R.drawable.face_dialog_pulse_dark_to_light, true); - } - - public void showIcon(int iconRes) { - final Drawable drawable = mContext.getDrawable(iconRes); - mBiometricIcon.setImageDrawable(drawable); - } - - private void animateIcon(int iconRes, boolean repeat) { - final AnimatedVectorDrawable icon = - (AnimatedVectorDrawable) mContext.getDrawable(iconRes); - mBiometricIcon.setImageDrawable(icon); - icon.forceAnimationOnUI(); - if (repeat) { - icon.registerAnimationCallback(this); - } - icon.start(); - } - - private void pulseInNextDirection() { - int iconRes = mLastPulseDirection ? R.drawable.face_dialog_pulse_dark_to_light - : R.drawable.face_dialog_pulse_light_to_dark; - animateIcon(iconRes, true /* repeat */); - mLastPulseDirection = !mLastPulseDirection; - } - - @Override - public void onAnimationEnd(Drawable drawable) { - super.onAnimationEnd(drawable); - - if (mState == STATE_AUTHENTICATING) { - // Still authenticating, pulse the icon - pulseInNextDirection(); - } - } - } - - private final Runnable mErrorToIdleAnimationRunnable = () -> { - updateState(STATE_IDLE); - mErrorText.setVisibility(View.INVISIBLE); - announceAccessibilityEvent(); - }; - - protected FaceDialogView(Context context, AuthDialogCallback callback, Injector injector) { - super(context, callback, injector); - mIconController = new IconController(); - } - - @Override - public void onSaveState(Bundle bundle) { - super.onSaveState(bundle); - bundle.putBoolean(KEY_DIALOG_ANIMATED_IN, mDialogAnimatedIn); - } - - - @Override - protected void handleResetMessage() { - mErrorText.setTextColor(mTextColor); - mErrorText.setVisibility(View.INVISIBLE); - announceAccessibilityEvent(); - } - - @Override - public void restoreState(Bundle bundle) { - super.restoreState(bundle); - mDialogAnimatedIn = bundle.getBoolean(KEY_DIALOG_ANIMATED_IN); - } - - @Override - public void onAuthenticationFailed(String message) { - super.onAuthenticationFailed(message); - showTryAgainButton(true); - } - - @Override - protected int getHintStringResourceId() { - return 0; - } - - @Override - protected int getAuthenticatedAccessibilityResourceId() { - if (mRequireConfirmation) { - return com.android.internal.R.string.face_authenticated_confirmation_required; - } else { - return com.android.internal.R.string.face_authenticated_no_confirmation_required; - } - } - - @Override - protected int getIconDescriptionResourceId() { - return R.string.accessibility_face_dialog_face_icon; - } - - @Override - protected void updateIcon(int oldState, int newState) { - mIconController.mState = newState; - - if (newState == STATE_AUTHENTICATING) { - mHandler.removeCallbacks(mErrorToIdleAnimationRunnable); - if (mDialogAnimatedIn) { - mIconController.startPulsing(); - } else { - mIconController.showIcon(R.drawable.face_dialog_pulse_dark_to_light); - } - mBiometricIcon.setContentDescription(mContext.getString( - R.string.biometric_dialog_face_icon_description_authenticating)); - } else if (oldState == STATE_PENDING_CONFIRMATION && newState == STATE_AUTHENTICATED) { - mIconController.animateOnce(R.drawable.face_dialog_dark_to_checkmark); - mBiometricIcon.setContentDescription(mContext.getString( - R.string.biometric_dialog_face_icon_description_confirmed)); - } else if (oldState == STATE_ERROR && newState == STATE_IDLE) { - mIconController.animateOnce(R.drawable.face_dialog_error_to_idle); - mBiometricIcon.setContentDescription(mContext.getString( - R.string.biometric_dialog_face_icon_description_idle)); - } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATED) { - mHandler.removeCallbacks(mErrorToIdleAnimationRunnable); - mIconController.animateOnce(R.drawable.face_dialog_dark_to_checkmark); - mBiometricIcon.setContentDescription(mContext.getString( - R.string.biometric_dialog_face_icon_description_authenticated)); - } else if (newState == STATE_ERROR) { - // It's easier to only check newState and gate showing the animation on the - // mErrorToIdleAnimationRunnable as a proxy, than add a ton of extra state. For example, - // we may go from error -> error due to configuration change which is valid and we - // should show the animation, or we can go from error -> error by receiving repeated - // acquire messages in which case we do not want to repeatedly start the animation. - if (!mHandler.hasCallbacks(mErrorToIdleAnimationRunnable)) { - mIconController.animateOnce(R.drawable.face_dialog_dark_to_error); - mHandler.postDelayed(mErrorToIdleAnimationRunnable, - BiometricPrompt.HIDE_DIALOG_DELAY); - } - } else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) { - mIconController.animateOnce(R.drawable.face_dialog_dark_to_checkmark); - mBiometricIcon.setContentDescription(mContext.getString( - R.string.biometric_dialog_face_icon_description_authenticated)); - } else if (newState == STATE_PENDING_CONFIRMATION) { - mHandler.removeCallbacks(mErrorToIdleAnimationRunnable); - mIconController.animateOnce(R.drawable.face_dialog_wink_from_dark); - mBiometricIcon.setContentDescription(mContext.getString( - R.string.biometric_dialog_face_icon_description_authenticated)); - } else if (newState == STATE_IDLE) { - mIconController.showStatic(R.drawable.face_dialog_idle_static); - mBiometricIcon.setContentDescription(mContext.getString( - R.string.biometric_dialog_face_icon_description_idle)); - } else { - Log.w(TAG, "Unknown animation from " + oldState + " -> " + newState); - } - - // Note that this must be after the newState == STATE_ERROR check above since this affects - // the logic. - if (oldState == STATE_ERROR && newState == STATE_ERROR) { - // Keep the error icon and text around for a while longer if we keep receiving - // STATE_ERROR - mHandler.removeCallbacks(mErrorToIdleAnimationRunnable); - mHandler.postDelayed(mErrorToIdleAnimationRunnable, BiometricPrompt.HIDE_DIALOG_DELAY); - } - } - - @Override - protected boolean supportsSmallDialog() { - return true; - } - - @Override - public void onDialogAnimatedIn() { - super.onDialogAnimatedIn(); - mDialogAnimatedIn = true; - mIconController.startPulsing(); - } - - @Override - protected int getDelayAfterAuthenticatedDurationMs() { - return HIDE_DIALOG_DELAY; - } - - @Override - protected boolean shouldGrayAreaDismissDialog() { - if (getSize() == SIZE_SMALL) { - return false; - } - return true; - } - - -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java deleted file mode 100644 index cda217619eed..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics; - -import android.content.Context; -import android.graphics.drawable.AnimatedVectorDrawable; -import android.graphics.drawable.Drawable; -import android.util.Log; - -import com.android.systemui.R; - -/** - * This class loads the view for the system-provided dialog. The view consists of: - * Application Icon, Title, Subtitle, Description, Biometric Icon, Error/Help message area, - * and positive/negative buttons. - */ -public class FingerprintDialogView extends BiometricDialogView { - - private static final String TAG = "BiometricPrompt/FingerprintDialogView"; - - protected FingerprintDialogView(Context context, AuthDialogCallback callback, - Injector injector) { - super(context, callback, injector); - } - - @Override - protected void handleResetMessage() { - updateState(STATE_AUTHENTICATING); - mErrorText.setText(getHintStringResourceId()); - mErrorText.setTextColor(mTextColor); - } - - @Override - protected int getHintStringResourceId() { - return R.string.fingerprint_dialog_touch_sensor; - } - - @Override - protected int getAuthenticatedAccessibilityResourceId() { - return com.android.internal.R.string.fingerprint_authenticated; - } - - @Override - protected int getIconDescriptionResourceId() { - return R.string.accessibility_fingerprint_dialog_fingerprint_icon; - } - - @Override - protected void updateIcon(int lastState, int newState) { - final Drawable icon = getAnimationForTransition(lastState, newState); - if (icon == null) { - Log.e(TAG, "Animation not found, " + lastState + " -> " + newState); - return; - } - - final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable - ? (AnimatedVectorDrawable) icon - : null; - - mBiometricIcon.setImageDrawable(icon); - - if (animation != null && shouldAnimateForTransition(lastState, newState)) { - animation.forceAnimationOnUI(); - animation.start(); - } - } - - @Override - protected boolean supportsSmallDialog() { - return false; - } - - protected boolean shouldAnimateForTransition(int oldState, int newState) { - if (newState == STATE_ERROR) { - return true; - } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATING) { - return true; - } else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) { - // TODO(b/77328470): add animation when fingerprint is authenticated - return false; - } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATED) { - // TODO(b/77328470): add animation when fingerprint is authenticated - return false; - } else if (newState == STATE_AUTHENTICATING) { - return false; - } - return false; - } - - @Override - protected int getDelayAfterAuthenticatedDurationMs() { - return 0; - } - - @Override - protected boolean shouldGrayAreaDismissDialog() { - // Fingerprint dialog always dismisses when region outside the dialog is tapped - return true; - } - - protected Drawable getAnimationForTransition(int oldState, int newState) { - int iconRes; - if (newState == STATE_ERROR) { - iconRes = R.drawable.fingerprint_dialog_fp_to_error; - } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATING) { - iconRes = R.drawable.fingerprint_dialog_error_to_fp; - } else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) { - // TODO(b/77328470): add animation when fingerprint is authenticated - iconRes = R.drawable.fingerprint_dialog_fp_to_error; - } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATED) { - // TODO(b/77328470): add animation when fingerprint is authenticated - iconRes = R.drawable.fingerprint_dialog_fp_to_error; - } else if (newState == STATE_AUTHENTICATING) { - iconRes = R.drawable.fingerprint_dialog_fp_to_error; - } else { - return null; - } - return mContext.getDrawable(iconRes); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java index c9f5b79918f8..85bc22bab36f 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java @@ -34,8 +34,8 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.FalsingPlugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.shared.plugins.PluginManager; -import com.android.systemui.util.AsyncSensorManager; import com.android.systemui.util.DeviceConfigProxy; +import com.android.systemui.util.ProximitySensor; import java.io.PrintWriter; @@ -51,6 +51,9 @@ import javax.inject.Singleton; @Singleton public class FalsingManagerProxy implements FalsingManager { + private static final String PROXIMITY_SENSOR_TAG = "FalsingManager"; + + private final ProximitySensor mProximitySensor; private FalsingManager mInternalFalsingManager; private DeviceConfig.OnPropertiesChangedListener mDeviceConfigListener; private final DeviceConfigProxy mDeviceConfig; @@ -58,7 +61,11 @@ public class FalsingManagerProxy implements FalsingManager { @Inject FalsingManagerProxy(Context context, PluginManager pluginManager, - @Named(MAIN_HANDLER_NAME) Handler handler, DeviceConfigProxy deviceConfig) { + @Named(MAIN_HANDLER_NAME) Handler handler, + ProximitySensor proximitySensor, + DeviceConfigProxy deviceConfig) { + mProximitySensor = proximitySensor; + mProximitySensor.setTag(PROXIMITY_SENSOR_TAG); mDeviceConfig = deviceConfig; mDeviceConfigListener = properties -> onDeviceConfigPropertiesChanged(context, properties.getNamespace()); @@ -113,8 +120,8 @@ public class FalsingManagerProxy implements FalsingManager { } else { mInternalFalsingManager = new BrightLineFalsingManager( new FalsingDataProvider(context.getResources().getDisplayMetrics()), - Dependency.get(AsyncSensorManager.class), Dependency.get(KeyguardUpdateMonitor.class), + mProximitySensor, mDeviceConfig ); } 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 9e646b627bd2..3f5cae678d01 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java @@ -19,10 +19,6 @@ package com.android.systemui.classifier.brightline; import static com.android.systemui.classifier.FalsingManagerImpl.FALSING_REMAIN_LOCKED; import static com.android.systemui.classifier.FalsingManagerImpl.FALSING_SUCCESS; -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; import android.hardware.biometrics.BiometricSourceType; import android.net.Uri; import android.util.Log; @@ -34,12 +30,11 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.classifier.Classifier; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.util.DeviceConfigProxy; +import com.android.systemui.util.ProximitySensor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; /** * FalsingManager designed to make clear why a touch was rejected. @@ -49,9 +44,9 @@ public class BrightLineFalsingManager implements FalsingManager { static final boolean DEBUG = false; private static final String TAG = "FalsingManagerPlugin"; - private final SensorManager mSensorManager; private final FalsingDataProvider mDataProvider; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final ProximitySensor mProximitySensor; private boolean mSessionStarted; private MetricsLogger mMetricsLogger; private int mIsFalseTouchCalls; @@ -59,20 +54,9 @@ public class BrightLineFalsingManager implements FalsingManager { private boolean mScreenOn; private boolean mJustUnlockedWithFace; - private final ExecutorService mBackgroundExecutor = Executors.newSingleThreadExecutor(); - private final List<FalsingClassifier> mClassifiers; - private SensorEventListener mSensorEventListener = new SensorEventListener() { - @Override - public synchronized void onSensorChanged(SensorEvent event) { - onSensorEvent(event); - } - - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { - } - }; + private ProximitySensor.ProximitySensorListener mSensorEventListener = this::onProximityEvent; private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback = new KeyguardUpdateMonitorCallback() { @@ -88,12 +72,12 @@ public class BrightLineFalsingManager implements FalsingManager { public BrightLineFalsingManager( FalsingDataProvider falsingDataProvider, - SensorManager sensorManager, KeyguardUpdateMonitor keyguardUpdateMonitor, + ProximitySensor proximitySensor, DeviceConfigProxy deviceConfigProxy) { mKeyguardUpdateMonitor = keyguardUpdateMonitor; mDataProvider = falsingDataProvider; - mSensorManager = sensorManager; + mProximitySensor = proximitySensor; mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback); mMetricsLogger = new MetricsLogger(); @@ -111,24 +95,12 @@ public class BrightLineFalsingManager implements FalsingManager { } private void registerSensors() { - Sensor s = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); - if (s != null) { - // This can be expensive, and doesn't need to happen on the main thread. - mBackgroundExecutor.submit(() -> { - logDebug("registering sensor listener"); - mSensorManager.registerListener( - mSensorEventListener, s, SensorManager.SENSOR_DELAY_GAME); - }); - } + mProximitySensor.register(mSensorEventListener); } private void unregisterSensors() { - // This can be expensive, and doesn't need to happen on the main thread. - mBackgroundExecutor.submit(() -> { - logDebug("unregistering sensor listener"); - mSensorManager.unregisterListener(mSensorEventListener); - }); + mProximitySensor.unregister(mSensorEventListener); } private void sessionStart() { @@ -190,10 +162,10 @@ public class BrightLineFalsingManager implements FalsingManager { mClassifiers.forEach((classifier) -> classifier.onTouchEvent(motionEvent)); } - private void onSensorEvent(SensorEvent sensorEvent) { + private void onProximityEvent(ProximitySensor.ProximityEvent proximityEvent) { // TODO: some of these classifiers might allow us to abort early, meaning we don't have to // make these calls. - mClassifiers.forEach((classifier) -> classifier.onSensorEvent(sensorEvent)); + mClassifiers.forEach((classifier) -> classifier.onProximityEvent(proximityEvent)); } @Override 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 685e7c534b66..bf397518de46 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java @@ -16,10 +16,10 @@ package com.android.systemui.classifier.brightline; -import android.hardware.SensorEvent; import android.view.MotionEvent; import com.android.systemui.classifier.Classifier; +import com.android.systemui.util.ProximitySensor; import java.util.List; @@ -98,9 +98,9 @@ abstract class FalsingClassifier { void onTouchEvent(MotionEvent motionEvent) {}; /** - * Called whenever a SensorEvent occurs, specifically the ProximitySensor. + * Called when a ProximityEvent occurs (change in near/far). */ - void onSensorEvent(SensorEvent sensorEvent) {}; + void onProximityEvent(ProximitySensor.ProximityEvent proximityEvent) {}; /** * The phone screen has turned on and we need to begin falsing detection. diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java index 182704726129..eeca409866a8 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java @@ -19,12 +19,11 @@ package com.android.systemui.classifier.brightline; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; -import android.hardware.Sensor; -import android.hardware.SensorEvent; import android.provider.DeviceConfig; import android.view.MotionEvent; import com.android.systemui.util.DeviceConfigProxy; +import com.android.systemui.util.ProximitySensor; /** @@ -99,14 +98,12 @@ class ProximityClassifier extends FalsingClassifier { } @Override - public void onSensorEvent(SensorEvent sensorEvent) { - if (sensorEvent.sensor.getType() == Sensor.TYPE_PROXIMITY) { - logDebug("Sensor is: " + (sensorEvent.values[0] < sensorEvent.sensor.getMaximumRange()) - + " at time " + sensorEvent.timestamp); - update( - sensorEvent.values[0] < sensorEvent.sensor.getMaximumRange(), - sensorEvent.timestamp); - } + public void onProximityEvent( + ProximitySensor.ProximityEvent proximityEvent) { + boolean near = proximityEvent.getNear(); + long timestampNs = proximityEvent.getTimestampNs(); + logDebug("Sensor is: " + near + " at time " + timestampNs); + update(near, timestampNs); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index 41f66f7e2021..9221b6852112 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -155,7 +155,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha } else if (MOVE_FULL_ROWS.equals(key)) { mFullRows = TunerService.parseIntegerSwitch(newValue, true); } else if (QuickQSPanel.NUM_QUICK_TILES.equals(key)) { - mNumQuickTiles = mQuickQsPanel.getNumQuickTiles(mQs.getContext()); + mNumQuickTiles = QuickQSPanel.parseNumTiles(newValue); clearAnimationState(); } updateAnimators(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java index 147633b0ea25..55ae61de5bc6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java @@ -16,11 +16,13 @@ package com.android.systemui.qs; -import static com.android.systemui.DejankUtils.whitelistIpcs; +import static com.android.systemui.Dependency.BG_HANDLER; +import static com.android.systemui.Dependency.BG_HANDLER_NAME; import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; import android.content.Context; import android.content.Intent; +import android.os.Handler; import android.provider.Settings; import android.telephony.SubscriptionManager; import android.util.AttributeSet; @@ -53,6 +55,7 @@ public class QSCarrierGroup extends LinearLayout implements */ private static final int SIM_SLOTS = 3; private final NetworkController mNetworkController; + private final Handler mBgHandler; private View[] mCarrierDividers = new View[SIM_SLOTS - 1]; private QSCarrier[] mCarrierGroups = new QSCarrier[SIM_SLOTS]; @@ -65,17 +68,20 @@ public class QSCarrierGroup extends LinearLayout implements @Inject public QSCarrierGroup(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, - NetworkController networkController, ActivityStarter activityStarter) { + NetworkController networkController, ActivityStarter activityStarter, + @Named(BG_HANDLER_NAME) Handler handler) { super(context, attrs); mNetworkController = networkController; mActivityStarter = activityStarter; + mBgHandler = handler; } @VisibleForTesting public QSCarrierGroup(Context context, AttributeSet attrs) { this(context, attrs, Dependency.get(NetworkController.class), - Dependency.get(ActivityStarter.class)); + Dependency.get(ActivityStarter.class), + Dependency.get(BG_HANDLER)); } @Override @@ -115,8 +121,7 @@ public class QSCarrierGroup extends LinearLayout implements return; } mListening = listening; - // TODO(b/140053526) - whitelistIpcs(this::updateListeners); + mBgHandler.post(this::updateListeners); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java index b597a72ba899..0a2533a8426e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java @@ -29,6 +29,11 @@ public interface QSFooter { void setQSPanel(@Nullable QSPanel panel); /** + * Sets the given {@link QuickQSPanel} to be the one associated with quick settings. + */ + default void setQQSPanel(@Nullable QuickQSPanel panel) {}; + + /** * Sets whether or not the footer should be visible. * * @param visibility One of {@link View#VISIBLE}, {@link View#INVISIBLE} or {@link View#GONE}. diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java index 644664ecaeec..0134aa3a15df 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java @@ -81,6 +81,7 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, private boolean mQsDisabled; private QSPanel mQsPanel; + private QuickQSPanel mQuickQsPanel; private boolean mExpanded; @@ -177,7 +178,8 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, } private void updateAnimator(int width) { - int numTiles = QuickQSPanel.getNumQuickTiles(mContext); + int numTiles = mQuickQsPanel != null ? mQuickQsPanel.getNumQuickTiles() + : QuickQSPanel.getDefaultMaxTiles(); int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size) - mContext.getResources().getDimensionPixelSize(dimen.qs_quick_tile_padding); int remaining = (width - numTiles * size) / (numTiles - 1); @@ -346,6 +348,11 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, } @Override + public void setQQSPanel(@Nullable QuickQSPanel panel) { + mQuickQsPanel = panel; + } + + @Override public void onClick(View v) { // Don't do anything until view are unhidden if (!mExpanded) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index 1211e135d1b7..85aafa06961a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -16,7 +16,6 @@ package com.android.systemui.qs; -import static com.android.systemui.DejankUtils.whitelistIpcs; import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; import android.content.Context; @@ -50,9 +49,10 @@ public class QuickQSPanel extends QSPanel { public static final String NUM_QUICK_TILES = "sysui_qqs_count"; private static final String TAG = "QuickQSPanel"; + // Start it at 6 so a non-zero value can be obtained statically. + private static int sDefaultMaxTiles = 6; private boolean mDisabledByPolicy; - private static int mDefaultMaxTiles; private int mMaxTiles; protected QSPanel mFullPanel; @@ -69,7 +69,7 @@ public class QuickQSPanel extends QSPanel { } removeView((View) mTileLayout); } - mDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns); + sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns); mTileLayout = new HeaderTileLayout(context); mTileLayout.setListening(mListening); addView((View) mTileLayout, 0 /* Between brightness and footer */); @@ -155,14 +155,31 @@ public class QuickQSPanel extends QSPanel { private final Tunable mNumTiles = new Tunable() { @Override public void onTuningChanged(String key, String newValue) { - setMaxTiles(getNumQuickTiles(mContext)); + setMaxTiles(parseNumTiles(newValue)); } }; - public static int getNumQuickTiles(Context context) { - // TODO(b/140052679) - return whitelistIpcs(() -> - Dependency.get(TunerService.class).getValue(NUM_QUICK_TILES, mDefaultMaxTiles)); + public int getNumQuickTiles() { + return mMaxTiles; + } + + /** + * Parses the String setting into the number of tiles. Defaults to {@code mDefaultMaxTiles} + * + * @param numTilesValue value of the setting to parse + * @return parsed value of numTilesValue OR {@code mDefaultMaxTiles} on error + */ + public static int parseNumTiles(String numTilesValue) { + try { + return Integer.parseInt(numTilesValue); + } catch (NumberFormatException e) { + // Couldn't read an int from the new setting value. Use default. + return sDefaultMaxTiles; + } + } + + public static int getDefaultMaxTiles() { + return sDefaultMaxTiles; } void setDisabledByPolicy(boolean disabled) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 2ae2ac570b33..156e3c072dbe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar; -import static com.android.systemui.DejankUtils.whitelistIpcs; - import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.app.admin.DevicePolicyManager; @@ -93,7 +91,6 @@ public class KeyguardIndicationController implements StateListener, private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private ViewGroup mIndicationArea; private KeyguardIndicationTextView mTextView; - private KeyguardIndicationTextView mDisclosure; private final UserManager mUserManager; private final IBatteryStats mBatteryInfo; private final SettableWakeLock mWakeLock; @@ -180,7 +177,6 @@ public class KeyguardIndicationController implements StateListener, mDevicePolicyManager = (DevicePolicyManager) context.getSystemService( Context.DEVICE_POLICY_SERVICE); setIndicationArea(indicationArea); - updateDisclosure(); mKeyguardUpdateMonitor.registerCallback(getKeyguardCallback()); mKeyguardUpdateMonitor.registerCallback(mTickReceiver); @@ -193,7 +189,6 @@ public class KeyguardIndicationController implements StateListener, mTextView = indicationArea.findViewById(R.id.keyguard_indication_text); mInitialTextColorState = mTextView != null ? mTextView.getTextColors() : ColorStateList.valueOf(Color.WHITE); - mDisclosure = indicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure); updateIndication(false /* animate */); } @@ -231,27 +226,6 @@ public class KeyguardIndicationController implements StateListener, return mUpdateMonitorCallback; } - private void updateDisclosure() { - if (mDevicePolicyManager == null) { - return; - } - - // TODO(b/140053632) - if (!mDozing && whitelistIpcs(mDevicePolicyManager::isDeviceManaged)) { - final CharSequence organizationName = - mDevicePolicyManager.getDeviceOwnerOrganizationName(); - if (organizationName != null) { - mDisclosure.switchIndication(mContext.getResources().getString( - R.string.do_disclosure_with_name, organizationName)); - } else { - mDisclosure.switchIndication(R.string.do_disclosure_generic); - } - mDisclosure.setVisibility(View.VISIBLE); - } else { - mDisclosure.setVisibility(View.GONE); - } - } - public void setVisible(boolean visible) { mVisible = visible; mIndicationArea.setVisibility(visible ? View.VISIBLE : View.GONE); @@ -580,7 +554,6 @@ public class KeyguardIndicationController implements StateListener, } mDozing = dozing; updateIndication(false); - updateDisclosure(); } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { @@ -640,13 +613,6 @@ public class KeyguardIndicationController implements StateListener, } @Override - public void onKeyguardVisibilityChanged(boolean showing) { - if (showing) { - updateDisclosure(); - } - } - - @Override public void onBiometricHelp(int msgId, String helpString, BiometricSourceType biometricSourceType) { KeyguardUpdateMonitor updateMonitor = Dependency.get(KeyguardUpdateMonitor.class); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index b6f82a1b4382..83ecb55960cf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -120,7 +120,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private KeyguardAffordanceView mRightAffordanceView; private KeyguardAffordanceView mLeftAffordanceView; private ViewGroup mIndicationArea; - private TextView mEnterpriseDisclosure; private TextView mIndicationText; private ViewGroup mPreviewContainer; private ViewGroup mOverlayContainer; @@ -234,8 +233,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mRightAffordanceView = findViewById(R.id.camera_button); mLeftAffordanceView = findViewById(R.id.left_button); mIndicationArea = findViewById(R.id.keyguard_indication_area); - mEnterpriseDisclosure = findViewById( - R.id.keyguard_indication_enterprise_disclosure); mIndicationText = findViewById(R.id.keyguard_indication_text); mIndicationBottomMargin = getResources().getDimensionPixelSize( R.dimen.keyguard_indication_margin_bottom); @@ -312,9 +309,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } // Respect font size setting. - mEnterpriseDisclosure.setTextSize(TypedValue.COMPLEX_UNIT_PX, - getResources().getDimensionPixelSize( - com.android.internal.R.dimen.text_size_small_material)); mIndicationText.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize( com.android.internal.R.dimen.text_size_small_material)); 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 8c95b844ae2f..f853b638db46 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -503,8 +503,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo * device is dozing when the light sensor is on. */ public void setAodFrontScrimAlpha(float alpha) { - if (mState == ScrimState.AOD && mDozeParameters.getAlwaysOn() - && mInFrontAlpha != alpha) { + if (((mState == ScrimState.AOD && mDozeParameters.getAlwaysOn()) + || mState == ScrimState.PULSING) && mInFrontAlpha != alpha) { mInFrontAlpha = alpha; updateScrims(); } diff --git a/packages/SystemUI/src/com/android/systemui/util/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/ProximitySensor.java new file mode 100644 index 000000000000..a905eba1f0ed --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/ProximitySensor.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.util.Log; + +import com.android.systemui.R; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +/** + * Simple wrapper around SensorManager customized for the Proximity sensor. + */ +public class ProximitySensor { + private static final String TAG = "ProxSensor"; + private static final boolean DEBUG = false; + + private final Sensor mSensor; + private final AsyncSensorManager mSensorManager; + private final boolean mUsingBrightnessSensor; + private final float mMaxRange; + + private SensorEventListener mSensorEventListener = new SensorEventListener() { + @Override + public synchronized void onSensorChanged(SensorEvent event) { + onSensorEvent(event); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + } + }; + private boolean mNear; + private List<ProximitySensorListener> mListeners = new ArrayList<>(); + private String mTag = null; + + @Inject + public ProximitySensor(Context context, AsyncSensorManager sensorManager) { + mSensorManager = sensorManager; + Sensor sensor = findBrightnessSensor(context, sensorManager); + + if (sensor == null) { + mUsingBrightnessSensor = false; + sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); + } else { + mUsingBrightnessSensor = true; + } + mSensor = sensor; + if (mSensor != null) { + mMaxRange = mSensor.getMaximumRange(); + } else { + mMaxRange = 0; + } + } + + public void setTag(String tag) { + mTag = tag; + } + + private Sensor findBrightnessSensor(Context context, SensorManager sensorManager) { + String sensorType = context.getString(R.string.doze_brightness_sensor_type); + List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL); + Sensor sensor = null; + for (Sensor s : sensorList) { + if (sensorType.equals(s.getStringType())) { + sensor = s; + break; + } + } + + return sensor; + } + + /** + * Returns {@code false} if a Proximity sensor is not available. + */ + public boolean getSensorAvailable() { + return mSensor != null; + } + + /** + * Add a listener. + * + * Registers itself with the {@link SensorManager} if this is the first listener + * added. + */ + public boolean register(ProximitySensorListener listener) { + if (!getSensorAvailable()) { + return false; + } + + logDebug("using brightness sensor? " + mUsingBrightnessSensor); + mListeners.add(listener); + if (mListeners.size() == 1) { + logDebug("registering sensor listener"); + mSensorManager.registerListener( + mSensorEventListener, mSensor, SensorManager.SENSOR_DELAY_GAME); + } + + return true; + } + + /** + * Remove a listener. + * + * If all listeners are removed from an instance of this class, + * it will unregister itself with the SensorManager. + */ + public void unregister(ProximitySensorListener listener) { + mListeners.remove(listener); + if (mListeners.size() == 0) { + logDebug("unregistering sensor listener"); + mSensorManager.unregisterListener(mSensorEventListener); + } + } + + public boolean isNear() { + return getSensorAvailable() && mNear; + } + + private void onSensorEvent(SensorEvent event) { + boolean near = event.values[0] < mMaxRange; + if (mUsingBrightnessSensor) { + near = event.values[0] == 0; + } + mNear = near; + mListeners.forEach(proximitySensorListener -> + proximitySensorListener.onProximitySensorEvent( + new ProximityEvent(mNear, event.timestamp))); + } + + /** Implement to be notified of ProximityEvents. */ + public interface ProximitySensorListener { + /** Called when the ProximitySensor changes. */ + void onProximitySensorEvent(ProximityEvent proximityEvent); + } + + /** + * Returned when the near/far state of a {@link ProximitySensor} changes. + */ + public static class ProximityEvent { + private final boolean mNear; + private final long mTimestampNs; + + public ProximityEvent(boolean near, long timestampNs) { + mNear = near; + mTimestampNs = timestampNs; + } + + public boolean getNear() { + return mNear; + } + + public long getTimestampNs() { + return mTimestampNs; + } + } + + private void logDebug(String msg) { + if (DEBUG) { + Log.d(TAG, (mTag != null ? "[" + mTag + "] " : "") + msg); + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDialogViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDialogViewTest.java deleted file mode 100644 index 3ff1f383fdee..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDialogViewTest.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNotSame; -import static junit.framework.Assert.assertTrue; - -import static org.mockito.Mockito.spy; - -import android.app.admin.DevicePolicyManager; -import android.content.Context; -import android.hardware.biometrics.BiometricPrompt; -import android.os.Bundle; -import android.os.UserManager; -import android.test.suitebuilder.annotation.SmallTest; -import android.testing.AndroidTestingRunner; -import android.testing.TestableContext; -import android.testing.TestableLooper.RunWithLooper; -import android.view.View; - -import com.android.systemui.R; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.keyguard.WakefulnessLifecycle; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@RunWith(AndroidTestingRunner.class) -@RunWithLooper -@SmallTest -public class BiometricDialogViewTest extends SysuiTestCase { - - FaceDialogView mFaceDialogView; - - private static final String TITLE = "Title"; - private static final String SUBTITLE = "Subtitle"; - private static final String DESCRIPTION = "Description"; - private static final String NEGATIVE_BUTTON = "Negative Button"; - - private static final String TEST_HELP = "Help"; - - TestableContext mTestableContext; - @Mock - private AuthDialogCallback mCallback; - @Mock - private UserManager mUserManager; - @Mock - private DevicePolicyManager mDpm; - - private static class Injector extends BiometricDialogView.Injector { - @Override - public WakefulnessLifecycle getWakefulnessLifecycle() { - final WakefulnessLifecycle lifecycle = new WakefulnessLifecycle(); - lifecycle.dispatchFinishedWakingUp(); - return lifecycle; - } - } - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - mTestableContext = spy(mContext); - mTestableContext.addMockSystemService(UserManager.class, mUserManager); - mTestableContext.addMockSystemService(DevicePolicyManager.class, mDpm); - } - - @Test - public void testContentStates_confirmationRequired_authenticated() { - mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback, - true /* requireConfirmation */); - mFaceDialogView.onAttachedToWindow(); - - // When starting authentication - assertEquals(View.VISIBLE, mFaceDialogView.mTitleText.getVisibility()); - assertEquals(View.VISIBLE, mFaceDialogView.mSubtitleText.getVisibility()); - assertEquals(View.VISIBLE, mFaceDialogView.mDescriptionText.getVisibility()); - assertEquals(View.INVISIBLE, mFaceDialogView.mErrorText.getVisibility()); - assertEquals(View.VISIBLE, mFaceDialogView.mPositiveButton.getVisibility()); - assertEquals(View.VISIBLE, mFaceDialogView.mNegativeButton.getVisibility()); - assertEquals(View.GONE, mFaceDialogView.mTryAgainButton.getVisibility()); - - // Contents are as expected - assertTrue(TITLE.contentEquals(mFaceDialogView.mTitleText.getText())); - assertTrue(SUBTITLE.contentEquals(mFaceDialogView.mSubtitleText.getText())); - assertTrue(DESCRIPTION.contentEquals(mFaceDialogView.mDescriptionText.getText())); - assertTrue(mFaceDialogView.mPositiveButton.getText().toString() - .contentEquals(mContext.getString(R.string.biometric_dialog_confirm))); - assertTrue(NEGATIVE_BUTTON.contentEquals(mFaceDialogView.mNegativeButton.getText())); - assertTrue(mFaceDialogView.mTryAgainButton.getText().toString() - .contentEquals(mContext.getString(R.string.biometric_dialog_try_again))); - - // When help message is received - mFaceDialogView.onHelp(TEST_HELP); - assertEquals(mFaceDialogView.mErrorText.getVisibility(), View.VISIBLE); - assertTrue(TEST_HELP.contentEquals(mFaceDialogView.mErrorText.getText())); - - // When authenticated, confirm button comes out - mFaceDialogView.onAuthenticationSucceeded(); - assertEquals(View.VISIBLE, mFaceDialogView.mPositiveButton.getVisibility()); - assertEquals(true, mFaceDialogView.mPositiveButton.isEnabled()); - } - - @Test - public void testContentStates_confirmationNotRequired_authenticated() { - mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback, - false /* requireConfirmation */); - mFaceDialogView.onAttachedToWindow(); - mFaceDialogView.updateSize(FaceDialogView.SIZE_SMALL); - - assertEquals(View.INVISIBLE, mFaceDialogView.mTitleText.getVisibility()); - assertNotSame(View.VISIBLE, mFaceDialogView.mSubtitleText.getVisibility()); - assertNotSame(View.VISIBLE, mFaceDialogView.mDescriptionText.getVisibility()); - assertEquals(View.INVISIBLE, mFaceDialogView.mErrorText.getVisibility()); - assertEquals(View.GONE, mFaceDialogView.mPositiveButton.getVisibility()); - assertEquals(View.GONE, mFaceDialogView.mTryAgainButton.getVisibility()); - assertEquals(View.GONE, mFaceDialogView.mTryAgainButton.getVisibility()); - } - - @Test - public void testContentStates_confirmationNotRequired_help() { - mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback, - false /* requireConfirmation */); - mFaceDialogView.onAttachedToWindow(); - - mFaceDialogView.onHelp(TEST_HELP); - assertEquals(mFaceDialogView.mErrorText.getVisibility(), View.VISIBLE); - assertTrue(TEST_HELP.contentEquals(mFaceDialogView.mErrorText.getText())); - } - - @Test - public void testBack_sendsUserCanceled() { - // TODO: Need robolectric framework to wait for handler to complete - } - - @Test - public void testScreenOff_sendsUserCanceled() { - // TODO: Need robolectric framework to wait for handler to complete - } - - @Test - public void testRestoreState_contentStatesCorrect() { - mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback, - false /* requireConfirmation */); - mFaceDialogView.onAttachedToWindow(); - mFaceDialogView.onAuthenticationFailed(TEST_HELP); - - final Bundle bundle = new Bundle(); - mFaceDialogView.onSaveState(bundle); - - mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback, - false /* requireConfirmation */); - mFaceDialogView.restoreState(bundle); - mFaceDialogView.onAttachedToWindow(); - - assertEquals(View.VISIBLE, mFaceDialogView.mTryAgainButton.getVisibility()); - } - - private FaceDialogView buildFaceDialogView(Context context, AuthDialogCallback callback, - boolean requireConfirmation) { - return (FaceDialogView) new BiometricDialogView.Builder(context) - .setCallback(callback) - .setBiometricPromptBundle(createTestDialogBundle()) - .setRequireConfirmation(requireConfirmation) - .setUserId(0) - .setOpPackageName("test_package") - .build(BiometricDialogView.Builder.TYPE_FACE, new Injector()); - } - - private Bundle createTestDialogBundle() { - Bundle bundle = new Bundle(); - - bundle.putCharSequence(BiometricPrompt.KEY_TITLE, TITLE); - bundle.putCharSequence(BiometricPrompt.KEY_SUBTITLE, SUBTITLE); - bundle.putCharSequence(BiometricPrompt.KEY_DESCRIPTION, DESCRIPTION); - bundle.putCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT, NEGATIVE_BUTTON); - - // RequireConfirmation is a hint to BiometricService. This can be forced to be required - // by user settings, and should be tested in BiometricService. - bundle.putBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true); - - return bundle; - } -} 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 e1457ef28e36..b9793562d8d8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java @@ -34,6 +34,7 @@ import com.android.systemui.classifier.brightline.BrightLineFalsingManager; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.DeviceConfigProxyFake; +import com.android.systemui.util.ProximitySensor; import org.junit.After; import org.junit.Before; @@ -46,8 +47,10 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper public class FalsingManagerProxyTest extends SysuiTestCase { - @Mock + @Mock(stubOnly = true) PluginManager mPluginManager; + @Mock(stubOnly = true) + ProximitySensor mProximitySensor; private Handler mHandler; private FalsingManagerProxy mProxy; private DeviceConfigProxy mDeviceConfig; @@ -73,7 +76,8 @@ public class FalsingManagerProxyTest extends SysuiTestCase { @Test public void test_brightLineFalsingManagerDisabled() { - mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler, mDeviceConfig); + mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler, mProximitySensor, + mDeviceConfig); assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class)); } @@ -82,13 +86,15 @@ public class FalsingManagerProxyTest extends SysuiTestCase { mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_MANAGER_ENABLED, "true", false); mTestableLooper.processAllMessages(); - mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler, mDeviceConfig); + mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler, mProximitySensor, + mDeviceConfig); assertThat(mProxy.getInternalFalsingManager(), instanceOf(BrightLineFalsingManager.class)); } @Test public void test_brightLineFalsingManagerToggled() throws InterruptedException { - mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler, mDeviceConfig); + mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler, mProximitySensor, + mDeviceConfig); assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class)); mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java index c76fe74b1af7..3fc5d7202d0a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java @@ -23,8 +23,6 @@ import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.when; -import android.hardware.Sensor; -import android.hardware.SensorEvent; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.MotionEvent; @@ -32,17 +30,15 @@ import android.view.MotionEvent; import androidx.test.filters.SmallTest; import com.android.systemui.util.DeviceConfigProxyFake; +import com.android.systemui.util.ProximitySensor; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import java.lang.reflect.Field; - @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @@ -81,8 +77,8 @@ public class ProximityClassifierTest extends ClassifierTest { @Test public void testPass_mostlyUncovered() { touchDown(); - mClassifier.onSensorEvent(createSensorEvent(true, 1)); - mClassifier.onSensorEvent(createSensorEvent(false, 2)); + mClassifier.onProximityEvent(createSensorEvent(true, 1)); + mClassifier.onProximityEvent(createSensorEvent(false, 2)); touchUp(20); assertThat(mClassifier.isFalseTouch(), is(false)); } @@ -91,8 +87,8 @@ public class ProximityClassifierTest extends ClassifierTest { public void testPass_quickSettings() { touchDown(); when(mDataProvider.getInteractionType()).thenReturn(QUICK_SETTINGS); - mClassifier.onSensorEvent(createSensorEvent(true, 1)); - mClassifier.onSensorEvent(createSensorEvent(false, 11)); + mClassifier.onProximityEvent(createSensorEvent(true, 1)); + mClassifier.onProximityEvent(createSensorEvent(false, 11)); touchUp(10); assertThat(mClassifier.isFalseTouch(), is(false)); } @@ -100,8 +96,8 @@ public class ProximityClassifierTest extends ClassifierTest { @Test public void testFail_covered() { touchDown(); - mClassifier.onSensorEvent(createSensorEvent(true, 1)); - mClassifier.onSensorEvent(createSensorEvent(false, 11)); + mClassifier.onProximityEvent(createSensorEvent(true, 1)); + mClassifier.onProximityEvent(createSensorEvent(false, 11)); touchUp(10); assertThat(mClassifier.isFalseTouch(), is(true)); } @@ -109,10 +105,10 @@ public class ProximityClassifierTest extends ClassifierTest { @Test public void testFail_mostlyCovered() { touchDown(); - mClassifier.onSensorEvent(createSensorEvent(true, 1)); - mClassifier.onSensorEvent(createSensorEvent(true, 95)); - mClassifier.onSensorEvent(createSensorEvent(true, 96)); - mClassifier.onSensorEvent(createSensorEvent(false, 100)); + mClassifier.onProximityEvent(createSensorEvent(true, 1)); + mClassifier.onProximityEvent(createSensorEvent(true, 95)); + mClassifier.onProximityEvent(createSensorEvent(true, 96)); + mClassifier.onProximityEvent(createSensorEvent(false, 100)); touchUp(100); assertThat(mClassifier.isFalseTouch(), is(true)); } @@ -120,8 +116,8 @@ public class ProximityClassifierTest extends ClassifierTest { @Test public void testPass_coveredWithLongSwipe() { touchDown(); - mClassifier.onSensorEvent(createSensorEvent(true, 1)); - mClassifier.onSensorEvent(createSensorEvent(false, 11)); + mClassifier.onProximityEvent(createSensorEvent(true, 1)); + mClassifier.onProximityEvent(createSensorEvent(false, 11)); touchUp(10); when(mDistanceClassifier.isLongSwipe()).thenReturn(true); assertThat(mClassifier.isFalseTouch(), is(false)); @@ -142,26 +138,7 @@ public class ProximityClassifierTest extends ClassifierTest { motionEvent.recycle(); } - private SensorEvent createSensorEvent(boolean covered, long timestampMs) { - SensorEvent sensorEvent = Mockito.mock(SensorEvent.class); - Sensor sensor = Mockito.mock(Sensor.class); - when(sensor.getType()).thenReturn(Sensor.TYPE_PROXIMITY); - when(sensor.getMaximumRange()).thenReturn(1f); - sensorEvent.sensor = sensor; - sensorEvent.timestamp = timestampMs * NS_PER_MS; - try { - Field valuesField = SensorEvent.class.getField("values"); - valuesField.setAccessible(true); - float[] sensorValue = {covered ? 0 : 1}; - try { - valuesField.set(sensorEvent, sensorValue); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } catch (NoSuchFieldException e) { - e.printStackTrace(); - } - - return sensorEvent; + private ProximitySensor.ProximityEvent createSensorEvent(boolean covered, long timestampMs) { + return new ProximitySensor.ProximityEvent(covered, timestampMs * NS_PER_MS); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index 2427cfc77f39..0817ee908184 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -27,7 +27,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.app.Instrumentation; @@ -46,7 +45,6 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -68,17 +66,11 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) public class KeyguardIndicationControllerTest extends SysuiTestCase { - private final String ORGANIZATION_NAME = "organization"; - - private String mDisclosureWithOrganization; - @Mock private DevicePolicyManager mDevicePolicyManager; @Mock private ViewGroup mIndicationArea; @Mock - private KeyguardIndicationTextView mDisclosure; - @Mock private LockIcon mLockIcon; @Mock private LockPatternUtils mLockPatternUtils; @@ -107,11 +99,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mContext.addMockSystemService(Context.DEVICE_POLICY_SERVICE, mDevicePolicyManager); mContext.addMockSystemService(Context.TRUST_SERVICE, mock(TrustManager.class)); mContext.addMockSystemService(Context.FINGERPRINT_SERVICE, mock(FingerprintManager.class)); - mDisclosureWithOrganization = mContext.getString(R.string.do_disclosure_with_name, - ORGANIZATION_NAME); - when(mIndicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure)) - .thenReturn(mDisclosure); when(mIndicationArea.findViewById(R.id.keyguard_indication_text)).thenReturn(mTextView); mWakeLock = new WakeLockFake(); @@ -127,72 +115,6 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { } @Test - public void unmanaged() { - when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false); - createController(); - - verify(mDisclosure).setVisibility(View.GONE); - verifyNoMoreInteractions(mDisclosure); - } - - @Test - public void managedNoOwnerName() { - when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true); - when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null); - createController(); - - verify(mDisclosure).setVisibility(View.VISIBLE); - verify(mDisclosure).switchIndication(R.string.do_disclosure_generic); - verifyNoMoreInteractions(mDisclosure); - } - - @Test - public void managedOwnerName() { - when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true); - when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(ORGANIZATION_NAME); - createController(); - - verify(mDisclosure).setVisibility(View.VISIBLE); - verify(mDisclosure).switchIndication(mDisclosureWithOrganization); - verifyNoMoreInteractions(mDisclosure); - } - - @Test - public void updateOnTheFly() { - when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false); - createController(); - - final KeyguardUpdateMonitorCallback monitor = mController.getKeyguardCallback(); - reset(mDisclosure); - - when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true); - when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null); - monitor.onKeyguardVisibilityChanged(true); - - verify(mDisclosure).setVisibility(View.VISIBLE); - verify(mDisclosure).switchIndication(R.string.do_disclosure_generic); - verifyNoMoreInteractions(mDisclosure); - reset(mDisclosure); - - when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true); - when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(ORGANIZATION_NAME); - monitor.onKeyguardVisibilityChanged(false); - monitor.onKeyguardVisibilityChanged(true); - - verify(mDisclosure).setVisibility(View.VISIBLE); - verify(mDisclosure).switchIndication(mDisclosureWithOrganization); - verifyNoMoreInteractions(mDisclosure); - reset(mDisclosure); - - when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false); - monitor.onKeyguardVisibilityChanged(false); - monitor.onKeyguardVisibilityChanged(true); - - verify(mDisclosure).setVisibility(View.GONE); - verifyNoMoreInteractions(mDisclosure); - } - - @Test public void transientIndication_holdsWakeLock_whenDozing() { createController(); 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 2623b46ba50f..5d3cdc88aa99 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 @@ -243,7 +243,7 @@ public class ScrimControllerTest extends SysuiTestCase { } @Test - public void transitionToPulsing() { + public void transitionToPulsing_withFrontAlphaUpdates() { // Pre-condition // Need to go to AoD first because PULSING doesn't change // the back scrim opacity - otherwise it would hide AoD wallpapers. @@ -267,11 +267,22 @@ public class ScrimControllerTest extends SysuiTestCase { true /* behind */, false /* bubble */); + // ... and when ambient goes dark, front scrim should be semi-transparent + mScrimController.setAodFrontScrimAlpha(0.5f); + mScrimController.finishAnimationsImmediately(); + // Front scrim should be semi-transparent + assertScrimAlpha(SEMI_TRANSPARENT /* front */, + OPAQUE /* back */, + TRANSPARENT /* bubble */); + mScrimController.setWakeLockScreenSensorActive(true); mScrimController.finishAnimationsImmediately(); - assertScrimAlpha(TRANSPARENT /* front */, + assertScrimAlpha(SEMI_TRANSPARENT /* front */, SEMI_TRANSPARENT /* back */, TRANSPARENT /* bubble */); + + // Reset value since enums are static. + mScrimController.setAodFrontScrimAlpha(0f); } @Test diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 5089ee0ace57..69f226f67aa6 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -21,9 +21,7 @@ import static android.location.LocationManager.FUSED_PROVIDER; import static android.location.LocationManager.GPS_PROVIDER; import static android.location.LocationManager.NETWORK_PROVIDER; import static android.location.LocationManager.PASSIVE_PROVIDER; -import static android.location.LocationProvider.AVAILABLE; import static android.os.PowerManager.locationPowerSaveModeToString; -import static android.provider.Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS; import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.internal.util.Preconditions.checkState; @@ -1088,34 +1086,6 @@ public class LocationManagerService extends ILocationManager.Stub { pw.decreaseIndent(); } - @GuardedBy("mLock") - public long getStatusUpdateTimeLocked() { - if (mProvider != null) { - long identity = Binder.clearCallingIdentity(); - try { - return mProvider.getStatusUpdateTime(); - } finally { - Binder.restoreCallingIdentity(identity); - } - } else { - return 0; - } - } - - @GuardedBy("mLock") - public int getStatusLocked(Bundle extras) { - if (mProvider != null) { - long identity = Binder.clearCallingIdentity(); - try { - return mProvider.getStatus(extras); - } finally { - Binder.restoreCallingIdentity(identity); - } - } else { - return AVAILABLE; - } - } - @Override public void onReportLocation(Location location) { synchronized (mLock) { @@ -1324,18 +1294,6 @@ public class LocationManagerService extends ILocationManager.Stub { super.setRequest(request, workSource); mCurrentRequest = request; } - - @GuardedBy("mLock") - public void setStatusLocked(int status, Bundle extras, long updateTime) { - if (mProvider != null) { - long identity = Binder.clearCallingIdentity(); - try { - ((MockProvider) mProvider).setStatus(status, extras, updateTime); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } } /** @@ -1516,34 +1474,6 @@ public class LocationManagerService extends ILocationManager.Stub { throw new IllegalStateException("Request for non-existent listener"); } - public boolean callStatusChangedLocked(String provider, int status, Bundle extras) { - if (mListener != null) { - try { - mListener.onStatusChanged(provider, status, extras); - // call this after broadcasting so we do not increment - // if we throw an exception. - incrementPendingBroadcastsLocked(); - } catch (RemoteException e) { - return false; - } - } else { - Intent statusChanged = new Intent(); - statusChanged.putExtras(new Bundle(extras)); - statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status); - try { - mPendingIntent.send(mContext, 0, statusChanged, this, mHandler, - getResolutionPermission(mAllowedResolutionLevel), - PendingIntentUtils.createDontSendToRestrictedAppsBundle(null)); - // call this after broadcasting so we do not increment - // if we throw an exception. - incrementPendingBroadcastsLocked(); - } catch (PendingIntent.CanceledException e) { - return false; - } - } - return true; - } - public boolean callLocationChangedLocked(Location location) { if (mListener != null) { try { @@ -2296,7 +2226,6 @@ public class LocationManagerService extends ILocationManager.Stub { private final Receiver mReceiver; private boolean mIsForegroundUid; private Location mLastFixBroadcast; - private long mLastStatusBroadcast; private Throwable mStackTrace; // for debugging only /** @@ -3406,26 +3335,6 @@ public class LocationManagerService extends ILocationManager.Stub { } } - // TODO: location provider status callbacks have been disabled and deprecated, and are - // guarded behind this setting now. should be removed completely post-Q - if (Settings.Global.getInt(mContext.getContentResolver(), - LOCATION_DISABLE_STATUS_CALLBACKS, 1) == 0) { - long newStatusUpdateTime = provider.getStatusUpdateTimeLocked(); - Bundle extras = new Bundle(); - int status = provider.getStatusLocked(extras); - - long prevStatusUpdateTime = r.mLastStatusBroadcast; - if ((newStatusUpdateTime > prevStatusUpdateTime) - && (prevStatusUpdateTime != 0 || status != AVAILABLE)) { - - r.mLastStatusBroadcast = newStatusUpdateTime; - if (!receiver.callStatusChangedLocked(provider.getName(), status, extras)) { - receiverDead = true; - Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver); - } - } - } - // track expired records if (r.mRealRequest.getNumUpdates() <= 0 || r.mRealRequest.getExpireAt() < now) { if (deadUpdateRecords == null) { @@ -3625,23 +3534,6 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public void setTestProviderStatus(String providerName, int status, Bundle extras, - long updateTime, String opPackageName) { - if (!canCallerAccessMockLocation(opPackageName)) { - return; - } - - synchronized (mLock) { - LocationProvider testProvider = getLocationProviderLocked(providerName); - if (testProvider == null || !testProvider.isMock()) { - throw new IllegalArgumentException("Provider \"" + providerName + "\" unknown"); - } - - ((MockLocationProvider) testProvider).setStatusLocked(status, extras, updateTime); - } - } - - @Override @NonNull public List<LocationRequest> getTestProviderCurrentRequests(String providerName, String opPackageName) { diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index 548665ba3a32..8c60f1aa439b 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -34,6 +34,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; +import android.util.LongArrayQueue; import android.util.Slog; import android.util.Xml; @@ -86,6 +87,8 @@ public class PackageWatchdog { // Number of package failures within the duration above before we notify observers @VisibleForTesting static final int DEFAULT_TRIGGER_FAILURE_COUNT = 5; + @VisibleForTesting + static final long DEFAULT_OBSERVING_DURATION_MS = TimeUnit.DAYS.toMillis(2); // Whether explicit health checks are enabled or not private static final boolean DEFAULT_EXPLICIT_HEALTH_CHECK_ENABLED = true; @@ -224,8 +227,10 @@ public class PackageWatchdog { * check state will be reset to a default depending on if the package is contained in * {@link mPackagesWithExplicitHealthCheckEnabled}. * - * @throws IllegalArgumentException if {@code packageNames} is empty - * or {@code durationMs} is less than 1 + * <p>If {@code packageNames} is empty, this will be a no-op. + * + * <p>If {@code durationMs} is less than 1, a default monitoring duration + * {@link #DEFAULT_OBSERVING_DURATION_MS} will be used. */ public void startObservingHealth(PackageHealthObserver observer, List<String> packageNames, long durationMs) { @@ -234,9 +239,9 @@ public class PackageWatchdog { return; } if (durationMs < 1) { - // TODO: Instead of failing, monitor for default? 48hrs? - throw new IllegalArgumentException("Invalid duration " + durationMs + "ms for observer " + Slog.wtf(TAG, "Invalid duration " + durationMs + "ms for observer " + observer.getName() + ". Not observing packages " + packageNames); + durationMs = DEFAULT_OBSERVING_DURATION_MS; } List<MonitoredPackage> packages = new ArrayList<>(); @@ -969,6 +974,9 @@ public class PackageWatchdog { class MonitoredPackage { //TODO(b/120598832): VersionedPackage? private final String mName; + // Times when package failures happen sorted in ascending order + @GuardedBy("mLock") + private final LongArrayQueue mFailureHistory = new LongArrayQueue(); // One of STATE_[ACTIVE|INACTIVE|PASSED|FAILED]. Updated on construction and after // methods that could change the health check state: handleElapsedTimeLocked and // tryPassHealthCheckLocked @@ -988,12 +996,6 @@ public class PackageWatchdog { // of the package, see #getHealthCheckStateLocked @GuardedBy("mLock") private long mHealthCheckDurationMs = Long.MAX_VALUE; - // System uptime of first package failure - @GuardedBy("mLock") - private long mUptimeStartMs; - // Number of failures since mUptimeStartMs - @GuardedBy("mLock") - private int mFailures; MonitoredPackage(String name, long durationMs, boolean hasPassedHealthCheck) { this(name, durationMs, Long.MAX_VALUE, hasPassedHealthCheck); @@ -1028,20 +1030,17 @@ public class PackageWatchdog { */ @GuardedBy("mLock") public boolean onFailureLocked() { + // Sliding window algorithm: find out if there exists a window containing failures >= + // mTriggerFailureCount. final long now = mSystemClock.uptimeMillis(); - final long duration = now - mUptimeStartMs; - if (duration > mTriggerFailureDurationMs) { - // TODO(b/120598832): Reseting to 1 is not correct - // because there may be more than 1 failure in the last trigger window from now - // This is the RescueParty impl, will leave for now - mFailures = 1; - mUptimeStartMs = now; - } else { - mFailures++; + mFailureHistory.addLast(now); + while (now - mFailureHistory.peekFirst() > mTriggerFailureDurationMs) { + // Prune values falling out of the window + mFailureHistory.removeFirst(); } - boolean failed = mFailures >= mTriggerFailureCount; + boolean failed = mFailureHistory.size() >= mTriggerFailureCount; if (failed) { - mFailures = 0; + mFailureHistory.clear(); } return failed; } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 09bfb7a1adca..55d422d64bb9 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -15262,7 +15262,12 @@ public class ActivityManagerService extends IActivityManager.Stub final int uid = getUidFromIntent(intent); if (uid >= 0) { mBatteryStatsService.removeUid(uid); - mAppOpsService.uidRemoved(uid); + if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { + mAppOpsService.resetAllModes(UserHandle.getUserId(uid), + intent.getData().getSchemeSpecificPart()); + } else { + mAppOpsService.uidRemoved(uid); + } } break; case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE: @@ -19291,6 +19296,12 @@ public class ActivityManagerService extends IActivityManager.Stub // null permissions means all permissions are targeted return (mPermissions == null || ArrayUtils.contains(mPermissions, permission)); } + + @Override + public String toString() { + return "ShellDelegate{targetPackageName=" + mTargetPackageName + + ", permissions=" + mPermissions + "}"; + } } /** diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 7f69a683b18a..146be5aa7044 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -882,20 +882,20 @@ public class AppOpsService extends IAppOpsService.Stub { final int[] changedUids = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST); final String[] changedPkgs = intent.getStringArrayExtra( Intent.EXTRA_CHANGED_PACKAGE_LIST); - ArraySet<ModeCallback> callbacks; - synchronized (AppOpsService.this) { - callbacks = mOpModeWatchers.get(OP_PLAY_AUDIO); - if (callbacks == null) { - return; + for (int code : OPS_RESTRICTED_ON_SUSPEND) { + ArraySet<ModeCallback> callbacks; + synchronized (AppOpsService.this) { + callbacks = mOpModeWatchers.get(code); + if (callbacks == null) { + continue; + } + callbacks = new ArraySet<>(callbacks); } - callbacks = new ArraySet<>(callbacks); - } - for (int i = 0; i < changedUids.length; i++) { - final int changedUid = changedUids[i]; - final String changedPkg = changedPkgs[i]; - // We trust packagemanager to insert matching uid and packageNames in the - // extras - for (int code : OPS_RESTRICTED_ON_SUSPEND) { + for (int i = 0; i < changedUids.length; i++) { + final int changedUid = changedUids[i]; + final String changedPkg = changedPkgs[i]; + // We trust packagemanager to insert matching uid and packageNames in the + // extras notifyOpChanged(callbacks, code, changedUid, changedPkg); } } @@ -2852,9 +2852,11 @@ public class AppOpsService extends IAppOpsService.Stub { } private boolean isOpRestrictedDueToSuspend(int code, String packageName, int uid) { + if (!ArrayUtils.contains(OPS_RESTRICTED_ON_SUSPEND, code)) { + return false; + } final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); - return ArrayUtils.contains(OPS_RESTRICTED_ON_SUSPEND, code) - && pmi.isPackageSuspended(packageName, UserHandle.getUserId(uid)); + return pmi.isPackageSuspended(packageName, UserHandle.getUserId(uid)); } private boolean isOpRestrictedLocked(int uid, int code, String packageName, diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 50b6ced7f81e..066e765e6e30 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1905,16 +1905,9 @@ public class AudioService extends IAudioService.Stub } } - if (mHdmiAudioSystemClient != null && - mHdmiSystemAudioSupported && - streamTypeAlias == AudioSystem.STREAM_MUSIC && - (oldIndex != newIndex || isMuteAdjust)) { - final long identity = Binder.clearCallingIdentity(); - mHdmiAudioSystemClient.sendReportAudioStatusCecCommand( - isMuteAdjust, getStreamVolume(AudioSystem.STREAM_MUSIC), - getStreamMaxVolume(AudioSystem.STREAM_MUSIC), - isStreamMute(AudioSystem.STREAM_MUSIC)); - Binder.restoreCallingIdentity(identity); + if (streamTypeAlias == AudioSystem.STREAM_MUSIC + && (oldIndex != newIndex || isMuteAdjust)) { + maybeSendSystemAudioStatusCommand(isMuteAdjust); } } } @@ -1925,12 +1918,35 @@ public class AudioService extends IAudioService.Stub // Called after a delay when volume down is pressed while muted private void onUnmuteStream(int stream, int flags) { - VolumeStreamState streamState = mStreamStates[stream]; - streamState.mute(false); + boolean wasMuted; + synchronized (VolumeStreamState.class) { + final VolumeStreamState streamState = mStreamStates[stream]; + wasMuted = streamState.mute(false); // if unmuting causes a change, it was muted - final int device = getDeviceForStream(stream); - final int index = mStreamStates[stream].getIndex(device); - sendVolumeUpdate(stream, index, index, flags, device); + final int device = getDeviceForStream(stream); + final int index = streamState.getIndex(device); + sendVolumeUpdate(stream, index, index, flags, device); + } + if (stream == AudioSystem.STREAM_MUSIC && wasMuted) { + synchronized (mHdmiClientLock) { + maybeSendSystemAudioStatusCommand(true); + } + } + } + + @GuardedBy("mHdmiClientLock") + private void maybeSendSystemAudioStatusCommand(boolean isMuteAdjust) { + if (mHdmiAudioSystemClient == null + || !mHdmiSystemAudioSupported) { + return; + } + + final long identity = Binder.clearCallingIdentity(); + mHdmiAudioSystemClient.sendReportAudioStatusCecCommand( + isMuteAdjust, getStreamVolume(AudioSystem.STREAM_MUSIC), + getStreamMaxVolume(AudioSystem.STREAM_MUSIC), + isStreamMute(AudioSystem.STREAM_MUSIC)); + Binder.restoreCallingIdentity(identity); } private void setSystemAudioVolume(int oldVolume, int newVolume, int maxVolume, int flags) { @@ -2343,17 +2359,9 @@ public class AudioService extends IAudioService.Stub } } synchronized (mHdmiClientLock) { - if (mHdmiManager != null && - mHdmiAudioSystemClient != null && - mHdmiSystemAudioSupported && - streamTypeAlias == AudioSystem.STREAM_MUSIC && - (oldIndex != index)) { - final long identity = Binder.clearCallingIdentity(); - mHdmiAudioSystemClient.sendReportAudioStatusCecCommand( - false, getStreamVolume(AudioSystem.STREAM_MUSIC), - getStreamMaxVolume(AudioSystem.STREAM_MUSIC), - isStreamMute(AudioSystem.STREAM_MUSIC)); - Binder.restoreCallingIdentity(identity); + if (streamTypeAlias == AudioSystem.STREAM_MUSIC + && (oldIndex != index)) { + maybeSendSystemAudioStatusCommand(false); } } sendVolumeUpdate(streamType, oldIndex, index, flags, device); @@ -4683,7 +4691,12 @@ public class AudioService extends IAudioService.Stub } } - public void mute(boolean state) { + /** + * Mute/unmute the stream + * @param state the new mute state + * @return true if the mute state was changed + */ + public boolean mute(boolean state) { boolean changed = false; synchronized (VolumeStreamState.class) { if (state != mIsMuted) { @@ -4708,6 +4721,7 @@ public class AudioService extends IAudioService.Stub intent.putExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, state); sendBroadcastToAll(intent); } + return changed; } public int getStreamType() { diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java index c1a63940c080..b05742af04ee 100644 --- a/services/core/java/com/android/server/location/AbstractLocationProvider.java +++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java @@ -18,7 +18,6 @@ package com.android.server.location; import android.content.Context; import android.location.Location; -import android.location.LocationProvider; import android.os.Bundle; import android.os.WorkSource; @@ -132,26 +131,4 @@ public abstract class AbstractLocationProvider { * thread. */ public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args); - - /** - * Invoked by the location service to retrieve the current status of the provider. May be - * invoked from any thread. - * - * @deprecated Will be removed in a future release. - */ - @Deprecated - public int getStatus(Bundle extras) { - return LocationProvider.AVAILABLE; - } - - /** - * Invoked by the location service to retrieve the last update time of the status of the - * provider. May be invoked from any thread. - * - * @deprecated Will be removed in a future release. - */ - @Deprecated - public long getStatusUpdateTime() { - return 0; - } } diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index a4d7fc681ecc..c6226fa98197 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -37,7 +37,6 @@ import android.location.INetInitiatedListener; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; -import android.location.LocationProvider; import android.location.LocationRequest; import android.os.AsyncTask; import android.os.BatteryStats; @@ -279,15 +278,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private final Object mLock = new Object(); - // current status - private int mStatus = LocationProvider.TEMPORARILY_UNAVAILABLE; - - // time for last status update - private long mStatusUpdateTime = SystemClock.elapsedRealtime(); - - // turn off GPS fix icon if we haven't received a fix in 10 seconds - private static final long RECENT_FIX_TIMEOUT = 10 * 1000; - // stop trying if we do not receive a fix within 60 seconds private static final int NO_FIX_TIMEOUT = 60 * 1000; @@ -485,7 +475,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements break; case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED: case TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED: - subscriptionOrCarrierConfigChanged(context); + subscriptionOrCarrierConfigChanged(); break; } } @@ -500,7 +490,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements mGnssMetrics.resetConstellationTypes(); } - private void subscriptionOrCarrierConfigChanged(Context context) { + private void subscriptionOrCarrierConfigChanged() { if (DEBUG) Log.d(TAG, "received SIM related action: "); TelephonyManager phone = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); @@ -1010,24 +1000,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } @Override - public int getStatus(Bundle extras) { - mLocationExtras.setBundle(extras); - return mStatus; - } - - private void updateStatus(int status) { - if (status != mStatus) { - mStatus = status; - mStatusUpdateTime = SystemClock.elapsedRealtime(); - } - } - - @Override - public long getStatusUpdateTime() { - return mStatusUpdateTime; - } - - @Override public void onSetRequest(ProviderRequest request, WorkSource source) { sendMessage(SET_REQUEST, 0, new GpsRequest(request, source)); } @@ -1266,7 +1238,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } // reset SV count to zero - updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE); mLocationExtras.reset(); mFixRequestTime = SystemClock.elapsedRealtime(); if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) { @@ -1290,7 +1261,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements mLastPositionMode = null; // reset SV count to zero - updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE); mLocationExtras.reset(); } } @@ -1381,7 +1351,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements mGnssStatusListenerHelper.onFirstFix(mTimeToFirstFix); } - if (mStarted && mStatus != LocationProvider.AVAILABLE) { + if (mStarted) { // For devices that use framework scheduling, a timer may be set to ensure we don't // spend too much power searching for a location, when the requested update rate is // slow. @@ -1389,8 +1359,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mFixInterval < NO_FIX_TIMEOUT) { mAlarmManager.cancel(mTimeoutIntent); } - - updateStatus(LocationProvider.AVAILABLE); } if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mStarted && @@ -1505,11 +1473,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements // return number of sats used in fix instead of total reported mLocationExtras.set(usedInFixCount, meanCn0, maxCn0); - if (mNavigating && mStatus == LocationProvider.AVAILABLE && mLastFixTime > 0 && - SystemClock.elapsedRealtime() - mLastFixTime > RECENT_FIX_TIMEOUT) { - updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE); - } - mGnssMetrics.logSvStatus(info.mSvCount, info.mSvidWithFlags, info.mSvCarrierFreqs); } diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java index 09911ff1a74e..694f14904668 100644 --- a/services/core/java/com/android/server/location/LocationProviderProxy.java +++ b/services/core/java/com/android/server/location/LocationProviderProxy.java @@ -22,7 +22,6 @@ import android.annotation.Nullable; import android.content.Context; import android.content.pm.PackageManager; import android.location.Location; -import android.location.LocationProvider; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; @@ -190,22 +189,6 @@ public class LocationProviderProxy extends AbstractLocationProvider { } @Override - public int getStatus(Bundle extras) { - return mServiceWatcher.runOnBinderBlocking(binder -> { - ILocationProvider service = ILocationProvider.Stub.asInterface(binder); - return service.getStatus(extras); - }, LocationProvider.TEMPORARILY_UNAVAILABLE); - } - - @Override - public long getStatusUpdateTime() { - return mServiceWatcher.runOnBinderBlocking(binder -> { - ILocationProvider service = ILocationProvider.Stub.asInterface(binder); - return service.getStatusUpdateTime(); - }, 0L); - } - - @Override public void onSendExtraCommand(int uid, int pid, String command, Bundle extras) { mServiceWatcher.runOnBinder(binder -> { ILocationProvider service = ILocationProvider.Stub.asInterface(binder); diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java index b0c4c2e65fd8..472876bfd86a 100644 --- a/services/core/java/com/android/server/location/MockProvider.java +++ b/services/core/java/com/android/server/location/MockProvider.java @@ -19,8 +19,6 @@ package com.android.server.location; import android.annotation.Nullable; import android.content.Context; import android.location.Location; -import android.location.LocationProvider; -import android.os.Bundle; import android.os.WorkSource; import com.android.internal.location.ProviderProperties; @@ -38,9 +36,6 @@ public class MockProvider extends AbstractLocationProvider { private boolean mEnabled; @Nullable private Location mLocation; - private int mStatus; - private long mStatusUpdateTime; - private Bundle mExtras; public MockProvider(Context context, LocationProviderManager locationProviderManager, ProviderProperties properties) { @@ -48,9 +43,6 @@ public class MockProvider extends AbstractLocationProvider { mEnabled = true; mLocation = null; - mStatus = LocationProvider.AVAILABLE; - mStatusUpdateTime = 0; - mExtras = null; setProperties(properties); } @@ -72,13 +64,6 @@ public class MockProvider extends AbstractLocationProvider { } } - /** Sets the status for this mock provider. */ - public void setStatus(int status, Bundle extras, long updateTime) { - mStatus = status; - mStatusUpdateTime = updateTime; - mExtras = extras; - } - @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("last location=" + mLocation); @@ -86,19 +71,4 @@ public class MockProvider extends AbstractLocationProvider { @Override public void onSetRequest(ProviderRequest request, WorkSource source) {} - - @Override - public int getStatus(Bundle extras) { - if (mExtras != null) { - extras.clear(); - extras.putAll(mExtras); - } - - return mStatus; - } - - @Override - public long getStatusUpdateTime() { - return mStatusUpdateTime; - } } diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index 7eb74381f7ae..48678bfac86e 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -16,13 +16,18 @@ package com.android.server.pm; +import static android.content.pm.PackageParser.Component; +import static android.content.pm.PackageParser.IntentInfo; import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE; import android.Manifest; import android.annotation.Nullable; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.PackageParser; +import android.content.pm.ProviderInfo; +import android.net.Uri; import android.os.Build; import android.os.Process; import android.os.RemoteException; @@ -38,6 +43,7 @@ import com.android.server.FgThread; import com.android.server.compat.PlatformCompat; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -55,13 +61,13 @@ class AppsFilter { // Forces filtering logic to run for debug purposes. // STOPSHIP (b/136675067): should be false after development is complete - private static final boolean DEBUG_RUN_WHEN_DISABLED = true; + private static final boolean DEBUG_RUN_WHEN_DISABLED = false; // Logs all filtering instead of enforcing private static final boolean DEBUG_ALLOW_ALL = false; @SuppressWarnings("ConstantExpression") - private static final boolean DEBUG_LOGGING = false | DEBUG_RUN_WHEN_DISABLED | DEBUG_ALLOW_ALL; + private static final boolean DEBUG_LOGGING = false | DEBUG_ALLOW_ALL; /** * This contains a list of packages that are implicitly queryable because another app explicitly @@ -200,23 +206,43 @@ class AppsFilter { return false; } for (Intent intent : querying.mQueriesIntents) { - for (PackageParser.Activity activity : potentialTarget.activities) { - if (activity.intents != null) { - for (PackageParser.ActivityIntentInfo filter : activity.intents) { - if (matches(intent, filter)) { - return true; - } - } - } + if (matches(intent, potentialTarget.providers, potentialTarget.activities, + potentialTarget.services, potentialTarget.receivers)) { + return true; } } return false; } - /** Returns true if the given intent matches the given filter. */ - private static boolean matches(Intent intent, PackageParser.ActivityIntentInfo filter) { - return filter.match(intent.getAction(), intent.getType(), intent.getScheme(), - intent.getData(), intent.getCategories(), "AppsFilter") > 0; + private static boolean matches(Intent intent, + ArrayList<PackageParser.Provider> providerList, + ArrayList<? extends Component<? extends IntentInfo>>... componentLists) { + for (int p = providerList.size() - 1; p >= 0; p--) { + PackageParser.Provider provider = providerList.get(p); + final ProviderInfo providerInfo = provider.info; + final Uri data = intent.getData(); + if ("content".equalsIgnoreCase(intent.getScheme()) + && data != null + && providerInfo.authority.equalsIgnoreCase(data.getAuthority())) { + return true; + } + } + + for (int l = componentLists.length - 1; l >= 0; l--) { + ArrayList<? extends Component<? extends IntentInfo>> components = componentLists[l]; + for (int c = components.size() - 1; c >= 0; c--) { + Component<? extends IntentInfo> component = components.get(c); + ArrayList<? extends IntentInfo> intents = component.intents; + for (int i = intents.size() - 1; i >= 0; i--) { + IntentFilter intentFilter = intents.get(i); + if (intentFilter.match(intent.getAction(), intent.getType(), intent.getScheme(), + intent.getData(), intent.getCategories(), "AppsFilter") > 0) { + return true; + } + } + } + } + return false; } /** @@ -328,9 +354,15 @@ class AppsFilter { PackageSetting targetPkgSetting, int userId) { final boolean featureEnabled = mFeatureConfig.isGloballyEnabled(); if (!featureEnabled && !DEBUG_RUN_WHEN_DISABLED) { + if (DEBUG_LOGGING) { + Slog.d(TAG, "filtering disabled; skipped"); + } return false; } if (callingUid < Process.FIRST_APPLICATION_UID) { + if (DEBUG_LOGGING) { + Slog.d(TAG, "filtering skipped; " + callingUid + " is system"); + } return false; } if (callingSetting == null) { @@ -376,14 +408,13 @@ class AppsFilter { } if (mFeatureConfig.packageIsEnabled(callingPkgSetting.pkg)) { if (DEBUG_LOGGING) { - Slog.d(TAG, "interaction: " + callingPkgSetting.name + " -> " - + targetPkgSetting.name + (DEBUG_ALLOW_ALL ? " ALLOWED" : "BLOCKED")); + log(callingPkgSetting, targetPkgSetting, + DEBUG_ALLOW_ALL ? "ALLOWED" : "BLOCKED"); } return !DEBUG_ALLOW_ALL; } else { if (DEBUG_LOGGING) { - Slog.d(TAG, "interaction: " + callingPkgSetting.name + " -> " - + targetPkgSetting.name + " DISABLED"); + log(callingPkgSetting, targetPkgSetting, "DISABLED"); } return false; } @@ -397,38 +428,65 @@ class AppsFilter { // This package isn't technically installed and won't be written to settings, so we can // treat it as filtered until it's available again. if (targetPkg == null) { + if (DEBUG_LOGGING) { + Slog.wtf(TAG, "shouldFilterApplication: " + "targetPkg is null"); + } return true; } final String targetName = targetPkg.packageName; if (callingPkgSetting.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.R) { + if (DEBUG_LOGGING) { + log(callingPkgSetting, targetPkgSetting, "caller pre-R"); + } return false; } if (isImplicitlyQueryableSystemApp(targetPkgSetting)) { + if (DEBUG_LOGGING) { + log(callingPkgSetting, targetPkgSetting, "implicitly queryable sys"); + } return false; } if (targetPkg.mForceQueryable) { + if (DEBUG_LOGGING) { + log(callingPkgSetting, targetPkgSetting, "manifest forceQueryable"); + } return false; } if (mForceQueryable.contains(targetName)) { + if (DEBUG_LOGGING) { + log(callingPkgSetting, targetPkgSetting, "whitelist forceQueryable"); + } return false; } if (mQueriesViaPackage.containsKey(callingName) && mQueriesViaPackage.get(callingName).contains( targetName)) { // the calling package has explicitly declared the target package; allow + if (DEBUG_LOGGING) { + log(callingPkgSetting, targetPkgSetting, "queries package"); + } return false; } else if (mQueriesViaIntent.containsKey(callingName) && mQueriesViaIntent.get(callingName).contains(targetName)) { + if (DEBUG_LOGGING) { + log(callingPkgSetting, targetPkgSetting, "queries intent"); + } return false; } if (mImplicitlyQueryable.get(userId) != null && mImplicitlyQueryable.get(userId).containsKey(callingName) && mImplicitlyQueryable.get(userId).get(callingName).contains(targetName)) { + if (DEBUG_LOGGING) { + log(callingPkgSetting, targetPkgSetting, "implicitly queryable for user"); + } return false; } if (callingPkgSetting.pkg.instrumentation.size() > 0) { for (int i = 0, max = callingPkgSetting.pkg.instrumentation.size(); i < max; i++) { if (callingPkgSetting.pkg.instrumentation.get(i).info.targetPackage == targetName) { + if (DEBUG_LOGGING) { + log(callingPkgSetting, targetPkgSetting, "instrumentation"); + } return false; } } @@ -437,6 +495,9 @@ class AppsFilter { if (mPermissionManager.checkPermission( Manifest.permission.QUERY_ALL_PACKAGES, callingName, userId) == PackageManager.PERMISSION_GRANTED) { + if (DEBUG_LOGGING) { + log(callingPkgSetting, targetPkgSetting, "permission"); + } return false; } } catch (RemoteException e) { @@ -445,6 +506,13 @@ class AppsFilter { return true; } + private static void log(PackageSetting callingPkgSetting, PackageSetting targetPkgSetting, + String description) { + Slog.wtf(TAG, + "interaction: " + callingPkgSetting.name + " -> " + targetPkgSetting.name + " " + + description); + } + private boolean isImplicitlyQueryableSystemApp(PackageSetting targetPkgSetting) { return targetPkgSetting.isSystem() && (mSystemAppsQueryable || mForceQueryableByDevice.contains(targetPkgSetting.pkg.packageName)); @@ -467,7 +535,9 @@ class AppsFilter { for (int user : users) { pw.append(" User ").append(Integer.toString(user)).println(":"); final HashMap<String, Set<String>> queryMapForUser = mImplicitlyQueryable.get(user); - dumpQueriesMap(pw, filteringPackageName, queryMapForUser, " "); + if (queryMapForUser != null) { + dumpQueriesMap(pw, filteringPackageName, queryMapForUser, " "); + } } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index b124c4b18efa..58596aa0aad2 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -17931,7 +17931,7 @@ public class PackageManagerService extends IPackageManager.Stub } if (removedAppId >= 0) { packageSender.sendPackageBroadcast(Intent.ACTION_UID_REMOVED, - null, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, + removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null, null, broadcastUsers, instantUserIds); } } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index a9e3f046e425..226adc8acc74 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -4283,6 +4283,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { public void setCheckPermissionDelegate(CheckPermissionDelegate delegate) { synchronized (mLock) { mCheckPermissionDelegate = delegate; + Slog.d(TAG, "CheckPermissionDelegate set to " + delegate); } } diff --git a/services/core/java/com/android/server/tv/TvRemoteProviderProxy.java b/services/core/java/com/android/server/tv/TvRemoteProviderProxy.java index b27d5ea30c67..f8ffb7c1c0e2 100644 --- a/services/core/java/com/android/server/tv/TvRemoteProviderProxy.java +++ b/services/core/java/com/android/server/tv/TvRemoteProviderProxy.java @@ -23,7 +23,6 @@ import android.content.ServiceConnection; import android.media.tv.ITvRemoteProvider; import android.media.tv.ITvRemoteServiceInput; import android.os.Binder; -import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; @@ -49,7 +48,6 @@ final class TvRemoteProviderProxy implements ServiceConnection { private final ComponentName mComponentName; private final int mUserId; private final int mUid; - private final Handler mHandler; /** * State guarded by mLock. @@ -65,15 +63,14 @@ final class TvRemoteProviderProxy implements ServiceConnection { private boolean mRunning; private boolean mBound; private Connection mActiveConnection; - private boolean mConnectionReady; - public TvRemoteProviderProxy(Context context, ComponentName componentName, int userId, - int uid) { + TvRemoteProviderProxy(Context context, ProviderMethods provider, + ComponentName componentName, int userId, int uid) { mContext = context; + mProviderMethods = provider; mComponentName = componentName; mUserId = userId; mUid = uid; - mHandler = new Handler(); } public void dump(PrintWriter pw, String prefix) { @@ -82,11 +79,6 @@ final class TvRemoteProviderProxy implements ServiceConnection { pw.println(prefix + " mRunning=" + mRunning); pw.println(prefix + " mBound=" + mBound); pw.println(prefix + " mActiveConnection=" + mActiveConnection); - pw.println(prefix + " mConnectionReady=" + mConnectionReady); - } - - public void setProviderSink(ProviderMethods provider) { - mProviderMethods = provider; } public boolean hasComponentName(String packageName, String className) { @@ -101,7 +93,7 @@ final class TvRemoteProviderProxy implements ServiceConnection { } mRunning = true; - updateBinding(); + bind(); } } @@ -112,31 +104,19 @@ final class TvRemoteProviderProxy implements ServiceConnection { } mRunning = false; - updateBinding(); + unbind(); } } public void rebindIfDisconnected() { synchronized (mLock) { - if (mActiveConnection == null && shouldBind()) { + if (mActiveConnection == null && mRunning) { unbind(); bind(); } } } - private void updateBinding() { - if (shouldBind()) { - bind(); - } else { - unbind(); - } - } - - private boolean shouldBind() { - return mRunning; - } - private void bind() { if (!mBound) { if (DEBUG) { @@ -208,48 +188,19 @@ final class TvRemoteProviderProxy implements ServiceConnection { disconnect(); } - - private void onConnectionReady(Connection connection) { - synchronized (mLock) { - if (DEBUG) Slog.d(TAG, "onConnectionReady"); - if (mActiveConnection == connection) { - if (DEBUG) Slog.d(TAG, "mConnectionReady = true"); - mConnectionReady = true; - } - } - } - - private void onConnectionDied(Connection connection) { - if (mActiveConnection == connection) { - if (DEBUG) Slog.d(TAG, this + ": Service connection died"); - disconnect(); - } - } - private void disconnect() { synchronized (mLock) { if (mActiveConnection != null) { - mConnectionReady = false; mActiveConnection.dispose(); mActiveConnection = null; } } } - // Provider helpers - public void inputBridgeConnected(IBinder token) { - synchronized (mLock) { - if (DEBUG) Slog.d(TAG, this + ": inputBridgeConnected token: " + token); - if (mConnectionReady) { - mActiveConnection.onInputBridgeConnected(token); - } - } - } - - public interface ProviderMethods { + interface ProviderMethods { // InputBridge - void openInputBridge(TvRemoteProviderProxy provider, IBinder token, String name, - int width, int height, int maxPointers); + boolean openInputBridge(TvRemoteProviderProxy provider, IBinder token, String name, + int width, int height, int maxPointers); void closeInputBridge(TvRemoteProviderProxy provider, IBinder token); @@ -267,7 +218,7 @@ final class TvRemoteProviderProxy implements ServiceConnection { void sendPointerSync(TvRemoteProviderProxy provider, IBinder token); } - private final class Connection implements IBinder.DeathRecipient { + private final class Connection { private final ITvRemoteProvider mTvRemoteProvider; private final RemoteServiceInputProvider mServiceInputProvider; @@ -279,24 +230,16 @@ final class TvRemoteProviderProxy implements ServiceConnection { public boolean register() { if (DEBUG) Slog.d(TAG, "Connection::register()"); try { - mTvRemoteProvider.asBinder().linkToDeath(this, 0); mTvRemoteProvider.setRemoteServiceInputSink(mServiceInputProvider); - mHandler.post(new Runnable() { - @Override - public void run() { - onConnectionReady(Connection.this); - } - }); return true; } catch (RemoteException ex) { - binderDied(); + dispose(); + return false; } - return false; } public void dispose() { if (DEBUG) Slog.d(TAG, "Connection::dispose()"); - mTvRemoteProvider.asBinder().unlinkToDeath(this, 0); mServiceInputProvider.dispose(); } @@ -310,16 +253,6 @@ final class TvRemoteProviderProxy implements ServiceConnection { } } - @Override - public void binderDied() { - mHandler.post(new Runnable() { - @Override - public void run() { - onConnectionDied(Connection.this); - } - }); - } - void openInputBridge(final IBinder token, final String name, final int width, final int height, final int maxPointers) { synchronized (mLock) { @@ -330,9 +263,9 @@ final class TvRemoteProviderProxy implements ServiceConnection { } final long idToken = Binder.clearCallingIdentity(); try { - if (mProviderMethods != null) { - mProviderMethods.openInputBridge(TvRemoteProviderProxy.this, token, - name, width, height, maxPointers); + if (mProviderMethods.openInputBridge(TvRemoteProviderProxy.this, token, + name, width, height, maxPointers)) { + onInputBridgeConnected(token); } } finally { Binder.restoreCallingIdentity(idToken); @@ -356,9 +289,7 @@ final class TvRemoteProviderProxy implements ServiceConnection { } final long idToken = Binder.clearCallingIdentity(); try { - if (mProviderMethods != null) { - mProviderMethods.closeInputBridge(TvRemoteProviderProxy.this, token); - } + mProviderMethods.closeInputBridge(TvRemoteProviderProxy.this, token); } finally { Binder.restoreCallingIdentity(idToken); } @@ -381,9 +312,7 @@ final class TvRemoteProviderProxy implements ServiceConnection { } final long idToken = Binder.clearCallingIdentity(); try { - if (mProviderMethods != null) { - mProviderMethods.clearInputBridge(TvRemoteProviderProxy.this, token); - } + mProviderMethods.clearInputBridge(TvRemoteProviderProxy.this, token); } finally { Binder.restoreCallingIdentity(idToken); } @@ -412,10 +341,7 @@ final class TvRemoteProviderProxy implements ServiceConnection { } final long idToken = Binder.clearCallingIdentity(); try { - if (mProviderMethods != null) { - mProviderMethods.sendKeyDown(TvRemoteProviderProxy.this, token, - keyCode); - } + mProviderMethods.sendKeyDown(TvRemoteProviderProxy.this, token, keyCode); } finally { Binder.restoreCallingIdentity(idToken); } @@ -438,9 +364,7 @@ final class TvRemoteProviderProxy implements ServiceConnection { } final long idToken = Binder.clearCallingIdentity(); try { - if (mProviderMethods != null) { - mProviderMethods.sendKeyUp(TvRemoteProviderProxy.this, token, keyCode); - } + mProviderMethods.sendKeyUp(TvRemoteProviderProxy.this, token, keyCode); } finally { Binder.restoreCallingIdentity(idToken); } @@ -463,10 +387,8 @@ final class TvRemoteProviderProxy implements ServiceConnection { } final long idToken = Binder.clearCallingIdentity(); try { - if (mProviderMethods != null) { - mProviderMethods.sendPointerDown(TvRemoteProviderProxy.this, token, - pointerId, x, y); - } + mProviderMethods.sendPointerDown(TvRemoteProviderProxy.this, token, + pointerId, x, y); } finally { Binder.restoreCallingIdentity(idToken); } @@ -489,10 +411,8 @@ final class TvRemoteProviderProxy implements ServiceConnection { } final long idToken = Binder.clearCallingIdentity(); try { - if (mProviderMethods != null) { - mProviderMethods.sendPointerUp(TvRemoteProviderProxy.this, token, - pointerId); - } + mProviderMethods.sendPointerUp(TvRemoteProviderProxy.this, token, + pointerId); } finally { Binder.restoreCallingIdentity(idToken); } diff --git a/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java b/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java index d27970f4882c..0d29edd02663 100644 --- a/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java +++ b/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java @@ -45,7 +45,7 @@ final class TvRemoteProviderWatcher { private static final boolean DEBUG = Log.isLoggable(TAG, Log.VERBOSE); private final Context mContext; - private final ProviderMethods mProvider; + private final TvRemoteProviderProxy.ProviderMethods mProvider; private final Handler mHandler; private final PackageManager mPackageManager; private final ArrayList<TvRemoteProviderProxy> mProviderProxies = new ArrayList<>(); @@ -54,10 +54,10 @@ final class TvRemoteProviderWatcher { private boolean mRunning; - public TvRemoteProviderWatcher(Context context, ProviderMethods provider, Handler handler) { + TvRemoteProviderWatcher(Context context, TvRemoteProviderProxy.ProviderMethods provider) { mContext = context; mProvider = provider; - mHandler = handler; + mHandler = new Handler(true); mUserId = UserHandle.myUserId(); mPackageManager = context.getPackageManager(); mUnbundledServicePackage = context.getString( @@ -116,12 +116,11 @@ final class TvRemoteProviderWatcher { int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name); if (sourceIndex < 0) { TvRemoteProviderProxy providerProxy = - new TvRemoteProviderProxy(mContext, + new TvRemoteProviderProxy(mContext, mProvider, new ComponentName(serviceInfo.packageName, serviceInfo.name), mUserId, serviceInfo.applicationInfo.uid); providerProxy.start(); mProviderProxies.add(targetIndex++, providerProxy); - mProvider.addProvider(providerProxy); } else if (sourceIndex >= targetIndex) { TvRemoteProviderProxy provider = mProviderProxies.get(sourceIndex); provider.start(); // restart the provider if needed @@ -135,7 +134,6 @@ final class TvRemoteProviderWatcher { if (targetIndex < mProviderProxies.size()) { for (int i = mProviderProxies.size() - 1; i >= targetIndex; i--) { TvRemoteProviderProxy providerProxy = mProviderProxies.get(i); - mProvider.removeProvider(providerProxy); mProviderProxies.remove(providerProxy); providerProxy.stop(); } @@ -212,10 +210,4 @@ final class TvRemoteProviderWatcher { scanPackages(); } }; - - public interface ProviderMethods { - void addProvider(TvRemoteProviderProxy providerProxy); - - void removeProvider(TvRemoteProviderProxy providerProxy); - } } diff --git a/services/core/java/com/android/server/tv/TvRemoteService.java b/services/core/java/com/android/server/tv/TvRemoteService.java index ba74791842d1..bee6fb34a899 100644 --- a/services/core/java/com/android/server/tv/TvRemoteService.java +++ b/services/core/java/com/android/server/tv/TvRemoteService.java @@ -17,11 +17,8 @@ package com.android.server.tv; import android.content.Context; -import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; -import android.os.Looper; -import android.os.Message; import android.util.ArrayMap; import android.util.Slog; @@ -29,7 +26,6 @@ import com.android.server.SystemService; import com.android.server.Watchdog; import java.io.IOException; -import java.util.ArrayList; import java.util.Map; /** @@ -44,9 +40,8 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor { private static final boolean DEBUG = false; private static final boolean DEBUG_KEYS = false; + private final TvRemoteProviderWatcher mWatcher; private Map<IBinder, UinputBridge> mBridgeMap = new ArrayMap(); - private Map<IBinder, TvRemoteProviderProxy> mProviderMap = new ArrayMap(); - private ArrayList<TvRemoteProviderProxy> mProviderList = new ArrayList<>(); /** * State guarded by mLock. @@ -60,11 +55,10 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor { */ private final Object mLock = new Object(); - public final UserHandler mHandler; - public TvRemoteService(Context context) { super(context); - mHandler = new UserHandler(new UserProvider(TvRemoteService.this), context); + mWatcher = new TvRemoteProviderWatcher(context, + new UserProvider(TvRemoteService.this)); Watchdog.getInstance().addMonitor(this); } @@ -80,21 +74,17 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor { @Override public void onBootPhase(int phase) { + // All lifecycle methods are called from the system server's main looper thread. if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { if (DEBUG) Slog.d(TAG, "PHASE_THIRD_PARTY_APPS_CAN_START"); - mHandler.sendEmptyMessage(UserHandler.MSG_START); - } - } - //Outgoing calls. - private void informInputBridgeConnected(IBinder token) { - mHandler.obtainMessage(UserHandler.MSG_INPUT_BRIDGE_CONNECTED, 0, 0, token).sendToTarget(); + mWatcher.start(); // Also schedules the start of all providers. + } } - // Incoming calls. - private void openInputBridgeInternalLocked(TvRemoteProviderProxy provider, final IBinder token, - String name, int width, int height, - int maxPointers) { + private boolean openInputBridgeInternalLocked(final IBinder token, + String name, int width, int height, + int maxPointers) { if (DEBUG) { Slog.d(TAG, "openInputBridgeInternalLocked(), token: " + token + ", name: " + name + ", width: " + width + ", height: " + height + ", maxPointers: " + maxPointers); @@ -104,15 +94,11 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor { //Create a new bridge, if one does not exist already if (mBridgeMap.containsKey(token)) { if (DEBUG) Slog.d(TAG, "RemoteBridge already exists"); - // Respond back with success. - informInputBridgeConnected(token); - return; + return true; } UinputBridge inputBridge = new UinputBridge(token, name, width, height, maxPointers); - mBridgeMap.put(token, inputBridge); - mProviderMap.put(token, provider); try { token.linkToDeath(new IBinder.DeathRecipient() { @@ -126,15 +112,13 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor { } catch (RemoteException e) { if (DEBUG) Slog.d(TAG, "Token is already dead"); closeInputBridgeInternalLocked(token); - return; + return false; } - - // Respond back with success. - informInputBridgeConnected(token); - } catch (IOException ioe) { Slog.e(TAG, "Cannot create device for " + name); + return false; } + return true; } private void closeInputBridgeInternalLocked(IBinder token) { @@ -149,7 +133,6 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor { } mBridgeMap.remove(token); - mProviderMap.remove(token); } private void clearInputBridgeInternalLocked(IBinder token) { @@ -220,47 +203,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor { } } - private final class UserHandler extends Handler { - - public static final int MSG_START = 1; - public static final int MSG_INPUT_BRIDGE_CONNECTED = 2; - - private final TvRemoteProviderWatcher mWatcher; - private boolean mRunning; - - public UserHandler(UserProvider provider, Context context) { - super(Looper.getMainLooper(), null, true); - mWatcher = new TvRemoteProviderWatcher(context, provider, this); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_START: { - start(); - break; - } - case MSG_INPUT_BRIDGE_CONNECTED: { - IBinder token = (IBinder) msg.obj; - TvRemoteProviderProxy provider = mProviderMap.get(token); - if (provider != null) { - provider.inputBridgeConnected(token); - } - break; - } - } - } - - private void start() { - if (!mRunning) { - mRunning = true; - mWatcher.start(); // also starts all providers - } - } - } - - private final class UserProvider implements TvRemoteProviderWatcher.ProviderMethods, - TvRemoteProviderProxy.ProviderMethods { + private final class UserProvider implements TvRemoteProviderProxy.ProviderMethods { private final TvRemoteService mService; @@ -269,8 +212,8 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor { } @Override - public void openInputBridge(TvRemoteProviderProxy provider, IBinder token, String name, - int width, int height, int maxPointers) { + public boolean openInputBridge(TvRemoteProviderProxy provider, IBinder token, String name, + int width, int height, int maxPointers) { if (DEBUG) { Slog.d(TAG, "openInputBridge(), token: " + token + ", name: " + name + ", width: " + width + @@ -278,10 +221,8 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor { } synchronized (mLock) { - if (mProviderList.contains(provider)) { - mService.openInputBridgeInternalLocked(provider, token, name, width, height, - maxPointers); - } + return mService.openInputBridgeInternalLocked(token, name, width, + height, maxPointers); } } @@ -289,9 +230,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor { public void closeInputBridge(TvRemoteProviderProxy provider, IBinder token) { if (DEBUG) Slog.d(TAG, "closeInputBridge(), token: " + token); synchronized (mLock) { - if (mProviderList.contains(provider)) { mService.closeInputBridgeInternalLocked(token); - } } } @@ -299,9 +238,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor { public void clearInputBridge(TvRemoteProviderProxy provider, IBinder token) { if (DEBUG) Slog.d(TAG, "clearInputBridge(), token: " + token); synchronized (mLock) { - if (mProviderList.contains(provider)) { mService.clearInputBridgeInternalLocked(token); - } } } @@ -311,9 +248,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor { Slog.d(TAG, "sendKeyDown(), token: " + token + ", keyCode: " + keyCode); } synchronized (mLock) { - if (mProviderList.contains(provider)) { mService.sendKeyDownInternalLocked(token, keyCode); - } } } @@ -323,9 +258,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor { Slog.d(TAG, "sendKeyUp(), token: " + token + ", keyCode: " + keyCode); } synchronized (mLock) { - if (mProviderList.contains(provider)) { mService.sendKeyUpInternalLocked(token, keyCode); - } } } @@ -336,9 +269,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor { Slog.d(TAG, "sendPointerDown(), token: " + token + ", pointerId: " + pointerId); } synchronized (mLock) { - if (mProviderList.contains(provider)) { mService.sendPointerDownInternalLocked(token, pointerId, x, y); - } } } @@ -348,9 +279,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor { Slog.d(TAG, "sendPointerUp(), token: " + token + ", pointerId: " + pointerId); } synchronized (mLock) { - if (mProviderList.contains(provider)) { mService.sendPointerUpInternalLocked(token, pointerId); - } } } @@ -358,29 +287,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor { public void sendPointerSync(TvRemoteProviderProxy provider, IBinder token) { if (DEBUG_KEYS) Slog.d(TAG, "sendPointerSync(), token: " + token); synchronized (mLock) { - if (mProviderList.contains(provider)) { mService.sendPointerSyncInternalLocked(token); - } - } - } - - @Override - public void addProvider(TvRemoteProviderProxy provider) { - if (DEBUG) Slog.d(TAG, "addProvider " + provider); - synchronized (mLock) { - provider.setProviderSink(this); - mProviderList.add(provider); - Slog.d(TAG, "provider: " + provider.toString()); - } - } - - @Override - public void removeProvider(TvRemoteProviderProxy provider) { - if (DEBUG) Slog.d(TAG, "removeProvider " + provider); - synchronized (mLock) { - if (mProviderList.remove(provider) == false) { - Slog.e(TAG, "Unknown provider " + provider); - } } } } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index b0f1e5d69be4..3cdb59beb23c 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -37,6 +37,7 @@ import android.app.UserSwitchObserver; import android.app.WallpaperColors; import android.app.WallpaperInfo; import android.app.WallpaperManager; +import android.app.WallpaperManager.SetWallpaperFlags; import android.app.admin.DevicePolicyManager; import android.app.backup.WallpaperBackupHelper; import android.content.BroadcastReceiver; @@ -73,7 +74,6 @@ import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SELinux; -import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; @@ -91,9 +91,9 @@ import android.util.SparseBooleanArray; import android.util.Xml; import android.view.Display; import android.view.DisplayInfo; -import android.view.IWindowManager; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.PackageMonitor; import com.android.internal.os.BackgroundThread; import com.android.internal.util.DumpUtils; @@ -739,7 +739,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } private final Context mContext; - private final IWindowManager mIWindowManager; private final WindowManagerInternal mWindowManagerInternal; private final IPackageManager mIPackageManager; private final MyPackageMonitor mMonitor; @@ -792,7 +791,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub */ private final SparseArray<SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>> mColorsChangedListeners; - private WallpaperData mLastWallpaper; + protected WallpaperData mLastWallpaper; private IWallpaperManagerCallback mKeyguardListener; private boolean mWaitingForUnlock; private boolean mShuttingDown; @@ -825,7 +824,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub private SparseArray<DisplayData> mDisplayDatas = new SparseArray<>(); - private WallpaperData mFallbackWallpaper; + protected WallpaperData mFallbackWallpaper; private final SparseBooleanArray mUserRestorecon = new SparseBooleanArray(); private int mCurrentUserId = UserHandle.USER_NULL; @@ -900,9 +899,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub */ final Rect cropHint = new Rect(0, 0, 0, 0); - WallpaperData(int userId, String inputFileName, String cropFileName) { + WallpaperData(int userId, File wallpaperDir, String inputFileName, String cropFileName) { this.userId = userId; - final File wallpaperDir = getWallpaperDir(userId); wallpaperFile = new File(wallpaperDir, inputFileName); cropFile = new File(wallpaperDir, cropFileName); } @@ -917,7 +915,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } - private static final class DisplayData { + @VisibleForTesting + static final class DisplayData { int mWidth = -1; int mHeight = -1; final Rect mPadding = new Rect(0, 0, 0, 0); @@ -1057,13 +1056,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub return; } if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken); - try { - mIWindowManager.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId); - } catch (RemoteException e) { - Slog.e(TAG, "Failed add wallpaper window token on display " + mDisplayId, e); - return; - } - + mWindowManagerInternal.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId); final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId); try { connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false, @@ -1081,10 +1074,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub void disconnectLocked() { if (DEBUG) Slog.v(TAG, "Removing window token: " + mToken); - try { - mIWindowManager.removeWindowToken(mToken, mDisplayId); - } catch (RemoteException e) { - } + mWindowManagerInternal.removeWindowToken(mToken, false/* removeWindows */, + mDisplayId); try { if (mEngine != null) { mEngine.destroy(); @@ -1562,6 +1553,15 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } + @VisibleForTesting + WallpaperData getCurrentWallpaperData(@SetWallpaperFlags int which, int userId) { + synchronized (mLock) { + final SparseArray<WallpaperData> wallpaperDataMap = + which == FLAG_SYSTEM ? mWallpaperMap : mLockWallpaperMap; + return wallpaperDataMap.get(userId); + } + } + public WallpaperManagerService(Context context) { if (DEBUG) Slog.v(TAG, "WallpaperService startup"); mContext = context; @@ -1569,8 +1569,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub mImageWallpaper = ComponentName.unflattenFromString( context.getResources().getString(R.string.image_wallpaper_component)); mDefaultWallpaperComponent = WallpaperManager.getDefaultWallpaperComponent(context); - mIWindowManager = IWindowManager.Stub.asInterface( - ServiceManager.getService(Context.WINDOW_SERVICE)); mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); mIPackageManager = AppGlobals.getPackageManager(); mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); @@ -1600,7 +1598,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub getWallpaperSafeLocked(UserHandle.USER_SYSTEM, FLAG_SYSTEM); } - private static File getWallpaperDir(int userId) { + File getWallpaperDir(int userId) { return Environment.getUserSystemDirectory(userId); } @@ -1819,7 +1817,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // while locked, so pretend like the component was actually // bound into place wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent; - final WallpaperData fallback = new WallpaperData(wallpaper.userId, + final WallpaperData fallback = + new WallpaperData(wallpaper.userId, getWallpaperDir(wallpaper.userId), WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP); ensureSaneWallpaperData(fallback, DEFAULT_DISPLAY); bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply); @@ -2380,7 +2379,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } // We know a-priori that there is no lock-only wallpaper currently - WallpaperData lockWP = new WallpaperData(userId, + WallpaperData lockWP = new WallpaperData(userId, getWallpaperDir(userId), WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP); lockWP.wallpaperId = sysWP.wallpaperId; lockWP.cropHint.set(sysWP.cropHint); @@ -2793,7 +2792,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } - private static JournaledFile makeJournaledFile(int userId) { + private JournaledFile makeJournaledFile(int userId) { final String base = new File(getWallpaperDir(userId), WALLPAPER_INFO).getAbsolutePath(); return new JournaledFile(new File(base), new File(base + ".tmp")); } @@ -2958,7 +2957,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // it now. if (wallpaper == null) { if (which == FLAG_LOCK) { - wallpaper = new WallpaperData(userId, + wallpaper = new WallpaperData(userId, getWallpaperDir(userId), WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP); mLockWallpaperMap.put(userId, wallpaper); ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY); @@ -2966,7 +2965,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // sanity fallback: we're in bad shape, but establishing a known // valid system+lock WallpaperData will keep us from dying. Slog.wtf(TAG, "Didn't find wallpaper in non-lock case!"); - wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP); + wallpaper = new WallpaperData(userId, getWallpaperDir(userId), + WALLPAPER, WALLPAPER_CROP); mWallpaperMap.put(userId, wallpaper); ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY); } @@ -2985,7 +2985,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // Do this once per boot migrateFromOld(); - wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP); + wallpaper = new WallpaperData(userId, getWallpaperDir(userId), + WALLPAPER, WALLPAPER_CROP); wallpaper.allowBackup = true; mWallpaperMap.put(userId, wallpaper); if (!wallpaper.cropExists()) { @@ -3037,7 +3038,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // keyguard-specific wallpaper for this user WallpaperData lockWallpaper = mLockWallpaperMap.get(userId); if (lockWallpaper == null) { - lockWallpaper = new WallpaperData(userId, + lockWallpaper = new WallpaperData(userId, getWallpaperDir(userId), WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP); mLockWallpaperMap.put(userId, lockWallpaper); } @@ -3088,8 +3089,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub private void initializeFallbackWallpaper() { if (mFallbackWallpaper == null) { if (DEBUG) Slog.d(TAG, "Initialize fallback wallpaper"); - mFallbackWallpaper = new WallpaperData( - UserHandle.USER_SYSTEM, WALLPAPER, WALLPAPER_CROP); + final int systemUserId = UserHandle.USER_SYSTEM; + mFallbackWallpaper = new WallpaperData(systemUserId, getWallpaperDir(systemUserId), + WALLPAPER, WALLPAPER_CROP); mFallbackWallpaper.allowBackup = false; mFallbackWallpaper.wallpaperId = makeWallpaperIdLocked(); bindWallpaperComponentLocked(mImageWallpaper, true, false, mFallbackWallpaper, null); diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 55db1a06664e..b35bd9e4e81a 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1535,6 +1535,11 @@ class ActivityStarter { final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null) ? mSourceRecord.getTaskRecord() : null; setNewTask(taskToAffiliate); + if (mService.getLockTaskController().isLockTaskModeViolation( + mStartActivity.getTaskRecord())) { + Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity); + return START_RETURN_LOCK_TASK_MODE_VIOLATION; + } } else if (mAddingToTask) { addOrReparentStartingActivity(targetTask, "adding to task"); } @@ -1654,9 +1659,8 @@ class ActivityStarter { final boolean isNewClearTask = (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK); - if (mService.getLockTaskController().isInLockTaskMode() && (newTask - || mService.getLockTaskController().isLockTaskModeViolation(targetTask, - isNewClearTask))) { + if (!newTask && mService.getLockTaskController().isLockTaskModeViolation(targetTask, + isNewClearTask)) { Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity); return START_RETURN_LOCK_TASK_MODE_VIOLATION; } @@ -2538,7 +2542,8 @@ class ActivityStarter { final boolean onTop = (aOptions == null || !aOptions.getAvoidMoveToFront()) && !mLaunchTaskBehind; final ActivityStack stack = - mRootActivityContainer.getLaunchStack(r, aOptions, task, onTop, mLaunchParams); + mRootActivityContainer.getLaunchStack(r, aOptions, task, onTop, mLaunchParams, + mRequest.realCallingPid, mRequest.realCallingUid); return stack; } // Otherwise handle adjacent launch. @@ -2656,11 +2661,24 @@ class ActivityStarter { return this; } + /** + * Sets the pid of the caller who originally started the activity. + * + * Normally, the pid/uid would be the calling pid from the binder call. + * However, in case of a {@link PendingIntent}, the pid/uid pair of the caller is considered + * the original entity that created the pending intent, in contrast to setRealCallingPid/Uid, + * which represents the entity who invoked pending intent via {@link PendingIntent#send}. + */ ActivityStarter setCallingPid(int pid) { mRequest.callingPid = pid; return this; } + /** + * Sets the uid of the caller who originally started the activity. + * + * @see #setCallingPid + */ ActivityStarter setCallingUid(int uid) { mRequest.callingUid = uid; return this; @@ -2671,11 +2689,25 @@ class ActivityStarter { return this; } + /** + * Sets the pid of the caller who requested to launch the activity. + * + * The pid/uid represents the caller who launches the activity in this request. + * It will almost same as setCallingPid/Uid except when processing {@link PendingIntent}: + * the pid/uid will be the caller who called {@link PendingIntent#send()}. + * + * @see #setCallingPid + */ ActivityStarter setRealCallingPid(int pid) { mRequest.realCallingPid = pid; return this; } + /** + * Sets the uid of the caller who requested to launch the activity. + * + * @see #setRealCallingPid + */ ActivityStarter setRealCallingUid(int uid) { mRequest.realCallingUid = uid; return this; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 50ee9b9c808f..f2ad56a8fdfa 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -100,6 +100,7 @@ import static com.android.server.wm.DisplayContentProto.ROTATION; import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION; import static com.android.server.wm.DisplayContentProto.STACKS; import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT; @@ -2920,9 +2921,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mWmService.mH.obtainMessage(REPORT_FOCUS_CHANGE, this).sendToTarget(); } - if (DEBUG_FOCUS_LIGHT || mWmService.localLOGV) Slog.v(TAG_WM, "Changing focus from " - + mCurrentFocus + " to " + newFocus + " displayId=" + getDisplayId() - + " Callers=" + Debug.getCallers(4)); + if (DEBUG_FOCUS_LIGHT || DEBUG) { + Slog.v(TAG_WM, "Changing focus from " + + mCurrentFocus + " to " + newFocus + " displayId=" + getDisplayId() + + " Callers=" + Debug.getCallers(4)); + } final WindowState oldFocus = mCurrentFocus; mCurrentFocus = newFocus; mLosingFocus.remove(newFocus); diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index b502bd54bfc5..4dbb0092140c 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -103,7 +103,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; -import static com.android.server.wm.WindowManagerService.localLOGV; import android.Manifest.permission; import android.annotation.NonNull; @@ -2597,7 +2596,7 @@ public class DisplayPolicy { } final int fl = PolicyControl.getWindowFlags(null, mTopFullscreenOpaqueWindowState.getAttrs()); - if (localLOGV) { + if (WindowManagerDebugConfig.DEBUG) { Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw()); Slog.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs() + " lp.flags=0x" + Integer.toHexString(fl)); diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 1b7b92bca250..34253ed6fc8c 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -51,8 +51,11 @@ class InsetsSourceProvider { private final @NonNull InsetsSource mSource; private final DisplayContent mDisplayContent; private final InsetsStateController mStateController; + private final InsetsSourceControl mFakeControl; private @Nullable InsetsSourceControl mControl; private @Nullable InsetsControlTarget mControlTarget; + private @Nullable InsetsControlTarget mFakeControlTarget; + private @Nullable ControlAdapter mAdapter; private WindowState mWin; private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider; @@ -73,6 +76,8 @@ class InsetsSourceProvider { mSource = source; mDisplayContent = displayContent; mStateController = stateController; + mFakeControl = new InsetsSourceControl(source.getType(), null /* leash */, + new Point()); final int type = source.getType(); if (type == TYPE_TOP_BAR || type == TYPE_NAVIGATION_BAR) { @@ -150,6 +155,16 @@ class InsetsSourceProvider { && !mWin.mGivenInsetsPending); } + /** + * @see InsetsStateController#onControlFakeTargetChanged(int, InsetsControlTarget) + */ + void updateControlForFakeTarget(@Nullable InsetsControlTarget fakeTarget) { + if (fakeTarget == mFakeControlTarget) { + return; + } + mFakeControlTarget = fakeTarget; + } + void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) { if (mWin == null) { mControlTarget = target; @@ -199,8 +214,14 @@ class InsetsSourceProvider { mSource.setVisible(mServerVisible && mClientVisible); } - InsetsSourceControl getControl() { - return mControl; + InsetsSourceControl getControl(InsetsControlTarget target) { + if (target == mControlTarget) { + return mControl; + } + if (target == mFakeControlTarget) { + return mFakeControl; + } + return null; } boolean isClientVisible() { @@ -257,5 +278,5 @@ class InsetsSourceProvider { @Override public void writeToProto(ProtoOutputStream proto) { } - }; + } } diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index bb704957a55a..4ebb553318e8 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -20,6 +20,8 @@ import static android.view.InsetsState.InternalInsetType; import static android.view.InsetsState.TYPE_IME; import static android.view.InsetsState.TYPE_NAVIGATION_BAR; import static android.view.InsetsState.TYPE_TOP_BAR; +import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; +import static android.view.ViewRootImpl.sNewInsetsMode; import android.annotation.NonNull; import android.annotation.Nullable; @@ -47,6 +49,10 @@ class InsetsStateController { private final ArrayMap<InsetsControlTarget, ArrayList<Integer>> mControlTargetTypeMap = new ArrayMap<>(); private final SparseArray<InsetsControlTarget> mTypeControlTargetMap = new SparseArray<>(); + + /** @see #onControlFakeTargetChanged */ + private final SparseArray<InsetsControlTarget> mTypeFakeControlTargetMap = new SparseArray<>(); + private final ArraySet<InsetsControlTarget> mPendingControlChanged = new ArraySet<>(); private final Consumer<WindowState> mDispatchInsetsChanged = w -> { @@ -93,7 +99,7 @@ class InsetsStateController { final int size = controlled.size(); final InsetsSourceControl[] result = new InsetsSourceControl[size]; for (int i = 0; i < size; i++) { - result[i] = mProviders.get(controlled.get(i)).getControl(); + result[i] = mProviders.get(controlled.get(i)).getControl(target); } return result; } @@ -157,7 +163,8 @@ class InsetsStateController { void notifyControlRevoked(@NonNull InsetsControlTarget previousControlTarget, InsetsSourceProvider provider) { - removeFromControlMaps(previousControlTarget, provider.getSource().getType()); + removeFromControlMaps(previousControlTarget, provider.getSource().getType(), + false /* fake */); } private void onControlChanged(@InternalInsetType int type, @@ -175,17 +182,47 @@ class InsetsStateController { } provider.updateControlForTarget(target, false /* force */); if (previous != null) { - removeFromControlMaps(previous, type); + removeFromControlMaps(previous, type, false /* fake */); mPendingControlChanged.add(previous); } if (target != null) { - addToControlMaps(target, type); + addToControlMaps(target, type, false /* fake */); mPendingControlChanged.add(target); } } + /** + * The fake target saved here will be used to pretend to the app that it's still under control + * of the bars while it's not really, but we still need to find out the apps intentions around + * showing/hiding. For example, when the transient bars are showing, and the fake target + * requests to show system bars, the transient state will be aborted. + */ + void onControlFakeTargetChanged(@InternalInsetType int type, + @Nullable InsetsControlTarget fakeTarget) { + if (sNewInsetsMode != NEW_INSETS_MODE_FULL) { + return; + } + final InsetsControlTarget previous = mTypeFakeControlTargetMap.get(type); + if (fakeTarget == previous) { + return; + } + final InsetsSourceProvider provider = mProviders.get(type); + if (provider == null) { + return; + } + provider.updateControlForFakeTarget(fakeTarget); + if (previous != null) { + removeFromControlMaps(previous, type, true /* fake */); + mPendingControlChanged.add(previous); + } + if (fakeTarget != null) { + addToControlMaps(fakeTarget, type, true /* fake */); + mPendingControlChanged.add(fakeTarget); + } + } + private void removeFromControlMaps(@NonNull InsetsControlTarget target, - @InternalInsetType int type) { + @InternalInsetType int type, boolean fake) { final ArrayList<Integer> array = mControlTargetTypeMap.get(target); if (array == null) { return; @@ -194,15 +231,23 @@ class InsetsStateController { if (array.isEmpty()) { mControlTargetTypeMap.remove(target); } - mTypeControlTargetMap.remove(type); + if (fake) { + mTypeFakeControlTargetMap.remove(type); + } else { + mTypeControlTargetMap.remove(type); + } } private void addToControlMaps(@NonNull InsetsControlTarget target, - @InternalInsetType int type) { + @InternalInsetType int type, boolean fake) { final ArrayList<Integer> array = mControlTargetTypeMap.computeIfAbsent(target, key -> new ArrayList<>()); array.add(type); - mTypeControlTargetMap.put(type, target); + if (fake) { + mTypeFakeControlTargetMap.put(type, target); + } else { + mTypeControlTargetMap.put(type, target); + } } void notifyControlChanged(InsetsControlTarget target) { diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java index caf87cd6a906..b30da5e156e2 100644 --- a/services/core/java/com/android/server/wm/LockTaskController.java +++ b/services/core/java/com/android/server/wm/LockTaskController.java @@ -280,13 +280,6 @@ public class LockTaskController { } /** - * @return true if currently in the lock task mode, otherwise, return false. - */ - boolean isInLockTaskMode() { - return !mLockTaskModeTasks.isEmpty(); - } - - /** * @return whether the requested task is disallowed to be launched. */ boolean isLockTaskModeViolation(TaskRecord task) { diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java index d9e30a2da9a5..eb6b51e3388b 100644 --- a/services/core/java/com/android/server/wm/RootActivityContainer.java +++ b/services/core/java/com/android/server/wm/RootActivityContainer.java @@ -1615,7 +1615,8 @@ class RootActivityContainer extends ConfigurationContainer <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r, @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop) { - return getLaunchStack(r, options, candidateTask, onTop, null /* launchParams */); + return getLaunchStack(r, options, candidateTask, onTop, null /* launchParams */, + -1 /* no realCallingPid */, -1 /* no realCallingUid */); } /** @@ -1624,13 +1625,16 @@ class RootActivityContainer extends ConfigurationContainer * @param r The activity we are trying to launch. Can be null. * @param options The activity options used to the launch. Can be null. * @param candidateTask The possible task the activity might be launched in. Can be null. - * @params launchParams The resolved launch params to use. + * @param launchParams The resolved launch params to use. + * @param realCallingPid The pid from {@link ActivityStarter#setRealCallingPid} + * @param realCallingUid The uid from {@link ActivityStarter#setRealCallingUid} * * @return The stack to use for the launch or INVALID_STACK_ID. */ <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r, @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop, - @Nullable LaunchParamsController.LaunchParams launchParams) { + @Nullable LaunchParamsController.LaunchParams launchParams, int realCallingPid, + int realCallingUid) { int taskId = INVALID_TASK_ID; int displayId = INVALID_DISPLAY; //Rect bounds = null; @@ -1661,7 +1665,15 @@ class RootActivityContainer extends ConfigurationContainer if (launchParams != null && launchParams.mPreferredDisplayId != INVALID_DISPLAY) { displayId = launchParams.mPreferredDisplayId; } - if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) { + final boolean canLaunchOnDisplayFromStartRequest = + realCallingPid != 0 && realCallingUid > 0 && r != null + && mStackSupervisor.canPlaceEntityOnDisplay(displayId, realCallingPid, + realCallingUid, r.info); + // Checking if the activity's launch caller, or the realCallerId of the activity from + // start request (i.e. entity that invokes PendingIntent) is allowed to launch on the + // display. + if (displayId != INVALID_DISPLAY && (canLaunchOnDisplay(r, displayId) + || canLaunchOnDisplayFromStartRequest)) { if (r != null) { stack = (T) getValidLaunchStackOnDisplay(displayId, r, candidateTask, options, launchParams); diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 4a76042a2c66..72bb355146d1 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -24,6 +24,7 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -227,8 +228,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { @Override public void finishDrawing(IWindow window, @Nullable SurfaceControl.Transaction postDrawTransaction) { - if (WindowManagerService.localLOGV) Slog.v( - TAG_WM, "IWindow finishDrawing called for " + window); + if (DEBUG) Slog.v(TAG_WM, "IWindow finishDrawing called for " + window); mService.finishDrawingWindow(this, window, postDrawTransaction); } @@ -474,8 +474,9 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { mPackageName = packageName; mRelayoutTag = "relayoutWindow: " + mPackageName; if (mSurfaceSession == null) { - if (WindowManagerService.localLOGV) Slog.v( - TAG_WM, "First window added to " + this + ", creating SurfaceSession"); + if (DEBUG) { + Slog.v(TAG_WM, "First window added to " + this + ", creating SurfaceSession"); + } mSurfaceSession = new SurfaceSession(); if (SHOW_TRANSACTIONS) Slog.i( TAG_WM, " NEW SURFACE SESSION " + mSurfaceSession); @@ -565,8 +566,10 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { return; } - if (WindowManagerService.localLOGV) Slog.v(TAG_WM, "Last window removed from " + this - + ", destroying " + mSurfaceSession); + if (DEBUG) { + Slog.v(TAG_WM, "Last window removed from " + this + + ", destroying " + mSurfaceSession); + } if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " KILL SURFACE SESSION " + mSurfaceSession); try { mSurfaceSession.kill(); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index a7f6688f1ce7..607a013abc51 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -297,7 +297,6 @@ public class WindowManagerService extends IWindowManager.Stub static final int LAYOUT_REPEAT_THRESHOLD = 4; static final boolean PROFILE_ORIENTATION = false; - static final boolean localLOGV = DEBUG; /** How much to multiply the policy's type layer, to reserve room * for multiple windows of the same type and Z-ordering adjustment @@ -1217,7 +1216,8 @@ public class WindowManagerService extends IWindowManager.Stub mPropertiesChangedListener = properties -> { synchronized (mGlobalLock) { final int exclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP, - properties.getInt(KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0)); + DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER, + KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0)); final boolean excludedByPreQSticky = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false); @@ -1671,8 +1671,10 @@ public class WindowManagerService extends IWindowManager.Stub } displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/); - if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addWindow: New client " - + client.asBinder() + ": window=" + win + " Callers=" + Debug.getCallers(5)); + if (DEBUG || DEBUG_ADD_REMOVE) { + Slog.v(TAG_WM, "addWindow: New client " + client.asBinder() + + ": window=" + win + " Callers=" + Debug.getCallers(5)); + } if (win.isVisibleOrAdding() && displayContent.updateOrientation()) { displayContent.sendNewConfiguration(); @@ -2339,16 +2341,18 @@ public class WindowManagerService extends IWindowManager.Stub outCutout.set(win.getWmDisplayCutout().getDisplayCutout()); outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw())); outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win)); - if (localLOGV) Slog.v( - TAG_WM, "Relayout given client " + client.asBinder() - + ", requestedWidth=" + requestedWidth - + ", requestedHeight=" + requestedHeight - + ", viewVisibility=" + viewVisibility - + "\nRelayout returning frame=" + outFrame - + ", surface=" + outSurfaceControl); + if (DEBUG) { + Slog.v(TAG_WM, "Relayout given client " + client.asBinder() + + ", requestedWidth=" + requestedWidth + + ", requestedHeight=" + requestedHeight + + ", viewVisibility=" + viewVisibility + + "\nRelayout returning frame=" + outFrame + + ", surface=" + outSurfaceControl); + } - if (localLOGV || DEBUG_FOCUS) Slog.v( - TAG_WM, "Relayout of " + win + ": focusMayChange=" + focusMayChange); + if (DEBUG || DEBUG_FOCUS) { + Slog.v(TAG_WM, "Relayout of " + win + ": focusMayChange=" + focusMayChange); + } result |= mInTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0; @@ -5227,7 +5231,7 @@ public class WindowManagerService extends IWindowManager.Stub final WindowState windowForClientLocked(Session session, IBinder client, boolean throwOnError) { WindowState win = mWindowMap.get(client); - if (localLOGV) Slog.v(TAG_WM, "Looking up client " + client + ": " + win); + if (DEBUG) Slog.v(TAG_WM, "Looking up client " + client + ": " + win); if (win == null) { if (throwOnError) { throw new IllegalArgumentException( @@ -7745,4 +7749,64 @@ public class WindowManagerService extends IWindowManager.Stub // InputDispatcher hold the last ref. inputChannel.release(); } + + /** Return whether layer tracing is enabled */ + public boolean isLayerTracing() { + mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.DUMP, + "isLayerTracing"); + long token = Binder.clearCallingIdentity(); + try { + Parcel data = null; + Parcel reply = null; + try { + IBinder sf = ServiceManager.getService("SurfaceFlinger"); + if (sf != null) { + reply = Parcel.obtain(); + data = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + sf.transact(/* LAYER_TRACE_STATUS_CODE */ 1026, data, reply, 0 /* flags */); + return reply.readBoolean(); + } + } catch (RemoteException e) { + Slog.e(TAG, "Failed to get layer tracing"); + } finally { + if (data != null) { + data.recycle(); + } + if (reply != null) { + reply.recycle(); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + return false; + } + + /** Enable or disable layer tracing */ + public void setLayerTracing(boolean enabled) { + mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.DUMP, + "setLayerTracing"); + long token = Binder.clearCallingIdentity(); + try { + Parcel data = null; + try { + IBinder sf = ServiceManager.getService("SurfaceFlinger"); + if (sf != null) { + data = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + data.writeInt(enabled ? 1 : 0); + sf.transact(/* LAYER_TRACE_CONTROL_CODE */ 1025, data, null, 0 /* flags */); + } + } catch (RemoteException e) { + Slog.e(TAG, "Failed to set layer tracing"); + } finally { + if (data != null) { + data.recycle(); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index cbb0b3aab687..99ae18d67be5 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -90,6 +90,7 @@ import static com.android.server.wm.IdentifierProto.USER_ID; import static com.android.server.wm.MoveAnimationSpecProto.DURATION_MS; import static com.android.server.wm.MoveAnimationSpecProto.FROM; import static com.android.server.wm.MoveAnimationSpecProto.TO; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; @@ -115,7 +116,6 @@ import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES; import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT; -import static com.android.server.wm.WindowManagerService.localLOGV; import static com.android.server.wm.WindowStateAnimator.COMMIT_DRAW_PENDING; import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING; import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN; @@ -755,9 +755,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mSeq = seq; mPowerManagerWrapper = powerManagerWrapper; mForceSeamlesslyRotate = token.mRoundedCornerOverlay; - if (localLOGV) Slog.v( - TAG, "Window " + this + " client=" + c.asBinder() - + " token=" + token + " (" + mAttrs.token + ")" + " params=" + a); + if (DEBUG) { + Slog.v(TAG, "Window " + this + " client=" + c.asBinder() + + " token=" + token + " (" + mAttrs.token + ")" + " params=" + a); + } try { c.asBinder().linkToDeath(deathRecipient, 0); } catch (RemoteException e) { @@ -825,7 +826,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } void attach() { - if (localLOGV) Slog.v(TAG, "Attaching " + this + " token=" + mToken); + if (DEBUG) Slog.v(TAG, "Attaching " + this + " token=" + mToken); mSession.windowAddedLocked(mAttrs.packageName); } @@ -1125,13 +1126,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } - if (DEBUG_LAYOUT || localLOGV) Slog.v(TAG, - "Resolving (mRequestedWidth=" - + mRequestedWidth + ", mRequestedheight=" - + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph - + "): frame=" + mWindowFrames.mFrame.toShortString() - + " " + mWindowFrames.getInsetsInfo() - + " " + mAttrs.getTitle()); + if (DEBUG_LAYOUT || DEBUG) { + Slog.v(TAG, "Resolving (mRequestedWidth=" + + mRequestedWidth + ", mRequestedheight=" + + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph + + "): frame=" + mWindowFrames.mFrame.toShortString() + + " " + mWindowFrames.getInsetsInfo() + + " " + mAttrs.getTitle()); + } } // TODO: Look into whether this override is still necessary. @@ -1280,9 +1282,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final boolean dragResizingChanged = isDragResizeChanged() && !isDragResizingChangeReported(); - if (localLOGV) Slog.v(TAG_WM, "Resizing " + this + ": configChanged=" + configChanged - + " dragResizingChanged=" + dragResizingChanged - + " last=" + mWindowFrames.mLastFrame + " frame=" + mWindowFrames.mFrame); + if (DEBUG) { + Slog.v(TAG_WM, "Resizing " + this + ": configChanged=" + configChanged + + " dragResizingChanged=" + dragResizingChanged + + " last=" + mWindowFrames.mLastFrame + " frame=" + mWindowFrames.mFrame); + } // We update mLastFrame always rather than in the conditional with the last inset // variables, because mFrameSizeChanged only tracks the width and height changing. @@ -1979,11 +1983,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (startingWindow && DEBUG_STARTING_WINDOW) Slog.d(TAG_WM, "Starting window removed " + this); - if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && isFocused()) + if (DEBUG || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && isFocused()) { Slog.v(TAG_WM, "Remove " + this + " client=" - + Integer.toHexString(System.identityHashCode(mClient.asBinder())) - + ", surfaceController=" + mWinAnimator.mSurfaceController + " Callers=" - + Debug.getCallers(5)); + + Integer.toHexString(System.identityHashCode(mClient.asBinder())) + + ", surfaceController=" + mWinAnimator.mSurfaceController + " Callers=" + + Debug.getCallers(5)); + } final long origId = Binder.clearCallingIdentity(); @@ -4377,8 +4382,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return; } - if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG, - "Exit animation finished in " + this + ": remove=" + mRemoveOnExit); + if (DEBUG || DEBUG_ADD_REMOVE) { + Slog.v(TAG, "Exit animation finished in " + this + ": remove=" + mRemoveOnExit); + } mDestroying = true; diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 6dfbc36ce6f7..ef1d110c9617 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -27,6 +27,7 @@ import static android.view.WindowManager.TRANSIT_NONE; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; @@ -540,8 +541,10 @@ class WindowStateAnimator { return null; } - if (WindowManagerService.localLOGV) Slog.v(TAG, "Got surface: " + mSurfaceController - + ", set left=" + w.getFrameLw().left + " top=" + w.getFrameLw().top); + if (DEBUG) { + Slog.v(TAG, "Got surface: " + mSurfaceController + + ", set left=" + w.getFrameLw().left + " top=" + w.getFrameLw().top); + } if (SHOW_LIGHT_TRANSACTIONS) { Slog.i(TAG, ">>> OPEN TRANSACTION createSurfaceLocked"); @@ -552,7 +555,7 @@ class WindowStateAnimator { mLastHidden = true; - if (WindowManagerService.localLOGV) Slog.v(TAG, "Created surface " + this); + if (DEBUG) Slog.v(TAG, "Created surface " + this); return mSurfaceController; } @@ -745,11 +748,11 @@ class WindowStateAnimator { mShownAlpha *= screenRotationAnimation.getEnterTransformation().getAlpha(); } - if ((DEBUG_ANIM || WindowManagerService.localLOGV) - && (mShownAlpha == 1.0 || mShownAlpha == 0.0)) Slog.v( - TAG, "computeShownFrameLocked: Animating " + this + " mAlpha=" + mAlpha - + " screen=" + (screenAnimation ? - screenRotationAnimation.getEnterTransformation().getAlpha() : "null")); + if ((DEBUG_ANIM || DEBUG) && (mShownAlpha == 1.0 || mShownAlpha == 0.0)) { + Slog.v(TAG, "computeShownFrameLocked: Animating " + this + " mAlpha=" + mAlpha + + " screen=" + (screenAnimation + ? screenRotationAnimation.getEnterTransformation().getAlpha() : "null")); + } return; } else if (mIsWallpaper && mService.mRoot.mWallpaperActionPending) { return; @@ -762,9 +765,10 @@ class WindowStateAnimator { return; } - if (WindowManagerService.localLOGV) Slog.v( - TAG, "computeShownFrameLocked: " + this + - " not attached, mAlpha=" + mAlpha); + if (DEBUG) { + Slog.v(TAG, "computeShownFrameLocked: " + this + + " not attached, mAlpha=" + mAlpha); + } mShownAlpha = mAlpha; mHaveMatrix = false; diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 97b20472b12d..d9c7fed0ff00 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -57,12 +57,15 @@ cc_library_static { ], include_dirs: [ - "bionic/libc/private", "frameworks/base/libs", "frameworks/native/services", "system/gatekeeper/include", ], + header_libs: [ + "bionic_libc_platform_headers", + ], + product_variables: { arc: { exclude_srcs: [ diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp index 159a4960731d..78b64ca072ad 100644 --- a/services/core/jni/com_android_server_SystemServer.cpp +++ b/services/core/jni/com_android_server_SystemServer.cpp @@ -24,7 +24,7 @@ #include <sensorservice/SensorService.h> #include <sensorservicehidl/SensorManager.h> -#include <bionic_malloc.h> +#include <bionic/malloc.h> #include <cutils/properties.h> #include <utils/Log.h> diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 478bc88fe815..3154c7021255 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -1999,6 +1999,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return LocalServices.getService(LockSettingsInternal.class); } + boolean hasUserSetupCompleted(DevicePolicyData userData) { + return userData.mUserSetupComplete; + } + boolean isBuildDebuggable() { return Build.IS_DEBUGGABLE; } @@ -8271,7 +8275,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return true; } - return getUserData(userHandle).mUserSetupComplete; + return mInjector.hasUserSetupCompleted(getUserData(userHandle)); } private boolean hasPaired(int userHandle) { diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index ad94e6159b87..8699669bf4a5 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -25,6 +25,7 @@ android_test { "mockito-target-extended-minus-junit4", "platform-test-annotations", "truth-prebuilt", + "testables", ], libs: [ diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java new file mode 100644 index 000000000000..307092d24d84 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java @@ -0,0 +1,364 @@ +/* + * 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.server.wallpaper; + +import static android.app.WallpaperManager.FLAG_SYSTEM; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + +import static org.hamcrest.core.IsNot.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.reset; + +import android.app.AppGlobals; +import android.app.AppOpsManager; +import android.app.WallpaperManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.IPackageManager; +import android.content.pm.PackageManager; +import android.content.pm.ParceledListSlice; +import android.content.pm.ServiceInfo; +import android.hardware.display.DisplayManager; +import android.os.UserHandle; +import android.platform.test.annotations.Presubmit; +import android.service.wallpaper.IWallpaperConnection; +import android.service.wallpaper.WallpaperService; +import android.testing.TestableContext; +import android.util.Log; +import android.util.SparseArray; +import android.view.Display; + +import androidx.test.filters.FlakyTest; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.dx.mockito.inline.extended.StaticMockitoSession; +import com.android.internal.R; +import com.android.server.LocalServices; +import com.android.server.wallpaper.WallpaperManagerService.WallpaperData; +import com.android.server.wm.WindowManagerInternal; + +import org.hamcrest.CoreMatchers; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.quality.Strictness; + +import java.io.File; +import java.io.IOException; + +/** + * Tests for the {@link WallpaperManagerService} class. + * + * Build/Install/Run: + * atest FrameworksMockingServicesTests:WallpaperManagerServiceTests + */ +@Presubmit +@FlakyTest(bugId = 129797242) +@RunWith(AndroidJUnit4.class) +public class WallpaperManagerServiceTests { + private static final int DISPLAY_SIZE_DIMENSION = 100; + private static StaticMockitoSession sMockitoSession; + + @ClassRule + public static final TestableContext sContext = new TestableContext( + InstrumentationRegistry.getInstrumentation().getTargetContext(), null); + private static ComponentName sImageWallpaperComponentName; + private static ComponentName sDefaultWallpaperComponent; + + private IPackageManager mIpm = AppGlobals.getPackageManager(); + + @Mock + private DisplayManager mDisplayManager; + + @Rule + public final TemporaryFolder mFolder = new TemporaryFolder(); + private final SparseArray<File> mTempDirs = new SparseArray<>(); + private WallpaperManagerService mService; + + @BeforeClass + public static void setUpClass() { + sMockitoSession = mockitoSession() + .strictness(Strictness.LENIENT) + .spyStatic(LocalServices.class) + .spyStatic(WallpaperManager.class) + .startMocking(); + + final WindowManagerInternal dmi = mock(WindowManagerInternal.class); + LocalServices.addService(WindowManagerInternal.class, dmi); + + sContext.addMockSystemService(Context.APP_OPS_SERVICE, mock(AppOpsManager.class)); + + spyOn(sContext); + sContext.getTestablePermissions().setPermission( + android.Manifest.permission.SET_WALLPAPER_COMPONENT, + PackageManager.PERMISSION_GRANTED); + sContext.getTestablePermissions().setPermission( + android.Manifest.permission.SET_WALLPAPER, + PackageManager.PERMISSION_GRANTED); + doNothing().when(sContext).sendBroadcastAsUser(any(), any()); + + //Wallpaper components + final IWallpaperConnection.Stub wallpaperService = mock(IWallpaperConnection.Stub.class); + sImageWallpaperComponentName = ComponentName.unflattenFromString( + sContext.getResources().getString(R.string.image_wallpaper_component)); + // Mock default wallpaper as image wallpaper if there is no pre-defined default wallpaper. + sDefaultWallpaperComponent = WallpaperManager.getDefaultWallpaperComponent(sContext); + + if (sDefaultWallpaperComponent == null) { + sDefaultWallpaperComponent = sImageWallpaperComponentName; + doReturn(sImageWallpaperComponentName).when(() -> + WallpaperManager.getDefaultWallpaperComponent(any())); + } else { + sContext.addMockService(sDefaultWallpaperComponent, wallpaperService); + } + + sContext.addMockService(sImageWallpaperComponentName, wallpaperService); + } + + @AfterClass + public static void tearDownClass() { + if (sMockitoSession != null) { + sMockitoSession.finishMocking(); + sMockitoSession = null; + } + LocalServices.removeServiceForTest(WindowManagerInternal.class); + sImageWallpaperComponentName = null; + sDefaultWallpaperComponent = null; + reset(sContext); + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + sContext.addMockSystemService(DisplayManager.class, mDisplayManager); + + final Display mockDisplay = mock(Display.class); + doReturn(DISPLAY_SIZE_DIMENSION).when(mockDisplay).getMaximumSizeDimension(); + doReturn(mockDisplay).when(mDisplayManager).getDisplay(anyInt()); + + final Display[] displays = new Display[]{mockDisplay}; + doReturn(displays).when(mDisplayManager).getDisplays(); + + spyOn(mIpm); + mService = new TestWallpaperManagerService(sContext); + spyOn(mService); + mService.systemReady(); + } + + @After + public void tearDown() { + LocalServices.removeServiceForTest(WallpaperManagerInternal.class); + + mTempDirs.clear(); + reset(mIpm); + mService = null; + } + + protected class TestWallpaperManagerService extends WallpaperManagerService { + private static final String TAG = "TestWallpaperManagerService"; + + TestWallpaperManagerService(Context context) { + super(context); + } + + @Override + File getWallpaperDir(int userId) { + File tempDir = mTempDirs.get(userId); + if (tempDir == null) { + try { + tempDir = mFolder.newFolder(String.valueOf(userId)); + mTempDirs.append(userId, tempDir); + } catch (IOException e) { + Log.e(TAG, "getWallpaperDir failed at userId= " + userId); + } + } + return tempDir; + } + + // Always return true for test + @Override + public boolean isWallpaperSupported(String callingPackage) { + return true; + } + + // Always return true for test + @Override + public boolean isSetWallpaperAllowed(String callingPackage) { + return true; + } + } + + /** + * Tests that internal basic data should be correct after boot up. + */ + @Test + public void testDataCorrectAfterBoot() { + mService.switchUser(UserHandle.USER_SYSTEM, null); + + final WallpaperData fallbackData = mService.mFallbackWallpaper; + assertEquals("Fallback wallpaper component should be ImageWallpaper.", + sImageWallpaperComponentName, fallbackData.wallpaperComponent); + + verifyLastWallpaperData(UserHandle.USER_SYSTEM, sDefaultWallpaperComponent); + verifyDisplayData(); + } + + /** + * Tests setWallpaperComponent and clearWallpaper should work as expected. + */ + @Test + public void testSetThenClearComponent() { + // Skip if there is no pre-defined default wallpaper component. + assumeThat(sDefaultWallpaperComponent, + not(CoreMatchers.equalTo(sImageWallpaperComponentName))); + + final int testUserId = UserHandle.USER_SYSTEM; + mService.switchUser(testUserId, null); + verifyLastWallpaperData(testUserId, sDefaultWallpaperComponent); + verifyCurrentSystemData(testUserId); + + mService.setWallpaperComponent(sImageWallpaperComponentName); + verifyLastWallpaperData(testUserId, sImageWallpaperComponentName); + verifyCurrentSystemData(testUserId); + + mService.clearWallpaper(null, FLAG_SYSTEM, testUserId); + verifyLastWallpaperData(testUserId, sDefaultWallpaperComponent); + verifyCurrentSystemData(testUserId); + } + + /** + * Tests internal data should be correct and no crash after switch user continuously. + */ + @Test + public void testSwitchMultipleUsers() throws Exception { + final int lastUserId = 5; + final ServiceInfo pi = mIpm.getServiceInfo(sDefaultWallpaperComponent, + PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, 0); + doReturn(pi).when(mIpm).getServiceInfo(any(), anyInt(), anyInt()); + + final Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE); + final ParceledListSlice ris = + mIpm.queryIntentServices(intent, + intent.resolveTypeIfNeeded(sContext.getContentResolver()), + PackageManager.GET_META_DATA, 0); + doReturn(ris).when(mIpm).queryIntentServices(any(), any(), anyInt(), anyInt()); + doReturn(PackageManager.PERMISSION_GRANTED).when(mIpm).checkPermission( + eq(android.Manifest.permission.AMBIENT_WALLPAPER), any(), anyInt()); + + for (int userId = 0; userId <= lastUserId; userId++) { + mService.switchUser(userId, null); + verifyLastWallpaperData(userId, sDefaultWallpaperComponent); + verifyCurrentSystemData(userId); + } + verifyNoConnectionBeforeLastUser(lastUserId); + } + + /** + * Tests internal data should be correct and no crash after switch user + unlock user + * continuously. + * Simulating that the selected WallpaperService is not built-in. After switching users, the + * service should not be bound, but bound to the image wallpaper. After receiving the user + * unlock callback and can find the selected service for the user, the selected service should + * be bound. + */ + @Test + public void testSwitchThenUnlockMultipleUsers() throws Exception { + final int lastUserId = 5; + final ServiceInfo pi = mIpm.getServiceInfo(sDefaultWallpaperComponent, + PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, 0); + doReturn(pi).when(mIpm).getServiceInfo(any(), anyInt(), anyInt()); + + final Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE); + final ParceledListSlice ris = + mIpm.queryIntentServices(intent, + intent.resolveTypeIfNeeded(sContext.getContentResolver()), + PackageManager.GET_META_DATA, 0); + doReturn(PackageManager.PERMISSION_GRANTED).when(mIpm).checkPermission( + eq(android.Manifest.permission.AMBIENT_WALLPAPER), any(), anyInt()); + + for (int userId = 1; userId <= lastUserId; userId++) { + mService.switchUser(userId, null); + verifyLastWallpaperData(userId, sImageWallpaperComponentName); + // Simulate user unlocked + doReturn(ris).when(mIpm).queryIntentServices(any(), any(), anyInt(), eq(userId)); + mService.onUnlockUser(userId); + verifyLastWallpaperData(userId, sDefaultWallpaperComponent); + verifyCurrentSystemData(userId); + } + verifyNoConnectionBeforeLastUser(lastUserId); + verifyDisplayData(); + } + + // Verify that after continue switch user from userId 0 to lastUserId, the wallpaper data for + // non-current user must not bind to wallpaper service. + private void verifyNoConnectionBeforeLastUser(int lastUserId) { + for (int i = 0; i < lastUserId; i++) { + final WallpaperData userData = mService.getCurrentWallpaperData(FLAG_SYSTEM, i); + assertNull("No user data connection left", userData.connection); + } + } + + private void verifyLastWallpaperData(int lastUserId, ComponentName expectedComponent) { + final WallpaperData lastData = mService.mLastWallpaper; + assertNotNull("Last wallpaper must not be null", lastData); + assertEquals("Last wallpaper component must be equals.", expectedComponent, + lastData.wallpaperComponent); + assertEquals("The user id in last wallpaper should be the last switched user", + lastUserId, lastData.userId); + assertNotNull("Must exist user data connection on last wallpaper data", + lastData.connection); + } + + private void verifyCurrentSystemData(int userId) { + final WallpaperData lastData = mService.mLastWallpaper; + final WallpaperData wallpaper = mService.getCurrentWallpaperData(FLAG_SYSTEM, userId); + assertEquals("Last wallpaper should be equals to current system wallpaper", + lastData, wallpaper); + } + + private void verifyDisplayData() { + mService.forEachDisplayData(data -> { + assertTrue("Display width must larger than maximum screen size", + data.mWidth >= DISPLAY_SIZE_DIMENSION); + assertTrue("Display height must larger than maximum screen size", + data.mHeight >= DISPLAY_SIZE_DIMENSION); + }); + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java b/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java index bd3d9ab2220d..3852b9fec001 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java @@ -17,6 +17,7 @@ package com.android.server.pm; import android.content.Context; import android.content.pm.ModuleInfo; +import android.content.pm.PackageManager; import android.test.InstrumentationTestCase; import com.android.frameworks.servicestests.R; @@ -28,7 +29,7 @@ public class ModuleInfoProviderTest extends InstrumentationTestCase { public void testSuccessfulParse() { ModuleInfoProvider provider = getProvider(R.xml.well_formed_metadata); - List<ModuleInfo> mi = provider.getInstalledModules(0); + List<ModuleInfo> mi = provider.getInstalledModules(PackageManager.MATCH_ALL); assertEquals(2, mi.size()); Collections.sort(mi, (ModuleInfo m1, ModuleInfo m2) -> @@ -49,18 +50,18 @@ public class ModuleInfoProviderTest extends InstrumentationTestCase { public void testParseFailure_incorrectTopLevelElement() { ModuleInfoProvider provider = getProvider(R.xml.unparseable_metadata1); - assertEquals(0, provider.getInstalledModules(0).size()); + assertEquals(0, provider.getInstalledModules(PackageManager.MATCH_ALL).size()); } public void testParseFailure_incorrectModuleElement() { ModuleInfoProvider provider = getProvider(R.xml.unparseable_metadata2); - assertEquals(0, provider.getInstalledModules(0).size()); + assertEquals(0, provider.getInstalledModules(PackageManager.MATCH_ALL).size()); } public void testParse_unknownAttributesIgnored() { ModuleInfoProvider provider = getProvider(R.xml.well_formed_metadata); - List<ModuleInfo> mi = provider.getInstalledModules(0); + List<ModuleInfo> mi = provider.getInstalledModules(PackageManager.MATCH_ALL); assertEquals(2, mi.size()); ModuleInfo mi1 = provider.getModuleInfo("com.android.module1", 0); diff --git a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java index 5ae043402526..2290ef79da78 100644 --- a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java @@ -18,7 +18,10 @@ package com.android.server.pm; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_IGNORED; +import static android.app.AppOpsManager.OP_CAMERA; import static android.app.AppOpsManager.OP_PLAY_AUDIO; +import static android.app.AppOpsManager.OP_RECORD_AUDIO; +import static android.app.AppOpsManager.opToName; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -39,7 +42,6 @@ import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.SuspendDialogInfo; import android.content.res.Resources; -import android.media.AudioAttributes; import android.os.BaseBundle; import android.os.Bundle; import android.os.Handler; @@ -553,28 +555,42 @@ public class SuspendPackagesTest { } @Test - public void testAudioOpBlockedOnSuspend() throws Exception { + public void testCameraBlockedOnSuspend() throws Exception { + assertOpBlockedOnSuspend(OP_CAMERA); + } + + @Test + public void testPlayAudioBlockedOnSuspend() throws Exception { + assertOpBlockedOnSuspend(OP_PLAY_AUDIO); + } + + @Test + public void testRecordAudioBlockedOnSuspend() throws Exception { + assertOpBlockedOnSuspend(OP_RECORD_AUDIO); + } + + private void assertOpBlockedOnSuspend(int code) throws Exception { final IAppOpsService iAppOps = IAppOpsService.Stub.asInterface( ServiceManager.getService(Context.APP_OPS_SERVICE)); final CountDownLatch latch = new CountDownLatch(1); final IAppOpsCallback watcher = new IAppOpsCallback.Stub() { @Override public void opChanged(int op, int uid, String packageName) { - if (op == OP_PLAY_AUDIO && packageName.equals(TEST_APP_PACKAGE_NAME)) { + if (op == code && packageName.equals(TEST_APP_PACKAGE_NAME)) { latch.countDown(); } } }; - iAppOps.startWatchingMode(OP_PLAY_AUDIO, TEST_APP_PACKAGE_NAME, watcher); + iAppOps.startWatchingMode(code, TEST_APP_PACKAGE_NAME, watcher); final int testPackageUid = mPackageManager.getPackageUid(TEST_APP_PACKAGE_NAME, 0); - int audioOpMode = iAppOps.checkAudioOperation(OP_PLAY_AUDIO, - AudioAttributes.USAGE_UNKNOWN, testPackageUid, TEST_APP_PACKAGE_NAME); - assertEquals("Audio muted for unsuspended package", MODE_ALLOWED, audioOpMode); + int opMode = iAppOps.checkOperation(code, testPackageUid, TEST_APP_PACKAGE_NAME); + assertEquals("Op " + opToName(code) + " disallowed for unsuspended package", MODE_ALLOWED, + opMode); suspendTestPackage(null, null, null); assertTrue("AppOpsWatcher did not callback", latch.await(5, TimeUnit.SECONDS)); - audioOpMode = iAppOps.checkAudioOperation(OP_PLAY_AUDIO, - AudioAttributes.USAGE_UNKNOWN, testPackageUid, TEST_APP_PACKAGE_NAME); - assertEquals("Audio not muted for suspended package", MODE_IGNORED, audioOpMode); + opMode = iAppOps.checkOperation(code, testPackageUid, TEST_APP_PACKAGE_NAME); + assertEquals("Op " + opToName(code) + " allowed for suspended package", MODE_IGNORED, + opMode); iAppOps.stopWatchingMode(watcher); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 34cc0c742005..8393ae0c3aec 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -354,7 +354,7 @@ public class ActivityStarterTests extends ActivityTestsBase { doReturn(stack).when(mRootActivityContainer) .getLaunchStack(any(), any(), any(), anyBoolean()); doReturn(stack).when(mRootActivityContainer) - .getLaunchStack(any(), any(), any(), anyBoolean(), any()); + .getLaunchStack(any(), any(), any(), anyBoolean(), any(), anyInt(), anyInt()); } // Set up mock package manager internal and make sure no unmocked methods are called @@ -501,7 +501,6 @@ public class ActivityStarterTests extends ActivityTestsBase { final ActivityStarter starter = prepareStarter(0); final LockTaskController lockTaskController = mService.getLockTaskController(); - doReturn(true).when(lockTaskController).isInLockTaskMode(); doReturn(true).when(lockTaskController).isLockTaskModeViolation(any()); final int result = starter.setReason("testTaskModeViolation").execute(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java index 1731f7cdd59c..d311dfc60675 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -139,6 +139,8 @@ class ActivityTestsBase { private int mScreenOrientation = SCREEN_ORIENTATION_UNSPECIFIED; private boolean mLaunchTaskBehind; private int mConfigChanges; + private int mLaunchedFromPid; + private int mLaunchedFromUid; ActivityBuilder(ActivityTaskManagerService service) { mService = service; @@ -214,6 +216,16 @@ class ActivityTestsBase { return this; } + ActivityBuilder setLaunchedFromPid(int pid) { + mLaunchedFromPid = pid; + return this; + } + + ActivityBuilder setLaunchedFromUid(int uid) { + mLaunchedFromUid = uid; + return this; + } + ActivityRecord build() { if (mComponent == null) { final int id = sCurrentActivityId++; @@ -250,10 +262,11 @@ class ActivityTestsBase { } final ActivityRecord activity = new ActivityRecord(mService, null /* caller */, - 0 /* launchedFromPid */, 0, null, intent, null, - aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */, - 0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */, - mService.mStackSupervisor, options, null /* sourceRecord */); + mLaunchedFromPid /* launchedFromPid */, mLaunchedFromUid /* launchedFromUid */, + null, intent, null, aInfo /*aInfo*/, new Configuration(), null /* resultTo */, + null /* resultWho */, 0 /* reqCode */, false /*componentSpecified*/, + false /* rootVoiceInteraction */, mService.mStackSupervisor, options, + null /* sourceRecord */); spyOn(activity); if (mTaskRecord != null) { // fullscreen value is normally read from resources in ctor, so for testing we need 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 86ee75ebf3df..3e2e4382a68c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java @@ -31,14 +31,15 @@ import android.platform.test.annotations.Presubmit; import android.view.InsetsSource; import android.view.InsetsState; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; @SmallTest @Presubmit +@RunWith(WindowTestRunner.class) public class InsetsSourceProviderTest extends WindowTestsBase { private InsetsSource mSource = new InsetsSource(TYPE_TOP_BAR); @@ -53,7 +54,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase { @Test public void testPostLayout() { - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar"); topBar.getFrameLw().set(0, 0, 500, 100); topBar.mHasSurface = true; mProvider.setWindow(topBar, null); @@ -66,7 +67,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase { @Test public void testPostLayout_invisible() { - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar"); topBar.getFrameLw().set(0, 0, 500, 100); mProvider.setWindow(topBar, null); mProvider.onPostLayout(); @@ -76,7 +77,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase { @Test public void testPostLayout_frameProvider() { - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar"); topBar.getFrameLw().set(0, 0, 500, 100); mProvider.setWindow(topBar, (displayFrames, windowState, rect) -> { @@ -88,19 +89,32 @@ public class InsetsSourceProviderTest extends WindowTestsBase { @Test public void testUpdateControlForTarget() { - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar"); final WindowState target = createWindow(null, TYPE_APPLICATION, "target"); topBar.getFrameLw().set(0, 0, 500, 100); mProvider.setWindow(topBar, null); mProvider.updateControlForTarget(target, false /* force */); - assertNotNull(mProvider.getControl()); + assertNotNull(mProvider.getControl(target)); mProvider.updateControlForTarget(null, false /* force */); - assertNull(mProvider.getControl()); + assertNull(mProvider.getControl(target)); + } + + @Test + public void testUpdateControlForFakeTarget() { + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar"); + final WindowState target = createWindow(null, TYPE_APPLICATION, "target"); + topBar.getFrameLw().set(0, 0, 500, 100); + mProvider.setWindow(topBar, null); + mProvider.updateControlForFakeTarget(target); + assertNotNull(mProvider.getControl(target)); + assertNull(mProvider.getControl(target).getLeash()); + mProvider.updateControlForFakeTarget(null); + assertNull(mProvider.getControl(target)); } @Test public void testInsetsModified() { - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar"); final WindowState target = createWindow(null, TYPE_APPLICATION, "target"); topBar.getFrameLw().set(0, 0, 500, 100); mProvider.setWindow(topBar, null); @@ -113,7 +127,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase { @Test public void testInsetsModified_noControl() { - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar"); final WindowState target = createWindow(null, TYPE_APPLICATION, "target"); topBar.getFrameLw().set(0, 0, 500, 100); mProvider.setWindow(topBar, null); diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java index c67b860b656e..aa97de72e507 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -25,6 +25,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.TYPE_VIRTUAL; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; @@ -61,6 +62,7 @@ import android.content.res.Resources; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.util.Pair; +import android.view.DisplayInfo; import androidx.test.filters.MediumTest; @@ -820,6 +822,41 @@ public class RootActivityContainerTests extends ActivityTestsBase { } /** + * Test that {@link RootActivityContainer#getLaunchStack} with the real caller id will get the + * expected stack when requesting the activity launch on the secondary display. + */ + @Test + public void testGetLaunchStackWithRealCallerId() { + // Create a non-system owned virtual display. + final DisplayInfo info = new DisplayInfo(); + mSupervisor.mService.mContext.getDisplay().getDisplayInfo(info); + info.type = TYPE_VIRTUAL; + info.ownerUid = 100; + final TestActivityDisplay secondaryDisplay = TestActivityDisplay.create(mSupervisor, info); + mRootActivityContainer.addChild(secondaryDisplay, POSITION_TOP); + + // Create an activity with specify the original launch pid / uid. + final ActivityRecord r = new ActivityBuilder(mService).setLaunchedFromPid(200) + .setLaunchedFromUid(200).build(); + + // Simulate ActivityStarter to find a launch stack for requesting the activity to launch + // on the secondary display with realCallerId. + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(secondaryDisplay.mDisplayId); + options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN); + doReturn(true).when(mSupervisor).canPlaceEntityOnDisplay(secondaryDisplay.mDisplayId, + 300 /* test realCallerPid */, 300 /* test realCallerUid */, r.info); + final ActivityStack result = mRootActivityContainer.getLaunchStack(r, options, + null /* task */, true /* onTop */, null, 300 /* test realCallerPid */, + 300 /* test realCallerUid */); + + // Assert that the stack is returned as expected. + assertNotNull(result); + assertEquals("The display ID of the stack should same as secondary display ", + secondaryDisplay.mDisplayId, result.mDisplayId); + } + + /** * Mock {@link RootActivityContainer#resolveHomeActivity} for returning consistent activity * info for test cases (the original implementation will resolve from the real package manager). */ diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java index c8c55ca96e94..d7b6b5d0d36a 100644 --- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java +++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java @@ -46,6 +46,8 @@ import android.service.usb.UsbProfileGroupSettingsManagerProto; import android.service.usb.UsbSettingsAccessoryPreferenceProto; import android.service.usb.UsbSettingsDevicePreferenceProto; import android.service.usb.UserPackageProto; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.AtomicFile; import android.util.Log; import android.util.Slog; @@ -70,6 +72,7 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.net.ProtocolException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; @@ -102,10 +105,20 @@ class UsbProfileGroupSettingsManager { @GuardedBy("mLock") private final HashMap<DeviceFilter, UserPackage> mDevicePreferenceMap = new HashMap<>(); + /** Maps DeviceFilter to set of UserPackages not to ask for launch preference anymore */ + @GuardedBy("mLock") + private final ArrayMap<DeviceFilter, ArraySet<UserPackage>> mDevicePreferenceDeniedMap = + new ArrayMap<>(); + /** Maps AccessoryFilter to user preferred application package */ @GuardedBy("mLock") private final HashMap<AccessoryFilter, UserPackage> mAccessoryPreferenceMap = new HashMap<>(); + /** Maps AccessoryFilter to set of UserPackages not to ask for launch preference anymore */ + @GuardedBy("mLock") + private final ArrayMap<AccessoryFilter, ArraySet<UserPackage>> mAccessoryPreferenceDeniedMap = + new ArrayMap<>(); + private final Object mLock = new Object(); /** @@ -248,11 +261,11 @@ class UsbProfileGroupSettingsManager { } /** - * Remove all defaults for a user. + * Remove all defaults and denied packages for a user. * - * @param userToRemove The user the defaults belong to. + * @param userToRemove The user */ - void removeAllDefaultsForUser(@NonNull UserHandle userToRemove) { + void removeUser(@NonNull UserHandle userToRemove) { synchronized (mLock) { boolean needToPersist = false; Iterator<Map.Entry<DeviceFilter, UserPackage>> devicePreferenceIt = mDevicePreferenceMap @@ -277,6 +290,28 @@ class UsbProfileGroupSettingsManager { } } + int numEntries = mDevicePreferenceDeniedMap.size(); + for (int i = 0; i < numEntries; i++) { + ArraySet<UserPackage> userPackages = mDevicePreferenceDeniedMap.valueAt(i); + for (int j = userPackages.size() - 1; j >= 0; j--) { + if (userPackages.valueAt(j).user.equals(userToRemove)) { + userPackages.removeAt(j); + needToPersist = true; + } + } + } + + numEntries = mAccessoryPreferenceDeniedMap.size(); + for (int i = 0; i < numEntries; i++) { + ArraySet<UserPackage> userPackages = mAccessoryPreferenceDeniedMap.valueAt(i); + for (int j = userPackages.size() - 1; j >= 0; j--) { + if (userPackages.valueAt(j).user.equals(userToRemove)) { + userPackages.removeAt(j); + needToPersist = true; + } + } + } + if (needToPersist) { scheduleWriteSettingsLocked(); } @@ -284,7 +319,7 @@ class UsbProfileGroupSettingsManager { } private void readPreference(XmlPullParser parser) - throws XmlPullParserException, IOException { + throws IOException, XmlPullParserException { String packageName = null; // If not set, assume it to be the parent profile @@ -317,6 +352,67 @@ class UsbProfileGroupSettingsManager { XmlUtils.nextElement(parser); } + private void readPreferenceDeniedList(@NonNull XmlPullParser parser) + throws IOException, XmlPullParserException { + int outerDepth = parser.getDepth(); + if (!XmlUtils.nextElementWithin(parser, outerDepth)) { + return; + } + + if ("usb-device".equals(parser.getName())) { + DeviceFilter filter = DeviceFilter.read(parser); + while (XmlUtils.nextElementWithin(parser, outerDepth)) { + if ("user-package".equals(parser.getName())) { + try { + int userId = XmlUtils.readIntAttribute(parser, "user"); + + String packageName = XmlUtils.readStringAttribute(parser, "package"); + if (packageName == null) { + Slog.e(TAG, "Unable to parse package name"); + } + + ArraySet<UserPackage> set = mDevicePreferenceDeniedMap.get(filter); + if (set == null) { + set = new ArraySet<>(); + mDevicePreferenceDeniedMap.put(filter, set); + } + set.add(new UserPackage(packageName, UserHandle.of(userId))); + } catch (ProtocolException e) { + Slog.e(TAG, "Unable to parse user id", e); + } + } + } + } else if ("usb-accessory".equals(parser.getName())) { + AccessoryFilter filter = AccessoryFilter.read(parser); + + while (XmlUtils.nextElementWithin(parser, outerDepth)) { + if ("user-package".equals(parser.getName())) { + try { + int userId = XmlUtils.readIntAttribute(parser, "user"); + + String packageName = XmlUtils.readStringAttribute(parser, "package"); + if (packageName == null) { + Slog.e(TAG, "Unable to parse package name"); + } + + ArraySet<UserPackage> set = mAccessoryPreferenceDeniedMap.get(filter); + if (set == null) { + set = new ArraySet<>(); + mAccessoryPreferenceDeniedMap.put(filter, set); + } + set.add(new UserPackage(packageName, UserHandle.of(userId))); + } catch (ProtocolException e) { + Slog.e(TAG, "Unable to parse user id", e); + } + } + } + } + + while (parser.getDepth() > outerDepth) { + parser.nextTag(); // ignore unknown tags + } + } + /** * Upgrade any single-user settings from {@link #sSingleUserSettingsFile}. * Should only be called by owner. @@ -373,6 +469,8 @@ class UsbProfileGroupSettingsManager { String tagName = parser.getName(); if ("preference".equals(tagName)) { readPreference(parser); + } else if ("preference-denied-list".equals(tagName)) { + readPreferenceDeniedList(parser); } else { XmlUtils.nextElement(parser); } @@ -436,6 +534,46 @@ class UsbProfileGroupSettingsManager { serializer.endTag(null, "preference"); } + int numEntries = mDevicePreferenceDeniedMap.size(); + for (int i = 0; i < numEntries; i++) { + DeviceFilter filter = mDevicePreferenceDeniedMap.keyAt(i); + ArraySet<UserPackage> userPackageSet = mDevicePreferenceDeniedMap + .valueAt(i); + serializer.startTag(null, "preference-denied-list"); + filter.write(serializer); + + int numUserPackages = userPackageSet.size(); + for (int j = 0; j < numUserPackages; j++) { + UserPackage userPackage = userPackageSet.valueAt(j); + serializer.startTag(null, "user-package"); + serializer.attribute(null, "user", + String.valueOf(getSerial(userPackage.user))); + serializer.attribute(null, "package", userPackage.packageName); + serializer.endTag(null, "user-package"); + } + serializer.endTag(null, "preference-denied-list"); + } + + numEntries = mAccessoryPreferenceDeniedMap.size(); + for (int i = 0; i < numEntries; i++) { + AccessoryFilter filter = mAccessoryPreferenceDeniedMap.keyAt(i); + ArraySet<UserPackage> userPackageSet = + mAccessoryPreferenceDeniedMap.valueAt(i); + serializer.startTag(null, "preference-denied-list"); + filter.write(serializer); + + int numUserPackages = userPackageSet.size(); + for (int j = 0; j < numUserPackages; j++) { + UserPackage userPackage = userPackageSet.valueAt(j); + serializer.startTag(null, "user-package"); + serializer.attribute(null, "user", + String.valueOf(getSerial(userPackage.user))); + serializer.attribute(null, "package", userPackage.packageName); + serializer.endTag(null, "user-package"); + } + serializer.endTag(null, "preference-denied-list"); + } + serializer.endTag(null, "settings"); serializer.endDocument(); @@ -834,6 +972,25 @@ class UsbProfileGroupSettingsManager { private void resolveActivity(@NonNull Intent intent, @NonNull ArrayList<ResolveInfo> matches, @Nullable ActivityInfo defaultActivity, @Nullable UsbDevice device, @Nullable UsbAccessory accessory) { + // Remove all matches which are on the denied list + ArraySet deniedPackages = null; + if (device != null) { + deniedPackages = mDevicePreferenceDeniedMap.get(new DeviceFilter(device)); + } else if (accessory != null) { + deniedPackages = mAccessoryPreferenceDeniedMap.get(new AccessoryFilter(accessory)); + } + if (deniedPackages != null) { + for (int i = matches.size() - 1; i >= 0; i--) { + ResolveInfo match = matches.get(i); + String packageName = match.activityInfo.packageName; + UserHandle user = UserHandle + .getUserHandleForUid(match.activityInfo.applicationInfo.uid); + if (deniedPackages.contains(new UserPackage(packageName, user))) { + matches.remove(i); + } + } + } + // don't show the resolver activity if there are no choices available if (matches.size() == 0) { if (accessory != null) { @@ -1076,6 +1233,156 @@ class UsbProfileGroupSettingsManager { } /** + * Add package to the denied for handling a device + * + * @param device the device to add to the denied + * @param packageNames the packages to not become handler + * @param user the user + */ + void addDevicePackagesToDenied(@NonNull UsbDevice device, @NonNull String[] packageNames, + @NonNull UserHandle user) { + if (packageNames.length == 0) { + return; + } + DeviceFilter filter = new DeviceFilter(device); + + synchronized (mLock) { + ArraySet<UserPackage> userPackages; + if (mDevicePreferenceDeniedMap.containsKey(filter)) { + userPackages = mDevicePreferenceDeniedMap.get(filter); + } else { + userPackages = new ArraySet<>(); + mDevicePreferenceDeniedMap.put(filter, userPackages); + } + + boolean shouldWrite = false; + for (String packageName : packageNames) { + UserPackage userPackage = new UserPackage(packageName, user); + if (!userPackages.contains(userPackage)) { + userPackages.add(userPackage); + shouldWrite = true; + } + } + + if (shouldWrite) { + scheduleWriteSettingsLocked(); + } + } + } + + /** + * Add package to the denied for handling a accessory + * + * @param accessory the accessory to add to the denied + * @param packageNames the packages to not become handler + * @param user the user + */ + void addAccessoryPackagesToDenied(@NonNull UsbAccessory accessory, + @NonNull String[] packageNames, @NonNull UserHandle user) { + if (packageNames.length == 0) { + return; + } + AccessoryFilter filter = new AccessoryFilter(accessory); + + synchronized (mLock) { + ArraySet<UserPackage> userPackages; + if (mAccessoryPreferenceDeniedMap.containsKey(filter)) { + userPackages = mAccessoryPreferenceDeniedMap.get(filter); + } else { + userPackages = new ArraySet<>(); + mAccessoryPreferenceDeniedMap.put(filter, userPackages); + } + + boolean shouldWrite = false; + for (String packageName : packageNames) { + UserPackage userPackage = new UserPackage(packageName, user); + if (!userPackages.contains(userPackage)) { + userPackages.add(userPackage); + shouldWrite = true; + } + } + + if (shouldWrite) { + scheduleWriteSettingsLocked(); + } + } + } + + /** + * Remove UserPackage from the denied for handling a device + * + * @param device the device to remove denied packages from + * @param packageName the packages to remove + * @param user the user + */ + void removeDevicePackagesFromDenied(@NonNull UsbDevice device, @NonNull String[] packageNames, + @NonNull UserHandle user) { + DeviceFilter filter = new DeviceFilter(device); + + synchronized (mLock) { + ArraySet<UserPackage> userPackages = mDevicePreferenceDeniedMap.get(filter); + + if (userPackages != null) { + boolean shouldWrite = false; + for (String packageName : packageNames) { + UserPackage userPackage = new UserPackage(packageName, user); + + if (userPackages.contains(userPackage)) { + userPackages.remove(userPackage); + shouldWrite = true; + + if (userPackages.size() == 0) { + mDevicePreferenceDeniedMap.remove(filter); + break; + } + } + } + + if (shouldWrite) { + scheduleWriteSettingsLocked(); + } + } + } + } + + /** + * Remove UserPackage from the denied for handling a accessory + * + * @param accessory the accessory to remove denied packages from + * @param packageName the packages to remove + * @param user the user + */ + void removeAccessoryPackagesFromDenied(@NonNull UsbAccessory accessory, + @NonNull String[] packageNames, @NonNull UserHandle user) { + AccessoryFilter filter = new AccessoryFilter(accessory); + + synchronized (mLock) { + ArraySet<UserPackage> userPackages = mAccessoryPreferenceDeniedMap.get(filter); + + if (userPackages != null) { + boolean shouldWrite = false; + for (String packageName : packageNames) { + UserPackage userPackage = new UserPackage(packageName, user); + + if (userPackages.contains(userPackage)) { + userPackages.remove(userPackage); + shouldWrite = true; + + if (userPackages.size() == 0) { + mAccessoryPreferenceDeniedMap.remove(filter); + break; + } + } + } + + if (shouldWrite) { + scheduleWriteSettingsLocked(); + } + } + } + } + + /** * Set a package as default handler for a accessory. * * @param accessory The accessory that should be handled by default diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java index be32c86f108a..ce6f592e2b0d 100644 --- a/services/usb/java/com/android/server/usb/UsbService.java +++ b/services/usb/java/com/android/server/usb/UsbService.java @@ -361,6 +361,78 @@ public class UsbService extends IUsbManager.Stub { } @Override + public void addDevicePackagesToPreferenceDenied(UsbDevice device, String[] packageNames, + UserHandle user) { + device = Preconditions.checkNotNull(device); + packageNames = Preconditions.checkArrayElementsNotNull(packageNames, "packageNames"); + user = Preconditions.checkNotNull(user); + + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); + + final long token = Binder.clearCallingIdentity(); + try { + mSettingsManager.getSettingsForProfileGroup(user) + .addDevicePackagesToDenied(device, packageNames, user); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void addAccessoryPackagesToPreferenceDenied(UsbAccessory accessory, + String[] packageNames, UserHandle user) { + accessory = Preconditions.checkNotNull(accessory); + packageNames = Preconditions.checkArrayElementsNotNull(packageNames, "packageNames"); + user = Preconditions.checkNotNull(user); + + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); + + final long token = Binder.clearCallingIdentity(); + try { + mSettingsManager.getSettingsForProfileGroup(user) + .addAccessoryPackagesToDenied(accessory, packageNames, user); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void removeDevicePackagesFromPreferenceDenied(UsbDevice device, String[] packageNames, + UserHandle user) { + device = Preconditions.checkNotNull(device); + packageNames = Preconditions.checkArrayElementsNotNull(packageNames, "packageNames"); + user = Preconditions.checkNotNull(user); + + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); + + final long token = Binder.clearCallingIdentity(); + try { + mSettingsManager.getSettingsForProfileGroup(user) + .removeDevicePackagesFromDenied(device, packageNames, user); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void removeAccessoryPackagesFromPreferenceDenied(UsbAccessory accessory, + String[] packageNames, UserHandle user) { + accessory = Preconditions.checkNotNull(accessory); + packageNames = Preconditions.checkArrayElementsNotNull(packageNames, "packageNames"); + user = Preconditions.checkNotNull(user); + + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); + + final long token = Binder.clearCallingIdentity(); + try { + mSettingsManager.getSettingsForProfileGroup(user) + .removeAccessoryPackagesFromDenied(accessory, packageNames, user); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override public void setDevicePersistentPermission(UsbDevice device, int uid, UserHandle user, boolean shouldBeGranted) { device = Preconditions.checkNotNull(device); diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java index fbd8782a5d1b..7b677eea6b8f 100644 --- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java +++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java @@ -130,7 +130,7 @@ class UsbSettingsManager { // it from all profile groups. int numProfileGroups = mSettingsByProfileGroup.size(); for (int i = 0; i < numProfileGroups; i++) { - mSettingsByProfileGroup.valueAt(i).removeAllDefaultsForUser(userToRemove); + mSettingsByProfileGroup.valueAt(i).removeUser(userToRemove); } } } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 8eeaf8d8f012..b4495787bb80 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -108,6 +108,19 @@ public class CarrierConfigManager { "call_forwarding_visibility_bool"; /** + * Boolean indicating if carrier supports call forwarding option "When unreachable". + * + * {@code true}: Call forwarding option "When unreachable" is supported. + * {@code false}: Call forwarding option "When unreachable" is not supported. Option will be + * greyed out in the UI. + * + * By default this value is true. + * @hide + */ + public static final String KEY_CALL_FORWARDING_WHEN_UNREACHABLE_SUPPORTED_BOOL = + "call_forwarding_when_unreachable_supported_bool"; + + /** * Boolean indicating if the "Caller ID" item is visible in the Additional Settings menu. * true means visible. false means gone. * @hide @@ -3250,6 +3263,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL, true); sDefaults.putBoolean(KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL, true); sDefaults.putBoolean(KEY_CALL_FORWARDING_VISIBILITY_BOOL, true); + sDefaults.putBoolean(KEY_CALL_FORWARDING_WHEN_UNREACHABLE_SUPPORTED_BOOL, true); sDefaults.putBoolean(KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL, true); sDefaults.putBoolean(KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL, true); sDefaults.putBoolean(KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL, false); diff --git a/tests/FlickerTests/lib/Android.bp b/tests/FlickerTests/lib/Android.bp index 5d8ed2c205e9..e0f0188ee618 100644 --- a/tests/FlickerTests/lib/Android.bp +++ b/tests/FlickerTests/lib/Android.bp @@ -30,10 +30,23 @@ java_test { } java_library { + name: "flickerlib_without_helpers", + platform_apis: true, + srcs: ["src/**/*.java"], + exclude_srcs: ["src/**/helpers/*.java"], + static_libs: [ + "cts-wm-util", + "platformprotosnano", + "layersprotosnano", + "truth-prebuilt" + ], +} + +java_library { name: "flickerautomationhelperlib", sdk_version: "test_current", srcs: [ - "src/com/android/server/wm/flicker/AutomationUtils.java", + "src/com/android/server/wm/flicker/helpers/AutomationUtils.java", "src/com/android/server/wm/flicker/WindowUtils.java", ], static_libs: [ diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java index 84f9f871324c..38255ee6fe8d 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java @@ -24,14 +24,14 @@ import java.util.function.Function; * results. Assertions are functions that are applied over a single trace entry and returns a * result which includes a detailed reason if the assertion fails. */ -class Assertions { +public class Assertions { /** * Checks assertion on a single trace entry. * * @param <T> trace entry type to perform the assertion on. */ @FunctionalInterface - interface TraceAssertion<T> extends Function<T, Result> { + public interface TraceAssertion<T> extends Function<T, Result> { /** * Returns an assertion that represents the logical negation of this assertion. * @@ -46,7 +46,7 @@ class Assertions { * Checks assertion on a single layers trace entry. */ @FunctionalInterface - interface LayersTraceAssertion extends TraceAssertion<LayersTrace.Entry> { + public interface LayersTraceAssertion extends TraceAssertion<LayersTrace.Entry> { } @@ -54,11 +54,11 @@ class Assertions { * Utility class to store assertions with an identifier to help generate more useful debug * data when dealing with multiple assertions. */ - static class NamedAssertion<T> { - final TraceAssertion<T> assertion; - final String name; + public static class NamedAssertion<T> { + public final TraceAssertion<T> assertion; + public final String name; - NamedAssertion(TraceAssertion<T> assertion, String name) { + public NamedAssertion(TraceAssertion<T> assertion, String name) { this.assertion = assertion; this.name = name; } @@ -67,21 +67,21 @@ class Assertions { /** * Contains the result of an assertion including the reason for failed assertions. */ - static class Result { - static final String NEGATION_PREFIX = "!"; - final boolean success; - final long timestamp; - final String assertionName; - final String reason; - - Result(boolean success, long timestamp, String assertionName, String reason) { + public static class Result { + public static final String NEGATION_PREFIX = "!"; + public final boolean success; + public final long timestamp; + public final String assertionName; + public final String reason; + + public Result(boolean success, long timestamp, String assertionName, String reason) { this.success = success; this.timestamp = timestamp; this.assertionName = assertionName; this.reason = reason; } - Result(boolean success, String reason) { + public Result(boolean success, String reason) { this.success = success; this.reason = reason; this.assertionName = ""; @@ -91,7 +91,7 @@ class Assertions { /** * Returns the negated {@code Result} and adds a negation prefix to the assertion name. */ - Result negate() { + public Result negate() { String negatedAssertionName; if (this.assertionName.startsWith(NEGATION_PREFIX)) { negatedAssertionName = this.assertionName.substring(NEGATION_PREFIX.length() + 1); @@ -101,11 +101,11 @@ class Assertions { return new Result(!this.success, this.timestamp, negatedAssertionName, this.reason); } - boolean passed() { + public boolean passed() { return this.success; } - boolean failed() { + public boolean failed() { return !this.success; } diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java index 3c65d3c341b3..5c4df81299c1 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java @@ -38,11 +38,11 @@ public class AssertionsChecker<T extends ITraceEntry> { private AssertionOption mOption = AssertionOption.NONE; private List<NamedAssertion<T>> mAssertions = new LinkedList<>(); - void add(Assertions.TraceAssertion<T> assertion, String name) { + public void add(Assertions.TraceAssertion<T> assertion, String name) { mAssertions.add(new NamedAssertion<>(assertion, name)); } - void filterByRange(long startTime, long endTime) { + public void filterByRange(long startTime, long endTime) { mFilterEntriesByRange = true; mFilterStartTime = startTime; mFilterEndTime = endTime; @@ -75,7 +75,7 @@ public class AssertionsChecker<T extends ITraceEntry> { * @param entries list of entries to perform assertions on * @return list of failed assertion results */ - List<Result> test(List<T> entries) { + public List<Result> test(List<T> entries) { List<T> filteredEntries; List<Result> failures; diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java index 9525f41b46b2..c47f7f42e54e 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java @@ -19,7 +19,7 @@ package com.android.server.wm.flicker; /** * Common interface for Layer and WindowManager trace entries. */ -interface ITraceEntry { +public interface ITraceEntry { /** * @return timestamp of current entry */ diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java index 660ec0fe4833..68986d48783a 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java @@ -16,7 +16,6 @@ package com.android.server.wm.flicker; -import android.annotation.Nullable; import android.graphics.Rect; import android.surfaceflinger.nano.Layers.LayerProto; import android.surfaceflinger.nano.Layers.RectProto; @@ -25,11 +24,14 @@ import android.surfaceflinger.nano.Layerstrace.LayersTraceFileProto; import android.surfaceflinger.nano.Layerstrace.LayersTraceProto; import android.util.SparseArray; +import androidx.annotation.Nullable; + import com.android.server.wm.flicker.Assertions.Result; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; +import java.util.LinkedList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -57,7 +59,7 @@ public class LayersTrace { * @param data binary proto data * @param source Path to source of data for additional debug information */ - static LayersTrace parseFrom(byte[] data, Path source) { + public static LayersTrace parseFrom(byte[] data, Path source) { List<Entry> entries = new ArrayList<>(); LayersTraceFileProto fileProto; try { @@ -79,15 +81,15 @@ public class LayersTrace { * * @param data binary proto data */ - static LayersTrace parseFrom(byte[] data) { + public static LayersTrace parseFrom(byte[] data) { return parseFrom(data, null); } - List<Entry> getEntries() { + public List<Entry> getEntries() { return mEntries; } - Entry getEntry(long timestamp) { + public Entry getEntry(long timestamp) { Optional<Entry> entry = mEntries.stream() .filter(e -> e.getTimestamp() == timestamp) .findFirst(); @@ -97,14 +99,14 @@ public class LayersTrace { return entry.get(); } - Optional<Path> getSource() { + public Optional<Path> getSource() { return Optional.ofNullable(mSource); } /** * Represents a single Layer trace entry. */ - static class Entry implements ITraceEntry { + public static class Entry implements ITraceEntry { private long mTimestamp; private List<Layer> mRootLayers; // hierarchical representation of layers private List<Layer> mFlattenedLayers = null; @@ -117,7 +119,7 @@ public class LayersTrace { /** * Constructs the layer hierarchy from a flattened list of layers. */ - static Entry fromFlattenedLayers(long timestamp, LayerProto[] protos) { + public static Entry fromFlattenedLayers(long timestamp, LayerProto[] protos) { SparseArray<Layer> layerMap = new SparseArray<>(); ArrayList<Layer> orphans = new ArrayList<>(); for (LayerProto proto : protos) { @@ -181,7 +183,7 @@ public class LayersTrace { /** * Checks if a region specified by {@code testRect} is covered by all visible layers. */ - Result coversRegion(Rect testRect) { + public Result coversRegion(Rect testRect) { String assertionName = "coversRegion"; Collection<Layer> layers = asFlattenedLayers(); @@ -224,7 +226,7 @@ public class LayersTrace { * Checks if a layer with name {@code layerName} has a visible region * {@code expectedVisibleRegion}. */ - Result hasVisibleRegion(String layerName, Rect expectedVisibleRegion) { + public Result hasVisibleRegion(String layerName, Rect expectedVisibleRegion) { String assertionName = "hasVisibleRegion"; String reason = "Could not find " + layerName; for (Layer layer : asFlattenedLayers()) { @@ -252,7 +254,7 @@ public class LayersTrace { /** * Checks if a layer with name {@code layerName} is visible. */ - Result isVisible(String layerName) { + public Result isVisible(String layerName) { String assertionName = "isVisible"; String reason = "Could not find " + layerName; for (Layer layer : asFlattenedLayers()) { @@ -277,24 +279,27 @@ public class LayersTrace { return mTimestamp; } - List<Layer> getRootLayers() { + public List<Layer> getRootLayers() { return mRootLayers; } - List<Layer> asFlattenedLayers() { + /** + * Returns all layers as a flattened list using a depth first traversal. + */ + public List<Layer> asFlattenedLayers() { if (mFlattenedLayers == null) { - mFlattenedLayers = new ArrayList<>(); + mFlattenedLayers = new LinkedList<>(); ArrayList<Layer> pendingLayers = new ArrayList<>(this.mRootLayers); while (!pendingLayers.isEmpty()) { Layer layer = pendingLayers.remove(0); mFlattenedLayers.add(layer); - pendingLayers.addAll(layer.mChildren); + pendingLayers.addAll(0, layer.mChildren); } } return mFlattenedLayers; } - Rect getVisibleBounds(String layerName) { + public Rect getVisibleBounds(String layerName) { List<Layer> layers = asFlattenedLayers(); for (Layer layer : layers) { if (layer.mProto.name.contains(layerName) && layer.isVisible()) { @@ -308,12 +313,12 @@ public class LayersTrace { /** * Represents a single layer with links to its parent and child layers. */ - static class Layer { + public static class Layer { @Nullable - LayerProto mProto; - List<Layer> mChildren; + public LayerProto mProto; + public List<Layer> mChildren; @Nullable - Layer mParent = null; + public Layer mParent = null; private Layer(LayerProto proto) { this.mProto = proto; @@ -328,16 +333,16 @@ public class LayersTrace { this.mParent = parentLayer; } - int getId() { + public int getId() { return mProto.id; } - boolean isActiveBufferEmpty() { + public boolean isActiveBufferEmpty() { return this.mProto.activeBuffer == null || this.mProto.activeBuffer.height == 0 || this.mProto.activeBuffer.width == 0; } - boolean isVisibleRegionEmpty() { + public boolean isVisibleRegionEmpty() { if (this.mProto.visibleRegion == null) { return true; } @@ -345,32 +350,35 @@ public class LayersTrace { return visibleRect.height() == 0 || visibleRect.width() == 0; } - boolean isHidden() { + public boolean isHidden() { return (this.mProto.flags & /* FLAG_HIDDEN */ 0x1) != 0x0; } - boolean isVisible() { - return (!isActiveBufferEmpty() || isColorLayer()) && - !isHidden() && this.mProto.color.a > 0 && !isVisibleRegionEmpty(); + public boolean isVisible() { + return (!isActiveBufferEmpty() || isColorLayer()) + && !isHidden() + && this.mProto.color != null + && this.mProto.color.a > 0 + && !isVisibleRegionEmpty(); } - boolean isColorLayer() { + public boolean isColorLayer() { return this.mProto.type.equals("ColorLayer"); } - boolean isRootLayer() { + public boolean isRootLayer() { return mParent == null || mParent.mProto == null; } - boolean isInvisible() { + public boolean isInvisible() { return !isVisible(); } - boolean isHiddenByParent() { + public boolean isHiddenByParent() { return !isRootLayer() && (mParent.isHidden() || mParent.isHiddenByParent()); } - String getHiddenByParentReason() { + public String getHiddenByParentReason() { String reason = "Layer " + mProto.name; if (isHiddenByParent()) { reason += " is hidden by parent: " + mParent.mProto.name; @@ -380,7 +388,7 @@ public class LayersTrace { return reason; } - String getVisibilityReason() { + public String getVisibilityReason() { String reason = "Layer " + mProto.name; if (isVisible()) { reason += " is visible:"; @@ -399,7 +407,7 @@ public class LayersTrace { if (isHidden()) { reason += " flags=" + this.mProto.flags + " (FLAG_HIDDEN set)"; } - if (this.mProto.color.a == 0) { + if (this.mProto.color == null || this.mProto.color.a == 0) { reason += " color.a=0"; } if (isVisibleRegionEmpty()) { diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java index 4085810a213d..4a5129ed2269 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java @@ -19,9 +19,10 @@ package com.android.server.wm.flicker; import static com.google.common.truth.Truth.assertAbout; import static com.google.common.truth.Truth.assertWithMessage; -import android.annotation.Nullable; import android.graphics.Rect; +import androidx.annotation.Nullable; + import com.android.server.wm.flicker.Assertions.Result; import com.android.server.wm.flicker.LayersTrace.Entry; import com.android.server.wm.flicker.TransitionRunner.TransitionResult; diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java index 0a3fe3c00de2..241a1c04bdb8 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java @@ -16,10 +16,12 @@ package com.android.server.wm.flicker; -import android.annotation.Nullable; -import android.support.annotation.VisibleForTesting; +import static com.android.server.wm.flicker.monitor.ITransitionMonitor.OUTPUT_DIR; + import android.util.Log; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import androidx.test.InstrumentationRegistry; import com.android.server.wm.flicker.monitor.ITransitionMonitor; @@ -89,7 +91,7 @@ import java.util.List; * } * </pre> */ -class TransitionRunner { +public class TransitionRunner { private static final String TAG = "FLICKER"; private final ScreenRecorder mScreenRecorder; private final WindowManagerTraceMonitor mWmTraceMonitor; @@ -128,8 +130,12 @@ class TransitionRunner { mTestTag = builder.mTestTag; } - static TransitionBuilder newBuilder() { - return new TransitionBuilder(); + public static TransitionBuilder newBuilder() { + return newBuilder(OUTPUT_DIR.toString()); + } + + public static TransitionBuilder newBuilder(String outputDir) { + return new TransitionBuilder(outputDir); } /** @@ -138,7 +144,7 @@ class TransitionRunner { * * @return itself */ - TransitionRunner run() { + public TransitionRunner run() { mResults = new ArrayList<>(); mAllRunsMonitors.forEach(ITransitionMonitor::start); mBeforeAlls.forEach(Runnable::run); @@ -159,8 +165,7 @@ class TransitionRunner { mAfterAlls.forEach(Runnable::run); mAllRunsMonitors.forEach(monitor -> { monitor.stop(); - Path path = monitor.save(mTestTag); - Log.e(TAG, "Video saved to " + path.toString()); + monitor.save(mTestTag); }); return this; } @@ -170,7 +175,7 @@ class TransitionRunner { * * @return list of transition results. */ - List<TransitionResult> getResults() { + public List<TransitionResult> getResults() { if (mResults == null) { throw new IllegalStateException("Results do not exist!"); } @@ -182,7 +187,7 @@ class TransitionRunner { * * @return list of transition results. */ - void deleteResults() { + public void deleteResults() { if (mResults == null) { return; } @@ -228,33 +233,33 @@ class TransitionRunner { @VisibleForTesting public static class TransitionResult { @Nullable - final Path layersTrace; + public final Path layersTrace; @Nullable - final Path windowManagerTrace; + public final Path windowManagerTrace; @Nullable - final Path screenCaptureVideo; + public final Path screenCaptureVideo; private boolean flaggedForSaving; - TransitionResult(@Nullable Path layersTrace, @Nullable Path windowManagerTrace, + public TransitionResult(@Nullable Path layersTrace, @Nullable Path windowManagerTrace, @Nullable Path screenCaptureVideo) { this.layersTrace = layersTrace; this.windowManagerTrace = windowManagerTrace; this.screenCaptureVideo = screenCaptureVideo; } - void flagForSaving() { + public void flagForSaving() { flaggedForSaving = true; } - boolean canDelete() { + public boolean canDelete() { return !flaggedForSaving; } - boolean layersTraceExists() { + public boolean layersTraceExists() { return layersTrace != null && layersTrace.toFile().exists(); } - byte[] getLayersTrace() { + public byte[] getLayersTrace() { try { return Files.toByteArray(this.layersTrace.toFile()); } catch (IOException e) { @@ -262,11 +267,11 @@ class TransitionRunner { } } - Path getLayersTracePath() { + public Path getLayersTracePath() { return layersTrace; } - boolean windowManagerTraceExists() { + public boolean windowManagerTraceExists() { return windowManagerTrace != null && windowManagerTrace.toFile().exists(); } @@ -278,19 +283,19 @@ class TransitionRunner { } } - Path getWindowManagerTracePath() { + public Path getWindowManagerTracePath() { return windowManagerTrace; } - boolean screenCaptureVideoExists() { + public boolean screenCaptureVideoExists() { return screenCaptureVideo != null && screenCaptureVideo.toFile().exists(); } - Path screenCaptureVideoPath() { + public Path screenCaptureVideoPath() { return screenCaptureVideo; } - void delete() { + public void delete() { if (layersTraceExists()) layersTrace.toFile().delete(); if (windowManagerTraceExists()) windowManagerTrace.toFile().delete(); if (screenCaptureVideoExists()) screenCaptureVideo.toFile().delete(); @@ -300,7 +305,7 @@ class TransitionRunner { /** * Builds a {@link TransitionRunner} instance. */ - static class TransitionBuilder { + public static class TransitionBuilder { private ScreenRecorder mScreenRecorder; private WindowManagerTraceMonitor mWmTraceMonitor; private LayersTraceMonitor mLayersTraceMonitor; @@ -323,15 +328,15 @@ class TransitionRunner { private boolean mRecordAllRuns = false; - TransitionBuilder() { + public TransitionBuilder(String outputDir) { mScreenRecorder = new ScreenRecorder(); - mWmTraceMonitor = new WindowManagerTraceMonitor(); - mLayersTraceMonitor = new LayersTraceMonitor(); + mWmTraceMonitor = new WindowManagerTraceMonitor(outputDir); + mLayersTraceMonitor = new LayersTraceMonitor(outputDir); mFrameStatsMonitor = new WindowAnimationFrameStatsMonitor(InstrumentationRegistry.getInstrumentation()); } - TransitionRunner build() { + public TransitionRunner build() { if (mCaptureWindowManagerTrace) { mPerRunMonitors.add(mWmTraceMonitor); } @@ -355,52 +360,52 @@ class TransitionRunner { return new TransitionRunner(this); } - TransitionBuilder runBeforeAll(Runnable runnable) { + public TransitionBuilder runBeforeAll(Runnable runnable) { mBeforeAlls.add(runnable); return this; } - TransitionBuilder runBefore(Runnable runnable) { + public TransitionBuilder runBefore(Runnable runnable) { mBefores.add(runnable); return this; } - TransitionBuilder run(Runnable runnable) { + public TransitionBuilder run(Runnable runnable) { mTransitions.add(runnable); return this; } - TransitionBuilder runAfter(Runnable runnable) { + public TransitionBuilder runAfter(Runnable runnable) { mAfters.add(runnable); return this; } - TransitionBuilder runAfterAll(Runnable runnable) { + public TransitionBuilder runAfterAll(Runnable runnable) { mAfterAlls.add(runnable); return this; } - TransitionBuilder repeat(int iterations) { + public TransitionBuilder repeat(int iterations) { mIterations = iterations; return this; } - TransitionBuilder skipWindowManagerTrace() { + public TransitionBuilder skipWindowManagerTrace() { mCaptureWindowManagerTrace = false; return this; } - TransitionBuilder skipLayersTrace() { + public TransitionBuilder skipLayersTrace() { mCaptureLayersTrace = false; return this; } - TransitionBuilder includeJankyRuns() { + public TransitionBuilder includeJankyRuns() { mRunJankFree = false; return this; } - TransitionBuilder recordEachRun() { + public TransitionBuilder recordEachRun() { if (mRecordAllRuns) { throw new IllegalArgumentException("Invalid option with recordAllRuns"); } @@ -408,7 +413,7 @@ class TransitionRunner { return this; } - TransitionBuilder recordAllRuns() { + public TransitionBuilder recordAllRuns() { if (mRecordEachRun) { throw new IllegalArgumentException("Invalid option with recordEachRun"); } @@ -416,7 +421,11 @@ class TransitionRunner { return this; } - TransitionBuilder withTag(String testTag) { + public TransitionBuilder withTag(String testTag) { + if (testTag.contains(" ")) { + throw new IllegalArgumentException("The test tag can not contain spaces since it " + + "is a part of the file name"); + } mTestTag = testTag; return this; } diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java index e3592eb8cd01..412e72d82e55 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java @@ -16,7 +16,7 @@ package com.android.server.wm.flicker; -import android.annotation.Nullable; +import androidx.annotation.Nullable; import com.android.server.wm.flicker.Assertions.Result; import com.android.server.wm.nano.AppWindowTokenProto; @@ -58,7 +58,7 @@ public class WindowManagerTrace { * @param data binary proto data * @param source Path to source of data for additional debug information */ - static WindowManagerTrace parseFrom(byte[] data, Path source) { + public static WindowManagerTrace parseFrom(byte[] data, Path source) { List<Entry> entries = new ArrayList<>(); WindowManagerTraceFileProto fileProto; @@ -73,7 +73,7 @@ public class WindowManagerTrace { return new WindowManagerTrace(entries, source); } - static WindowManagerTrace parseFrom(byte[] data) { + public static WindowManagerTrace parseFrom(byte[] data) { return parseFrom(data, null); } @@ -81,7 +81,7 @@ public class WindowManagerTrace { return mEntries; } - Entry getEntry(long timestamp) { + public Entry getEntry(long timestamp) { Optional<Entry> entry = mEntries.stream() .filter(e -> e.getTimestamp() == timestamp) .findFirst(); @@ -91,17 +91,17 @@ public class WindowManagerTrace { return entry.get(); } - Optional<Path> getSource() { + public Optional<Path> getSource() { return Optional.ofNullable(mSource); } /** * Represents a single WindowManager trace entry. */ - static class Entry implements ITraceEntry { + public static class Entry implements ITraceEntry { private final WindowManagerTraceProto mProto; - Entry(WindowManagerTraceProto proto) { + public Entry(WindowManagerTraceProto proto) { mProto = proto; } @@ -162,7 +162,7 @@ public class WindowManagerTrace { /** * Checks if aboveAppWindow with {@code windowTitle} is visible. */ - Result isAboveAppWindowVisible(String windowTitle) { + public Result isAboveAppWindowVisible(String windowTitle) { WindowTokenProto[] windowTokenProtos = mProto.windowManagerService .rootWindowContainer .displays[DEFAULT_DISPLAY].aboveAppWindows; @@ -173,7 +173,7 @@ public class WindowManagerTrace { /** * Checks if belowAppWindow with {@code windowTitle} is visible. */ - Result isBelowAppWindowVisible(String windowTitle) { + public Result isBelowAppWindowVisible(String windowTitle) { WindowTokenProto[] windowTokenProtos = mProto.windowManagerService .rootWindowContainer .displays[DEFAULT_DISPLAY].belowAppWindows; @@ -185,7 +185,7 @@ public class WindowManagerTrace { /** * Checks if imeWindow with {@code windowTitle} is visible. */ - Result isImeWindowVisible(String windowTitle) { + public Result isImeWindowVisible(String windowTitle) { WindowTokenProto[] windowTokenProtos = mProto.windowManagerService .rootWindowContainer .displays[DEFAULT_DISPLAY].imeWindows; @@ -197,7 +197,7 @@ public class WindowManagerTrace { /** * Checks if app window with {@code windowTitle} is on top. */ - Result isVisibleAppWindowOnTop(String windowTitle) { + public Result isVisibleAppWindowOnTop(String windowTitle) { String topAppWindow = getTopVisibleAppWindow(); boolean success = topAppWindow.contains(windowTitle); String reason = "wanted=" + windowTitle + " found=" + topAppWindow; @@ -207,7 +207,7 @@ public class WindowManagerTrace { /** * Checks if app window with {@code windowTitle} is visible. */ - Result isAppWindowVisible(String windowTitle) { + public Result isAppWindowVisible(String windowTitle) { final String assertionName = "isAppWindowVisible"; boolean titleFound = false; StackProto[] stacks = mProto.windowManagerService.rootWindowContainer diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java index c54396f895e4..3d25fbed5135 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java @@ -28,9 +28,9 @@ import androidx.test.InstrumentationRegistry; /** * Helper functions to retrieve system window sizes and positions. */ -class WindowUtils { +public class WindowUtils { - static Rect getDisplayBounds() { + public static Rect getDisplayBounds() { Point display = new Point(); WindowManager wm = (WindowManager) InstrumentationRegistry.getContext().getSystemService( @@ -46,7 +46,7 @@ class WindowUtils { return wm.getDefaultDisplay().getRotation(); } - static Rect getDisplayBounds(int requestedRotation) { + public static Rect getDisplayBounds(int requestedRotation) { Rect displayBounds = getDisplayBounds(); int currentDisplayRotation = getCurrentRotation(); @@ -66,7 +66,7 @@ class WindowUtils { } - static Rect getAppPosition(int requestedRotation) { + public static Rect getAppPosition(int requestedRotation) { Rect displayBounds = getDisplayBounds(); int currentDisplayRotation = getCurrentRotation(); @@ -85,7 +85,7 @@ class WindowUtils { return new Rect(0, 0, displayBounds.width(), displayBounds.height()); } - static Rect getStatusBarPosition(int requestedRotation) { + public static Rect getStatusBarPosition(int requestedRotation) { Resources resources = InstrumentationRegistry.getContext().getResources(); String resourceName; Rect displayBounds = getDisplayBounds(); @@ -104,7 +104,7 @@ class WindowUtils { return new Rect(0, 0, width, height); } - static Rect getNavigationBarPosition(int requestedRotation) { + public static Rect getNavigationBarPosition(int requestedRotation) { Resources resources = InstrumentationRegistry.getContext().getResources(); Rect displayBounds = getDisplayBounds(); int displayWidth = Math.min(displayBounds.width(), displayBounds.height()); @@ -129,13 +129,13 @@ class WindowUtils { } } - static int getNavigationBarHeight() { + public static int getNavigationBarHeight() { Resources resources = InstrumentationRegistry.getContext().getResources(); int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android"); return resources.getDimensionPixelSize(resourceId); } - static int getDockedStackDividerInset() { + public static int getDockedStackDividerInset() { Resources resources = InstrumentationRegistry.getContext().getResources(); int resourceId = resources.getIdentifier("docked_stack_divider_insets", "dimen", "android"); diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java index e76da6e90834..064cc2702f39 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java @@ -19,7 +19,7 @@ package com.android.server.wm.flicker; import static com.google.common.truth.Truth.assertAbout; import static com.google.common.truth.Truth.assertWithMessage; -import android.annotation.Nullable; +import androidx.annotation.Nullable; import com.android.server.wm.flicker.Assertions.Result; import com.android.server.wm.flicker.TransitionRunner.TransitionResult; diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/helpers/AutomationUtils.java index e00a2474556c..6821ff02e371 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/helpers/AutomationUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.wm.flicker; +package com.android.server.wm.flicker.helpers; import static android.os.SystemClock.sleep; import static android.system.helpers.OverviewHelper.isRecentsInLauncher; @@ -44,6 +44,8 @@ import android.view.ViewConfiguration; import androidx.test.InstrumentationRegistry; +import com.android.server.wm.flicker.WindowUtils; + /** * Collection of UI Automation helper functions. */ @@ -70,14 +72,14 @@ public class AutomationUtils { * This removes some delays when using the UIAutomator library required to create fast UI * transitions. */ - static void setFastWait() { + public static void setFastWait() { Configurator.getInstance().setWaitForIdleTimeout(0); } /** * Reverts {@link android.app.UiAutomation#waitForIdle(long, long)} to default behavior. */ - static void setDefaultWait() { + public static void setDefaultWait() { Configurator.getInstance().setWaitForIdleTimeout(10000); } @@ -124,7 +126,7 @@ public class AutomationUtils { device.waitForIdle(); } - static void clearRecents(UiDevice device) { + public static void clearRecents(UiDevice device) { if (isQuickstepEnabled(device)) { openQuickstep(device); @@ -201,7 +203,7 @@ public class AutomationUtils { sleep(2000); } - static void resizeSplitScreen(UiDevice device, Rational windowHeightRatio) { + public static void resizeSplitScreen(UiDevice device, Rational windowHeightRatio) { BySelector dividerSelector = By.res(SYSTEMUI_PACKAGE, "docked_divider_handle"); UiObject2 divider = device.wait(Until.findObject(dividerSelector), FIND_TIMEOUT); assertNotNull("Unable to find Split screen divider", divider); @@ -218,7 +220,7 @@ public class AutomationUtils { sleep(2000); } - static void closePipWindow(UiDevice device) { + public static void closePipWindow(UiDevice device) { UiObject2 pipWindow = device.findObject( By.res(SYSTEMUI_PACKAGE, "background")); pipWindow.click(); @@ -229,7 +231,7 @@ public class AutomationUtils { sleep(2000); } - static void expandPipWindow(UiDevice device) { + public static void expandPipWindow(UiDevice device) { UiObject2 pipWindow = device.findObject( By.res(SYSTEMUI_PACKAGE, "background")); pipWindow.click(); diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java index c55d068b41b8..da75b3e86d6b 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java @@ -16,21 +16,22 @@ package com.android.server.wm.flicker.monitor; -import android.os.IBinder; -import android.os.Parcel; import android.os.RemoteException; -import android.os.ServiceManager; -import android.util.Log; +import android.view.IWindowManager; +import android.view.WindowManagerGlobal; /** * Captures Layers trace from SurfaceFlinger. */ public class LayersTraceMonitor extends TraceMonitor { - private static final String TAG = "LayersTraceMonitor"; - private IBinder mSurfaceFlinger = ServiceManager.getService("SurfaceFlinger"); + private IWindowManager mWm = WindowManagerGlobal.getWindowManagerService(); public LayersTraceMonitor() { - traceFileName = "layers_trace.pb"; + this(OUTPUT_DIR.toString()); + } + + public LayersTraceMonitor(String outputDir) { + super(outputDir, "layers_trace.pb"); } @Override @@ -45,30 +46,19 @@ public class LayersTraceMonitor extends TraceMonitor { @Override public boolean isEnabled() throws RemoteException { - Parcel data = Parcel.obtain(); - Parcel reply = Parcel.obtain(); - data.writeInterfaceToken("android.ui.ISurfaceComposer"); - mSurfaceFlinger.transact(/* LAYER_TRACE_STATUS_CODE */ 1026, - data, reply, 0 /* flags */); - return reply.readBoolean(); + try { + return mWm.isLayerTracing(); + } catch (RemoteException e) { + e.printStackTrace(); + } + return false; } private void setEnabled(boolean isEnabled) { - Parcel data = null; try { - if (mSurfaceFlinger != null) { - data = Parcel.obtain(); - data.writeInterfaceToken("android.ui.ISurfaceComposer"); - data.writeInt(isEnabled ? 1 : 0); - mSurfaceFlinger.transact( /* LAYER_TRACE_CONTROL_CODE */ 1025, - data, null, 0 /* flags */); - } + mWm.setLayerTracing(isEnabled); } catch (RemoteException e) { - Log.e(TAG, "Could not set layer tracing." + e.toString()); - } finally { - if (data != null) { - data.recycle(); - } + e.printStackTrace(); } } } diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java index 4787586777ae..dce1c2739b15 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java @@ -20,25 +20,25 @@ import static com.android.compatibility.common.util.SystemUtil.runShellCommand; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; -import android.support.annotation.VisibleForTesting; import android.util.Log; +import androidx.annotation.VisibleForTesting; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; /** * Captures screen contents and saves it as a mp4 video file. */ public class ScreenRecorder implements ITransitionMonitor { @VisibleForTesting - static final Path DEFAULT_OUTPUT_PATH = OUTPUT_DIR.resolve("transition.mp4"); + public static final Path DEFAULT_OUTPUT_PATH = OUTPUT_DIR.resolve("transition.mp4"); private static final String TAG = "FLICKER"; private Thread recorderThread; @VisibleForTesting - static Path getPath(String testTag) { + public static Path getPath(String testTag) { return OUTPUT_DIR.resolve(testTag + ".mp4"); } @@ -69,8 +69,10 @@ public class ScreenRecorder implements ITransitionMonitor { @Override public Path save(String testTag) { try { - return Files.move(DEFAULT_OUTPUT_PATH, getPath(testTag), + Path targetPath = Files.move(DEFAULT_OUTPUT_PATH, getPath(testTag), REPLACE_EXISTING); + Log.i(TAG, "Video saved to " + targetPath.toString()); + return targetPath; } catch (IOException e) { throw new RuntimeException(e); } diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java index 0e154ecd5d4d..1ba36bba92ef 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java @@ -20,7 +20,7 @@ import static com.android.compatibility.common.util.SystemUtil.runShellCommand; import android.os.RemoteException; -import com.android.internal.annotations.VisibleForTesting; +import androidx.annotation.VisibleForTesting; import java.nio.file.Path; import java.nio.file.Paths; @@ -34,9 +34,15 @@ public abstract class TraceMonitor implements ITransitionMonitor { public static final String TAG = "FLICKER"; private static final String TRACE_DIR = "/data/misc/wmtrace/"; - String traceFileName; + private Path mOutputDir; + public String mTraceFileName; - abstract boolean isEnabled() throws RemoteException; + public abstract boolean isEnabled() throws RemoteException; + + public TraceMonitor(String outputDir, String traceFileName) { + mOutputDir = Paths.get(outputDir); + mTraceFileName = traceFileName; + } /** * Saves trace file to the external storage directory suffixing the name with the testtag @@ -53,14 +59,16 @@ public abstract class TraceMonitor implements ITransitionMonitor { public Path save(String testTag) { OUTPUT_DIR.toFile().mkdirs(); Path traceFileCopy = getOutputTraceFilePath(testTag); + + // Read the input stream fully. String copyCommand = String.format(Locale.getDefault(), "mv %s%s %s", TRACE_DIR, - traceFileName, traceFileCopy.toString()); + mTraceFileName, traceFileCopy.toString()); runShellCommand(copyCommand); return traceFileCopy; } @VisibleForTesting - Path getOutputTraceFilePath(String testTag) { - return OUTPUT_DIR.resolve(traceFileName + "_" + testTag); + public Path getOutputTraceFilePath(String testTag) { + return mOutputDir.resolve(mTraceFileName + "_" + testTag); } } diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java index ae160b68c976..11de4aa86343 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java @@ -24,16 +24,20 @@ import android.view.WindowManagerGlobal; * Captures WindowManager trace from WindowManager. */ public class WindowManagerTraceMonitor extends TraceMonitor { - private IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); + private IWindowManager mWm = WindowManagerGlobal.getWindowManagerService(); public WindowManagerTraceMonitor() { - traceFileName = "wm_trace.pb"; + this(OUTPUT_DIR.toString()); + } + + public WindowManagerTraceMonitor(String outputDir) { + super(outputDir, "wm_trace.pb"); } @Override public void start() { try { - wm.startWindowTrace(); + mWm.startWindowTrace(); } catch (RemoteException e) { throw new RuntimeException("Could not start trace", e); } @@ -42,7 +46,7 @@ public class WindowManagerTraceMonitor extends TraceMonitor { @Override public void stop() { try { - wm.stopWindowTrace(); + mWm.stopWindowTrace(); } catch (RemoteException e) { throw new RuntimeException("Could not stop trace", e); } @@ -50,6 +54,6 @@ public class WindowManagerTraceMonitor extends TraceMonitor { @Override public boolean isEnabled() throws RemoteException{ - return wm.isWindowTraceEnabled(); + return mWm.isWindowTraceEnabled(); } } diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java index dd6fed04d3e6..f31238477e95 100644 --- a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java +++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java @@ -16,7 +16,7 @@ package com.android.server.wm.flicker.monitor; -import static com.android.server.wm.flicker.AutomationUtils.wakeUpAndGoToHomeScreen; +import static com.android.server.wm.flicker.helpers.AutomationUtils.wakeUpAndGoToHomeScreen; import androidx.test.InstrumentationRegistry; diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java index 65888acc184b..5cf2c1cd6827 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java @@ -19,12 +19,12 @@ package com.android.server.wm.flicker; import static android.os.SystemClock.sleep; import static android.view.Surface.rotationToString; -import static com.android.server.wm.flicker.AutomationUtils.clearRecents; -import static com.android.server.wm.flicker.AutomationUtils.closePipWindow; -import static com.android.server.wm.flicker.AutomationUtils.exitSplitScreen; -import static com.android.server.wm.flicker.AutomationUtils.expandPipWindow; -import static com.android.server.wm.flicker.AutomationUtils.launchSplitScreen; -import static com.android.server.wm.flicker.AutomationUtils.stopPackage; +import static com.android.server.wm.flicker.helpers.AutomationUtils.clearRecents; +import static com.android.server.wm.flicker.helpers.AutomationUtils.closePipWindow; +import static com.android.server.wm.flicker.helpers.AutomationUtils.exitSplitScreen; +import static com.android.server.wm.flicker.helpers.AutomationUtils.expandPipWindow; +import static com.android.server.wm.flicker.helpers.AutomationUtils.launchSplitScreen; +import static com.android.server.wm.flicker.helpers.AutomationUtils.stopPackage; import android.content.Context; import android.content.Intent; @@ -40,6 +40,7 @@ import android.view.Surface; import androidx.test.InstrumentationRegistry; import com.android.server.wm.flicker.TransitionRunner.TransitionBuilder; +import com.android.server.wm.flicker.helpers.AutomationUtils; /** * Collection of common transitions which can be used to test different apps or scenarios. diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java index 00e11c0cef41..8c9d6b4dc7a0 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java @@ -16,7 +16,7 @@ package com.android.server.wm.flicker; -import static com.android.server.wm.flicker.AutomationUtils.setDefaultWait; +import static com.android.server.wm.flicker.helpers.AutomationUtils.setDefaultWait; import static com.google.common.truth.Truth.assertWithMessage; diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index ab31ed7389a3..79f5095010e8 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -765,6 +765,78 @@ public class PackageWatchdogTest { assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_B); } + /** + * Test default monitoring duration is used when PackageWatchdog#startObservingHealth is offered + * an invalid durationMs. + */ + @Test + public void testInvalidMonitoringDuration_beforeExpiry() { + PackageWatchdog watchdog = createWatchdog(); + TestObserver observer = new TestObserver(OBSERVER_NAME_1); + + watchdog.startObservingHealth(observer, Arrays.asList(APP_A), -1); + // Note: Don't move too close to the expiration time otherwise the handler will be thrashed + // by PackageWatchdog#scheduleNextSyncStateLocked which keeps posting runnables with very + // small timeouts. + moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_OBSERVING_DURATION_MS - 100); + raiseFatalFailureAndDispatch(watchdog, + Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + + // We should receive APP_A since the observer hasn't expired + assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A); + } + + /** + * Test default monitoring duration is used when PackageWatchdog#startObservingHealth is offered + * an invalid durationMs. + */ + @Test + public void testInvalidMonitoringDuration_afterExpiry() { + PackageWatchdog watchdog = createWatchdog(); + TestObserver observer = new TestObserver(OBSERVER_NAME_1); + + watchdog.startObservingHealth(observer, Arrays.asList(APP_A), -1); + moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_OBSERVING_DURATION_MS + 1); + raiseFatalFailureAndDispatch(watchdog, + Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + + // We should receive nothing since the observer has expired + assertThat(observer.mHealthCheckFailedPackages).isEmpty(); + } + + /** Test we are notified when enough failures are triggered within any window. */ + @Test + public void testFailureTriggerWindow() { + adoptShellPermissions( + Manifest.permission.WRITE_DEVICE_CONFIG, + Manifest.permission.READ_DEVICE_CONFIG); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, + PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT, + Integer.toString(3), /*makeDefault*/false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, + PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS, + Integer.toString(1000), /*makeDefault*/false); + PackageWatchdog watchdog = createWatchdog(); + TestObserver observer = new TestObserver(OBSERVER_NAME_1); + + watchdog.startObservingHealth(observer, Arrays.asList(APP_A), Long.MAX_VALUE); + // Raise 2 failures at t=0 and t=900 respectively + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + mTestLooper.dispatchAll(); + moveTimeForwardAndDispatch(900); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + mTestLooper.dispatchAll(); + + // Raise 2 failures at t=1100 + moveTimeForwardAndDispatch(200); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + mTestLooper.dispatchAll(); + + // We should receive APP_A since there are 3 failures within 1000ms window + assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A); + } + private void adoptShellPermissions(String... permissions) { InstrumentationRegistry .getInstrumentation() diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp index 5e06818d7a13..e36668e5a043 100644 --- a/tools/aapt2/cmd/Optimize.cpp +++ b/tools/aapt2/cmd/Optimize.cpp @@ -53,9 +53,9 @@ using ::android::ConfigDescription; using ::android::ResTable_config; using ::android::StringPiece; using ::android::base::ReadFileToString; -using ::android::base::WriteStringToFile; using ::android::base::StringAppendF; using ::android::base::StringPrintf; +using ::android::base::WriteStringToFile; namespace aapt { @@ -300,29 +300,7 @@ class Optimizer { OptimizeContext* context_; }; -bool ExtractObfuscationWhitelistFromConfig(const std::string& path, OptimizeContext* context, - OptimizeOptions* options) { - std::string contents; - if (!ReadFileToString(path, &contents, true)) { - context->GetDiagnostics()->Error(DiagMessage() - << "failed to parse whitelist from config file: " << path); - return false; - } - for (StringPiece resource_name : util::Tokenize(contents, ',')) { - options->table_flattener_options.whitelisted_resources.insert( - resource_name.to_string()); - } - return true; -} - -bool ExtractConfig(const std::string& path, OptimizeContext* context, - OptimizeOptions* options) { - std::string content; - if (!android::base::ReadFileToString(path, &content, true /*follow_symlinks*/)) { - context->GetDiagnostics()->Error(DiagMessage(path) << "failed reading whitelist"); - return false; - } - +bool ParseConfig(const std::string& content, IAaptContext* context, OptimizeOptions* options) { size_t line_no = 0; for (StringPiece line : util::Tokenize(content, '\n')) { line_no++; @@ -351,15 +329,24 @@ bool ExtractConfig(const std::string& path, OptimizeContext* context, for (StringPiece directive : util::Tokenize(directives, ',')) { if (directive == "remove") { options->resources_blacklist.insert(resource_name.ToResourceName()); - } else if (directive == "no_obfuscate") { - options->table_flattener_options.whitelisted_resources.insert( - resource_name.entry.to_string()); + } else if (directive == "no_collapse" || directive == "no_obfuscate") { + options->table_flattener_options.name_collapse_exemptions.insert( + resource_name.ToResourceName()); } } } return true; } +bool ExtractConfig(const std::string& path, IAaptContext* context, OptimizeOptions* options) { + std::string content; + if (!android::base::ReadFileToString(path, &content, true /*follow_symlinks*/)) { + context->GetDiagnostics()->Error(DiagMessage(path) << "failed reading config file"); + return false; + } + return ParseConfig(content, context, options); +} + bool ExtractAppDataFromManifest(OptimizeContext* context, const LoadedApk* apk, OptimizeOptions* out_options) { const xml::XmlResource* manifest = apk->GetManifest(); @@ -467,15 +454,6 @@ int OptimizeCommand::Action(const std::vector<std::string>& args) { } } - if (options_.table_flattener_options.collapse_key_stringpool) { - if (whitelist_path_) { - std::string& path = whitelist_path_.value(); - if (!ExtractObfuscationWhitelistFromConfig(path, &context, &options_)) { - return 1; - } - } - } - if (resources_config_path_) { std::string& path = resources_config_path_.value(); if (!ExtractConfig(path, &context, &options_)) { diff --git a/tools/aapt2/cmd/Optimize.h b/tools/aapt2/cmd/Optimize.h index 0be7dad18380..5070ccc8afbf 100644 --- a/tools/aapt2/cmd/Optimize.h +++ b/tools/aapt2/cmd/Optimize.h @@ -78,10 +78,6 @@ class OptimizeCommand : public Command { "All the resources that would be unused on devices of the given densities will be \n" "removed from the APK.", &target_densities_); - AddOptionalFlag("--whitelist-path", - "Path to the whitelist.cfg file containing whitelisted resources \n" - "whose names should not be altered in final resource tables.", - &whitelist_path_); AddOptionalFlag("--resources-config-path", "Path to the resources.cfg file containing the list of resources and \n" "directives to each resource. \n" @@ -104,11 +100,13 @@ class OptimizeCommand : public Command { "Enables encoding sparse entries using a binary search tree.\n" "This decreases APK size at the cost of resource retrieval performance.", &options_.table_flattener_options.use_sparse_entries); - AddOptionalSwitch("--enable-resource-obfuscation", - "Enables obfuscation of key string pool to single value", + AddOptionalSwitch("--collapse-resource-names", + "Collapses resource names to a single value in the key string pool. Resources can \n" + "be exempted using the \"no_collapse\" directive in a file specified by " + "--resources-config-path.", &options_.table_flattener_options.collapse_key_stringpool); - AddOptionalSwitch("--enable-resource-path-shortening", - "Enables shortening of the path of the resources inside the APK.", + AddOptionalSwitch("--shorten-resource-paths", + "Shortens the paths of resources inside the APK.", &options_.shorten_resource_paths); AddOptionalFlag("--resource-path-shortening-map", "Path to output the map of old resource paths to shortened paths.", @@ -125,7 +123,6 @@ class OptimizeCommand : public Command { const std::string &file_path); Maybe<std::string> config_path_; - Maybe<std::string> whitelist_path_; Maybe<std::string> resources_config_path_; Maybe<std::string> target_densities_; std::vector<std::string> configs_; diff --git a/tools/aapt2/cmd/Optimize_test.cpp b/tools/aapt2/cmd/Optimize_test.cpp new file mode 100644 index 000000000000..ac681e85b3d6 --- /dev/null +++ b/tools/aapt2/cmd/Optimize_test.cpp @@ -0,0 +1,68 @@ +/* + * 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. + */ + +#include "Optimize.h" + +#include "AppInfo.h" +#include "Diagnostics.h" +#include "LoadedApk.h" +#include "Resource.h" +#include "test/Test.h" + +using testing::Contains; +using testing::Eq; + +namespace aapt { + +bool ParseConfig(const std::string&, IAaptContext*, OptimizeOptions*); + +using OptimizeTest = CommandTestFixture; + +TEST_F(OptimizeTest, ParseConfigWithNoCollapseExemptions) { + const std::string& content = R"( +string/foo#no_collapse +dimen/bar#no_collapse +)"; + aapt::test::Context context; + OptimizeOptions options; + ParseConfig(content, &context, &options); + + const std::set<ResourceName>& name_collapse_exemptions = + options.table_flattener_options.name_collapse_exemptions; + + ASSERT_THAT(name_collapse_exemptions.size(), Eq(2)); + EXPECT_THAT(name_collapse_exemptions, Contains(ResourceName({}, ResourceType::kString, "foo"))); + EXPECT_THAT(name_collapse_exemptions, Contains(ResourceName({}, ResourceType::kDimen, "bar"))); +} + +TEST_F(OptimizeTest, ParseConfigWithNoObfuscateExemptions) { + const std::string& content = R"( +string/foo#no_obfuscate +dimen/bar#no_obfuscate +)"; + aapt::test::Context context; + OptimizeOptions options; + ParseConfig(content, &context, &options); + + const std::set<ResourceName>& name_collapse_exemptions = + options.table_flattener_options.name_collapse_exemptions; + + ASSERT_THAT(name_collapse_exemptions.size(), Eq(2)); + EXPECT_THAT(name_collapse_exemptions, Contains(ResourceName({}, ResourceType::kString, "foo"))); + EXPECT_THAT(name_collapse_exemptions, Contains(ResourceName({}, ResourceType::kDimen, "bar"))); +} + +} // namespace aapt diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp index b9321174100b..58e232c33985 100644 --- a/tools/aapt2/format/binary/TableFlattener.cpp +++ b/tools/aapt2/format/binary/TableFlattener.cpp @@ -228,14 +228,15 @@ class PackageFlattener { public: PackageFlattener(IAaptContext* context, ResourceTablePackage* package, const std::map<size_t, std::string>* shared_libs, bool use_sparse_entries, - bool collapse_key_stringpool, const std::set<std::string>& whitelisted_resources) + bool collapse_key_stringpool, + const std::set<ResourceName>& name_collapse_exemptions) : context_(context), diag_(context->GetDiagnostics()), package_(package), shared_libs_(shared_libs), use_sparse_entries_(use_sparse_entries), collapse_key_stringpool_(collapse_key_stringpool), - whitelisted_resources_(whitelisted_resources) { + name_collapse_exemptions_(name_collapse_exemptions) { } bool FlattenPackage(BigBuffer* buffer) { @@ -652,11 +653,12 @@ class PackageFlattener { for (ResourceEntry* entry : sorted_entries) { uint32_t local_key_index; + ResourceName resource_name({}, type->type, entry->name); if (!collapse_key_stringpool_ || - whitelisted_resources_.find(entry->name) != whitelisted_resources_.end()) { + name_collapse_exemptions_.find(resource_name) != name_collapse_exemptions_.end()) { local_key_index = (uint32_t)key_pool_.MakeRef(entry->name).index(); } else { - // resource isn't whitelisted, add it as obfuscated value + // resource isn't exempt from collapse, add it as obfuscated value local_key_index = (uint32_t)key_pool_.MakeRef(obfuscated_resource_name).index(); } // Group values by configuration. @@ -712,7 +714,7 @@ class PackageFlattener { StringPool type_pool_; StringPool key_pool_; bool collapse_key_stringpool_; - const std::set<std::string>& whitelisted_resources_; + const std::set<ResourceName>& name_collapse_exemptions_; }; } // namespace @@ -760,7 +762,7 @@ bool TableFlattener::Consume(IAaptContext* context, ResourceTable* table) { PackageFlattener flattener(context, package.get(), &table->included_packages_, options_.use_sparse_entries, options_.collapse_key_stringpool, - options_.whitelisted_resources); + options_.name_collapse_exemptions); if (!flattener.FlattenPackage(&package_buffer)) { return false; } diff --git a/tools/aapt2/format/binary/TableFlattener.h b/tools/aapt2/format/binary/TableFlattener.h index 73c17295556b..4360db190146 100644 --- a/tools/aapt2/format/binary/TableFlattener.h +++ b/tools/aapt2/format/binary/TableFlattener.h @@ -19,6 +19,7 @@ #include "android-base/macros.h" +#include "Resource.h" #include "ResourceTable.h" #include "process/IResourceTableConsumer.h" #include "util/BigBuffer.h" @@ -41,8 +42,8 @@ struct TableFlattenerOptions { // have name indices that point to this single value bool collapse_key_stringpool = false; - // Set of whitelisted resource names to avoid altering in key stringpool - std::set<std::string> whitelisted_resources; + // Set of resources to avoid collapsing to a single entry in key stringpool. + std::set<ResourceName> name_collapse_exemptions; // Map from original resource paths to shortened resource paths. std::map<std::string, std::string> shortened_path_map; diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp index a9409235e07a..8fbdd7f27041 100644 --- a/tools/aapt2/format/binary/TableFlattener_test.cpp +++ b/tools/aapt2/format/binary/TableFlattener_test.cpp @@ -518,7 +518,7 @@ TEST_F(TableFlattenerTest, LongSharedLibraryPackageNameIsIllegal) { ASSERT_FALSE(Flatten(context.get(), {}, table.get(), &result)); } -TEST_F(TableFlattenerTest, ObfuscatingResourceNamesNoWhitelistSucceeds) { +TEST_F(TableFlattenerTest, ObfuscatingResourceNamesNoNameCollapseExemptionsSucceeds) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() .SetPackageId("com.app.test", 0x7f) @@ -572,7 +572,7 @@ TEST_F(TableFlattenerTest, ObfuscatingResourceNamesNoWhitelistSucceeds) { ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u)); } -TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithWhitelistSucceeds) { +TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithNameCollapseExemptionsSucceeds) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() .SetPackageId("com.app.test", 0x7f) @@ -591,21 +591,22 @@ TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithWhitelistSucceeds) { TableFlattenerOptions options; options.collapse_key_stringpool = true; - options.whitelisted_resources.insert("test"); - options.whitelisted_resources.insert("three"); + options.name_collapse_exemptions.insert(ResourceName({}, ResourceType::kId, "one")); + options.name_collapse_exemptions.insert(ResourceName({}, ResourceType::kString, "test")); ResTable res_table; ASSERT_TRUE(Flatten(context_.get(), options, table.get(), &res_table)); - EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated", + EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f020000), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u)); EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated", ResourceId(0x7f020001), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u)); - EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three", ResourceId(0x7f020002), {}, - Res_value::TYPE_REFERENCE, 0x7f020000u, 0u)); + EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated", + ResourceId(0x7f020002), {}, Res_value::TYPE_REFERENCE, 0x7f020000u, 0u)); + // Note that this resource is also named "one", but it's a different type, so gets obfuscated. EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/0_resource_name_obfuscated", ResourceId(0x7f030000), {}, Res_value::TYPE_INT_DEC, 1u, ResTable_config::CONFIG_VERSION)); |