diff options
222 files changed, 4648 insertions, 4231 deletions
diff --git a/api/current.txt b/api/current.txt index 2e099ed8e2a2..eeaabf661e73 100644 --- a/api/current.txt +++ b/api/current.txt @@ -2812,12 +2812,12 @@ package android.accessibilityservice { method public void onClicked(android.accessibilityservice.AccessibilityButtonController); } - public final class AccessibilityGestureInfo implements android.os.Parcelable { + public final class AccessibilityGestureEvent implements android.os.Parcelable { method public int describeContents(); method public int getDisplayId(); method public int getGestureId(); method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.accessibilityservice.AccessibilityGestureInfo> CREATOR; + field @NonNull public static final android.os.Parcelable.Creator<android.accessibilityservice.AccessibilityGestureEvent> CREATOR; } public abstract class AccessibilityService extends android.app.Service { @@ -2836,7 +2836,7 @@ package android.accessibilityservice { method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent); method public final android.os.IBinder onBind(android.content.Intent); method @Deprecated protected boolean onGesture(int); - method public boolean onGesture(@NonNull android.accessibilityservice.AccessibilityGestureInfo); + method public boolean onGesture(@NonNull android.accessibilityservice.AccessibilityGestureEvent); method public abstract void onInterrupt(); method protected boolean onKeyEvent(android.view.KeyEvent); method protected void onServiceConnected(); @@ -23968,6 +23968,7 @@ package android.media { method @Nullable public long[] getThumbnailRange(); method public boolean hasAttribute(@NonNull String); method public boolean hasThumbnail(); + method public static boolean isSupportedMimeType(@NonNull String); method public boolean isThumbnailCompressed(); method public void saveAttributes() throws java.io.IOException; method public void setAttribute(@NonNull String, @Nullable String); diff --git a/api/system-current.txt b/api/system-current.txt index 4859d542096a..c4c01af630a8 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -8242,6 +8242,7 @@ package android.telephony { method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannelBySlot(int, @Nullable String, int); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduBasicChannelBySlot(int, int, int, int, int, int, @Nullable String); method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduLogicalChannelBySlot(int, int, int, int, int, int, int, @Nullable String); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int); method public boolean isDataConnectivityPossible(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle(); @@ -9516,7 +9517,8 @@ package android.telephony.ims.stub { method public void onReady(); method public final void onSendSmsResult(int, int, int, int) throws java.lang.RuntimeException; method public final void onSmsReceived(int, String, byte[]) throws java.lang.RuntimeException; - method public final void onSmsStatusReportReceived(int, int, String, byte[]) throws java.lang.RuntimeException; + method @Deprecated public final void onSmsStatusReportReceived(int, int, String, byte[]) throws java.lang.RuntimeException; + method public final void onSmsStatusReportReceived(int, String, byte[]) throws java.lang.RuntimeException; method public void sendSms(int, int, String, String, boolean, byte[]); field public static final int DELIVER_STATUS_ERROR_GENERIC = 2; // 0x2 field public static final int DELIVER_STATUS_ERROR_NO_MEMORY = 3; // 0x3 diff --git a/api/test-current.txt b/api/test-current.txt index 6e28f67e9a68..3718c4c8443a 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -34,8 +34,8 @@ package android { package android.accessibilityservice { - public final class AccessibilityGestureInfo implements android.os.Parcelable { - ctor public AccessibilityGestureInfo(int, int); + public final class AccessibilityGestureEvent implements android.os.Parcelable { + ctor public AccessibilityGestureEvent(int, int); } } 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/accessibilityservice/AccessibilityGestureInfo.aidl b/core/java/android/accessibilityservice/AccessibilityGestureEvent.aidl index 2539051f4069..58a9b64ee9c8 100644 --- a/core/java/android/accessibilityservice/AccessibilityGestureInfo.aidl +++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.aidl @@ -16,4 +16,4 @@ package android.accessibilityservice; -parcelable AccessibilityGestureInfo; +parcelable AccessibilityGestureEvent; diff --git a/core/java/android/accessibilityservice/AccessibilityGestureInfo.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java index 28c1dea1dbe7..b5401755a3c8 100644 --- a/core/java/android/accessibilityservice/AccessibilityGestureInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java @@ -44,17 +44,17 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * This class describes the gesture information including gesture id and which display it happens + * This class describes the gesture event including gesture id and which display it happens * on. * <p> * <strong>Note:</strong> Accessibility services setting the * {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} * flag can receive gestures. * - * @see AccessibilityService#onGesture(AccessibilityGestureInfo) + * @see AccessibilityService#onGesture(AccessibilityGestureEvent) */ -public final class AccessibilityGestureInfo implements Parcelable { +public final class AccessibilityGestureEvent implements Parcelable { /** @hide */ @IntDef(prefix = { "GESTURE_" }, value = { @@ -84,12 +84,12 @@ public final class AccessibilityGestureInfo implements Parcelable { /** @hide */ @TestApi - public AccessibilityGestureInfo(int gestureId, int displayId) { + public AccessibilityGestureEvent(int gestureId, int displayId) { mGestureId = gestureId; mDisplayId = displayId; } - private AccessibilityGestureInfo(@NonNull Parcel parcel) { + private AccessibilityGestureEvent(@NonNull Parcel parcel) { mGestureId = parcel.readInt(); mDisplayId = parcel.readInt(); } @@ -117,7 +117,7 @@ public final class AccessibilityGestureInfo implements Parcelable { @NonNull @Override public String toString() { - StringBuilder stringBuilder = new StringBuilder("AccessibilityGestureInfo["); + StringBuilder stringBuilder = new StringBuilder("AccessibilityGestureEvent["); stringBuilder.append("gestureId: ").append(mGestureId); stringBuilder.append(", "); stringBuilder.append("displayId: ").append(mDisplayId); @@ -142,14 +142,14 @@ public final class AccessibilityGestureInfo implements Parcelable { /** * @see Parcelable.Creator */ - public static final @NonNull Parcelable.Creator<AccessibilityGestureInfo> CREATOR = - new Parcelable.Creator<AccessibilityGestureInfo>() { - public AccessibilityGestureInfo createFromParcel(Parcel parcel) { - return new AccessibilityGestureInfo(parcel); + public static final @NonNull Parcelable.Creator<AccessibilityGestureEvent> CREATOR = + new Parcelable.Creator<AccessibilityGestureEvent>() { + public AccessibilityGestureEvent createFromParcel(Parcel parcel) { + return new AccessibilityGestureEvent(parcel); } - public AccessibilityGestureInfo[] newArray(int size) { - return new AccessibilityGestureInfo[size]; + public AccessibilityGestureEvent[] newArray(int size) { + return new AccessibilityGestureEvent[size]; } }; diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index a8daf91eadc0..d3c274f4bdfe 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -382,7 +382,7 @@ public abstract class AccessibilityService extends Service { void onServiceConnected(); void init(int connectionId, IBinder windowToken); /** The detected gesture information for different displays */ - boolean onGesture(AccessibilityGestureInfo gestureInfo); + boolean onGesture(AccessibilityGestureEvent gestureInfo); boolean onKeyEvent(KeyEvent event); /** Magnification changed callbacks for different displays */ void onMagnificationChanged(int displayId, @NonNull Region region, @@ -517,7 +517,7 @@ public abstract class AccessibilityService extends Service { } /** - * Called by {@link #onGesture(AccessibilityGestureInfo)} when the user performs a specific + * Called by {@link #onGesture(AccessibilityGestureEvent)} when the user performs a specific * gesture on the default display. * * <strong>Note:</strong> To receive gestures an accessibility service must @@ -528,7 +528,7 @@ public abstract class AccessibilityService extends Service { * @param gestureId The unique id of the performed gesture. * * @return Whether the gesture was handled. - * @deprecated Override {@link #onGesture(AccessibilityGestureInfo)} instead. + * @deprecated Override {@link #onGesture(AccessibilityGestureEvent)} instead. * * @see #GESTURE_SWIPE_UP * @see #GESTURE_SWIPE_UP_AND_LEFT @@ -564,14 +564,14 @@ public abstract class AccessibilityService extends Service { * <strong>Note:</strong> The default implementation calls {@link #onGesture(int)} when the * touch screen is default display. * - * @param gestureInfo The information of gesture. + * @param gestureEvent The information of gesture. * * @return Whether the gesture was handled. * */ - public boolean onGesture(@NonNull AccessibilityGestureInfo gestureInfo) { - if (gestureInfo.getDisplayId() == Display.DEFAULT_DISPLAY) { - onGesture(gestureInfo.getGestureId()); + public boolean onGesture(@NonNull AccessibilityGestureEvent gestureEvent) { + if (gestureEvent.getDisplayId() == Display.DEFAULT_DISPLAY) { + onGesture(gestureEvent.getGestureId()); } return false; } @@ -1725,8 +1725,8 @@ public abstract class AccessibilityService extends Service { } @Override - public boolean onGesture(AccessibilityGestureInfo gestureInfo) { - return AccessibilityService.this.onGesture(gestureInfo); + public boolean onGesture(AccessibilityGestureEvent gestureEvent) { + return AccessibilityService.this.onGesture(gestureEvent); } @Override @@ -1826,7 +1826,7 @@ public abstract class AccessibilityService extends Service { } @Override - public void onGesture(AccessibilityGestureInfo gestureInfo) { + public void onGesture(AccessibilityGestureEvent gestureInfo) { Message message = mCaller.obtainMessageO(DO_ON_GESTURE, gestureInfo); mCaller.sendMessage(message); } @@ -1942,7 +1942,7 @@ public abstract class AccessibilityService extends Service { case DO_ON_GESTURE: { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { - mCallback.onGesture((AccessibilityGestureInfo) message.obj); + mCallback.onGesture((AccessibilityGestureEvent) message.obj); } } return; diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl index e0d5e4438f23..8ec3aea2728f 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl @@ -20,7 +20,7 @@ import android.accessibilityservice.IAccessibilityServiceConnection; import android.graphics.Region; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityWindowInfo; -import android.accessibilityservice.AccessibilityGestureInfo; +import android.accessibilityservice.AccessibilityGestureEvent; import android.view.KeyEvent; /** @@ -36,7 +36,7 @@ import android.view.KeyEvent; void onInterrupt(); - void onGesture(in AccessibilityGestureInfo gestureInfo); + void onGesture(in AccessibilityGestureEvent gestureEvent); void clearAccessibilityCache(); 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/app/UiAutomation.java b/core/java/android/app/UiAutomation.java index f9b96c50e0b8..fd93450c29f0 100644 --- a/core/java/android/app/UiAutomation.java +++ b/core/java/android/app/UiAutomation.java @@ -16,7 +16,7 @@ package android.app; -import android.accessibilityservice.AccessibilityGestureInfo; +import android.accessibilityservice.AccessibilityGestureEvent; import android.accessibilityservice.AccessibilityService.Callbacks; import android.accessibilityservice.AccessibilityService.IAccessibilityServiceClientWrapper; import android.accessibilityservice.AccessibilityServiceInfo; @@ -1246,7 +1246,7 @@ public final class UiAutomation { } @Override - public boolean onGesture(AccessibilityGestureInfo gestureInfo) { + public boolean onGesture(AccessibilityGestureEvent gestureEvent) { /* do nothing */ return false; } diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index 9d523633feb6..099dea29eaf7 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -60,9 +60,7 @@ import java.util.Set; * multiple possible matching values (via {@link #addAction}, * {@link #addDataType}, {@link #addDataScheme}, {@link #addDataSchemeSpecificPart}, * {@link #addDataAuthority}, {@link #addDataPath}, and {@link #addCategory}, respectively). - * For actions, the field - * will not be tested if no values have been given (treating it as a wildcard); - * if no data characteristics are specified, however, then the filter will + * For actions, if no data characteristics are specified, then the filter will * only match intents that contain no data. * * <p>The data characteristic is 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..8b20f0bec7be 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2103,10 +2103,6 @@ public final class Settings { private static final String TAG = "Settings"; private static final boolean LOCAL_LOGV = false; - // Lock ensures that when enabling/disabling the master location switch, we don't end up - // with a partial enable/disable state in multi-threaded situations. - private static final Object mLocationSettingsLock = new Object(); - // Used in system server calling uid workaround in call() private static boolean sInSystemServer = false; private static final Object sInSystemServerLock = new Object(); @@ -8811,13 +8807,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 @@ -12927,8 +12916,7 @@ public final class Settings { } /** - * Subscription to be used for voice call on a multi sim device. The supported values - * are 0 = SUB1, 1 = SUB2 and etc. + * Subscription Id to be used for voice call on a multi sim device. * @hide */ public static final String MULTI_SIM_VOICE_CALL_SUBSCRIPTION = "multi_sim_voice_call"; @@ -12942,15 +12930,13 @@ public final class Settings { public static final String MULTI_SIM_VOICE_PROMPT = "multi_sim_voice_prompt"; /** - * Subscription to be used for data call on a multi sim device. The supported values - * are 0 = SUB1, 1 = SUB2 and etc. + * Subscription Id to be used for data call on a multi sim device. * @hide */ public static final String MULTI_SIM_DATA_CALL_SUBSCRIPTION = "multi_sim_data_call"; /** - * Subscription to be used for SMS on a multi sim device. The supported values - * are 0 = SUB1, 1 = SUB2 and etc. + * Subscription Id to be used for SMS on a multi sim device. * @hide */ public static final String MULTI_SIM_SMS_SUBSCRIPTION = "multi_sim_sms"; diff --git a/core/java/android/service/carrier/ApnService.java b/core/java/android/service/carrier/ApnService.java index 57e4b1b40748..0c12fd409078 100644 --- a/core/java/android/service/carrier/ApnService.java +++ b/core/java/android/service/carrier/ApnService.java @@ -26,7 +26,7 @@ import android.content.Intent; import android.os.IBinder; import android.util.Log; -import com.android.internal.telephony.IApnSourceService; +import android.service.carrier.IApnSourceService; import java.util.List; diff --git a/telephony/java/com/android/internal/telephony/IApnSourceService.aidl b/core/java/android/service/carrier/IApnSourceService.aidl index 34c9067c3f2f..fadd2ff1a772 100644 --- a/telephony/java/com/android/internal/telephony/IApnSourceService.aidl +++ b/core/java/android/service/carrier/IApnSourceService.aidl @@ -14,10 +14,11 @@ * limitations under the License. */ -package com.android.internal.telephony; +package android.service.carrier; import android.content.ContentValues; +/** @hide */ interface IApnSourceService { /** Retreive APNs. */ ContentValues[] getApns(int subId); 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/IPinnedStackController.aidl b/core/java/android/view/IPinnedStackController.aidl index f1d152ba29af..d2dcb568ef6c 100644 --- a/core/java/android/view/IPinnedStackController.aidl +++ b/core/java/android/view/IPinnedStackController.aidl @@ -16,8 +16,6 @@ package android.view; -import android.graphics.Rect; - /** * An interface to the PinnedStackController to update it of state changes, and to query * information based on the current state. @@ -32,17 +30,15 @@ interface IPinnedStackController { oneway void setIsMinimized(boolean isMinimized); /** - * @return what WM considers to be the current device rotation. + * Notifies the controller of the current min edge size, this is needed to allow the system to + * properly calculate the aspect ratio of the expanded PIP. The given {@param minEdgeSize} is + * always bounded to be larger than the default minEdgeSize, so the caller can call this method + * with 0 to reset to the default size. */ - int getDisplayRotation(); + oneway void setMinEdgeSize(int minEdgeSize); /** - * Notifies the controller to actually start the PiP animation. - * The bounds would be calculated based on the last save reentry fraction internally. - * {@param destinationBounds} is the stack bounds of the final PiP window - * and {@param sourceRectHint} is the source bounds hint used when entering picture-in-picture, - * expect the same bound passed via IPinnedStackListener#onPrepareAnimation. - * {@param animationDuration} suggests the animation duration transitioning to PiP window. + * @return what WM considers to be the current device rotation. */ - void startAnimation(in Rect destinationBounds, in Rect sourceRectHint, int animationDuration); + int getDisplayRotation(); } diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedStackListener.aidl index 806d81e39cca..2da353b1f0ee 100644 --- a/core/java/android/view/IPinnedStackListener.aidl +++ b/core/java/android/view/IPinnedStackListener.aidl @@ -16,10 +16,8 @@ package android.view; -import android.content.ComponentName; import android.content.pm.ParceledListSlice; import android.graphics.Rect; -import android.view.DisplayInfo; import android.view.IPinnedStackController; /** @@ -38,13 +36,18 @@ oneway interface IPinnedStackListener { /** * Called when the window manager has detected a change that would cause the movement bounds * to be changed (ie. after configuration change, aspect ratio change, etc). It then provides - * the components that allow the listener to calculate the movement bounds itself. - * The {@param animatingBounds} are provided to indicate the current target bounds of the - * pinned stack (the final bounds if animating, the current bounds if not), - * which may be helpful in calculating dependent animation bounds. + * the components that allow the listener to calculate the movement bounds itself. The + * {@param normalBounds} are also the default bounds that the PiP would be entered in its + * current state with the aspect ratio applied. The {@param animatingBounds} are provided + * to indicate the current target bounds of the pinned stack (the final bounds if animating, + * the current bounds if not), which may be helpful in calculating dependent animation bounds. + * + * The {@param displayRotation} is provided so that the client can verify when making certain + * calls that it will not provide stale information based on an old display rotation (ie. if + * the WM has changed in the mean time but the client has not received onMovementBoundsChanged). */ - void onMovementBoundsChanged(in Rect animatingBounds, boolean fromImeAdjustment, - boolean fromShelfAdjustment); + void onMovementBoundsChanged(in Rect insetBounds, in Rect normalBounds, in Rect animatingBounds, + boolean fromImeAdjustment, boolean fromShelfAdjustment, int displayRotation); /** * Called when window manager decides to adjust the pinned stack bounds because of the IME, or @@ -73,47 +76,4 @@ oneway interface IPinnedStackListener { * is first registered to allow the listener to synchronized its state with the controller. */ void onActionsChanged(in ParceledListSlice actions); - - /** - * Called by the window manager to notify the listener to save the reentry fraction, - * typically when an Activity leaves PiP (picture-in-picture) mode to fullscreen. - * {@param componentName} represents the application component of PiP window - * while {@param bounds} is the current PiP bounds used to calculate the - * reentry snap fraction. - */ - void onSaveReentrySnapFraction(in ComponentName componentName, in Rect bounds); - - /** - * Called by the window manager to notify the listener to reset saved reentry fraction, - * typically when an Activity enters PiP (picture-in-picture) mode from fullscreen. - * {@param componentName} represents the application component of PiP window. - */ - void onResetReentrySnapFraction(in ComponentName componentName); - - /** - * Called when the window manager has detected change on DisplayInfo, or - * when the listener is first registered to allow the listener to synchronized its state with - * the controller. - */ - void onDisplayInfoChanged(in DisplayInfo displayInfo); - - /** - * Called by the window manager at the beginning of a configuration update cascade - * since the metrics from these resources are used for bounds calculations. - */ - void onConfigurationChanged(); - - /** - * Called by the window manager when the aspect ratio is reset. - */ - void onAspectRatioChanged(float aspectRatio); - - /** - * Called by the window manager to notify the listener to prepare for PiP animation. - * Internally, the target bounds would be calculated from the given {@param aspectRatio} - * and {@param bounds}, the saved reentry snap fraction also contributes. - * Caller would wait for a IPinnedStackController#startAnimation callback to actually - * start the animation, see details in IPinnedStackController. - */ - void onPrepareAnimation(in Rect sourceRectHint, float aspectRatio, in Rect bounds); } 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/android/view/View.java b/core/java/android/view/View.java index bdfb3e318b3b..cfb6a79a674c 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -14466,8 +14466,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } notifyAppearedOrDisappearedForContentCaptureIfNeeded(isVisible); - if (isVisible != oldVisible) { - updateSystemGestureExclusionRects(); + if (!getSystemGestureExclusionRects().isEmpty() && isVisible != oldVisible) { + postUpdateSystemGestureExclusionRects(); } } diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 2f44d6ee88b1..1cb114856a4a 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -4109,7 +4109,7 @@ public class ListView extends AbsListView { final int rowsCount = getCount(); final int selectionMode = getSelectionModeForAccessibility(); final CollectionInfo collectionInfo = CollectionInfo.obtain( - rowsCount, 1, false, selectionMode); + -1, -1, false, selectionMode); info.setCollectionInfo(collectionInfo); if (rowsCount > 0) { 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_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp index cbae2da04281..b6427c9aa01c 100644 --- a/core/jni/android_os_HwBinder.cpp +++ b/core/jni/android_os_HwBinder.cpp @@ -152,7 +152,7 @@ status_t JHwBinder::onTransact( uint32_t flags, TransactCallback callback) { JNIEnv *env = AndroidRuntime::getJNIEnv(); - bool isOneway = (flags & TF_ONE_WAY) != 0; + bool isOneway = (flags & IBinder::FLAG_ONEWAY) != 0; ScopedLocalRef<jobject> replyObj(env, nullptr); sp<JHwParcel> replyContext = nullptr; 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/Android.bp b/core/tests/coretests/Android.bp index 1670d49a46c4..a4c504b9cbdf 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -43,7 +43,6 @@ android_test { libs: [ "android.test.runner", - "telephony-common", "testables", "org.apache.http.legacy", "android.test.base", 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/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index 55583d58e0c2..5b535651abd9 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -62,6 +62,7 @@ import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.TimeZone; @@ -1451,6 +1452,37 @@ public class ExifInterface { } /** + * Returns whether ExifInterface currently supports parsing data from the specified mime type + * or not. + * + * @param mimeType the string value of mime type + */ + public static boolean isSupportedMimeType(@NonNull String mimeType) { + if (mimeType == null) { + throw new NullPointerException("mimeType shouldn't be null"); + } + + switch (mimeType.toLowerCase(Locale.ROOT)) { + case "image/jpeg": + case "image/x-adobe-dng": + case "image/x-canon-cr2": + case "image/x-nikon-nef": + case "image/x-nikon-nrw": + case "image/x-sony-arw": + case "image/x-panasonic-rw2": + case "image/x-olympus-orf": + case "image/x-pentax-pef": + case "image/x-samsung-srw": + case "image/x-fuji-raf": + case "image/heic": + case "image/heif": + return true; + default: + return false; + } + } + + /** * Returns the EXIF attribute of the specified tag or {@code null} if there is no such tag in * the image file. * 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/CarrierDefaultApp/tests/unit/Android.bp b/packages/CarrierDefaultApp/tests/unit/Android.bp index 96144cf76ff7..5655abb046b2 100644 --- a/packages/CarrierDefaultApp/tests/unit/Android.bp +++ b/packages/CarrierDefaultApp/tests/unit/Android.bp @@ -17,7 +17,6 @@ android_test { certificate: "platform", libs: [ "android.test.runner", - "telephony-common", "android.test.base", ], static_libs: [ diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 32fc7ff978cd..720266a72423 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -1521,14 +1521,14 @@ public class SettingsProvider extends ContentProvider { return false; } - // Special cases for location providers (sigh). - if (Settings.Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) { - return updateLocationProvidersAllowedLocked(value, tag, owningUserId, makeDefault, - forceNotify); - } - // Mutate the value. synchronized (mLock) { + // Special cases for location providers (sigh). + if (Settings.Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) { + return updateLocationProvidersAllowedLocked(value, tag, owningUserId, makeDefault, + forceNotify); + } + switch (operation) { case MUTATION_OPERATION_INSERT: { return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SECURE, 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/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 38293bf2defd..61210d3e0011 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -480,4 +480,7 @@ <!-- Preferred refresh rate at keyguard, if supported by the display --> <integer name="config_keyguardRefreshRate">-1</integer> + <!-- Whether or not to add a "people" notifications section --> + <bool name="config_usePeopleFiltering">false</bool> + </resources> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java index 2797042ac160..3ae2df5b97bf 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java @@ -16,10 +16,9 @@ package com.android.systemui.shared.system; -import android.content.ComponentName; import android.content.pm.ParceledListSlice; import android.graphics.Rect; -import android.view.DisplayInfo; +import android.os.RemoteException; import android.view.IPinnedStackController; import android.view.IPinnedStackListener; @@ -33,132 +32,62 @@ import java.util.List; * previously set listener. */ public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub { - private List<PinnedStackListener> mListeners = new ArrayList<>(); + private List<IPinnedStackListener> mListeners = new ArrayList<>(); /** Adds a listener to receive updates from the WindowManagerService. */ - public void addListener(PinnedStackListener listener) { + public void addListener(IPinnedStackListener listener) { mListeners.add(listener); } /** Removes a listener so it will no longer receive updates from the WindowManagerService. */ - public void removeListener(PinnedStackListener listener) { + public void removeListener(IPinnedStackListener listener) { mListeners.remove(listener); } @Override - public void onListenerRegistered(IPinnedStackController controller) { - for (PinnedStackListener listener : mListeners) { + public void onListenerRegistered(IPinnedStackController controller) throws RemoteException { + for (IPinnedStackListener listener : mListeners) { listener.onListenerRegistered(controller); } } @Override - public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment, - boolean fromShelfAdjustment) { - for (PinnedStackListener listener : mListeners) { - listener.onMovementBoundsChanged(animatingBounds, fromImeAdjustment, - fromShelfAdjustment); + public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, Rect animatingBounds, + boolean fromImeAdjustment, boolean fromShelfAdjustment, int displayRotation) + throws RemoteException { + for (IPinnedStackListener listener : mListeners) { + listener.onMovementBoundsChanged( + insetBounds, normalBounds, animatingBounds, + fromImeAdjustment, fromShelfAdjustment, displayRotation); } } @Override - public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { - for (PinnedStackListener listener : mListeners) { + public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) throws RemoteException { + for (IPinnedStackListener listener : mListeners) { listener.onImeVisibilityChanged(imeVisible, imeHeight); } } @Override - public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) { - for (PinnedStackListener listener : mListeners) { + public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) + throws RemoteException { + for (IPinnedStackListener listener : mListeners) { listener.onShelfVisibilityChanged(shelfVisible, shelfHeight); } } @Override - public void onMinimizedStateChanged(boolean isMinimized) { - for (PinnedStackListener listener : mListeners) { + public void onMinimizedStateChanged(boolean isMinimized) throws RemoteException { + for (IPinnedStackListener listener : mListeners) { listener.onMinimizedStateChanged(isMinimized); } } @Override - public void onActionsChanged(ParceledListSlice actions) { - for (PinnedStackListener listener : mListeners) { + public void onActionsChanged(ParceledListSlice actions) throws RemoteException { + for (IPinnedStackListener listener : mListeners) { listener.onActionsChanged(actions); } } - - @Override - public void onSaveReentrySnapFraction(ComponentName componentName, Rect bounds) { - for (PinnedStackListener listener : mListeners) { - listener.onSaveReentrySnapFraction(componentName, bounds); - } - } - - @Override - public void onResetReentrySnapFraction(ComponentName componentName) { - for (PinnedStackListener listener : mListeners) { - listener.onResetReentrySnapFraction(componentName); - } - } - - @Override - public void onDisplayInfoChanged(DisplayInfo displayInfo) { - for (PinnedStackListener listener : mListeners) { - listener.onDisplayInfoChanged(displayInfo); - } - } - - @Override - public void onConfigurationChanged() { - for (PinnedStackListener listener : mListeners) { - listener.onConfigurationChanged(); - } - } - - @Override - public void onAspectRatioChanged(float aspectRatio) { - for (PinnedStackListener listener : mListeners) { - listener.onAspectRatioChanged(aspectRatio); - } - } - - @Override - public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) { - for (PinnedStackListener listener : mListeners) { - listener.onPrepareAnimation(sourceRectHint, aspectRatio, bounds); - } - } - - /** - * A counterpart of {@link IPinnedStackListener} with empty implementations. - * Subclasses can ignore those methods they do not intend to take action upon. - */ - public static class PinnedStackListener { - public void onListenerRegistered(IPinnedStackController controller) {} - - public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment, - boolean fromShelfAdjustment) {} - - public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {} - - public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {} - - public void onMinimizedStateChanged(boolean isMinimized) {} - - public void onActionsChanged(ParceledListSlice actions) {} - - public void onSaveReentrySnapFraction(ComponentName componentName, Rect bounds) {} - - public void onResetReentrySnapFraction(ComponentName componentName) {} - - public void onDisplayInfoChanged(DisplayInfo displayInfo) {} - - public void onConfigurationChanged() {} - - public void onAspectRatioChanged(float aspectRatio) {} - - public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {} - } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java index 9f1a1fafeec6..794c30a7c7c1 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java @@ -27,12 +27,12 @@ import android.graphics.Rect; import android.os.Handler; import android.os.RemoteException; import android.util.Log; +import android.view.IPinnedStackListener; import android.view.WindowManager; import android.view.WindowManagerGlobal; import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture; import com.android.systemui.shared.recents.view.RecentsTransition; -import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener; public class WindowManagerWrapper { @@ -212,7 +212,7 @@ public class WindowManagerWrapper { * Adds a pinned stack listener, which will receive updates from the window manager service * along with any other pinned stack listeners that were added via this method. */ - public void addPinnedStackListener(PinnedStackListener listener) throws RemoteException { + public void addPinnedStackListener(IPinnedStackListener listener) throws RemoteException { mPinnedStackListenerForwarder.addListener(listener); WindowManagerGlobal.getWindowManagerService().registerPinnedStackListener( DEFAULT_DISPLAY, mPinnedStackListenerForwarder); @@ -221,7 +221,7 @@ public class WindowManagerWrapper { /** * Removes a pinned stack listener. */ - public void removePinnedStackListener(PinnedStackListener listener) { + public void removePinnedStackListener(IPinnedStackListener listener) { mPinnedStackListenerForwarder.removeListener(listener); } } 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/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 43576a4e21ef..67fc3e32883f 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -48,6 +48,7 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.pm.ActivityInfo; +import android.content.pm.ParceledListSlice; import android.content.res.Configuration; import android.graphics.Rect; import android.os.RemoteException; @@ -60,6 +61,8 @@ import android.util.Log; import android.util.Pair; import android.util.SparseSetArray; import android.view.Display; +import android.view.IPinnedStackController; +import android.view.IPinnedStackListener; import android.view.ViewGroup; import android.widget.FrameLayout; @@ -73,7 +76,6 @@ import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.system.ActivityManagerWrapper; -import com.android.systemui.shared.system.PinnedStackListenerForwarder; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.shared.system.WindowManagerWrapper; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -991,12 +993,32 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } /** PinnedStackListener that dispatches IME visibility updates to the stack. */ - private class BubblesImeListener extends PinnedStackListenerForwarder.PinnedStackListener { + private class BubblesImeListener extends IPinnedStackListener.Stub { + + @Override + public void onListenerRegistered(IPinnedStackController controller) throws RemoteException { + } + + @Override + public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, + Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment, + int displayRotation) throws RemoteException {} + @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { if (mStackView != null && mStackView.getBubbleCount() > 0) { mStackView.post(() -> mStackView.onImeVisibilityChanged(imeVisible, imeHeight)); } } + + @Override + public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) + throws RemoteException {} + + @Override + public void onMinimizedStateChanged(boolean isMinimized) throws RemoteException {} + + @Override + public void onActionsChanged(ParceledListSlice actions) throws RemoteException {} } } 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/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java index 6795bff6409a..30be7754cffc 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java @@ -18,7 +18,6 @@ package com.android.systemui.pip; import static android.util.TypedValue.COMPLEX_UNIT_DIP; -import android.content.ComponentName; import android.content.Context; import android.content.res.Resources; import android.graphics.Point; @@ -47,6 +46,9 @@ public class PipBoundsHandler { private static final String TAG = PipBoundsHandler.class.getSimpleName(); private static final float INVALID_SNAP_FRACTION = -1f; + // System.identityHashCode guarantees zero for null object. + private static final int INVALID_SYSTEM_IDENTITY_TOKEN = 0; + private final Context mContext; private final IWindowManager mWindowManager; private final PipSnapAlgorithm mSnapAlgorithm; @@ -56,7 +58,7 @@ public class PipBoundsHandler { private final Point mTmpDisplaySize = new Point(); private IPinnedStackController mPinnedStackController; - private ComponentName mLastPipComponentName; + private int mLastPipToken; private float mReentrySnapFraction = INVALID_SNAP_FRACTION; private float mDefaultAspectRatio; @@ -78,11 +80,8 @@ public class PipBoundsHandler { mContext = context; mSnapAlgorithm = new PipSnapAlgorithm(context); mWindowManager = WindowManagerGlobal.getWindowManagerService(); - reloadResources(); - // Initialize the aspect ratio to the default aspect ratio. Don't do this in reload - // resources as it would clobber mAspectRatio when entering PiP from fullscreen which - // triggers a configuration change and the resources to be reloaded. mAspectRatio = mDefaultAspectRatio; + reloadResources(); } /** @@ -162,27 +161,27 @@ public class PipBoundsHandler { } /** - * Responds to IPinnedStackListener on saving reentry snap fraction - * for a given {@link ComponentName}. + * Responds to IPinnedStackListener on saving reentry snap fraction for a given token. + * Token should be generated via {@link System#identityHashCode(Object)} */ - public void onSaveReentrySnapFraction(ComponentName componentName, Rect bounds) { - mReentrySnapFraction = getSnapFraction(bounds); - mLastPipComponentName = componentName; + public void onSaveReentrySnapFraction(int token, Rect stackBounds) { + mReentrySnapFraction = getSnapFraction(stackBounds); + mLastPipToken = token; } /** - * Responds to IPinnedStackListener on resetting reentry snap fraction - * for a given {@link ComponentName}. + * Responds to IPinnedStackListener on resetting reentry snap fraction for a given token. + * Token should be generated via {@link System#identityHashCode(Object)} */ - public void onResetReentrySnapFraction(ComponentName componentName) { - if (componentName.equals(mLastPipComponentName)) { + public void onResetReentrySnapFraction(int token) { + if (mLastPipToken == token) { onResetReentrySnapFractionUnchecked(); } } private void onResetReentrySnapFractionUnchecked() { mReentrySnapFraction = INVALID_SNAP_FRACTION; - mLastPipComponentName = null; + mLastPipToken = INVALID_SYSTEM_IDENTITY_TOKEN; } /** @@ -213,28 +212,24 @@ public class PipBoundsHandler { /** * Responds to IPinnedStackListener on preparing the pinned stack animation. */ - public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) { - final Rect destinationBounds; - if (bounds == null) { - destinationBounds = getDefaultBounds(mReentrySnapFraction); + public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect stackBounds) { + final Rect targetStackBounds; + if (stackBounds == null) { + targetStackBounds = getDefaultBounds(mReentrySnapFraction); } else { - destinationBounds = new Rect(bounds); + targetStackBounds = new Rect(); + targetStackBounds.set(stackBounds); } if (isValidPictureInPictureAspectRatio(aspectRatio)) { - transformBoundsToAspectRatio(destinationBounds, aspectRatio, - false /* useCurrentMinEdgeSize */); + transformBoundsToAspectRatio(targetStackBounds, aspectRatio, + true /* useCurrentMinEdgeSize */); } - if (destinationBounds.equals(bounds)) { + if (targetStackBounds.equals(stackBounds)) { return; } mAspectRatio = aspectRatio; onResetReentrySnapFractionUnchecked(); - try { - mPinnedStackController.startAnimation(destinationBounds, sourceRectHint, - -1 /* animationDuration */); - } catch (RemoteException e) { - Log.e(TAG, "Failed to start PiP animation from SysUI", e); - } + // TODO: callback Window Manager on starting animation with calculated bounds } /** @@ -363,7 +358,6 @@ public class PipBoundsHandler { public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); - pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName); pw.println(innerPrefix + "mReentrySnapFraction=" + mReentrySnapFraction); pw.println(innerPrefix + "mDisplayInfo=" + mDisplayInfo); pw.println(innerPrefix + "mDefaultAspectRatio=" + mDefaultAspectRatio); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index 8dfae32a1939..3be3422a36ad 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -32,16 +32,14 @@ import android.os.Handler; import android.os.RemoteException; import android.util.Log; import android.util.Pair; -import android.view.DisplayInfo; import android.view.IPinnedStackController; +import android.view.IPinnedStackListener; import com.android.systemui.Dependency; import com.android.systemui.UiOffloadThread; import com.android.systemui.pip.BasePipManager; -import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.InputConsumerController; -import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.shared.system.WindowManagerWrapper; @@ -60,12 +58,8 @@ public class PipManager implements BasePipManager { private IActivityTaskManager mActivityTaskManager; private Handler mHandler = new Handler(); - private final PinnedStackListener mPinnedStackListener = new PipManagerPinnedStackListener(); - private final DisplayInfo mTmpDisplayInfo = new DisplayInfo(); - private final Rect mTmpInsetBounds = new Rect(); - private final Rect mTmpNormalBounds = new Rect(); + private final PinnedStackListener mPinnedStackListener = new PinnedStackListener(); - private PipBoundsHandler mPipBoundsHandler; private InputConsumerController mInputConsumerController; private PipMenuActivityController mMenuController; private PipMediaController mMediaController; @@ -125,11 +119,11 @@ public class PipManager implements BasePipManager { /** * Handler for messages from the PIP controller. */ - private class PipManagerPinnedStackListener extends PinnedStackListener { + private class PinnedStackListener extends IPinnedStackListener.Stub { + @Override public void onListenerRegistered(IPinnedStackController controller) { mHandler.post(() -> { - mPipBoundsHandler.setPinnedStackController(controller); mTouchHandler.setPinnedStackController(controller); }); } @@ -137,7 +131,6 @@ public class PipManager implements BasePipManager { @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { mHandler.post(() -> { - mPipBoundsHandler.onImeVisibilityChanged(imeVisible, imeHeight); mTouchHandler.onImeVisibilityChanged(imeVisible, imeHeight); }); } @@ -145,66 +138,31 @@ public class PipManager implements BasePipManager { @Override public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) { mHandler.post(() -> { - mPipBoundsHandler.onShelfVisibilityChanged(shelfVisible, shelfHeight); - mTouchHandler.onShelfVisibilityChanged(shelfVisible, shelfHeight); + mTouchHandler.onShelfVisibilityChanged(shelfVisible, shelfHeight); }); } @Override public void onMinimizedStateChanged(boolean isMinimized) { mHandler.post(() -> { - mPipBoundsHandler.onMinimizedStateChanged(isMinimized); mTouchHandler.setMinimizedState(isMinimized, true /* fromController */); }); } @Override - public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment, - boolean fromShelfAdjustment) { + public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, + Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment, + int displayRotation) { mHandler.post(() -> { - // Populate the inset / normal bounds and DisplayInfo from mPipBoundsHandler first. - mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds, - animatingBounds, mTmpDisplayInfo); - mTouchHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds, - animatingBounds, fromImeAdjustment, fromShelfAdjustment, - mTmpDisplayInfo.rotation); + mTouchHandler.onMovementBoundsChanged(insetBounds, normalBounds, animatingBounds, + fromImeAdjustment, fromShelfAdjustment, displayRotation); }); } @Override public void onActionsChanged(ParceledListSlice actions) { - mHandler.post(() -> mMenuController.setAppActions(actions)); - } - - @Override - public void onSaveReentrySnapFraction(ComponentName componentName, Rect bounds) { - mHandler.post(() -> mPipBoundsHandler.onSaveReentrySnapFraction(componentName, bounds)); - } - - @Override - public void onResetReentrySnapFraction(ComponentName componentName) { - mHandler.post(() -> mPipBoundsHandler.onResetReentrySnapFraction(componentName)); - } - - @Override - public void onDisplayInfoChanged(DisplayInfo displayInfo) { - mHandler.post(() -> mPipBoundsHandler.onDisplayInfoChanged(displayInfo)); - } - - @Override - public void onConfigurationChanged() { - mHandler.post(() -> mPipBoundsHandler.onConfigurationChanged()); - } - - @Override - public void onAspectRatioChanged(float aspectRatio) { - mHandler.post(() -> mPipBoundsHandler.onAspectRatioChanged(aspectRatio)); - } - - @Override - public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) { mHandler.post(() -> { - mPipBoundsHandler.onPrepareAnimation(sourceRectHint, aspectRatio, bounds); + mMenuController.setAppActions(actions); }); } } @@ -226,13 +184,12 @@ public class PipManager implements BasePipManager { } ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); - mPipBoundsHandler = new PipBoundsHandler(context); mInputConsumerController = InputConsumerController.getPipInputConsumer(); mMediaController = new PipMediaController(context, mActivityManager); mMenuController = new PipMenuActivityController(context, mActivityManager, mMediaController, mInputConsumerController); mTouchHandler = new PipTouchHandler(context, mActivityManager, mActivityTaskManager, - mMenuController, mInputConsumerController, mPipBoundsHandler); + mMenuController, mInputConsumerController); mAppOpsListener = new PipAppOpsListener(context, mActivityManager, mTouchHandler.getMotionHelper()); @@ -295,6 +252,5 @@ public class PipManager implements BasePipManager { mInputConsumerController.dump(pw, innerPrefix); mMenuController.dump(pw, innerPrefix); mTouchHandler.dump(pw, innerPrefix); - mPipBoundsHandler.dump(pw, innerPrefix); } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 1f36d97ce308..30cf412671bc 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -48,7 +48,6 @@ import android.view.accessibility.AccessibilityWindowInfo; import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.internal.policy.PipSnapAlgorithm; import com.android.systemui.R; -import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.statusbar.FlingAnimationUtils; @@ -76,7 +75,6 @@ public class PipTouchHandler { private final IActivityTaskManager mActivityTaskManager; private final ViewConfiguration mViewConfig; private final PipMenuListener mMenuListener = new PipMenuListener(); - private final PipBoundsHandler mPipBoundsHandler; private IPinnedStackController mPinnedStackController; private final PipMenuActivityController mMenuController; @@ -180,8 +178,7 @@ public class PipTouchHandler { public PipTouchHandler(Context context, IActivityManager activityManager, IActivityTaskManager activityTaskManager, PipMenuActivityController menuController, - InputConsumerController inputConsumerController, - PipBoundsHandler pipBoundsHandler) { + InputConsumerController inputConsumerController) { // Initialize the Pip input consumer mContext = context; @@ -214,8 +211,6 @@ public class PipTouchHandler { inputConsumerController.setInputListener(this::handleTouchEvent); inputConsumerController.setRegistrationListener(this::onRegistrationChanged); onRegistrationChanged(inputConsumerController.isRegistered()); - - mPipBoundsHandler = pipBoundsHandler; } public void setTouchEnabled(boolean enabled) { @@ -792,8 +787,14 @@ public class PipTouchHandler { mMovementBounds = isMenuExpanded ? mExpandedMovementBounds : mNormalMovementBounds; - mPipBoundsHandler.setMinEdgeSize( - isMenuExpanded ? mExpandedShortestEdgeSize : 0); + try { + if (mPinnedStackController != null) { + mPinnedStackController.setMinEdgeSize( + isMenuExpanded ? mExpandedShortestEdgeSize : 0); + } + } catch (RemoteException e) { + Log.e(TAG, "Could not set minimized state", e); + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index 81d6973efb2a..918af4f0cd4c 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -19,10 +19,13 @@ package com.android.systemui.pip.tv; import static android.app.ActivityTaskManager.INVALID_STACK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.view.Display.DEFAULT_DISPLAY; +import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.StackInfo; import android.app.ActivityTaskManager; +import android.app.IActivityManager; import android.app.IActivityTaskManager; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -43,15 +46,16 @@ import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import android.util.Pair; -import android.view.DisplayInfo; +import android.view.IPinnedStackController; +import android.view.IPinnedStackListener; +import android.view.IWindowManager; +import android.view.WindowManagerGlobal; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.UiOffloadThread; import com.android.systemui.pip.BasePipManager; -import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.shared.system.ActivityManagerWrapper; -import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.shared.system.WindowManagerWrapper; @@ -107,8 +111,9 @@ public class PipManager implements BasePipManager { private int mSuspendPipResizingReason; private Context mContext; - private PipBoundsHandler mPipBoundsHandler; + private IActivityManager mActivityManager; private IActivityTaskManager mActivityTaskManager; + private IWindowManager mWindowManager; private MediaSessionManager mMediaSessionManager; private int mState = STATE_NO_PIP; private int mResumeResizePinnedStackRunnableState = STATE_NO_PIP; @@ -130,16 +135,11 @@ public class PipManager implements BasePipManager { private PipNotification mPipNotification; private ParceledListSlice mCustomActions; - // Used to calculate the movement bounds - private final DisplayInfo mTmpDisplayInfo = new DisplayInfo(); - private final Rect mTmpInsetBounds = new Rect(); - private final Rect mTmpNormalBounds = new Rect(); - // Keeps track of the IME visibility to adjust the PiP when the IME is visible private boolean mImeVisible; private int mImeHeightAdjustment; - private final PinnedStackListener mPinnedStackListener = new PipManagerPinnedStackListener(); + private final PinnedStackListener mPinnedStackListener = new PinnedStackListener(); private final Runnable mResizePinnedStackRunnable = new Runnable() { @Override @@ -181,7 +181,11 @@ public class PipManager implements BasePipManager { /** * Handler for messages from the PIP controller. */ - private class PipManagerPinnedStackListener extends PinnedStackListener { + private class PinnedStackListener extends IPinnedStackListener.Stub { + + @Override + public void onListenerRegistered(IPinnedStackController controller) {} + @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { if (mState == STATE_PIP) { @@ -201,13 +205,17 @@ public class PipManager implements BasePipManager { } @Override - public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment, - boolean fromShelfAdjustment) { + public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {} + + @Override + public void onMinimizedStateChanged(boolean isMinimized) {} + + @Override + public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, + Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment, + int displayRotation) { mHandler.post(() -> { - // Populate the inset / normal bounds and DisplayInfo from mPipBoundsHandler first. - mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds, - animatingBounds, mTmpDisplayInfo); - mDefaultPipBounds.set(animatingBounds); + mDefaultPipBounds.set(normalBounds); }); } @@ -233,8 +241,10 @@ public class PipManager implements BasePipManager { } mInitialized = true; mContext = context; - mPipBoundsHandler = new PipBoundsHandler(context); + + mActivityManager = ActivityManager.getService(); mActivityTaskManager = ActivityTaskManager.getService(); + mWindowManager = WindowManagerGlobal.getWindowManagerService(); ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED); @@ -281,7 +291,7 @@ public class PipManager implements BasePipManager { (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE); try { - WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener); + mWindowManager.registerPinnedStackListener(DEFAULT_DISPLAY, mPinnedStackListener); } catch (RemoteException e) { Log.e(TAG, "Failed to register pinned stack listener", e); } 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/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index 289277e63760..f782fab7e49f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar; import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED; import static com.android.systemui.DejankUtils.whitelistIpcs; +import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT; import android.app.ActivityManager; import android.app.KeyguardManager; @@ -316,7 +317,7 @@ public class NotificationLockscreenUserManagerImpl implements boolean exceedsPriorityThreshold; if (NotificationUtils.useNewInterruptionModel(mContext) && hideSilentNotificationsOnLockscreen()) { - exceedsPriorityThreshold = entry.isTopBucket(); + exceedsPriorityThreshold = entry.getBucket() != BUCKET_SILENT; } else { exceedsPriorityThreshold = !getEntryManager().getNotificationData().isAmbient(entry.key); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index 4a27a4ed31a1..b6b149dd049a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -124,7 +124,7 @@ public class NotificationEntryManager implements @Inject public NotificationEntryManager(Context context) { - mNotificationData = new NotificationData(); + mNotificationData = new NotificationData(context); } /** Adds a {@link NotificationEntryListener}. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java index 1af47dd0f4c0..dfbbf987dd96 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java @@ -36,6 +36,7 @@ public class NotificationUtils { private static final int[] sLocationOffset = new int[2]; @Nullable private static Boolean sUseNewInterruptionModel = null; + @Nullable private static Boolean sUsePeopleFiltering = null; public static boolean isGrayscale(ImageView v, ContrastColorUtil colorUtil) { Object isGrayscale = v.getTag(R.id.icon_is_grayscale); @@ -87,4 +88,17 @@ public class NotificationUtils { } return sUseNewInterruptionModel; } + + /** + * Caches and returns the value of the people filtering setting. Cannot change except through + * process restarts. + */ + public static boolean usePeopleFiltering(Context context) { + if (sUsePeopleFiltering == null) { + sUsePeopleFiltering = context.getResources().getBoolean( + R.bool.config_usePeopleFiltering); + } + + return sUsePeopleFiltering; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java index 727e245ae38b..aacb2dd0682f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java @@ -16,10 +16,15 @@ package com.android.systemui.statusbar.notification.collection; +import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING; +import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE; +import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT; + import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.Person; +import android.content.Context; import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.SnoozeCriterion; @@ -30,6 +35,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.notification.NotificationFilter; +import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -44,6 +50,7 @@ import java.util.Objects; * The list of currently displaying notifications. */ public class NotificationData { + private static final String TAG = "NotificationData"; private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class); @@ -64,6 +71,11 @@ public class NotificationData { private RankingMap mRankingMap; private final Ranking mTmpRanking = new Ranking(); + private final boolean mUsePeopleFiltering; + + public NotificationData(Context context) { + mUsePeopleFiltering = NotificationUtils.usePeopleFiltering(context); + } public void setHeadsUpManager(HeadsUpManager headsUpManager) { mHeadsUpManager = headsUpManager; @@ -72,52 +84,25 @@ public class NotificationData { @VisibleForTesting protected final Comparator<NotificationEntry> mRankingComparator = new Comparator<NotificationEntry>() { - private final Ranking mRankingA = new Ranking(); - private final Ranking mRankingB = new Ranking(); - @Override public int compare(NotificationEntry a, NotificationEntry b) { final StatusBarNotification na = a.notification; final StatusBarNotification nb = b.notification; - int aImportance = NotificationManager.IMPORTANCE_DEFAULT; - int bImportance = NotificationManager.IMPORTANCE_DEFAULT; - int aRank = 0; - int bRank = 0; - - if (mRankingMap != null) { - // RankingMap as received from NoMan - getRanking(a.key, mRankingA); - getRanking(b.key, mRankingB); - aImportance = mRankingA.getImportance(); - bImportance = mRankingB.getImportance(); - aRank = mRankingA.getRank(); - bRank = mRankingB.getRank(); - } + int aRank = getRank(a.key); + int bRank = getRank(b.key); - String mediaNotification = getMediaManager().getMediaNotificationKey(); + boolean aMedia = isImportantMedia(a); + boolean bMedia = isImportantMedia(b); - // IMPORTANCE_MIN media streams are allowed to drift to the bottom - final boolean aMedia = a.key.equals(mediaNotification) - && aImportance > NotificationManager.IMPORTANCE_MIN; - final boolean bMedia = b.key.equals(mediaNotification) - && bImportance > NotificationManager.IMPORTANCE_MIN; + boolean aSystemMax = isSystemMax(a); + boolean bSystemMax = isSystemMax(b); - boolean aSystemMax = aImportance >= NotificationManager.IMPORTANCE_HIGH - && isSystemNotification(na); - boolean bSystemMax = bImportance >= NotificationManager.IMPORTANCE_HIGH - && isSystemNotification(nb); + boolean aHeadsUp = a.isRowHeadsUp(); + boolean bHeadsUp = b.isRowHeadsUp(); - - boolean aHeadsUp = a.getRow().isHeadsUp(); - boolean bHeadsUp = b.getRow().isHeadsUp(); - - // HACK: This should really go elsewhere, but it's currently not straightforward to - // extract the comparison code and we're guaranteed to touch every element, so this is - // the best place to set the buckets for the moment. - a.setIsTopBucket(aHeadsUp || aMedia || aSystemMax || a.isHighPriority()); - b.setIsTopBucket(bHeadsUp || bMedia || bSystemMax || b.isHighPriority()); - - if (aHeadsUp != bHeadsUp) { + if (mUsePeopleFiltering && a.hasAssociatedPeople() != b.hasAssociatedPeople()) { + return a.hasAssociatedPeople() ? -1 : 1; + } else if (aHeadsUp != bHeadsUp) { return aHeadsUp ? -1 : 1; } else if (aHeadsUp) { // Provide consistent ranking with headsUpManager @@ -317,14 +302,6 @@ public class NotificationData { return Ranking.VISIBILITY_NO_OVERRIDE; } - public int getImportance(String key) { - if (mRankingMap != null) { - getRanking(key, mTmpRanking); - return mTmpRanking.getImportance(); - } - return NotificationManager.IMPORTANCE_UNSPECIFIED; - } - public List<SnoozeCriterion> getSnoozeCriteria(String key) { if (mRankingMap != null) { getRanking(key, mTmpRanking); @@ -349,6 +326,22 @@ public class NotificationData { return 0; } + private boolean isImportantMedia(NotificationEntry e) { + int importance = e.ranking().getImportance(); + boolean media = e.key.equals(getMediaManager().getMediaNotificationKey()) + && importance > NotificationManager.IMPORTANCE_MIN; + + return media; + } + + private boolean isSystemMax(NotificationEntry e) { + int importance = e.ranking().getImportance(); + boolean sys = importance >= NotificationManager.IMPORTANCE_HIGH + && isSystemNotification(e.notification); + + return sys; + } + public boolean shouldHide(String key) { if (mRankingMap != null) { getRanking(key, mTmpRanking); @@ -414,13 +407,37 @@ public class NotificationData { } } - if (mSortedAndFiltered.size() == 1) { - // HACK: We need the comparator to run on all children in order to set the - // isHighPriority field. If there is only one child, then the comparison won't be run, - // so we have to trigger it manually. Get rid of this code as soon as possible. - mRankingComparator.compare(mSortedAndFiltered.get(0), mSortedAndFiltered.get(0)); + Collections.sort(mSortedAndFiltered, mRankingComparator); + + int bucket = BUCKET_PEOPLE; + for (NotificationEntry e : mSortedAndFiltered) { + assignBucketForEntry(e); + if (e.getBucket() < bucket) { + android.util.Log.wtf(TAG, "Detected non-contiguous bucket!"); + } + bucket = e.getBucket(); + } + } + + private void assignBucketForEntry(NotificationEntry e) { + boolean isHeadsUp = e.isRowHeadsUp(); + boolean isMedia = isImportantMedia(e); + boolean isSystemMax = isSystemMax(e); + + setBucket(e, isHeadsUp, isMedia, isSystemMax); + } + + private void setBucket( + NotificationEntry e, + boolean isHeadsUp, + boolean isMedia, + boolean isSystemMax) { + if (mUsePeopleFiltering && e.hasAssociatedPeople()) { + e.setBucket(BUCKET_PEOPLE); + } else if (isHeadsUp || isMedia || isSystemMax || e.isHighPriority()) { + e.setBucket(BUCKET_ALERTING); } else { - Collections.sort(mSortedAndFiltered, mRankingComparator); + e.setBucket(BUCKET_SILENT); } } @@ -466,6 +483,19 @@ public class NotificationData { } /** + * Get the current set of buckets for notification entries, as defined here + */ + public static int[] getNotificationBuckets(Context context) { + if (NotificationUtils.usePeopleFiltering(context)) { + return new int[]{BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT}; + } else if (NotificationUtils.useNewInterruptionModel(context)) { + return new int[]{BUCKET_ALERTING, BUCKET_SILENT}; + } else { + return new int[]{BUCKET_ALERTING}; + } + } + + /** * Provides access to keyguard state and user settings dependent data. */ public interface KeyguardEnvironment { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index d1b05603223d..a33d23c0b5d5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -21,6 +21,7 @@ import static android.app.Notification.CATEGORY_CALL; import static android.app.Notification.CATEGORY_EVENT; import static android.app.Notification.CATEGORY_MESSAGE; import static android.app.Notification.CATEGORY_REMINDER; +import static android.app.Notification.EXTRA_MESSAGES; import static android.app.Notification.FLAG_BUBBLE; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE; @@ -29,6 +30,8 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICAT import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; +import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING; + import android.annotation.NonNull; import android.app.Notification; import android.app.Notification.MessagingStyle.Message; @@ -38,6 +41,7 @@ import android.app.Person; import android.content.Context; import android.graphics.drawable.Icon; import android.os.Bundle; +import android.os.Parcelable; import android.os.SystemClock; import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.SnoozeCriterion; @@ -58,6 +62,7 @@ import com.android.systemui.statusbar.notification.InflationException; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag; import com.android.systemui.statusbar.notification.row.NotificationGuts; +import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager; import java.util.ArrayList; import java.util.List; @@ -99,6 +104,7 @@ public final class NotificationEntry { public int targetSdk; private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET; public CharSequence remoteInputText; + private final List<Person> mAssociatedPeople = new ArrayList<>(); /** * If {@link android.app.RemoteInput#getEditChoicesBeforeSending} is enabled, and the user is @@ -140,43 +146,18 @@ public final class NotificationEntry { */ private boolean mHighPriority; - private boolean mIsTopBucket; - private boolean mSensitive = true; private Runnable mOnSensitiveChangedListener; private boolean mAutoHeadsUp; private boolean mPulseSupressed; + private int mBucket = BUCKET_ALERTING; public NotificationEntry( - StatusBarNotification sbn, + @NonNull StatusBarNotification sbn, @NonNull Ranking ranking) { - this(sbn, ranking, false); - } - - private NotificationEntry( - StatusBarNotification sbn, - Ranking ranking, - boolean isTest) { this.key = sbn.getKey(); - this.notification = sbn; - - // TODO: Update tests to no longer need to pass null ranking - if (ranking != null) { - setRanking(ranking); - } else if (!isTest) { - throw new IllegalArgumentException("Ranking cannot be null"); - } - } - - /** - * Method for old tests that build NotificationEntries without a ranking. - * - * @deprecated Use NotificationEntryBuilder instead. - */ - @VisibleForTesting - @Deprecated - public static NotificationEntry buildForTest(StatusBarNotification sbn) { - return new NotificationEntry(sbn, null, true); + setNotification(sbn); + setRanking(ranking); } /** The key for this notification. Guaranteed to be immutable and unique */ @@ -197,11 +178,12 @@ public final class NotificationEntry { * TODO: Make this package-private */ public void setNotification(StatusBarNotification sbn) { - if (!sbn.getKey().equals(key)) { + if (sbn.getKey() != null && key != null && !sbn.getKey().equals(key)) { throw new IllegalArgumentException("New key " + sbn.getKey() + " doesn't match existing key " + key); } notification = sbn; + updatePeopleList(); } /** @@ -218,6 +200,10 @@ public final class NotificationEntry { * TODO: Make this package-private */ public void setRanking(@NonNull Ranking ranking) { + if (!ranking.getKey().equals(key)) { + throw new IllegalArgumentException("New key " + ranking.getKey() + + " doesn't match existing key " + key); + } mRanking = ranking; } @@ -283,20 +269,39 @@ public final class NotificationEntry { this.mHighPriority = highPriority; } - /** - * @return True if the notif should appear in the "top" or "important" section of notifications - * (as opposed to the "bottom" or "silent" section). This is usually the same as - * {@link #isHighPriority()}, but there are certain exceptions, such as media notifs. - */ - public boolean isTopBucket() { - return mIsTopBucket; + public boolean isBubble() { + return (notification.getNotification().flags & FLAG_BUBBLE) != 0; } - public void setIsTopBucket(boolean isTopBucket) { - mIsTopBucket = isTopBucket; + + private void updatePeopleList() { + mAssociatedPeople.clear(); + + Bundle extras = notification.getNotification().extras; + if (extras == null) { + return; + } + + List<Person> p = extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST); + + if (p != null) { + mAssociatedPeople.addAll(p); + } + + if (Notification.MessagingStyle.class.equals( + notification.getNotification().getNotificationStyle())) { + final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES); + if (!ArrayUtils.isEmpty(messages)) { + for (Notification.MessagingStyle.Message message : + Notification.MessagingStyle.Message + .getMessagesFromBundleArray(messages)) { + mAssociatedPeople.add(message.getSenderPerson()); + } + } + } } - public boolean isBubble() { - return (notification.getNotification().flags & FLAG_BUBBLE) != 0; + boolean hasAssociatedPeople() { + return mAssociatedPeople.size() > 0; } /** @@ -315,6 +320,15 @@ public final class NotificationEntry { } } + @NotificationSectionsManager.PriorityBucket + public int getBucket() { + return mBucket; + } + + public void setBucket(@NotificationSectionsManager.PriorityBucket int bucket) { + mBucket = bucket; + } + public ExpandableNotificationRow getRow() { return row; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java index 422184699952..ec0c6348fd89 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java @@ -16,11 +16,10 @@ package com.android.systemui.statusbar.notification.stack; -import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.NUM_SECTIONS; - - +import android.content.Context; import android.util.MathUtils; +import com.android.systemui.statusbar.notification.collection.NotificationData; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -51,11 +50,14 @@ public class NotificationRoundnessManager implements OnHeadsUpChangedListener { private float mAppearFraction; @Inject - NotificationRoundnessManager(KeyguardBypassController keyguardBypassController) { - mFirstInSectionViews = new ActivatableNotificationView[NUM_SECTIONS]; - mLastInSectionViews = new ActivatableNotificationView[NUM_SECTIONS]; - mTmpFirstInSectionViews = new ActivatableNotificationView[NUM_SECTIONS]; - mTmpLastInSectionViews = new ActivatableNotificationView[NUM_SECTIONS]; + NotificationRoundnessManager( + KeyguardBypassController keyguardBypassController, + Context context) { + int numberOfSections = NotificationData.getNotificationBuckets(context).length; + mFirstInSectionViews = new ActivatableNotificationView[numberOfSections]; + mLastInSectionViews = new ActivatableNotificationView[numberOfSections]; + mTmpFirstInSectionViews = new ActivatableNotificationView[numberOfSections]; + mTmpLastInSectionViews = new ActivatableNotificationView[numberOfSections]; mBypassController = keyguardBypassController; } @@ -157,7 +159,7 @@ public class NotificationRoundnessManager implements OnHeadsUpChangedListener { public void updateRoundedChildren(NotificationSection[] sections) { boolean anyChanged = false; - for (int i = 0; i < NUM_SECTIONS; i++) { + for (int i = 0; i < sections.length; i++) { mTmpFirstInSectionViews[i] = mFirstInSectionViews[i]; mTmpLastInSectionViews[i] = mLastInSectionViews[i]; mFirstInSectionViews[i] = sections[i].getFirstVisibleChild(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java index f39ed2e89432..9d456ef785a8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java @@ -33,6 +33,7 @@ import com.android.systemui.statusbar.notification.row.ActivatableNotificationVi * bounds change. */ class NotificationSection { + private @NotificationSectionsManager.PriorityBucket int mBucket; private View mOwningView; private Rect mBounds = new Rect(); private Rect mCurrentBounds = new Rect(-1, -1, -1, -1); @@ -43,8 +44,9 @@ class NotificationSection { private ActivatableNotificationView mFirstVisibleChild; private ActivatableNotificationView mLastVisibleChild; - NotificationSection(View owningView) { + NotificationSection(View owningView, @NotificationSectionsManager.PriorityBucket int bucket) { mOwningView = owningView; + mBucket = bucket; } public void cancelAnimators() { @@ -72,6 +74,11 @@ class NotificationSection { return mBottomAnimator != null || mTopAnimator != null; } + @NotificationSectionsManager.PriorityBucket + public int getBucket() { + return mBucket; + } + public void startBackgroundAnimation(boolean animateTop, boolean animateBottom) { // Left and right bounds are always applied immediately. mCurrentBounds.left = mBounds.left; @@ -199,12 +206,16 @@ class NotificationSection { return mLastVisibleChild; } - public void setFirstVisibleChild(ActivatableNotificationView child) { + public boolean setFirstVisibleChild(ActivatableNotificationView child) { + boolean changed = mFirstVisibleChild != child; mFirstVisibleChild = child; + return changed; } - public void setLastVisibleChild(ActivatableNotificationView child) { + public boolean setLastVisibleChild(ActivatableNotificationView child) { + boolean changed = mLastVisibleChild != child; mLastVisibleChild = child; + return changed; } public void resetCurrentBounds() { @@ -291,5 +302,4 @@ class NotificationSection { mBounds.bottom = bottom; return bottom; } - } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java index d119fb79e4c6..d0444ae81b89 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java @@ -18,6 +18,10 @@ package com.android.systemui.statusbar.notification.stack; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Intent; import android.provider.Settings; @@ -34,23 +38,31 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; +import java.lang.annotation.Retention; +import java.util.ArrayList; +import java.util.List; + /** * Manages the boundaries of the two notification sections (high priority and low priority). Also * shows/hides the headers for those sections where appropriate. * * TODO: Move remaining sections logic from NSSL into this class. */ -class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvider { +public class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvider { + + private static final String TAG = "NotifSectionsManager"; + private static final boolean DEBUG = false; + private final NotificationStackScrollLayout mParent; private final ActivityStarter mActivityStarter; private final StatusBarStateController mStatusBarStateController; private final ConfigurationController mConfigurationController; - private final boolean mUseMultipleSections; + private final int mNumberOfSections; private boolean mInitialized = false; private SectionHeaderView mGentleHeader; private boolean mGentleHeaderVisible = false; - @Nullable private ExpandableNotificationRow mFirstGentleNotif; + @Nullable private View.OnClickListener mOnClearGentleNotifsClickListener; NotificationSectionsManager( @@ -58,12 +70,21 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide ActivityStarter activityStarter, StatusBarStateController statusBarStateController, ConfigurationController configurationController, - boolean useMultipleSections) { + int numberOfSections) { mParent = parent; mActivityStarter = activityStarter; mStatusBarStateController = statusBarStateController; mConfigurationController = configurationController; - mUseMultipleSections = useMultipleSections; + mNumberOfSections = numberOfSections; + } + + NotificationSection[] createSectionsForBuckets(int[] buckets) { + NotificationSection[] sections = new NotificationSection[buckets.length]; + for (int i = 0; i < buckets.length; i++) { + sections[i] = new NotificationSection(mParent, buckets[i] /* bucket */); + } + + return sections; } /** Must be called before use. */ @@ -111,8 +132,38 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide } @Override - public boolean beginsSection(View view) { - return view == getFirstLowPriorityChild(); + public boolean beginsSection(@NonNull View view, @Nullable View previous) { + boolean begin = false; + if (view instanceof ExpandableNotificationRow) { + if (previous instanceof ExpandableNotificationRow) { + // If we're drawing the first non-person notification, break out a section + ExpandableNotificationRow curr = (ExpandableNotificationRow) view; + ExpandableNotificationRow prev = (ExpandableNotificationRow) previous; + + begin = curr.getEntry().getBucket() != prev.getEntry().getBucket(); + } + } + + if (!begin) { + begin = view == mGentleHeader; + } + + return begin; + } + + private boolean isUsingMultipleSections() { + return mNumberOfSections > 1; + } + + private @PriorityBucket int getBucket(ActivatableNotificationView view) + throws IllegalArgumentException { + if (view instanceof ExpandableNotificationRow) { + return ((ExpandableNotificationRow) view).getEntry().getBucket(); + } else if (view == mGentleHeader) { + return BUCKET_SILENT; + } + + throw new IllegalArgumentException("I don't know how to find a bucket for this view :("); } /** @@ -120,11 +171,10 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide * bookkeeping and adds/moves/removes section headers if appropriate. */ void updateSectionBoundaries() { - if (!mUseMultipleSections) { + if (!isUsingMultipleSections()) { return; } - mFirstGentleNotif = null; int firstGentleNotifIndex = -1; final int n = mParent.getChildCount(); @@ -133,9 +183,8 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide if (child instanceof ExpandableNotificationRow && child.getVisibility() != View.GONE) { ExpandableNotificationRow row = (ExpandableNotificationRow) child; - if (!row.getEntry().isTopBucket()) { + if (row.getEntry().getBucket() == BUCKET_SILENT) { firstGentleNotifIndex = i; - mFirstGentleNotif = row; break; } } @@ -184,78 +233,73 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide } /** - * Updates the boundaries (as tracked by their first and last views) of the high and low - * priority sections. + * Updates the boundaries (as tracked by their first and last views) of the priority sections. * * @return {@code true} If the last view in the top section changed (so we need to animate). */ - boolean updateFirstAndLastViewsInSections( - final NotificationSection highPrioritySection, - final NotificationSection lowPrioritySection, - ActivatableNotificationView firstChild, - ActivatableNotificationView lastChild) { - if (mUseMultipleSections) { - ActivatableNotificationView previousLastHighPriorityChild = - highPrioritySection.getLastVisibleChild(); - ActivatableNotificationView previousFirstLowPriorityChild = - lowPrioritySection.getFirstVisibleChild(); - ActivatableNotificationView lastHighPriorityChild = getLastHighPriorityChild(); - ActivatableNotificationView firstLowPriorityChild = getFirstLowPriorityChild(); - if (lastHighPriorityChild != null && firstLowPriorityChild != null) { - highPrioritySection.setFirstVisibleChild(firstChild); - highPrioritySection.setLastVisibleChild(lastHighPriorityChild); - lowPrioritySection.setFirstVisibleChild(firstLowPriorityChild); - lowPrioritySection.setLastVisibleChild(lastChild); - } else if (lastHighPriorityChild != null) { - highPrioritySection.setFirstVisibleChild(firstChild); - highPrioritySection.setLastVisibleChild(lastChild); - lowPrioritySection.setFirstVisibleChild(null); - lowPrioritySection.setLastVisibleChild(null); - } else { - highPrioritySection.setFirstVisibleChild(null); - highPrioritySection.setLastVisibleChild(null); - lowPrioritySection.setFirstVisibleChild(firstChild); - lowPrioritySection.setLastVisibleChild(lastChild); + boolean updateFirstAndLastViewsForAllSections( + NotificationSection[] sections, + List<ActivatableNotificationView> children) { + + if (sections.length <= 0 || children.size() <= 0) { + for (NotificationSection s : sections) { + s.setFirstVisibleChild(null); + s.setLastVisibleChild(null); } - return lastHighPriorityChild != previousLastHighPriorityChild - || firstLowPriorityChild != previousFirstLowPriorityChild; - } else { - highPrioritySection.setFirstVisibleChild(firstChild); - highPrioritySection.setLastVisibleChild(lastChild); return false; } - } - @VisibleForTesting - SectionHeaderView getGentleHeaderView() { - return mGentleHeader; - } + boolean changed = false; + ArrayList<ActivatableNotificationView> viewsInBucket = new ArrayList<>(); + for (NotificationSection s : sections) { + int filter = s.getBucket(); + viewsInBucket.clear(); - @Nullable - private ActivatableNotificationView getFirstLowPriorityChild() { - if (mGentleHeaderVisible) { - return mGentleHeader; - } else { - return mFirstGentleNotif; - } - } + //TODO: do this in a single pass, and more better + for (ActivatableNotificationView v : children) { + if (getBucket(v) == filter) { + viewsInBucket.add(v); + } - @Nullable - private ActivatableNotificationView getLastHighPriorityChild() { - ActivatableNotificationView lastChildBeforeGap = null; - int childCount = mParent.getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = mParent.getChildAt(i); - if (child.getVisibility() != View.GONE && child instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) child; - if (!row.getEntry().isTopBucket()) { - break; + if (viewsInBucket.size() >= 1) { + changed |= s.setFirstVisibleChild(viewsInBucket.get(0)); + changed |= s.setLastVisibleChild(viewsInBucket.get(viewsInBucket.size() - 1)); } else { - lastChildBeforeGap = row; + changed |= s.setFirstVisibleChild(null); + changed |= s.setLastVisibleChild(null); } } } - return lastChildBeforeGap; + + if (DEBUG) { + logSections(sections); + } + + return changed; + } + + private void logSections(NotificationSection[] sections) { + for (int i = 0; i < sections.length; i++) { + NotificationSection s = sections[i]; + ActivatableNotificationView first = s.getFirstVisibleChild(); + String fs = first == null ? "(null)" + : (first instanceof ExpandableNotificationRow) + ? ((ExpandableNotificationRow) first).getEntry().key + : Integer.toHexString(System.identityHashCode(first)); + ActivatableNotificationView last = s.getLastVisibleChild(); + String ls = last == null ? "(null)" + : (last instanceof ExpandableNotificationRow) + ? ((ExpandableNotificationRow) last).getEntry().key + : Integer.toHexString(System.identityHashCode(last)); + android.util.Log.d(TAG, "updateSections: f=" + fs + " s=" + i); + android.util.Log.d(TAG, "updateSections: l=" + ls + " s=" + i); + } + } + + + @VisibleForTesting + SectionHeaderView getGentleHeaderView() { + return mGentleHeader; } private final ConfigurationListener mConfigurationListener = new ConfigurationListener() { @@ -279,4 +323,20 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide mOnClearGentleNotifsClickListener.onClick(v); } } + + /** + * For now, declare the available notification buckets (sections) here so that other + * presentation code can decide what to do based on an entry's buckets + * + */ + @Retention(SOURCE) + @IntDef(prefix = { "BUCKET_" }, value = { + BUCKET_PEOPLE, + BUCKET_ALERTING, + BUCKET_SILENT + }) + public @interface PriorityBucket {} + public static final int BUCKET_PEOPLE = 0; + public static final int BUCKET_ALERTING = 1; + public static final int BUCKET_SILENT = 2; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 1c9b2256af9b..a67018ef9710 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.stack; import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters; +import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT; import static com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.ANCHOR_SCROLLING; import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE; import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY; @@ -112,6 +113,7 @@ import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.ShadeViewRefactor; import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent; import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.NotificationData; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; @@ -172,7 +174,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd * Sentinel value for no current active pointer. Used by {@link #mActivePointerId}. */ private static final int INVALID_POINTER = -1; - static final int NUM_SECTIONS = 2; /** * The distance in pixels between sections when the sections are directly adjacent (no visible * gap is drawn between them). In this case we don't want to round their corners. @@ -351,7 +352,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return true; } }; - private NotificationSection[] mSections = new NotificationSection[NUM_SECTIONS]; + private NotificationSection[] mSections; private boolean mAnimateNextBackgroundTop; private boolean mAnimateNextBackgroundBottom; private boolean mAnimateNextSectionBoundsChange; @@ -522,9 +523,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mAllowLongPress = allowLongPress; - for (int i = 0; i < NUM_SECTIONS; i++) { - mSections[i] = new NotificationSection(this); - } mRoundnessManager = notificationRoundnessManager; mHeadsUpManager = headsUpManager; @@ -533,19 +531,21 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mKeyguardBypassController = keyguardBypassController; mFalsingManager = falsingManager; + int[] buckets = NotificationData.getNotificationBuckets(context); mSectionsManager = new NotificationSectionsManager( this, activityStarter, statusBarStateController, configurationController, - NotificationUtils.useNewInterruptionModel(context)); + buckets.length); mSectionsManager.initialize(LayoutInflater.from(context)); mSectionsManager.setOnClearGentleNotifsClickListener(v -> { // Leave the shade open if there will be other notifs left over to clear final boolean closeShade = !hasActiveClearableNotifications(ROWS_HIGH_PRIORITY); clearNotifications(ROWS_GENTLE, closeShade); }); + mSections = mSectionsManager.createSectionsForBuckets(buckets); mAmbientState = new AmbientState(context, mSectionsManager, mHeadsUpManager); mBgColor = context.getColor(R.color.notification_shade_background_color); @@ -773,7 +773,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd protected void onDraw(Canvas canvas) { if (mShouldDrawNotificationBackground && (mSections[0].getCurrentBounds().top - < mSections[NUM_SECTIONS - 1].getCurrentBounds().bottom + < mSections[mSections.length - 1].getCurrentBounds().bottom || mAmbientState.isDozing())) { drawBackground(canvas); } else if (mInHeadsUpPinnedMode || mHeadsUpAnimatingAway) { @@ -819,7 +819,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd int lockScreenLeft = mSidePaddings; int lockScreenRight = getWidth() - mSidePaddings; int lockScreenTop = mSections[0].getCurrentBounds().top; - int lockScreenBottom = mSections[NUM_SECTIONS - 1].getCurrentBounds().bottom; + int lockScreenBottom = mSections[mSections.length - 1].getCurrentBounds().bottom; int hiddenLeft = getWidth() / 2; int hiddenTop = mTopPadding; @@ -2636,6 +2636,21 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return null; } + //TODO: We shouldn't have to generate this list every time + private List<ActivatableNotificationView> getChildrenWithBackground() { + ArrayList<ActivatableNotificationView> children = new ArrayList<>(); + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView + && child != mShelf) { + children.add((ActivatableNotificationView) child); + } + } + + return children; + } + /** * Fling the scroll view * @@ -3198,8 +3213,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd ActivatableNotificationView firstChild = getFirstChildWithBackground(); ActivatableNotificationView lastChild = getLastChildWithBackground(); - boolean sectionViewsChanged = mSectionsManager.updateFirstAndLastViewsInSections( - mSections[0], mSections[1], firstChild, lastChild); + boolean sectionViewsChanged = mSectionsManager.updateFirstAndLastViewsForAllSections( + mSections, getChildrenWithBackground()); if (mAnimationsEnabled && mIsExpanded) { mAnimateNextBackgroundTop = firstChild != previousFirstChild; @@ -5780,7 +5795,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd currentIndex++; boolean beforeSpeedBump; if (mHighPriorityBeforeSpeedBump) { - beforeSpeedBump = row.getEntry().isTopBucket(); + beforeSpeedBump = row.getEntry().getBucket() < BUCKET_SILENT; } else { beforeSpeedBump = !row.getEntry().isAmbient(); } @@ -5838,9 +5853,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd case ROWS_ALL: return true; case ROWS_HIGH_PRIORITY: - return row.getEntry().isTopBucket(); + return row.getEntry().getBucket() < BUCKET_SILENT; case ROWS_GENTLE: - return !row.getEntry().isTopBucket(); + return row.getEntry().getBucket() == BUCKET_SILENT; default: throw new IllegalArgumentException("Unknown selection: " + selection); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index ef8048487022..4b61064f4a54 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.notification.stack; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.content.res.Resources; import android.util.Log; @@ -411,8 +413,10 @@ public class StackScrollAlgorithm { float currentYPosition, boolean reverse) { ExpandableView child = algorithmState.visibleChildren.get(i); + ExpandableView previousChild = i > 0 ? algorithmState.visibleChildren.get(i - 1) : null; final boolean applyGapHeight = - childNeedsGapHeight(ambientState.getSectionProvider(), algorithmState, i, child); + childNeedsGapHeight( + ambientState.getSectionProvider(), algorithmState, i, child, previousChild); ExpandableViewState childViewState = child.getViewState(); childViewState.location = ExpandableViewState.LOCATION_UNKNOWN; @@ -477,8 +481,11 @@ public class StackScrollAlgorithm { SectionProvider sectionProvider, StackScrollAlgorithmState algorithmState, int visibleIndex, - View child) { - boolean needsGapHeight = sectionProvider.beginsSection(child) && visibleIndex > 0; + View child, + View previousChild) { + + boolean needsGapHeight = sectionProvider.beginsSection(child, previousChild) + && visibleIndex > 0; if (ANCHOR_SCROLLING) { needsGapHeight &= visibleIndex != algorithmState.anchorViewIndex; } @@ -749,6 +756,6 @@ public class StackScrollAlgorithm { * True if this view starts a new "section" of notifications, such as the gentle * notifications section. False if sections are not enabled. */ - boolean beginsSection(View view); + boolean beginsSection(@NonNull View view, @Nullable View previous); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java index 4cd3ad27ab34..575b5597b657 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone; import static android.view.Display.INVALID_DISPLAY; import android.content.Context; +import android.content.pm.ParceledListSlice; import android.content.res.Resources; import android.graphics.PixelFormat; import android.graphics.Point; @@ -35,6 +36,8 @@ import android.util.Log; import android.util.MathUtils; import android.util.StatsLog; import android.view.Gravity; +import android.view.IPinnedStackController; +import android.view.IPinnedStackListener; import android.view.ISystemGestureExclusionListener; import android.view.InputChannel; import android.view.InputDevice; @@ -54,7 +57,6 @@ import com.android.systemui.R; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.model.SysUiState; import com.android.systemui.recents.OverviewProxyService; -import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.WindowManagerWrapper; @@ -70,13 +72,35 @@ public class EdgeBackGestureHandler implements DisplayListener { private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt( "gestures.back_timeout", 250); - private final PinnedStackListener mImeChangedListener = new PinnedStackListener() { + private final IPinnedStackListener.Stub mImeChangedListener = new IPinnedStackListener.Stub() { + @Override + public void onListenerRegistered(IPinnedStackController controller) { + } + @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { // No need to thread jump, assignments are atomic mImeHeight = imeVisible ? imeHeight : 0; // TODO: Probably cancel any existing gesture } + + @Override + public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) { + } + + @Override + public void onMinimizedStateChanged(boolean isMinimized) { + } + + @Override + public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, + Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment, + int displayRotation) { + } + + @Override + public void onActionsChanged(ParceledListSlice actions) { + } }; private ISystemGestureExclusionListener mGestureExclusionListener = 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/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index a79ecd905403..ea113dffa658 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -102,6 +102,7 @@ import com.android.systemui.statusbar.notification.stack.AnimationProperties; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.policy.ZenModeController; @@ -177,12 +178,22 @@ public class NotificationPanelView extends PanelView implements @VisibleForTesting final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback = new KeyguardUpdateMonitorCallback() { + + @Override + public void onBiometricAuthenticated(int userId, + BiometricSourceType biometricSourceType) { + if (mFirstBypassAttempt && mUpdateMonitor.isUnlockingWithBiometricAllowed()) { + mDelayShowingKeyguardStatusBar = true; + } + } + @Override public void onBiometricRunningStateChanged(boolean running, BiometricSourceType biometricSourceType) { boolean keyguardOrShadeLocked = mBarState == StatusBarState.KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED; - if (!running && mFirstBypassAttempt && keyguardOrShadeLocked && !mDozing) { + if (!running && mFirstBypassAttempt && keyguardOrShadeLocked && !mDozing + && !mDelayShowingKeyguardStatusBar) { mFirstBypassAttempt = false; animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD); } @@ -191,6 +202,17 @@ public class NotificationPanelView extends PanelView implements @Override public void onFinishedGoingToSleep(int why) { mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled(); + mDelayShowingKeyguardStatusBar = false; + } + }; + private final KeyguardMonitor.Callback mKeyguardMonitorCallback = + new KeyguardMonitor.Callback() { + @Override + public void onKeyguardFadingAwayChanged() { + if (!mKeyguardMonitor.isKeyguardFadingAway()) { + mFirstBypassAttempt = false; + mDelayShowingKeyguardStatusBar = false; + } } }; @@ -413,7 +435,17 @@ public class NotificationPanelView extends PanelView implements private boolean mShowingKeyguardHeadsUp; private boolean mAllowExpandForSmallExpansion; private Runnable mExpandAfterLayoutRunnable; + + /** + * If face auth with bypass is running for the first time after you turn on the screen. + * (From aod or screen off) + */ private boolean mFirstBypassAttempt; + /** + * If auth happens successfully during {@code mFirstBypassAttempt}, and we should wait until + * the keyguard is dismissed to show the status bar. + */ + private boolean mDelayShowingKeyguardStatusBar; private PluginManager mPluginManager; private FrameLayout mPluginFrame; @@ -450,6 +482,7 @@ public class NotificationPanelView extends PanelView implements mKeyguardBypassController = bypassController; mUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled(); + mKeyguardMonitor.addCallback(mKeyguardMonitorCallback); dynamicPrivacyController.addListener(this); mBottomAreaShadeAlphaAnimator = ValueAnimator.ofFloat(1f, 0); @@ -2391,7 +2424,8 @@ public class NotificationPanelView extends PanelView implements * mKeyguardStatusBarAnimateAlpha; newAlpha *= 1.0f - mKeyguardHeadsUpShowingAmount; mKeyguardStatusBar.setAlpha(newAlpha); - boolean hideForBypass = mFirstBypassAttempt && mUpdateMonitor.shouldListenForFace(); + boolean hideForBypass = mFirstBypassAttempt && mUpdateMonitor.shouldListenForFace() + || mDelayShowingKeyguardStatusBar; mKeyguardStatusBar.setVisibility(newAlpha != 0f && !mDozing && !hideForBypass ? VISIBLE : INVISIBLE); } 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/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java index 9aeb147cc725..5c4ef18b61a6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java @@ -393,8 +393,12 @@ public class ForegroundServiceControllerTest extends SysuiTestCase { } private void entryRemoved(StatusBarNotification notification) { - mEntryListener.onEntryRemoved(NotificationEntry.buildForTest(notification), - null, false); + mEntryListener.onEntryRemoved( + new NotificationEntryBuilder() + .setSbn(notification) + .build(), + null, + false); } private void entryAdded(StatusBarNotification notification, int importance) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java index 5943b02b4e4a..212c93dc576d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java @@ -17,19 +17,17 @@ package com.android.systemui; import static com.android.systemui.ForegroundServiceLifetimeExtender.MIN_FGS_TIME_MS; +import static com.android.systemui.statusbar.NotificationEntryHelper.modifySbn; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import android.app.Notification; -import android.service.notification.NotificationListenerService.Ranking; -import android.service.notification.StatusBarNotification; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.systemui.statusbar.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import org.junit.Before; @@ -40,7 +38,6 @@ import org.junit.runner.RunWith; @SmallTest public class ForegroundServiceNotificationListenerTest extends SysuiTestCase { private ForegroundServiceLifetimeExtender mExtender = new ForegroundServiceLifetimeExtender(); - private StatusBarNotification mSbn; private NotificationEntry mEntry; private Notification mNotif; @@ -52,10 +49,9 @@ public class ForegroundServiceNotificationListenerTest extends SysuiTestCase { .setContentText("Text") .build(); - mSbn = mock(StatusBarNotification.class); - when(mSbn.getNotification()).thenReturn(mNotif); - - mEntry = new NotificationEntry(mSbn, mock(Ranking.class)); + mEntry = new NotificationEntryBuilder() + .setNotification(mNotif) + .build(); } /** @@ -66,21 +62,27 @@ public class ForegroundServiceNotificationListenerTest extends SysuiTestCase { // Extend the lifetime of a FGS notification iff it has not been visible // for the minimum time mNotif.flags |= Notification.FLAG_FOREGROUND_SERVICE; - when(mSbn.getPostTime()).thenReturn(System.currentTimeMillis()); + modifySbn(mEntry) + .setPostTime(System.currentTimeMillis()) + .build(); assertTrue(mExtender.shouldExtendLifetime(mEntry)); } @Test public void testShouldExtendLifetime_shouldNot_foreground() { mNotif.flags |= Notification.FLAG_FOREGROUND_SERVICE; - when(mSbn.getPostTime()).thenReturn(System.currentTimeMillis() - MIN_FGS_TIME_MS - 1); + modifySbn(mEntry) + .setPostTime(System.currentTimeMillis() - MIN_FGS_TIME_MS - 1) + .build(); assertFalse(mExtender.shouldExtendLifetime(mEntry)); } @Test public void testShouldExtendLifetime_shouldNot_notForeground() { mNotif.flags = 0; - when(mSbn.getPostTime()).thenReturn(System.currentTimeMillis() - MIN_FGS_TIME_MS - 1); + modifySbn(mEntry) + .setPostTime(System.currentTimeMillis() - MIN_FGS_TIME_MS - 1) + .build(); assertFalse(mExtender.shouldExtendLifetime(mEntry)); } } 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/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java index 238cfd78bfaf..392a7cbc4d6c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java @@ -37,6 +37,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.bubbles.BubbleData.TimeSource; +import com.android.systemui.statusbar.NotificationEntryBuilder; import com.android.systemui.statusbar.NotificationTestHelper; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -878,7 +879,7 @@ public class BubbleDataTest extends SysuiTestCase { when(sbn.getNotification()).thenReturn(notification); // NotificationEntry -> StatusBarNotification -> Notification -> BubbleMetadata - return NotificationEntry.buildForTest(sbn); + return new NotificationEntryBuilder().setSbn(sbn).build(); } private void setCurrentTime(long time) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java index 04cf4bb4c94b..57578611e3f2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java @@ -19,19 +19,16 @@ package com.android.systemui.bubbles; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.when; import android.app.Notification; import android.os.Bundle; -import android.service.notification.NotificationListenerService.Ranking; -import android.os.UserHandle; -import android.service.notification.StatusBarNotification; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import org.junit.Before; @@ -45,8 +42,6 @@ import org.mockito.MockitoAnnotations; @TestableLooper.RunWithLooper public class BubbleTest extends SysuiTestCase { @Mock - private StatusBarNotification mStatusBarNotification; - @Mock private Notification mNotif; private NotificationEntry mEntry; @@ -57,28 +52,16 @@ public class BubbleTest extends SysuiTestCase { public void setUp() { MockitoAnnotations.initMocks(this); - when(mStatusBarNotification.getKey()).thenReturn("key"); - when(mStatusBarNotification.getNotification()).thenReturn(mNotif); - when(mStatusBarNotification.getUser()).thenReturn(new UserHandle(0)); mExtras = new Bundle(); mNotif.extras = mExtras; - mEntry = NotificationEntry.buildForTest(mStatusBarNotification); + mEntry = new NotificationEntryBuilder() + .setNotification(mNotif) + .build(); mBubble = new Bubble(mContext, mEntry); } @Test - public void testInitialization() { - final Ranking ranking = new Ranking(); - - final NotificationEntry entry = new NotificationEntry(mStatusBarNotification, ranking); - - assertEquals("key", entry.key()); - assertEquals(mStatusBarNotification, entry.sbn()); - assertEquals(ranking, entry.ranking()); - } - - @Test public void testGetUpdateMessage_default() { final String msg = "Hello there!"; doReturn(Notification.Style.class).when(mNotif).getNotificationStyle(); 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/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java index e6f36e6abbfd..cf5a12fcc2af 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java @@ -129,7 +129,9 @@ public class AlertingNotificationManagerTest extends SysuiTestCase { public void setUp() { mTestHandler = Handler.createAsync(Looper.myLooper()); mSbn = createNewNotification(0 /* id */); - mEntry = NotificationEntry.buildForTest(mSbn); + mEntry = new NotificationEntryBuilder() + .setSbn(mSbn) + .build(); mEntry.setRow(mRow); mAlertingNotificationManager = createAlertingNotificationManager(); @@ -180,7 +182,9 @@ public class AlertingNotificationManagerTest extends SysuiTestCase { public void testReleaseAllImmediately() { for (int i = 0; i < TEST_NUM_NOTIFICATIONS; i++) { StatusBarNotification sbn = createNewNotification(i); - NotificationEntry entry = NotificationEntry.buildForTest(sbn); + NotificationEntry entry = new NotificationEntryBuilder() + .setSbn(sbn) + .build(); entry.setRow(mRow); mAlertingNotificationManager.showNotification(entry); } 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/NotificationEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryBuilder.java index ded3ba592abe..fcfdd11a1906 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryBuilder.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryBuilder.java @@ -38,7 +38,6 @@ import java.util.ArrayList; public class NotificationEntryBuilder { private final SbnBuilder mSbnBuilder = new SbnBuilder(); private final RankingBuilder mRankingBuilder = new RankingBuilder(); - private StatusBarNotification mSbn = null; public NotificationEntry build() { @@ -193,8 +192,18 @@ public class NotificationEntryBuilder { return this; } + public NotificationEntryBuilder setSmartActions(Notification.Action... smartActions) { + mRankingBuilder.setSmartActions(smartActions); + return this; + } + public NotificationEntryBuilder setSmartReplies(ArrayList<CharSequence> smartReplies) { mRankingBuilder.setSmartReplies(smartReplies); return this; } + + public NotificationEntryBuilder setSmartReplies(CharSequence... smartReplies) { + mRankingBuilder.setSmartReplies(smartReplies); + return this; + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryHelper.java index b5168c6452d1..33b0d2c32ba1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryHelper.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar; import android.service.notification.NotificationListenerService.Ranking; +import android.service.notification.StatusBarNotification; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -27,6 +28,10 @@ public class NotificationEntryHelper { return new ModifiedRankingBuilder(entry); } + public static ModifiedSbnBuilder modifySbn(NotificationEntry entry) { + return new ModifiedSbnBuilder(entry); + } + public static class ModifiedRankingBuilder extends RankingBuilder { private final NotificationEntry mTarget; @@ -42,4 +47,20 @@ public class NotificationEntryHelper { return ranking; } } + + public static class ModifiedSbnBuilder extends SbnBuilder { + private final NotificationEntry mTarget; + + private ModifiedSbnBuilder(NotificationEntry target) { + super(target.sbn()); + mTarget = target; + } + + @Override + public StatusBarNotification build() { + final StatusBarNotification sbn = super.build(); + mTarget.setNotification(sbn); + return sbn; + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java index 55ce8d60f2b4..86869bd65900 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java @@ -38,7 +38,6 @@ import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationData; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; import org.junit.Before; import org.junit.Test; @@ -91,7 +90,11 @@ public class NotificationListenerTest extends SysuiTestCase { @Test public void testNotificationUpdateCallsUpdateNotification() { - when(mNotificationData.get(mSbn.getKey())).thenReturn(NotificationEntry.buildForTest(mSbn)); + when(mNotificationData.get(mSbn.getKey())) + .thenReturn( + new NotificationEntryBuilder() + .setSbn(mSbn) + .build()); mListener.onNotificationPosted(mSbn, mRanking); TestableLooper.get(this).processAllMessages(); verify(mEntryManager).updateNotification(mSbn, mRanking); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java index 57dd8c94c790..a02764320b6c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java @@ -19,11 +19,12 @@ package com.android.systemui.statusbar; import static android.content.Intent.ACTION_USER_SWITCHED; import static android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL; +import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT; + import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -166,7 +167,10 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1); when(mNotificationData.isHighPriority(any())).thenReturn(false); - assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(mock(NotificationEntry.class))); + NotificationEntry entry = new NotificationEntryBuilder().build(); + entry.setBucket(BUCKET_SILENT); + + assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(entry)); } @Test @@ -179,7 +183,9 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0); when(mNotificationData.isHighPriority(any())).thenReturn(false); - assertFalse(mLockscreenUserManager.shouldShowOnKeyguard(mock(NotificationEntry.class))); + NotificationEntry entry = new NotificationEntryBuilder().build(); + entry.setBucket(BUCKET_SILENT); + assertFalse(mLockscreenUserManager.shouldShowOnKeyguard(entry)); } private class TestNotificationLockscreenUserManager diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java index 99d09f18eca6..852ddb2ae710 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java @@ -63,7 +63,6 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { @Mock private NotificationLockscreenUserManager mLockscreenUserManager; private TestableNotificationRemoteInputManager mRemoteInputManager; - private StatusBarNotification mSbn; private NotificationEntry mEntry; private RemoteInputHistoryExtender mRemoteInputHistoryExtender; private SmartReplyHistoryExtender mSmartReplyHistoryExtender; @@ -78,9 +77,13 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { () -> mock(ShadeController.class), mStateController, Handler.createAsync(Looper.myLooper())); - mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, - 0, new Notification(), UserHandle.CURRENT, null, 0); - mEntry = NotificationEntry.buildForTest(mSbn); + mEntry = new NotificationEntryBuilder() + .setPkg(TEST_PACKAGE_NAME) + .setOpPkg(TEST_PACKAGE_NAME) + .setUid(TEST_UID) + .setNotification(new Notification()) + .setUser(UserHandle.CURRENT) + .build(); mEntry.setRow(mRow); mRemoteInputManager.setUpWithPresenterForTest(mCallback, @@ -94,7 +97,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { @Test public void testPerformOnRemoveNotification() { when(mController.isRemoteInputActive(mEntry)).thenReturn(true); - mRemoteInputManager.onPerformRemoveNotification(mEntry, mSbn.getKey()); + mRemoteInputManager.onPerformRemoveNotification(mEntry, mEntry.key()); verify(mController).removeRemoteInput(mEntry, null); } @@ -175,7 +178,9 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { // Setup a notification entry with 1 remote input. StatusBarNotification newSbn = mRemoteInputManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", false); - NotificationEntry entry = NotificationEntry.buildForTest(newSbn); + NotificationEntry entry = new NotificationEntryBuilder() + .setSbn(newSbn) + .build(); // Try rebuilding to add another reply. newSbn = mRemoteInputManager.rebuildNotificationWithRemoteInput(entry, "Reply 2", true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java index b1e76120b752..de77af8f4d14 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java @@ -299,17 +299,7 @@ public class NotificationTestHelper { row.setGroupManager(mGroupManager); row.setHeadsUpManager(mHeadsUpManager); row.setAboveShelfChangedListener(aboveShelf -> {}); - StatusBarNotification sbn = new StatusBarNotification( - pkg, - pkg, - mId++, - null /* tag */, - uid, - 2000 /* initialPid */, - notification, - userHandle, - null /* overrideGroupKey */, - System.currentTimeMillis()); + final NotificationChannel channel = new NotificationChannel( notification.getChannelId(), @@ -317,14 +307,20 @@ public class NotificationTestHelper { importance); channel.setBlockableSystem(true); - NotificationEntry entry = new NotificationEntry( - sbn, - new RankingBuilder() - .setKey(sbn.getKey()) - .setChannel(channel) - .build()); + NotificationEntry entry = new NotificationEntryBuilder() + .setPkg(pkg) + .setOpPkg(pkg) + .setId(mId++) + .setUid(uid) + .setInitialPid(2000) + .setNotification(notification) + .setUser(userHandle) + .setPostTime(System.currentTimeMillis()) + .setChannel(channel) + .build(); + entry.setRow(row); - entry.createIcons(mContext, sbn); + entry.createIcons(mContext, entry.sbn()); row.setEntry(entry); row.getNotificationInflater().addInflationFlags(extraInflationFlags); NotificationContentInflaterTest.runThenWaitForInflation( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java index 388cf582237b..9e7250412499 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java @@ -120,7 +120,9 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { private NotificationEntry createEntry() throws Exception { ExpandableNotificationRow row = mHelper.createRow(); - NotificationEntry entry = NotificationEntry.buildForTest(row.getStatusBarNotification()); + NotificationEntry entry = new NotificationEntryBuilder() + .setSbn(row.getStatusBarNotification()) + .build(); entry.setRow(row); return entry; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java index b45077ade3e3..05f179ed4620 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java @@ -24,6 +24,7 @@ import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.SnoozeCriterion; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -192,11 +193,21 @@ public class RankingBuilder { return this; } + public RankingBuilder setSmartActions(Notification.Action... smartActions) { + mSmartActions = new ArrayList<>(Arrays.asList(smartActions)); + return this; + } + public RankingBuilder setSmartReplies(@NonNull ArrayList<CharSequence> smartReplies) { mSmartReplies = smartReplies; return this; } + public RankingBuilder setSmartReplies(CharSequence... smartReplies) { + mSmartReplies = new ArrayList<>(Arrays.asList(smartReplies)); + return this; + } + private static <E> ArrayList<E> copyList(List<E> list) { if (list == null) { return null; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java index a9aab7f56cd3..9bc962c77019 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java @@ -37,6 +37,22 @@ public class SbnBuilder { private String mOverrideGroupKey; private long mPostTime; + public SbnBuilder() { + } + + public SbnBuilder(StatusBarNotification source) { + mPkg = source.getPackageName(); + mOpPkg = source.getOpPkg(); + mId = source.getId(); + mTag = source.getTag(); + mUid = source.getUid(); + mInitialPid = source.getInitialPid(); + mNotification = source.getNotification(); + mUser = source.getUser(); + mOverrideGroupKey = source.getOverrideGroupKey(); + mPostTime = source.getPostTime(); + } + public StatusBarNotification build() { return new StatusBarNotification( mPkg, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java index e54ea6a7c8f9..88fb3e1d5c19 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java @@ -97,7 +97,9 @@ public class SmartReplyControllerTest extends SysuiTestCase { mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, mNotification, new UserHandle(ActivityManager.getCurrentUser()), null, 0); - mEntry = NotificationEntry.buildForTest(mSbn); + mEntry = new NotificationEntryBuilder() + .setSbn(mSbn) + .build(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java index e42d1558afa9..8d496a72e3b2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java @@ -30,7 +30,6 @@ import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.Notification; import android.os.UserHandle; -import android.service.notification.StatusBarNotification; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.ArraySet; @@ -41,6 +40,7 @@ import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.ForegroundServiceController; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.NotificationData; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; @@ -73,7 +73,7 @@ public class NotificationListControllerTest extends SysuiTestCase { private DeviceProvisionedListener mProvisionedListener; // TODO: Remove this once EntryManager no longer needs to be mocked - private NotificationData mNotificationData = new NotificationData(); + private NotificationData mNotificationData = new NotificationData(mContext); private int mNextNotifId = 0; @@ -222,19 +222,14 @@ public class NotificationListControllerTest extends SysuiTestCase { .setContentTitle("Title") .setContentText("Text"); - StatusBarNotification notification = - new StatusBarNotification( - TEST_PACKAGE_NAME, - TEST_PACKAGE_NAME, - mNextNotifId, - null, - TEST_UID, - 0, - n.build(), - new UserHandle(ActivityManager.getCurrentUser()), - null, - 0); - return NotificationEntry.buildForTest(notification); + return new NotificationEntryBuilder() + .setPkg(TEST_PACKAGE_NAME) + .setOpPkg(TEST_PACKAGE_NAME) + .setId(mNextNotifId) + .setUid(TEST_UID) + .setNotification(n.build()) + .setUser(new UserHandle(ActivityManager.getCurrentUser())) + .build(); } private static final String TEST_PACKAGE_NAME = "test"; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java index 49229ca050c3..6a4ddc7ec202 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java @@ -25,13 +25,13 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.os.Handler; -import android.service.notification.StatusBarNotification; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.NotificationEntryBuilder; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -62,7 +62,7 @@ public class VisualStabilityManagerTest extends SysuiTestCase { mVisualStabilityManager.setUpWithPresenter(mock(NotificationPresenter.class)); mVisualStabilityManager.setVisibilityLocationProvider(mLocationProvider); - mEntry = NotificationEntry.buildForTest(mock(StatusBarNotification.class)); + mEntry = new NotificationEntryBuilder().build(); mEntry.setRow(mRow); when(mRow.getEntry()).thenReturn(mEntry); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java index 365b80b50723..ab7f960d6a15 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java @@ -27,10 +27,13 @@ import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MIN; +import static com.android.systemui.statusbar.NotificationEntryHelper.modifySbn; import static com.android.systemui.statusbar.notification.collection.NotificationDataTest.TestableNotificationData.OVERRIDE_CHANNEL; import static com.android.systemui.statusbar.notification.collection.NotificationDataTest.TestableNotificationData.OVERRIDE_IMPORTANCE; import static com.android.systemui.statusbar.notification.collection.NotificationDataTest.TestableNotificationData.OVERRIDE_RANK; import static com.android.systemui.statusbar.notification.collection.NotificationDataTest.TestableNotificationData.OVERRIDE_VIS_EFFECTS; +import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING; +import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT; import static junit.framework.Assert.assertEquals; @@ -47,6 +50,7 @@ import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Person; +import android.content.Context; import android.content.Intent; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; @@ -70,7 +74,10 @@ import com.android.systemui.ForegroundServiceController; import com.android.systemui.InitController; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.NotificationEntryBuilder; import com.android.systemui.statusbar.NotificationTestHelper; +import com.android.systemui.statusbar.RankingBuilder; +import com.android.systemui.statusbar.SbnBuilder; import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.phone.NotificationGroupManager; @@ -83,9 +90,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; @SmallTest @@ -98,8 +103,8 @@ public class NotificationDataTest extends SysuiTestCase { private static final NotificationChannel NOTIFICATION_CHANNEL = new NotificationChannel("id", "name", NotificationChannel.USER_LOCKED_IMPORTANCE); - private final StatusBarNotification mMockStatusBarNotification = - mock(StatusBarNotification.class); + private NotificationEntry mEntry; + @Mock ForegroundServiceController mFsc; @Mock @@ -113,9 +118,10 @@ public class NotificationDataTest extends SysuiTestCase { public void setUp() throws Exception { com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper(); MockitoAnnotations.initMocks(this); - when(mMockStatusBarNotification.getUid()).thenReturn(UID_NORMAL); - when(mMockStatusBarNotification.cloneLight()).thenReturn(mMockStatusBarNotification); - when(mMockStatusBarNotification.getKey()).thenReturn("mock_key"); + + mEntry = new NotificationEntryBuilder() + .setUid(UID_NORMAL) + .build(); when(mMockPackageManager.checkUidPermission( eq(Manifest.permission.NOTIFICATION_DURING_SETUP), @@ -133,7 +139,7 @@ public class NotificationDataTest extends SysuiTestCase { mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment); when(mEnvironment.isDeviceProvisioned()).thenReturn(true); when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true); - mNotificationData = new TestableNotificationData(); + mNotificationData = new TestableNotificationData(mContext); mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class)); mRow = new NotificationTestHelper(getContext()).createRow(); Dependency.get(InitController.class).executePostInitTasks(); @@ -231,57 +237,57 @@ public class NotificationDataTest extends SysuiTestCase { public void testIsExemptFromDndVisualSuppression_foreground() { initStatusBarNotification(false); - Notification n = mMockStatusBarNotification.getNotification(); - n.flags = Notification.FLAG_FOREGROUND_SERVICE; - NotificationEntry entry = NotificationEntry.buildForTest(mMockStatusBarNotification); - entry.setRow(mRow); - mNotificationData.add(entry); + mEntry.sbn().getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE; + mEntry.setRow(mRow); + mNotificationData.add(mEntry); Bundle override = new Bundle(); override.putInt(OVERRIDE_VIS_EFFECTS, 255); - mNotificationData.rankingOverrides.put(entry.key, override); + mNotificationData.rankingOverrides.put(mEntry.key, override); - assertTrue(entry.isExemptFromDndVisualSuppression()); - assertFalse(entry.shouldSuppressAmbient()); + assertTrue(mEntry.isExemptFromDndVisualSuppression()); + assertFalse(mEntry.shouldSuppressAmbient()); } @Test public void testIsExemptFromDndVisualSuppression_media() { initStatusBarNotification(false); - Notification n = mMockStatusBarNotification.getNotification(); + Notification n = mEntry.sbn().getNotification(); Notification.Builder nb = Notification.Builder.recoverBuilder(mContext, n); nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class))); n = nb.build(); - when(mMockStatusBarNotification.getNotification()).thenReturn(n); - NotificationEntry entry = NotificationEntry.buildForTest(mMockStatusBarNotification); - entry.setRow(mRow); - mNotificationData.add(entry); + modifySbn(mEntry) + .setNotification(n) + .build(); + mEntry.setRow(mRow); + mNotificationData.add(mEntry); Bundle override = new Bundle(); override.putInt(OVERRIDE_VIS_EFFECTS, 255); - mNotificationData.rankingOverrides.put(entry.key, override); + mNotificationData.rankingOverrides.put(mEntry.key, override); - assertTrue(entry.isExemptFromDndVisualSuppression()); - assertFalse(entry.shouldSuppressAmbient()); + assertTrue(mEntry.isExemptFromDndVisualSuppression()); + assertFalse(mEntry.shouldSuppressAmbient()); } @Test public void testIsExemptFromDndVisualSuppression_system() { initStatusBarNotification(false); - NotificationEntry entry = NotificationEntry.buildForTest(mMockStatusBarNotification); - entry.setRow(mRow); - entry.mIsSystemNotification = true; - mNotificationData.add(entry); + mEntry.setRow(mRow); + mEntry.mIsSystemNotification = true; + mNotificationData.add(mEntry); Bundle override = new Bundle(); override.putInt(OVERRIDE_VIS_EFFECTS, 255); - mNotificationData.rankingOverrides.put(entry.key, override); + mNotificationData.rankingOverrides.put(mEntry.key, override); - assertTrue(entry.isExemptFromDndVisualSuppression()); - assertFalse(entry.shouldSuppressAmbient()); + assertTrue(mEntry.isExemptFromDndVisualSuppression()); + assertFalse(mEntry.shouldSuppressAmbient()); } @Test public void testIsNotExemptFromDndVisualSuppression_hiddenCategories() { initStatusBarNotification(false); - NotificationEntry entry = NotificationEntry.buildForTest(mMockStatusBarNotification); + NotificationEntry entry = new NotificationEntryBuilder() + .setUid(UID_NORMAL) + .build(); entry.setRow(mRow); entry.mIsSystemNotification = true; Bundle override = new Bundle(); @@ -289,58 +295,65 @@ public class NotificationDataTest extends SysuiTestCase { mNotificationData.rankingOverrides.put(entry.key, override); mNotificationData.add(entry); - when(mMockStatusBarNotification.getNotification()).thenReturn( - new Notification.Builder(mContext, "").setCategory(CATEGORY_CALL).build()); - + modifySbn(entry) + .setNotification( + new Notification.Builder(mContext, "").setCategory(CATEGORY_CALL).build()) + .build(); assertFalse(entry.isExemptFromDndVisualSuppression()); assertTrue(entry.shouldSuppressAmbient()); - when(mMockStatusBarNotification.getNotification()).thenReturn( - new Notification.Builder(mContext, "").setCategory(CATEGORY_REMINDER).build()); - + modifySbn(entry) + .setNotification( + new Notification.Builder(mContext, "") + .setCategory(CATEGORY_REMINDER) + .build()) + .build(); assertFalse(entry.isExemptFromDndVisualSuppression()); - when(mMockStatusBarNotification.getNotification()).thenReturn( - new Notification.Builder(mContext, "").setCategory(CATEGORY_ALARM).build()); - + modifySbn(entry) + .setNotification( + new Notification.Builder(mContext, "").setCategory(CATEGORY_ALARM).build()) + .build(); assertFalse(entry.isExemptFromDndVisualSuppression()); - when(mMockStatusBarNotification.getNotification()).thenReturn( - new Notification.Builder(mContext, "").setCategory(CATEGORY_EVENT).build()); - + modifySbn(entry) + .setNotification( + new Notification.Builder(mContext, "").setCategory(CATEGORY_EVENT).build()) + .build(); assertFalse(entry.isExemptFromDndVisualSuppression()); - when(mMockStatusBarNotification.getNotification()).thenReturn( - new Notification.Builder(mContext, "").setCategory(CATEGORY_MESSAGE).build()); - + modifySbn(entry) + .setNotification( + new Notification.Builder(mContext, "") + .setCategory(CATEGORY_MESSAGE) + .build()) + .build(); assertFalse(entry.isExemptFromDndVisualSuppression()); } @Test public void testCreateNotificationDataEntry_RankingUpdate() { - Ranking ranking = mock(Ranking.class); - initStatusBarNotification(false); + StatusBarNotification sbn = new SbnBuilder().build(); + sbn.getNotification().actions = + new Notification.Action[] { createContextualAction("appGeneratedAction") }; - List<Notification.Action> appGeneratedSmartActions = - Collections.singletonList(createContextualAction("appGeneratedAction")); - mMockStatusBarNotification.getNotification().actions = - appGeneratedSmartActions.toArray(new Notification.Action[0]); - - List<Notification.Action> systemGeneratedSmartActions = - Collections.singletonList(createAction("systemGeneratedAction")); - when(ranking.getSmartActions()).thenReturn(systemGeneratedSmartActions); - - when(ranking.getChannel()).thenReturn(NOTIFICATION_CHANNEL); - - when(ranking.getUserSentiment()).thenReturn(Ranking.USER_SENTIMENT_NEGATIVE); + ArrayList<Notification.Action> systemGeneratedSmartActions = + createActions("systemGeneratedAction"); SnoozeCriterion snoozeCriterion = new SnoozeCriterion("id", "explanation", "confirmation"); ArrayList<SnoozeCriterion> snoozeCriterions = new ArrayList<>(); snoozeCriterions.add(snoozeCriterion); - when(ranking.getSnoozeCriteria()).thenReturn(snoozeCriterions); + + Ranking ranking = new RankingBuilder() + .setKey(sbn.getKey()) + .setSmartActions(systemGeneratedSmartActions) + .setChannel(NOTIFICATION_CHANNEL) + .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE) + .setSnoozeCriteria(snoozeCriterions) + .build(); NotificationEntry entry = - new NotificationEntry(mMockStatusBarNotification, ranking); + new NotificationEntry(sbn, ranking); assertEquals(systemGeneratedSmartActions, entry.getSmartActions()); assertEquals(NOTIFICATION_CHANNEL, entry.getChannel()); @@ -366,10 +379,15 @@ public class NotificationDataTest extends SysuiTestCase { Notification notification = new Notification.Builder(mContext, "test") .addExtras(bundle) .build(); - StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, - notification, mContext.getUser(), "", 0); - NotificationEntry entry = NotificationEntry.buildForTest(sbn); + NotificationEntry entry = new NotificationEntryBuilder() + .setPkg("pkg") + .setOpPkg("pkg") + .setTag("tag") + .setNotification(notification) + .setUser(mContext.getUser()) + .setOverrideGroupKey("") + .build(); entry.setHasSentReply(); assertTrue(entry.isLastMessageFromReply()); @@ -472,9 +490,14 @@ public class NotificationDataTest extends SysuiTestCase { Notification aN = new Notification.Builder(mContext, "test") .setStyle(new Notification.MessagingStyle("")) .build(); - StatusBarNotification aSbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, - aN, mContext.getUser(), "", 0); - NotificationEntry a = NotificationEntry.buildForTest(aSbn); + NotificationEntry a = new NotificationEntryBuilder() + .setPkg("pkg") + .setOpPkg("pkg") + .setTag("tag") + .setNotification(aN) + .setUser(mContext.getUser()) + .setOverrideGroupKey("") + .build(); a.setRow(mock(ExpandableNotificationRow.class)); a.setIsHighPriority(false); @@ -486,9 +509,14 @@ public class NotificationDataTest extends SysuiTestCase { Notification bN = new Notification.Builder(mContext, "test") .setStyle(new Notification.MessagingStyle("")) .build(); - StatusBarNotification bSbn = new StatusBarNotification("pkg2", "pkg2", 0, "tag", 0, 0, - bN, mContext.getUser(), "", 0); - NotificationEntry b = NotificationEntry.buildForTest(bSbn); + NotificationEntry b = new NotificationEntryBuilder() + .setPkg("pkg2") + .setOpPkg("pkg2") + .setTag("tag") + .setNotification(bN) + .setUser(mContext.getUser()) + .setOverrideGroupKey("") + .build(); b.setIsHighPriority(true); b.setRow(mock(ExpandableNotificationRow.class)); @@ -502,14 +530,18 @@ public class NotificationDataTest extends SysuiTestCase { @Test public void testSort_samePriorityUsesNMSRank() { - // NMS rank says A and then B. But A is not high priority and B is, so B should sort in - // front + // NMS rank says A and then B, and they are the same priority so use that rank Notification aN = new Notification.Builder(mContext, "test") .setStyle(new Notification.MessagingStyle("")) .build(); - StatusBarNotification aSbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, - aN, mContext.getUser(), "", 0); - NotificationEntry a = NotificationEntry.buildForTest(aSbn); + NotificationEntry a = new NotificationEntryBuilder() + .setPkg("pkg") + .setOpPkg("pkg") + .setTag("tag") + .setNotification(aN) + .setUser(mContext.getUser()) + .setOverrideGroupKey("") + .build(); a.setRow(mock(ExpandableNotificationRow.class)); a.setIsHighPriority(false); @@ -521,9 +553,14 @@ public class NotificationDataTest extends SysuiTestCase { Notification bN = new Notification.Builder(mContext, "test") .setStyle(new Notification.MessagingStyle("")) .build(); - StatusBarNotification bSbn = new StatusBarNotification("pkg2", "pkg2", 0, "tag", 0, 0, - bN, mContext.getUser(), "", 0); - NotificationEntry b = NotificationEntry.buildForTest(bSbn); + NotificationEntry b = new NotificationEntryBuilder() + .setPkg("pkg2") + .setOpPkg("pkg2") + .setTag("tag") + .setNotification(bN) + .setUser(mContext.getUser()) + .setOverrideGroupKey("") + .build(); b.setRow(mock(ExpandableNotificationRow.class)); b.setIsHighPriority(false); @@ -536,59 +573,50 @@ public class NotificationDataTest extends SysuiTestCase { } @Test - public void testSort_properlySetsIsTopBucket() { - + public void testSort_properlySetsAlertingBucket() { Notification notification = new Notification.Builder(mContext, "test") .build(); - StatusBarNotification sbn = new StatusBarNotification( - "pkg", - "pkg", - 0, - "tag", - 0, - 0, - notification, - mContext.getUser(), - "", - 0); + NotificationEntry entry = new NotificationEntryBuilder() + .setPkg("pkg") + .setOpPkg("pkg") + .setTag("tag") + .setNotification(notification) + .setUser(mContext.getUser()) + .setOverrideGroupKey("") + .build(); Bundle override = new Bundle(); override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_DEFAULT); - mNotificationData.rankingOverrides.put(sbn.getKey(), override); + mNotificationData.rankingOverrides.put(entry.key(), override); - NotificationEntry entry = NotificationEntry.buildForTest(sbn); entry.setRow(mRow); mNotificationData.add(entry); - assertTrue(entry.isTopBucket()); + assertEquals(entry.getBucket(), BUCKET_ALERTING); } @Test - public void testSort_properlySetsIsNotTopBucket() { + public void testSort_properlySetsSilentBucket() { Notification notification = new Notification.Builder(mContext, "test") .build(); - StatusBarNotification sbn = new StatusBarNotification( - "pkg", - "pkg", - 0, - "tag", - 0, - 0, - notification, - mContext.getUser(), - "", - 0); + + NotificationEntry entry = new NotificationEntryBuilder() + .setPkg("pkg") + .setOpPkg("pkg") + .setTag("tag") + .setNotification(notification) + .setUser(mContext.getUser()) + .setOverrideGroupKey("") + .build(); Bundle override = new Bundle(); override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_LOW); - mNotificationData.rankingOverrides.put(sbn.getKey(), override); + mNotificationData.rankingOverrides.put(entry.key(), override); - NotificationEntry entry = NotificationEntry.buildForTest(sbn); entry.setRow(mRow); - mNotificationData.add(entry); - assertFalse(entry.isTopBucket()); + assertEquals(entry.getBucket(), BUCKET_SILENT); } private void initStatusBarNotification(boolean allowDuringSetup) { @@ -597,12 +625,14 @@ public class NotificationDataTest extends SysuiTestCase { Notification notification = new Notification.Builder(mContext, "test") .addExtras(bundle) .build(); - when(mMockStatusBarNotification.getNotification()).thenReturn(notification); + modifySbn(mEntry) + .setNotification(notification) + .build(); } public static class TestableNotificationData extends NotificationData { - public TestableNotificationData() { - super(); + public TestableNotificationData(Context context) { + super(context); } public static final String OVERRIDE_RANK = "r"; @@ -684,6 +714,11 @@ public class NotificationDataTest extends SysuiTestCase { ? overrides.getCharSequenceArrayList(OVERRIDE_SMART_REPLIES) : currentReplies, overrides.getBoolean(OVERRIDE_BUBBLE, outRanking.canBubble())); + } else { + outRanking.populate( + new RankingBuilder() + .setKey(key) + .build()); } return true; } @@ -704,4 +739,12 @@ public class NotificationDataTest extends SysuiTestCase { title, PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0)).build(); } + + private ArrayList<Notification.Action> createActions(String... titles) { + ArrayList<Notification.Action> actions = new ArrayList<>(); + for (String title : titles) { + actions.add(createAction(title)); + } + return actions; + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java index ff3a2e224788..24cd056b789c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java @@ -29,7 +29,6 @@ import android.app.Notification; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; -import android.service.notification.StatusBarNotification; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -40,6 +39,7 @@ import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.UiOffloadThread; +import com.android.systemui.statusbar.NotificationEntryBuilder; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.notification.NotificationEntryListener; @@ -93,10 +93,13 @@ public class NotificationLoggerTest extends SysuiTestCase { when(mEntryManager.getNotificationData()).thenReturn(mNotificationData); - StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, - 0, null, TEST_UID, - 0, new Notification(), UserHandle.CURRENT, null, 0); - mEntry = NotificationEntry.buildForTest(sbn); + mEntry = new NotificationEntryBuilder() + .setPkg(TEST_PACKAGE_NAME) + .setOpPkg(TEST_PACKAGE_NAME) + .setUid(TEST_UID) + .setNotification(new Notification()) + .setUser(UserHandle.CURRENT) + .build(); mEntry.setRow(mRow); mLogger = new TestableNotificationLogger(mListener, Dependency.get(UiOffloadThread.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java index 096acf9d9ce4..9eba4ebeb994 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java @@ -28,9 +28,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.NotificationChannel; import android.provider.Settings; -import android.service.notification.StatusBarNotification; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; @@ -40,7 +38,7 @@ import android.view.ViewGroup; import androidx.test.filters.SmallTest; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; -import com.android.systemui.statusbar.RankingBuilder; +import com.android.systemui.statusbar.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.utils.leaks.LeakCheckedTest; @@ -61,11 +59,7 @@ public class NotificationMenuRowTest extends LeakCheckedTest { public void setup() { injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES); mRow = mock(ExpandableNotificationRow.class); - NotificationEntry entry = new NotificationEntry( - mock(StatusBarNotification.class), - new RankingBuilder() - .setChannel(mock(NotificationChannel.class)) - .build()); + NotificationEntry entry = new NotificationEntryBuilder().build(); when(mRow.getEntry()).thenReturn(entry); } @@ -75,7 +69,6 @@ public class NotificationMenuRowTest extends LeakCheckedTest { NOTIFICATION_NEW_INTERRUPTION_MODEL, 0); } - @Test public void testAttachDetach() { NotificationMenuRowPlugin row = new NotificationMenuRow(mContext); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java index 524ad85c3a98..addceb5def6e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java @@ -64,7 +64,7 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mRoundnessManager = new NotificationRoundnessManager(mBypassController); + mRoundnessManager = new NotificationRoundnessManager(mBypassController, mContext); com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper(); NotificationTestHelper testHelper = new NotificationTestHelper(getContext()); mFirst = testHelper.createRow(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java index 59d0f912d38e..56ed0e3a6af3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java @@ -18,6 +18,9 @@ package com.android.systemui.statusbar.notification.stack; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING; +import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -74,7 +77,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { mActivityStarterDelegate, mStatusBarStateController, mConfigurationController, - true); + 2); // Required in order for the header inflation to work properly when(mNssl.generateLayoutParams(any(AttributeSet.class))) .thenReturn(new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); @@ -263,8 +266,8 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { when(notifRow.getVisibility()).thenReturn(View.VISIBLE); when(notifRow.getEntry().isHighPriority()) .thenReturn(children[i] == ChildType.HIPRI); - when(notifRow.getEntry().isTopBucket()) - .thenReturn(children[i] == ChildType.HIPRI); + when(notifRow.getEntry().getBucket()).thenReturn( + children[i] == ChildType.HIPRI ? BUCKET_ALERTING : BUCKET_SILENT); when(notifRow.getParent()).thenReturn(mNssl); child = notifRow; break; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java index de3623f86c44..ef9665a80848 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java @@ -32,6 +32,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.AlertingNotificationManager; import com.android.systemui.statusbar.AlertingNotificationManagerTest; +import com.android.systemui.statusbar.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; @@ -121,7 +122,9 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { @Test public void testCanRemoveImmediately_notTopEntry() { - NotificationEntry laterEntry = NotificationEntry.buildForTest(createNewNotification(1)); + NotificationEntry laterEntry = new NotificationEntryBuilder() + .setSbn(createNewNotification(1)) + .build(); laterEntry.setRow(mRow); mHeadsUpManager.showNotification(mEntry); mHeadsUpManager.showNotification(laterEntry); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java index e33545bba7a2..f1a7905d1d42 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java @@ -24,9 +24,9 @@ import android.app.ActivityManager; import android.app.Notification; import android.content.Context; import android.os.UserHandle; -import android.service.notification.StatusBarNotification; import com.android.systemui.R; +import com.android.systemui.statusbar.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -76,22 +76,18 @@ public final class NotificationGroupTestHelper { .setGroupSummary(isSummary) .setGroup(TEST_GROUP_ID) .build(); - StatusBarNotification sbn = new StatusBarNotification( - TEST_PACKAGE_NAME /* pkg */, - TEST_PACKAGE_NAME, - id, - null /* tag */, - 0, /* uid */ - 0 /* initialPid */, - notif, - new UserHandle(ActivityManager.getCurrentUser()), - null /* overrideGroupKey */, - 0 /* postTime */); - NotificationEntry entry = NotificationEntry.buildForTest(sbn); + NotificationEntry entry = new NotificationEntryBuilder() + .setPkg(TEST_PACKAGE_NAME) + .setOpPkg(TEST_PACKAGE_NAME) + .setId(id) + .setNotification(notif) + .setUser(new UserHandle(ActivityManager.getCurrentUser())) + .build(); + ExpandableNotificationRow row = mock(ExpandableNotificationRow.class); entry.setRow(row); when(row.getEntry()).thenReturn(entry); - when(row.getStatusBarNotification()).thenReturn(sbn); + when(row.getStatusBarNotification()).thenReturn(entry.sbn()); when(row.isInflationFlagSet(anyInt())).thenReturn(true); return entry; } 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/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index 45cd1e17ff93..24ec1097ea8b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -24,8 +24,6 @@ import android.app.Notification; import android.app.StatusBarManager; import android.content.Context; import android.metrics.LogMaker; -import android.os.UserHandle; -import android.service.notification.StatusBarNotification; import android.support.test.metricshelper.MetricsAsserts; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -39,6 +37,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.testing.FakeMetricsLogger; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationAlertingManager; @@ -84,29 +83,35 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { @Test public void testHeadsUp_disabledStatusBar() { Notification n = new Notification.Builder(getContext(), "a").build(); - StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n, - UserHandle.of(0), null, 0); - NotificationEntry entry = NotificationEntry.buildForTest(sbn); + NotificationEntry entry = new NotificationEntryBuilder() + .setPkg("a") + .setOpPkg("a") + .setTag("a") + .setNotification(n) + .build(); mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0, false /* animate */); TestableLooper.get(this).processAllMessages(); assertFalse("The panel shouldn't allow heads up while disabled", - mStatusBar.canHeadsUp(entry, sbn)); + mStatusBar.canHeadsUp(entry, entry.sbn())); } @Test public void testHeadsUp_disabledNotificationShade() { Notification n = new Notification.Builder(getContext(), "a").build(); - StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n, - UserHandle.of(0), null, 0); - NotificationEntry entry = NotificationEntry.buildForTest(sbn); + NotificationEntry entry = new NotificationEntryBuilder() + .setPkg("a") + .setOpPkg("a") + .setTag("a") + .setNotification(n) + .build(); mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */); TestableLooper.get(this).processAllMessages(); assertFalse("The panel shouldn't allow heads up while notitifcation shade disabled", - mStatusBar.canHeadsUp(entry, sbn)); + mStatusBar.canHeadsUp(entry, entry.sbn())); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java index 9831ce3c07ce..0c6f25786190 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java @@ -23,7 +23,6 @@ import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -37,7 +36,6 @@ import android.content.IntentFilter; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; -import android.service.notification.StatusBarNotification; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; @@ -53,6 +51,7 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; +import com.android.systemui.statusbar.NotificationEntryBuilder; import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; @@ -140,10 +139,10 @@ public class SmartReplyViewTest extends SysuiTestCase { .setSmallIcon(R.drawable.ic_person) .setContentTitle("Title") .setContentText("Text").build(); - StatusBarNotification sbn = mock(StatusBarNotification.class); - when(sbn.getNotification()).thenReturn(mNotification); - when(sbn.getKey()).thenReturn(TEST_NOTIFICATION_KEY); - mEntry = NotificationEntry.buildForTest(sbn); + + mEntry = new NotificationEntryBuilder() + .setNotification(mNotification) + .build(); mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person); } diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index cdb062d1963c..4f021ad3cee0 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -23,7 +23,7 @@ import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCE import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK; -import android.accessibilityservice.AccessibilityGestureInfo; +import android.accessibilityservice.AccessibilityGestureEvent; import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.IAccessibilityServiceClient; import android.accessibilityservice.IAccessibilityServiceConnection; @@ -1173,9 +1173,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } } - public void notifyGesture(AccessibilityGestureInfo gestureInfo) { + public void notifyGesture(AccessibilityGestureEvent gestureEvent) { mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_GESTURE, - gestureInfo).sendToTarget(); + gestureEvent).sendToTarget(); } public void notifyClearAccessibilityNodeInfoCache() { @@ -1264,7 +1264,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } } - private void notifyGestureInternal(AccessibilityGestureInfo gestureInfo) { + private void notifyGestureInternal(AccessibilityGestureEvent gestureInfo) { final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); if (listener != null) { try { @@ -1469,7 +1469,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final int type = message.what; switch (type) { case MSG_ON_GESTURE: { - notifyGestureInternal((AccessibilityGestureInfo) message.obj); + notifyGestureInternal((AccessibilityGestureEvent) message.obj); } break; case MSG_CLEAR_ACCESSIBILITY_CACHE: { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index feb7329bdda6..ba4d89cf53d1 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -27,7 +27,7 @@ import static com.android.internal.util.FunctionalUtils.ignoreRemoteException; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import android.Manifest; -import android.accessibilityservice.AccessibilityGestureInfo; +import android.accessibilityservice.AccessibilityGestureEvent; import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.IAccessibilityServiceClient; @@ -828,12 +828,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - - public boolean onGesture(AccessibilityGestureInfo gestureInfo) { + /** + * Called when a gesture is detected on a display. + * + * @param gestureEvent the detail of the gesture. + * @return true if the event is handled. + */ + public boolean onGesture(AccessibilityGestureEvent gestureEvent) { synchronized (mLock) { - boolean handled = notifyGestureLocked(gestureInfo, false); + boolean handled = notifyGestureLocked(gestureEvent, false); if (!handled) { - handled = notifyGestureLocked(gestureInfo, true); + handled = notifyGestureLocked(gestureEvent, true); } return handled; } @@ -1028,7 +1033,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private boolean notifyGestureLocked(AccessibilityGestureInfo gestureInfo, boolean isDefault) { + private boolean notifyGestureLocked(AccessibilityGestureEvent gestureEvent, boolean isDefault) { // TODO: Now we are giving the gestures to the last enabled // service that can handle them which is the last one // in our list since we write the last enabled as the @@ -1042,7 +1047,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { AccessibilityServiceConnection service = state.mBoundServices.get(i); if (service.mRequestTouchExplorationMode && service.mIsDefault == isDefault) { - service.notifyGesture(gestureInfo); + service.notifyGesture(gestureEvent); return true; } } diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java index 9101a01bc8fe..7e8fb295d036 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java @@ -16,7 +16,7 @@ package com.android.server.accessibility.gestures; -import android.accessibilityservice.AccessibilityGestureInfo; +import android.accessibilityservice.AccessibilityGestureEvent; import android.accessibilityservice.AccessibilityService; import android.content.Context; import android.gesture.GesturePoint; @@ -119,11 +119,11 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen /** * Called when an event stream is recognized as a gesture. * - * @param gestureInfo Information about the gesture. + * @param gestureEvent Information about the gesture. * * @return true if the event is consumed, else false */ - boolean onGestureCompleted(AccessibilityGestureInfo gestureInfo); + boolean onGestureCompleted(AccessibilityGestureEvent gestureEvent); /** * Called when the system has decided an event stream doesn't match any @@ -567,19 +567,19 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen switch (direction) { case LEFT: return mListener.onGestureCompleted( - new AccessibilityGestureInfo(AccessibilityService.GESTURE_SWIPE_LEFT, + new AccessibilityGestureEvent(AccessibilityService.GESTURE_SWIPE_LEFT, displayId)); case RIGHT: return mListener.onGestureCompleted( - new AccessibilityGestureInfo(AccessibilityService.GESTURE_SWIPE_RIGHT, + new AccessibilityGestureEvent(AccessibilityService.GESTURE_SWIPE_RIGHT, displayId)); case UP: return mListener.onGestureCompleted( - new AccessibilityGestureInfo(AccessibilityService.GESTURE_SWIPE_UP, + new AccessibilityGestureEvent(AccessibilityService.GESTURE_SWIPE_UP, displayId)); case DOWN: return mListener.onGestureCompleted( - new AccessibilityGestureInfo(AccessibilityService.GESTURE_SWIPE_DOWN, + new AccessibilityGestureEvent(AccessibilityService.GESTURE_SWIPE_DOWN, displayId)); default: // Do nothing. @@ -600,7 +600,7 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen int segmentDirection1 = toDirection(dX1, dY1); int gestureId = DIRECTIONS_TO_GESTURE_ID[segmentDirection0][segmentDirection1]; return mListener.onGestureCompleted( - new AccessibilityGestureInfo(gestureId, displayId)); + new AccessibilityGestureEvent(gestureId, displayId)); } // else if (path.size() < 2 || 3 < path.size()) then no gesture recognized. return mListener.onGestureCancelled(event, policyFlags); diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index a338b901d24c..7044c4d28bdf 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -20,7 +20,7 @@ import static android.view.MotionEvent.INVALID_POINTER_ID; import static com.android.server.accessibility.gestures.TouchState.ALL_POINTER_ID_BITS; -import android.accessibilityservice.AccessibilityGestureInfo; +import android.accessibilityservice.AccessibilityGestureEvent; import android.content.Context; import android.graphics.Point; import android.os.Handler; @@ -343,14 +343,14 @@ public class TouchExplorer extends BaseEventStreamTransformation } @Override - public boolean onGestureCompleted(AccessibilityGestureInfo gestureInfo) { + public boolean onGestureCompleted(AccessibilityGestureEvent gestureEvent) { if (!mState.isGestureDetecting()) { return false; } endGestureDetection(true); - mAms.onGesture(gestureInfo); + mAms.onGesture(gestureEvent); return true; } 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/MountServiceIdler.java b/services/core/java/com/android/server/MountServiceIdler.java index 79b1226a3f7e..6bc1a570b7c0 100644 --- a/services/core/java/com/android/server/MountServiceIdler.java +++ b/services/core/java/com/android/server/MountServiceIdler.java @@ -99,32 +99,28 @@ public class MountServiceIdler extends JobService { public static void scheduleIdlePass(Context context) { JobScheduler tm = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); - final long today3AM = MidnightInTime(0, 0).getTimeInMillis(); - final long today4AM = MidnightInTime(0, 1).getTimeInMillis(); + final long today3AM = offsetFromTodayMidnight(0, 3).getTimeInMillis(); + final long today4AM = offsetFromTodayMidnight(0, 4).getTimeInMillis(); + final long tomorrow3AM = offsetFromTodayMidnight(1, 3).getTimeInMillis(); - long nextScheduleTime, maxScheduleTime; + long nextScheduleTime; if (System.currentTimeMillis() > today3AM && System.currentTimeMillis() < today4AM) { nextScheduleTime = TimeUnit.SECONDS.toMillis(10); - maxScheduleTime = today4AM - System.currentTimeMillis(); } else { - final long tomorrow3AM = MidnightInTime(1, 0).getTimeInMillis(); - final long twodays3AM = MidnightInTime(2, 0).getTimeInMillis(); nextScheduleTime = tomorrow3AM - System.currentTimeMillis(); // 3AM tomorrow - maxScheduleTime = twodays3AM - System.currentTimeMillis(); // 3AM in two days } JobInfo.Builder builder = new JobInfo.Builder(MOUNT_JOB_ID, sIdleService); builder.setRequiresDeviceIdle(true); - builder.setRequiresCharging(true); + builder.setRequiresBatteryNotLow(true); builder.setMinimumLatency(nextScheduleTime); - builder.setOverrideDeadline(maxScheduleTime); tm.schedule(builder.build()); } - private static Calendar MidnightInTime(int nDays, int nHours) { + private static Calendar offsetFromTodayMidnight(int nDays, int nHours) { Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); - calendar.set(Calendar.HOUR_OF_DAY, 3 + nHours); + calendar.set(Calendar.HOUR_OF_DAY, nHours); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index 548665ba3a32..bc509561163a 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<>(); @@ -807,7 +812,6 @@ public class PackageWatchdog { */ private static class ObserverInternal { public final String name; - //TODO(b/120598832): Add getter for mPackages @GuardedBy("mLock") public final ArrayMap<String, MonitoredPackage> packages = new ArrayMap<>(); @Nullable @@ -969,6 +973,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 +995,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 +1029,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..6c42c7f2583c 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: 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..a57321e8012d 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -793,62 +793,68 @@ public class PermissionManagerService extends IPermissionManager.Stub { final CheckPermissionDelegate checkPermissionDelegate; synchronized (mLock) { - if (mCheckPermissionDelegate == null) { - return checkPermissionImpl(permName, pkgName, userId); - } checkPermissionDelegate = mCheckPermissionDelegate; } + if (checkPermissionDelegate == null) { + return checkPermissionImpl(permName, pkgName, userId); + } return checkPermissionDelegate.checkPermission(permName, pkgName, userId, - PermissionManagerService.this::checkPermissionImpl); + this::checkPermissionImpl); } - private int checkPermissionImpl(String permName, String pkgName, int userId) { - final PackageParser.Package pkg = mPackageManagerInt.getPackage(pkgName); + private int checkPermissionImpl(@NonNull String permissionName, @NonNull String packageName, + @UserIdInt int userId) { + final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName); if (pkg == null) { return PackageManager.PERMISSION_DENIED; } - return checkPermissionInternal(pkg, true, permName, userId); + return checkPermissionInternal(pkg, true, permissionName, true, userId) + ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED; } - private int checkPermissionInternal(@NonNull Package pkg, boolean isPackageExplicit, - @NonNull String permissionName, @UserIdInt int userId) { + private boolean checkPermissionInternal(@NonNull Package pkg, boolean isPackageExplicit, + @NonNull String permissionName, boolean useRequestedPermissionsForLegacyApps, + @UserIdInt int userId) { final int callingUid = getCallingUid(); if (isPackageExplicit || pkg.mSharedUserId == null) { if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { - return PackageManager.PERMISSION_DENIED; + return false; } } else { if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { - return PackageManager.PERMISSION_DENIED; + return false; } } final int uid = UserHandle.getUid(userId, pkg.applicationInfo.uid); final PackageSetting ps = (PackageSetting) pkg.mExtras; if (ps == null) { - return PackageManager.PERMISSION_DENIED; + return false; } final PermissionsState permissionsState = ps.getPermissionsState(); - if (checkSinglePermissionInternal(uid, permissionsState, permissionName)) { - return PackageManager.PERMISSION_GRANTED; + if (checkSinglePermissionInternal(uid, permissionsState, permissionName, + useRequestedPermissionsForLegacyApps)) { + return true; } final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName); - if (fullerPermissionName != null - && checkSinglePermissionInternal(uid, permissionsState, fullerPermissionName)) { - return PackageManager.PERMISSION_GRANTED; + if (fullerPermissionName != null && checkSinglePermissionInternal(uid, permissionsState, + fullerPermissionName, useRequestedPermissionsForLegacyApps)) { + return true; } - return PackageManager.PERMISSION_DENIED; + return false; } private boolean checkSinglePermissionInternal(int uid, - @NonNull PermissionsState permissionsState, @NonNull String permissionName) { + @NonNull PermissionsState permissionsState, @NonNull String permissionName, + boolean useRequestedPermissionsForLegacyApps) { boolean hasPermission = permissionsState.hasPermission(permissionName, UserHandle.getUserId(uid)); - if (!hasPermission && mSettings.isPermissionRuntime(permissionName)) { + if (!hasPermission && useRequestedPermissionsForLegacyApps + && mSettings.isPermissionRuntime(permissionName)) { final String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid); final int packageNamesSize = packageNames != null ? packageNames.length : 0; for (int i = 0; i < packageNamesSize; i++) { @@ -891,12 +897,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { checkPermissionDelegate = mCheckPermissionDelegate; } return checkPermissionDelegate.checkUidPermission(permName, uid, - PermissionManagerService.this::checkUidPermissionImpl); + this::checkUidPermissionImpl); } - private int checkUidPermissionImpl(String permName, int uid) { + private int checkUidPermissionImpl(@NonNull String permissionName, int uid) { final PackageParser.Package pkg = mPackageManagerInt.getPackage(uid); - return checkUidPermissionInternal(pkg, uid, permName); + return checkUidPermissionInternal(uid, pkg, permissionName, true) + ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED; } /** @@ -906,24 +913,25 @@ public class PermissionManagerService extends IPermissionManager.Stub { * * @see SystemConfig#getSystemPermissions() */ - private int checkUidPermissionInternal(@Nullable Package pkg, int uid, - @NonNull String permissionName) { + private boolean checkUidPermissionInternal(int uid, @Nullable Package pkg, + @NonNull String permissionName, boolean useRequestedPermissionsForLegacyApps) { if (pkg != null) { final int userId = UserHandle.getUserId(uid); - return checkPermissionInternal(pkg, false, permissionName, userId); + return checkPermissionInternal(pkg, false, permissionName, + useRequestedPermissionsForLegacyApps, userId); } if (checkSingleUidPermissionInternal(uid, permissionName)) { - return PackageManager.PERMISSION_GRANTED; + return true; } final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName); if (fullerPermissionName != null && checkSingleUidPermissionInternal(uid, fullerPermissionName)) { - return PackageManager.PERMISSION_GRANTED; + return true; } - return PackageManager.PERMISSION_DENIED; + return false; } private boolean checkSingleUidPermissionInternal(int uid, @NonNull String permissionName) { @@ -933,6 +941,17 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } + private int computeRuntimePermissionAppOpMode(int uid, @NonNull String permissionName) { + boolean granted = isUidPermissionGranted(uid, permissionName); + // TODO: Foreground permissions. + return granted ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED; + } + + private boolean isUidPermissionGranted(int uid, @NonNull String permissionName) { + final PackageParser.Package pkg = mPackageManagerInt.getPackage(uid); + return checkUidPermissionInternal(uid, pkg, permissionName, false); + } + @Override public void addOnPermissionsChangeListener(IOnPermissionsChangeListener listener) { mContext.enforceCallingOrSelfPermission( @@ -4446,6 +4465,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { StorageManager.UUID_PRIVATE_INTERNAL, true, mDefaultPermissionCallback); } } + + @Override + public int computeRuntimePermissionAppOpMode(int uid, @NonNull String permissionName) { + return PermissionManagerService.this.computeRuntimePermissionAppOpMode(uid, + permissionName); + } } private static final class OnPermissionChangeListeners extends Handler { diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index 04ec5ba04bb6..8f22f9245a53 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -445,4 +445,13 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager /** Called when a new user has been created. */ public abstract void onNewUserCreated(@UserIdInt int userId); + + /** + * Compute an app op mode based on its runtime permission state. + * + * @param uid the uid for the app op + * @param permissionName the permission name for the app op + * @return the computed mode + */ + public abstract int computeRuntimePermissionAppOpMode(int uid, @NonNull String permissionName); } 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/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 8bb37bbdfc87..50200a7cf7bc 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -4940,6 +4940,12 @@ class ActivityStack extends ConfigurationContainer { } } + + Rect getDefaultPictureInPictureBounds(float aspectRatio) { + if (getTaskStack() == null) return null; + return getTaskStack().getPictureInPictureBounds(aspectRatio, null /* currentStackBounds */); + } + void animateResizePinnedStack(Rect sourceHintBounds, Rect toBounds, int animationDuration, boolean fromFullscreen) { if (!inPinnedWindowingMode()) return; 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/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 71f02e8a8569..ffd9021989b4 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -1021,7 +1021,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (DEBUG_ADD_REMOVE) Slog.v(TAG, "notifyAppStopped: " + this); mAppStopped = true; // Reset the last saved PiP snap fraction on app stop. - mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(mActivityComponent); + mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(this); destroySurfaces(); // Remove any starting window that was added for this app if they are still around. removeStartingWindow(); @@ -1705,7 +1705,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return; } - if (prevWinMode == WINDOWING_MODE_PINNED && winMode != WINDOWING_MODE_UNDEFINED + if (prevWinMode != WINDOWING_MODE_UNDEFINED && winMode == WINDOWING_MODE_PINNED) { + // Entering PiP from fullscreen, reset the snap fraction + mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(this); + } else if (prevWinMode == WINDOWING_MODE_PINNED && winMode != WINDOWING_MODE_UNDEFINED && !isHidden()) { // Leaving PiP to fullscreen, save the snap fraction based on the pre-animation bounds // for the next re-entry into PiP (assuming the activity is not hidden or destroyed) @@ -1723,8 +1726,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree stackBounds = mTmpRect; pinnedStack.getBounds(stackBounds); } - mDisplayContent.mPinnedStackControllerLocked.saveReentrySnapFraction( - mActivityComponent, stackBounds); + mDisplayContent.mPinnedStackControllerLocked.saveReentrySnapFraction(this, + stackBounds); } } else if (shouldStartChangeTransition(prevWinMode, winMode)) { initializeChangeTransition(mTmpPrevBounds); 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/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java index 8e57fec6ba46..ef0049b068f4 100644 --- a/services/core/java/com/android/server/wm/PinnedStackController.java +++ b/services/core/java/com/android/server/wm/PinnedStackController.java @@ -27,7 +27,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.NonNull; import android.app.RemoteAction; -import android.content.ComponentName; import android.content.pm.ParceledListSlice; import android.content.res.Resources; import android.graphics.Point; @@ -51,6 +50,7 @@ import com.android.internal.util.Preconditions; import com.android.server.UiThread; import java.io.PrintWriter; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; @@ -74,7 +74,7 @@ class PinnedStackController { private static final String TAG = TAG_WITH_CLASS_NAME ? "PinnedStackController" : TAG_WM; - private static final float INVALID_SNAP_FRACTION = -1f; + public static final float INVALID_SNAP_FRACTION = -1f; private final WindowManagerService mService; private final DisplayContent mDisplayContent; private final Handler mHandler = UiThread.getHandler(); @@ -106,6 +106,9 @@ class PinnedStackController { private int mDefaultStackGravity; private float mDefaultAspectRatio; private Point mScreenEdgeInsets; + private int mCurrentMinSize; + private float mReentrySnapFraction = INVALID_SNAP_FRACTION; + private WeakReference<AppWindowToken> mLastPipActivity = null; // The aspect ratio bounds of the PIP. private float mMinAspectRatio; @@ -115,6 +118,7 @@ class PinnedStackController { private final DisplayMetrics mTmpMetrics = new DisplayMetrics(); private final Rect mTmpInsets = new Rect(); private final Rect mTmpRect = new Rect(); + private final Rect mTmpAnimatingBoundsRect = new Rect(); private final Point mTmpDisplaySize = new Point(); @@ -132,19 +136,16 @@ class PinnedStackController { } @Override - public int getDisplayRotation() { - synchronized (mService.mGlobalLock) { - return mDisplayInfo.rotation; - } + public void setMinEdgeSize(int minEdgeSize) { + mHandler.post(() -> { + mCurrentMinSize = Math.max(mDefaultMinSize, minEdgeSize); + }); } @Override - public void startAnimation(Rect destinationBounds, Rect sourceRectHint, - int animationDuration) { + public int getDisplayRotation() { synchronized (mService.mGlobalLock) { - final TaskStack pinnedStack = mDisplayContent.getPinnedStack(); - pinnedStack.animateResizePinnedStack(destinationBounds, - sourceRectHint, animationDuration, true /* fromFullscreen */); + return mDisplayInfo.rotation; } } } @@ -187,6 +188,7 @@ class PinnedStackController { final Resources res = mService.mContext.getResources(); mDefaultMinSize = res.getDimensionPixelSize( com.android.internal.R.dimen.default_minimal_size_pip_resizable_task); + mCurrentMinSize = mDefaultMinSize; mDefaultAspectRatio = res.getFloat( com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio); final String screenEdgeInsetsDpString = res.getString( @@ -214,7 +216,6 @@ class PinnedStackController { listener.asBinder().linkToDeath(mPinnedStackListenerDeathHandler, 0); listener.onListenerRegistered(mCallbacks); mPinnedStackListener = listener; - notifyDisplayInfoChanged(mDisplayInfo); notifyImeVisibilityChanged(mIsImeShowing, mImeHeight); notifyShelfVisibilityChanged(mIsShelfShowing, mShelfHeight); // The movement bounds notification needs to be sent before the minimized state, since @@ -237,34 +238,58 @@ class PinnedStackController { } /** - * Saves the current snap fraction for re-entry of the current activity into PiP. + * Returns the current bounds (or the default bounds if there are no current bounds) with the + * specified aspect ratio. */ - void saveReentrySnapFraction(final ComponentName componentName, final Rect stackBounds) { - if (mPinnedStackListener == null) return; - try { - mPinnedStackListener.onSaveReentrySnapFraction(componentName, stackBounds); - } catch (RemoteException e) { - Slog.e(TAG_WM, "Error delivering save reentry fraction event.", e); + Rect transformBoundsToAspectRatio(Rect stackBounds, float aspectRatio, + boolean useCurrentMinEdgeSize) { + // Save the snap fraction, calculate the aspect ratio based on screen size + final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds, + getMovementBounds(stackBounds)); + + final int minEdgeSize = useCurrentMinEdgeSize ? mCurrentMinSize : mDefaultMinSize; + final Size size = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, minEdgeSize, + mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); + final int left = (int) (stackBounds.centerX() - size.getWidth() / 2f); + final int top = (int) (stackBounds.centerY() - size.getHeight() / 2f); + stackBounds.set(left, top, left + size.getWidth(), top + size.getHeight()); + mSnapAlgorithm.applySnapFraction(stackBounds, getMovementBounds(stackBounds), snapFraction); + if (mIsMinimized) { + applyMinimizedOffset(stackBounds, getMovementBounds(stackBounds)); } + return stackBounds; + } + + /** + * Saves the current snap fraction for re-entry of the current activity into PiP. + */ + void saveReentrySnapFraction(final AppWindowToken token, final Rect stackBounds) { + mReentrySnapFraction = getSnapFraction(stackBounds); + mLastPipActivity = new WeakReference<>(token); } /** * Resets the last saved snap fraction so that the default bounds will be returned. */ - void resetReentrySnapFraction(ComponentName componentName) { - if (mPinnedStackListener == null) return; - try { - mPinnedStackListener.onResetReentrySnapFraction(componentName); - } catch (RemoteException e) { - Slog.e(TAG_WM, "Error delivering reset reentry fraction event.", e); + void resetReentrySnapFraction(AppWindowToken token) { + if (mLastPipActivity != null && mLastPipActivity.get() == token) { + mReentrySnapFraction = INVALID_SNAP_FRACTION; + mLastPipActivity = null; } } /** + * @return the default bounds to show the PIP when there is no active PIP. + */ + Rect getDefaultOrLastSavedBounds() { + return getDefaultBounds(mReentrySnapFraction); + } + + /** * @return the default bounds to show the PIP, if a {@param snapFraction} is provided, then it * will apply the default bounds to the provided snap fraction. */ - private Rect getDefaultBounds(float snapFraction) { + Rect getDefaultBounds(float snapFraction) { synchronized (mService.mGlobalLock) { final Rect insetBounds = new Rect(); getInsetBounds(insetBounds); @@ -286,18 +311,13 @@ class PinnedStackController { } } - private void setDisplayInfo(DisplayInfo displayInfo) { - mDisplayInfo.copyFrom(displayInfo); - notifyDisplayInfoChanged(mDisplayInfo); - } - /** * In the case where the display rotation is changed but there is no stack, we can't depend on * onTaskStackBoundsChanged() to be called. But we still should update our known display info * with the new state so that we can update SystemUI. */ synchronized void onDisplayInfoChanged(DisplayInfo displayInfo) { - setDisplayInfo(displayInfo); + mDisplayInfo.copyFrom(displayInfo); notifyMovementBoundsChanged(false /* fromImeAdjustment */, false /* fromShelfAdjustment */); } @@ -315,7 +335,7 @@ class PinnedStackController { } else if (targetBounds.isEmpty()) { // The stack is null, we are just initializing the stack, so just store the display // info and ignore - setDisplayInfo(displayInfo); + mDisplayInfo.copyFrom(displayInfo); outBounds.setEmpty(); return false; } @@ -325,8 +345,7 @@ class PinnedStackController { // Calculate the snap fraction of the current stack along the old movement bounds final float snapFraction = getSnapFraction(postChangeStackBounds); - - setDisplayInfo(displayInfo); + mDisplayInfo.copyFrom(displayInfo); // Calculate the stack bounds in the new orientation to the same same fraction along the // rotated movement bounds. @@ -387,11 +406,8 @@ class PinnedStackController { void setAspectRatio(float aspectRatio) { if (Float.compare(mAspectRatio, aspectRatio) != 0) { mAspectRatio = aspectRatio; - notifyAspectRatioChanged(aspectRatio); notifyMovementBoundsChanged(false /* fromImeAdjustment */, false /* fromShelfAdjustment */); - notifyPrepareAnimation(null /* sourceHintRect */, aspectRatio, - null /* stackBounds */); } } @@ -413,10 +429,6 @@ class PinnedStackController { notifyActionsChanged(mActions); } - void prepareAnimation(Rect sourceRectHint, float aspectRatio, Rect stackBounds) { - notifyPrepareAnimation(sourceRectHint, aspectRatio, stackBounds); - } - private boolean isSameDimensionAndRotation(@NonNull DisplayInfo display1, @NonNull DisplayInfo display2) { Preconditions.checkNotNull(display1); @@ -449,15 +461,6 @@ class PinnedStackController { } } - private void notifyAspectRatioChanged(float aspectRatio) { - if (mPinnedStackListener == null) return; - try { - mPinnedStackListener.onAspectRatioChanged(aspectRatio); - } catch (RemoteException e) { - Slog.e(TAG_WM, "Error delivering aspect ratio changed event.", e); - } - } - /** * Notifies listeners that the PIP minimized state has changed. */ @@ -494,13 +497,23 @@ class PinnedStackController { return; } try { - final Rect animatingBounds = new Rect(); + final Rect insetBounds = new Rect(); + getInsetBounds(insetBounds); + final Rect normalBounds = getDefaultBounds(INVALID_SNAP_FRACTION); + if (isValidPictureInPictureAspectRatio(mAspectRatio)) { + transformBoundsToAspectRatio(normalBounds, mAspectRatio, + false /* useCurrentMinEdgeSize */); + } + final Rect animatingBounds = mTmpAnimatingBoundsRect; final TaskStack pinnedStack = mDisplayContent.getPinnedStack(); if (pinnedStack != null) { pinnedStack.getAnimationOrCurrentBounds(animatingBounds); + } else { + animatingBounds.set(normalBounds); } - mPinnedStackListener.onMovementBoundsChanged(animatingBounds, - fromImeAdjustment, fromShelfAdjustment); + mPinnedStackListener.onMovementBoundsChanged(insetBounds, normalBounds, + animatingBounds, fromImeAdjustment, fromShelfAdjustment, + mDisplayInfo.rotation); } catch (RemoteException e) { Slog.e(TAG_WM, "Error delivering actions changed event.", e); } @@ -508,30 +521,6 @@ class PinnedStackController { } /** - * Notifies listeners that the PIP animation is about to happen. - */ - private void notifyDisplayInfoChanged(DisplayInfo displayInfo) { - if (mPinnedStackListener == null) return; - try { - mPinnedStackListener.onDisplayInfoChanged(displayInfo); - } catch (RemoteException e) { - Slog.e(TAG_WM, "Error delivering DisplayInfo changed event.", e); - } - } - - /** - * Notifies listeners that the PIP animation is about to happen. - */ - private void notifyPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect stackBounds) { - if (mPinnedStackListener == null) return; - try { - mPinnedStackListener.onPrepareAnimation(sourceRectHint, aspectRatio, stackBounds); - } catch (RemoteException e) { - Slog.e(TAG_WM, "Error delivering prepare animation event.", e); - } - } - - /** * @return the bounds on the screen that the PIP can be visible in. */ private void getInsetBounds(Rect outRect) { @@ -615,6 +604,7 @@ class PinnedStackController { pw.println(prefix + " mImeHeight=" + mImeHeight); pw.println(prefix + " mIsShelfShowing=" + mIsShelfShowing); pw.println(prefix + " mShelfHeight=" + mShelfHeight); + pw.println(prefix + " mReentrySnapFraction=" + mReentrySnapFraction); pw.println(prefix + " mIsMinimized=" + mIsMinimized); pw.println(prefix + " mAspectRatio=" + mAspectRatio); pw.println(prefix + " mMinAspectRatio=" + mMinAspectRatio); diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java index d9e30a2da9a5..4b2d4ce3d799 100644 --- a/services/core/java/com/android/server/wm/RootActivityContainer.java +++ b/services/core/java/com/android/server/wm/RootActivityContainer.java @@ -959,6 +959,10 @@ class RootActivityContainer extends ConfigurationContainer // Need to make sure the pinned stack exist so we can resize it below... stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP); + // Calculate the target bounds here before the task is reparented back into pinned windowing + // mode (which will reset the saved bounds) + final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio); + try { final TaskRecord task = r.getTaskRecord(); // Resize the pinned stack to match the current size of the task the activity we are @@ -997,14 +1001,9 @@ class RootActivityContainer extends ConfigurationContainer mService.continueWindowLayout(); } - // Notify the pinned stack controller to prepare the PiP animation, expect callback - // delivered from SystemUI to WM to start the animation. - final PinnedStackController pinnedStackController = - display.mDisplayContent.getPinnedStackController(); - pinnedStackController.prepareAnimation(sourceHintBounds, aspectRatio, - null /* stackBounds */); + stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */, + true /* fromFullscreen */); - // TODO: revisit the following statement after the animation is moved from WM to SysUI. // Update the visibility of all activities after the they have been reparented to the new // stack. This MUST run after the animation above is scheduled to ensure that the windows // drawn signal is scheduled after the bounds animation start call on the bounds animator @@ -1615,7 +1614,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 +1624,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 +1664,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/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 58542e582067..239bd004705f 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -1661,6 +1661,40 @@ public class TaskStack extends WindowContainer<Task> implements } /** + * @return the current stack bounds transformed to the given {@param aspectRatio}. If + * the default bounds is {@code null}, then the {@param aspectRatio} is applied to the + * default bounds. + */ + Rect getPictureInPictureBounds(float aspectRatio, Rect stackBounds) { + if (!mWmService.mAtmService.mSupportsPictureInPicture) { + return null; + } + + final DisplayContent displayContent = getDisplayContent(); + if (displayContent == null) { + return null; + } + + if (!inPinnedWindowingMode()) { + return null; + } + + final PinnedStackController pinnedStackController = + displayContent.getPinnedStackController(); + if (stackBounds == null) { + // Calculate the aspect ratio bounds from the default bounds + stackBounds = pinnedStackController.getDefaultOrLastSavedBounds(); + } + + if (pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)) { + return pinnedStackController.transformBoundsToAspectRatio(stackBounds, aspectRatio, + true /* useCurrentMinEdgeSize */); + } else { + return stackBounds; + } + } + + /** * Animates the pinned stack. */ void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds, @@ -1737,11 +1771,6 @@ public class TaskStack extends WindowContainer<Task> implements return; } - final DisplayContent displayContent = getDisplayContent(); - if (displayContent == null) { - return; - } - if (!inPinnedWindowingMode()) { return; } @@ -1752,10 +1781,13 @@ public class TaskStack extends WindowContainer<Task> implements if (Float.compare(aspectRatio, pinnedStackController.getAspectRatio()) == 0) { return; } - - // Notify the pinned stack controller about aspect ratio change. - // This would result a callback delivered from SystemUI to WM to start animation, - // if the bounds are ought to be altered due to aspect ratio change. + getAnimationOrCurrentBounds(mTmpFromBounds); + mTmpToBounds.set(mTmpFromBounds); + getPictureInPictureBounds(aspectRatio, mTmpToBounds); + if (!mTmpToBounds.equals(mTmpFromBounds)) { + animateResizePinnedStack(mTmpToBounds, null /* sourceHintBounds */, + -1 /* duration */, false /* fromFullscreen */); + } pinnedStackController.setAspectRatio( pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio) ? aspectRatio : -1f); 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 c05e01a40705..58adcf097c6e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -1981,6 +1981,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return LocalServices.getService(LockSettingsInternal.class); } + boolean hasUserSetupCompleted(DevicePolicyData userData) { + return userData.mUserSetupComplete; + } + boolean isBuildDebuggable() { return Build.IS_DEBUGGABLE; } @@ -8253,7 +8257,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/accessibility/gestures/AccessibilityGestureDetectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/AccessibilityGestureDetectorTest.java index 2585a2832094..b7079124fb79 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/AccessibilityGestureDetectorTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/AccessibilityGestureDetectorTest.java @@ -176,6 +176,6 @@ public class AccessibilityGestureDetectorTest { // Check that correct gesture was recognized. verify(mResultListener).onGestureCompleted( - argThat(gestureInfo -> gestureInfo.getGestureId() == gestureId)); + argThat(gestureEvent -> gestureEvent.getGestureId() == gestureId)); } } 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..4f00383d1789 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -38,6 +38,7 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.res.Configuration; +import android.graphics.Rect; import android.os.UserHandle; import android.service.voice.IVoiceInteractionSession; import android.testing.DexmakerShareClassLoaderRule; @@ -139,6 +140,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 +217,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 +263,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 @@ -432,7 +446,12 @@ class ActivityTestsBase { final ActivityStackSupervisor supervisor = mRootActivityContainer.mStackSupervisor; if (mWindowingMode == WINDOWING_MODE_PINNED) { stack = new ActivityStack(mDisplay, stackId, supervisor, - mWindowingMode, ACTIVITY_TYPE_STANDARD, mOnTop); + mWindowingMode, ACTIVITY_TYPE_STANDARD, mOnTop) { + @Override + Rect getDefaultPictureInPictureBounds(float aspectRatio) { + return new Rect(50, 50, 100, 100); + } + }; } else { stack = new ActivityStack(mDisplay, stackId, supervisor, mWindowingMode, mActivityType, mOnTop); 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/PinnedStackControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java index e9c226340164..efd468f1f77a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java @@ -66,8 +66,8 @@ public class PinnedStackControllerTest extends WindowTestsBase { verify(mIPinnedStackListener).onImeVisibilityChanged(false, 0); verify(mIPinnedStackListener).onShelfVisibilityChanged(false, 0); - verify(mIPinnedStackListener).onMovementBoundsChanged(any(), eq(false), - eq(false)); + verify(mIPinnedStackListener).onMovementBoundsChanged(any(), any(), any(), eq(false), + eq(false), anyInt()); verify(mIPinnedStackListener).onActionsChanged(any()); verify(mIPinnedStackListener).onMinimizedStateChanged(anyBoolean()); @@ -75,8 +75,8 @@ public class PinnedStackControllerTest extends WindowTestsBase { mWm.setShelfHeight(true, SHELF_HEIGHT); verify(mIPinnedStackListener).onShelfVisibilityChanged(true, SHELF_HEIGHT); - verify(mIPinnedStackListener).onMovementBoundsChanged(any(), eq(false), - eq(true)); + verify(mIPinnedStackListener).onMovementBoundsChanged(any(), any(), any(), eq(false), + eq(true), anyInt()); verify(mIPinnedStackListener, never()).onImeVisibilityChanged(anyBoolean(), anyInt()); } } 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/startop/apps/test/src/SystemServerBenchmarkActivity.java b/startop/apps/test/src/SystemServerBenchmarkActivity.java index 61d43226c41e..ed3cbe9e3e30 100644 --- a/startop/apps/test/src/SystemServerBenchmarkActivity.java +++ b/startop/apps/test/src/SystemServerBenchmarkActivity.java @@ -17,8 +17,10 @@ package com.android.startop.test; import android.app.Activity; +import android.app.ActivityManager; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -139,6 +141,10 @@ public class SystemServerBenchmarkActivity extends Activity { throw new RuntimeException(e); } }); + + new Benchmark(benchmarkList, "getPackagesForUid", () -> { + pm.getPackagesForUid(app.uid); + }); } catch (NameNotFoundException e) { throw new RuntimeException(e); } @@ -152,5 +158,45 @@ public class SystemServerBenchmarkActivity extends Activity { } }); + new Benchmark(benchmarkList, "getLaunchIntentForPackage", () -> { + pm.getLaunchIntentForPackage("com.android.startop.test"); + }); + + new Benchmark(benchmarkList, "getPackageUid", () -> { + try { + pm.getPackageUid("com.android.startop.test", 0); + } catch (NameNotFoundException e) { + throw new RuntimeException(e); + } + }); + + new Benchmark(benchmarkList, "checkPermission", () -> { + // Check for the first permission I could find. + pm.checkPermission("android.permission.SEND_SMS", "com.android.startop.test"); + }); + + new Benchmark(benchmarkList, "checkSignatures", () -> { + // Compare with settings, since settings is on both AOSP and Master builds + pm.checkSignatures("com.android.settings", "com.android.startop.test"); + }); + + Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED); + new Benchmark(benchmarkList, "queryBroadcastReceivers", () -> { + pm.queryBroadcastReceivers(intent, 0); + }); + + new Benchmark(benchmarkList, "hasSystemFeature", () -> { + pm.hasSystemFeature(PackageManager.FEATURE_CAMERA); + }); + + new Benchmark(benchmarkList, "resolveService", () -> { + pm.resolveService(intent, 0); + }); + + ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); + new Benchmark(benchmarkList, "getRunningAppProcesses", () -> { + am.getRunningAppProcesses(); + }); + } } 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/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java index 58f285851375..f527bc3b6df6 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -578,7 +578,8 @@ public class SubscriptionInfo implements Parcelable { PackageManager packageManager = context.getPackageManager(); PackageInfo packageInfo; try { - packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); + packageInfo = packageManager.getPackageInfo(packageName, + PackageManager.GET_SIGNING_CERTIFICATES); } catch (PackageManager.NameNotFoundException e) { Log.d("SubscriptionInfo", "canManageSubscription: Unknown package: " + packageName, e); return false; diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index d3cba2e3e889..51de903ed37e 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -2701,7 +2701,8 @@ public class SubscriptionManager { PackageManager packageManager = mContext.getPackageManager(); PackageInfo packageInfo; try { - packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); + packageInfo = packageManager.getPackageInfo(packageName, + PackageManager.GET_SIGNING_CERTIFICATES); } catch (PackageManager.NameNotFoundException e) { logd("Unknown package: " + packageName); return false; diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index f25b012389c4..b5dbbe749563 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -3251,6 +3251,31 @@ public class TelephonyManager { } } + + /** + * Returns true if the specified type of application (e.g. {@link #APPTYPE_CSIM} is present + * on the UICC card. + * + * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission + * + * @param appType the uicc app type like {@link APPTYPE_CSIM} + * @return true if the specified type of application in UICC CARD or false if no uicc or error. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public boolean isApplicationOnUicc(@UiccAppType int appType) { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.isApplicationOnUicc(getSubId(), appType); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#isApplicationOnUicc", e); + } + return false; + } + /** * Returns a constant indicating the state of the device SIM card in a logical slot. * diff --git a/telephony/java/android/telephony/UiccAccessRule.java b/telephony/java/android/telephony/UiccAccessRule.java index 811722f0bbff..93ccba1dd996 100644 --- a/telephony/java/android/telephony/UiccAccessRule.java +++ b/telephony/java/android/telephony/UiccAccessRule.java @@ -20,6 +20,7 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.pm.PackageInfo; import android.content.pm.Signature; +import android.content.pm.SigningInfo; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -169,17 +170,28 @@ public final class UiccAccessRule implements Parcelable { * * @param packageInfo package info fetched from * {@link android.content.pm.PackageManager#getPackageInfo}. - * {@link android.content.pm.PackageManager#GET_SIGNATURES} must have been passed in. + * {@link android.content.pm.PackageManager#GET_SIGNING_CERTIFICATES} must have been + * passed in. * @return either {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_HAS_ACCESS} or * {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_NO_ACCESS}. */ public int getCarrierPrivilegeStatus(PackageInfo packageInfo) { - if (packageInfo.signatures == null || packageInfo.signatures.length == 0) { + Signature[] signatures = packageInfo.signatures; + SigningInfo sInfo = packageInfo.signingInfo; + + if (sInfo != null) { + signatures = sInfo.getSigningCertificateHistory(); + if (sInfo.hasMultipleSigners()) { + signatures = sInfo.getApkContentsSigners(); + } + } + + if (signatures == null || signatures.length == 0) { throw new IllegalArgumentException( - "Must use GET_SIGNATURES when looking up package info"); + "Must use GET_SIGNING_CERTIFICATES when looking up package info"); } - for (Signature sig : packageInfo.signatures) { + for (Signature sig : signatures) { int accessStatus = getCarrierPrivilegeStatus(sig, packageInfo.packageName); if (accessStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS) { return accessStatus; diff --git a/telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl index 606df15b1782..6a35e33f44f9 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl @@ -22,7 +22,6 @@ package android.telephony.ims.aidl; */ oneway interface IImsSmsListener { void onSendSmsResult(int token, int messageRef, int status, int reason); - void onSmsStatusReportReceived(int token, int messageRef, in String format, - in byte[] pdu); + void onSmsStatusReportReceived(int token, in String format, in byte[] pdu); void onSmsReceived(int token, in String format, in byte[] pdu); -}
\ No newline at end of file +} diff --git a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java index 852c8e0618c8..2e4bfb3149be 100644 --- a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java @@ -147,7 +147,7 @@ public class ImsSmsImplBase { * {@link SmsMessage#FORMAT_3GPP2}. * @param smsc the Short Message Service Center address. * @param isRetry whether it is a retry of an already attempted message or not. - * @param pdu PDUs representing the contents of the message. + * @param pdu PDU representing the contents of the message. */ public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry, byte[] pdu) { @@ -166,27 +166,29 @@ public class ImsSmsImplBase { * provider. * * @param token token provided in {@link #onSmsReceived(int, String, byte[])} + * @param messageRef the message reference * @param result result of delivering the message. Valid values are: * {@link #DELIVER_STATUS_OK}, * {@link #DELIVER_STATUS_ERROR_GENERIC}, * {@link #DELIVER_STATUS_ERROR_NO_MEMORY}, * {@link #DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED} - * @param messageRef the message reference */ - public void acknowledgeSms(int token, @DeliverStatusResult int messageRef, int result) { + public void acknowledgeSms(int token, int messageRef, @DeliverStatusResult int result) { Log.e(LOG_TAG, "acknowledgeSms() not implemented."); } /** * This method will be triggered by the platform after - * {@link #onSmsStatusReportReceived(int, int, String, byte[])} has been called to provide the + * {@link #onSmsStatusReportReceived(int, int, String, byte[])} or + * {@link #onSmsStatusReportReceived(int, String, byte[])} has been called to provide the * result to the IMS provider. * - * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])} + * @param token token provided in {@link #onSmsStatusReportReceived(int, int, String, byte[])} + * or {@link #onSmsStatusReportReceived(int, String, byte[])} + * @param messageRef the message reference * @param result result of delivering the message. Valid values are: * {@link #STATUS_REPORT_STATUS_OK}, * {@link #STATUS_REPORT_STATUS_ERROR} - * @param messageRef the message reference */ public void acknowledgeSmsReport(int token, int messageRef, @StatusReportResult int result) { Log.e(LOG_TAG, "acknowledgeSmsReport() not implemented."); @@ -204,7 +206,7 @@ public class ImsSmsImplBase { * callbacks for this message. * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and * {@link SmsMessage#FORMAT_3GPP2}. - * @param pdu PDUs representing the contents of the message. + * @param pdu PDU representing the contents of the message. * @throws RuntimeException if called before {@link #onReady()} is triggered. */ public final void onSmsReceived(int token, String format, byte[] pdu) throws RuntimeException { @@ -285,23 +287,32 @@ public class ImsSmsImplBase { } /** - * Sets the status report of the sent message. + * This method should be triggered by the IMS providers when the status report of the sent + * message is received. The platform will handle the report and notify the IMS provider of the + * result by calling {@link #acknowledgeSmsReport(int, int, int)}. * + * This method must not be called before {@link #onReady()} is called or the call will fail. If + * the platform is not available, {@link #acknowledgeSmsReport(int, int, int)} will be called + * with the {@link #STATUS_REPORT_STATUS_ERROR} result code. * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])} * @param messageRef the message reference. * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and - * {@link SmsMessage#FORMAT_3GPP2}. - * @param pdu PDUs representing the content of the status report. + * {@link SmsMessage#FORMAT_3GPP2}. + * @param pdu PDU representing the content of the status report. * @throws RuntimeException if called before {@link #onReady()} is triggered + * + * @deprecated Use {@link #onSmsStatusReportReceived(int, String, byte[])} instead without the + * message reference. */ + @Deprecated public final void onSmsStatusReportReceived(int token, int messageRef, String format, - byte[] pdu) throws RuntimeException{ + byte[] pdu) throws RuntimeException { synchronized (mLock) { if (mListener == null) { throw new RuntimeException("Feature not ready."); } try { - mListener.onSmsStatusReportReceived(token, messageRef, format, pdu); + mListener.onSmsStatusReportReceived(token, format, pdu); } catch (RemoteException e) { Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage()); acknowledgeSmsReport(token, messageRef, STATUS_REPORT_STATUS_ERROR); @@ -310,6 +321,46 @@ public class ImsSmsImplBase { } /** + * This method should be triggered by the IMS providers when the status report of the sent + * message is received. The platform will handle the report and notify the IMS provider of the + * result by calling {@link #acknowledgeSmsReport(int, int, int)}. + * + * This method must not be called before {@link #onReady()} is called or the call will fail. If + * the platform is not available, {@link #acknowledgeSmsReport(int, int, int)} will be called + * with the {@link #STATUS_REPORT_STATUS_ERROR} result code. + * @param token unique token generated by IMS providers that the platform will use to trigger + * callbacks for this message. + * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and + * {@link SmsMessage#FORMAT_3GPP2}. + * @param pdu PDU representing the content of the status report. + * @throws RuntimeException if called before {@link #onReady()} is triggered + */ + public final void onSmsStatusReportReceived(int token, String format, byte[] pdu) + throws RuntimeException { + synchronized (mLock) { + if (mListener == null) { + throw new RuntimeException("Feature not ready."); + } + try { + mListener.onSmsStatusReportReceived(token, format, pdu); + } catch (RemoteException e) { + Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage()); + SmsMessage message = SmsMessage.createFromPdu(pdu, format); + if (message != null && message.mWrappedSmsMessage != null) { + acknowledgeSmsReport( + token, + message.mWrappedSmsMessage.mMessageRef, + STATUS_REPORT_STATUS_ERROR); + } else { + Log.w(LOG_TAG, + "onSmsStatusReportReceivedWithoutMessageRef: Invalid pdu entered."); + acknowledgeSmsReport(token, 0, STATUS_REPORT_STATUS_ERROR); + } + } + } + } + + /** * Returns the SMS format. Default is {@link SmsMessage#FORMAT_3GPP} unless overridden by IMS * Provider. * diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 9f1a2f765eee..866e936a1211 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -2012,6 +2012,13 @@ interface ITelephony { */ int getRadioHalVersion(); + /** + * Returns true if the specified type of application (e.g. {@link #APPTYPE_CSIM} is present + * on the UICC card. + * @hide + */ + boolean isApplicationOnUicc(int subId, int appType); + boolean isModemEnabledForSlot(int slotIndex, String callingPackage); boolean isDataEnabledForApn(int apnType, int subId, String callingPackage); 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)); |