diff options
164 files changed, 4115 insertions, 2030 deletions
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java index b1dbb28c9501..d07ed375b2ab 100644 --- a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java +++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java @@ -51,13 +51,14 @@ public class MyContentCaptureService extends ContentCaptureService { sServiceWatcher = null; } - public static void clearServiceWatcher() { - if (sServiceWatcher != null) { - if (sServiceWatcher.mReadyToClear) { - sServiceWatcher.mService = null; + private static void clearServiceWatcher() { + final ServiceWatcher sw = sServiceWatcher; + if (sw != null) { + if (sw.mReadyToClear) { + sw.mService = null; sServiceWatcher = null; } else { - sServiceWatcher.mReadyToClear = true; + sw.mReadyToClear = true; } } } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java index f1c624d1d9f5..67997cf31501 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java @@ -22,6 +22,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; +import android.annotation.Nullable; import android.app.job.JobInfo; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; @@ -86,9 +87,12 @@ public final class ConnectivityController extends RestrictingController implemen @GuardedBy("mLock") private final SparseArray<ArraySet<JobStatus>> mRequestedWhitelistJobs = new SparseArray<>(); - /** List of currently available networks. */ + /** + * Set of currently available networks mapped to their latest network capabilities. Cache the + * latest capabilities to avoid unnecessary calls into ConnectivityManager. + */ @GuardedBy("mLock") - private final ArraySet<Network> mAvailableNetworks = new ArraySet<>(); + private final ArrayMap<Network, NetworkCapabilities> mAvailableNetworks = new ArrayMap<>(); private static final int MSG_DATA_SAVER_TOGGLED = 0; private static final int MSG_UID_RULES_CHANGES = 1; @@ -165,9 +169,8 @@ public final class ConnectivityController extends RestrictingController implemen public boolean isNetworkAvailable(JobStatus job) { synchronized (mLock) { for (int i = 0; i < mAvailableNetworks.size(); ++i) { - final Network network = mAvailableNetworks.valueAt(i); - final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities( - network); + final Network network = mAvailableNetworks.keyAt(i); + final NetworkCapabilities capabilities = mAvailableNetworks.valueAt(i); final boolean satisfied = isSatisfied(job, network, capabilities, mConstants); if (DEBUG) { Slog.v(TAG, "isNetworkAvailable(" + job + ") with network " + network @@ -427,9 +430,33 @@ public final class ConnectivityController extends RestrictingController implemen return false; } + @Nullable + private NetworkCapabilities getNetworkCapabilities(@Nullable Network network) { + if (network == null) { + return null; + } + synchronized (mLock) { + // There is technically a race here if the Network object is reused. This can happen + // only if that Network disconnects and the auto-incrementing network ID in + // ConnectivityService wraps. This should no longer be a concern if/when we only make + // use of asynchronous calls. + if (mAvailableNetworks.get(network) != null) { + return mAvailableNetworks.get(network); + } + + // This should almost never happen because any time a new network connects, the + // NetworkCallback would populate mAvailableNetworks. However, it's currently necessary + // because we also call synchronous methods such as getActiveNetworkForUid. + // TODO(134978280): remove after switching to callback-based APIs + final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(network); + mAvailableNetworks.put(network, capabilities); + return capabilities; + } + } + private boolean updateConstraintsSatisfied(JobStatus jobStatus) { final Network network = mConnManager.getActiveNetworkForUid(jobStatus.getSourceUid()); - final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(network); + final NetworkCapabilities capabilities = getNetworkCapabilities(network); return updateConstraintsSatisfied(jobStatus, network, capabilities); } @@ -470,19 +497,13 @@ public final class ConnectivityController extends RestrictingController implemen */ private void updateTrackedJobs(int filterUid, Network filterNetwork) { synchronized (mLock) { - // Since this is a really hot codepath, temporarily cache any - // answers that we get from ConnectivityManager. - final ArrayMap<Network, NetworkCapabilities> networkToCapabilities = new ArrayMap<>(); - boolean changed = false; if (filterUid == -1) { for (int i = mTrackedJobs.size() - 1; i >= 0; i--) { - changed |= updateTrackedJobsLocked(mTrackedJobs.valueAt(i), - filterNetwork, networkToCapabilities); + changed |= updateTrackedJobsLocked(mTrackedJobs.valueAt(i), filterNetwork); } } else { - changed = updateTrackedJobsLocked(mTrackedJobs.get(filterUid), - filterNetwork, networkToCapabilities); + changed = updateTrackedJobsLocked(mTrackedJobs.get(filterUid), filterNetwork); } if (changed) { mStateChangedListener.onControllerStateChanged(); @@ -490,18 +511,13 @@ public final class ConnectivityController extends RestrictingController implemen } } - private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs, Network filterNetwork, - ArrayMap<Network, NetworkCapabilities> networkToCapabilities) { + private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs, Network filterNetwork) { if (jobs == null || jobs.size() == 0) { return false; } final Network network = mConnManager.getActiveNetworkForUid(jobs.valueAt(0).getSourceUid()); - NetworkCapabilities capabilities = networkToCapabilities.get(network); - if (capabilities == null) { - capabilities = mConnManager.getNetworkCapabilities(network); - networkToCapabilities.put(network, capabilities); - } + final NetworkCapabilities capabilities = getNetworkCapabilities(network); final boolean networkMatch = (filterNetwork == null || Objects.equals(filterNetwork, network)); @@ -544,9 +560,9 @@ public final class ConnectivityController extends RestrictingController implemen @Override public void onAvailable(Network network) { if (DEBUG) Slog.v(TAG, "onAvailable: " + network); - synchronized (mLock) { - mAvailableNetworks.add(network); - } + // Documentation says not to call getNetworkCapabilities here but wait for + // onCapabilitiesChanged instead. onCapabilitiesChanged should be called immediately + // after this, so no need to update mAvailableNetworks here. } @Override @@ -554,6 +570,9 @@ public final class ConnectivityController extends RestrictingController implemen if (DEBUG) { Slog.v(TAG, "onCapabilitiesChanged: " + network); } + synchronized (mLock) { + mAvailableNetworks.put(network, capabilities); + } updateTrackedJobs(-1, network); } @@ -630,6 +649,8 @@ public final class ConnectivityController extends RestrictingController implemen pw.println("Available networks:"); pw.increaseIndent(); for (int i = 0; i < mAvailableNetworks.size(); i++) { + pw.print(mAvailableNetworks.keyAt(i)); + pw.print(": "); pw.println(mAvailableNetworks.valueAt(i)); } pw.decreaseIndent(); @@ -667,7 +688,7 @@ public final class ConnectivityController extends RestrictingController implemen mRequestedWhitelistJobs.keyAt(i)); } for (int i = 0; i < mAvailableNetworks.size(); i++) { - Network network = mAvailableNetworks.valueAt(i); + Network network = mAvailableNetworks.keyAt(i); if (network != null) { network.dumpDebug(proto, StateControllerProto.ConnectivityController.AVAILABLE_NETWORKS); diff --git a/api/system-current.txt b/api/system-current.txt index dbe2e318a7ba..62503d73defa 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4199,8 +4199,10 @@ package android.media { public class AudioManager { method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException; + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForCapturePresetChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener) throws java.lang.SecurityException; method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener) throws java.lang.SecurityException; method public void clearAudioServerStateCallback(); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean clearPreferredDevicesForCapturePreset(int); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy); method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioProductStrategy> getAudioProductStrategies(); @@ -4211,6 +4213,7 @@ package android.media { method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDeviceAttributes getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy); + method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getSupportedSystemUsages(); method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); @@ -4219,6 +4222,7 @@ package android.media { method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy); method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForCapturePresetChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean removePreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException; @@ -4228,6 +4232,7 @@ package android.media { method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForCapturePreset(int, @NonNull android.media.AudioDeviceAttributes); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]); @@ -4257,6 +4262,10 @@ package android.media { method @Deprecated public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDeviceAttributes); } + public static interface AudioManager.OnPreferredDevicesForCapturePresetChangedListener { + method public void onPreferredDevicesForCapturePresetChanged(int, @NonNull java.util.List<android.media.AudioDeviceAttributes>); + } + public static interface AudioManager.OnPreferredDevicesForStrategyChangedListener { method public void onPreferredDevicesForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>); } diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index 573961276e5b..3dbb6ed47ff8 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -69,13 +69,14 @@ const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6; CountMetricProducer::CountMetricProducer( const ConfigKey& key, const CountMetric& metric, const int conditionIndex, const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, - const int64_t timeBaseNs, const int64_t startTimeNs, + const uint64_t protoHash, const int64_t timeBaseNs, const int64_t startTimeNs, const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap, const vector<int>& slicedStateAtoms, const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard, - eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap) { + protoHash, eventActivationMap, eventDeactivationMap, slicedStateAtoms, + stateGroupMap) { if (metric.has_bucket()) { mBucketSizeNs = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000; diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index f05fb061ccc1..6b2f2ca61ecc 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ b/cmds/statsd/src/metrics/CountMetricProducer.h @@ -44,7 +44,7 @@ public: CountMetricProducer( const ConfigKey& key, const CountMetric& countMetric, const int conditionIndex, const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, - const int64_t timeBaseNs, const int64_t startTimeNs, + const uint64_t protoHash, const int64_t timeBaseNs, const int64_t startTimeNs, const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {}, const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& eventDeactivationMap = {}, @@ -57,6 +57,10 @@ public: const HashableDimensionKey& primaryKey, const FieldValue& oldState, const FieldValue& newState) override; + MetricType getMetricType() const override { + return METRIC_TYPE_COUNT; + } + protected: void onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index e9b043876d3d..3acafaa3560e 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -66,14 +66,15 @@ DurationMetricProducer::DurationMetricProducer( const ConfigKey& key, const DurationMetric& metric, const int conditionIndex, const vector<ConditionState>& initialConditionCache, const size_t startIndex, const size_t stopIndex, const size_t stopAllIndex, const bool nesting, - const sp<ConditionWizard>& wizard, const FieldMatcher& internalDimensions, - const int64_t timeBaseNs, const int64_t startTimeNs, + const sp<ConditionWizard>& wizard, const uint64_t protoHash, + const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs, const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap, const vector<int>& slicedStateAtoms, const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard, - eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap), + protoHash, eventActivationMap, eventDeactivationMap, slicedStateAtoms, + stateGroupMap), mAggregationType(metric.aggregation_type()), mStartIndex(startIndex), mStopIndex(stopIndex), diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index bfe1010c89de..3a94d9c775aa 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -42,8 +42,9 @@ public: const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex, const vector<ConditionState>& initialConditionCache, const size_t startIndex, const size_t stopIndex, const size_t stopAllIndex, const bool nesting, - const sp<ConditionWizard>& wizard, const FieldMatcher& internalDimensions, - const int64_t timeBaseNs, const int64_t startTimeNs, + const sp<ConditionWizard>& wizard, const uint64_t protoHash, + const FieldMatcher& internalDimensions, const int64_t timeBaseNs, + const int64_t startTimeNs, const unordered_map<int, shared_ptr<Activation>>& eventActivationMap = {}, const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap = {}, const vector<int>& slicedStateAtoms = {}, @@ -58,6 +59,10 @@ public: const HashableDimensionKey& primaryKey, const FieldValue& oldState, const FieldValue& newState) override; + MetricType getMetricType() const override { + return METRIC_TYPE_DURATION; + } + protected: void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) override; diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index dc0036a687f3..dfe4559b05fb 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -55,13 +55,14 @@ const int FIELD_ID_ATOMS = 2; EventMetricProducer::EventMetricProducer( const ConfigKey& key, const EventMetric& metric, const int conditionIndex, const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, - const int64_t startTimeNs, + const uint64_t protoHash, const int64_t startTimeNs, const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap, const vector<int>& slicedStateAtoms, const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, initialConditionCache, wizard, - eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap) { + protoHash, eventActivationMap, eventDeactivationMap, slicedStateAtoms, + stateGroupMap) { if (metric.links().size() > 0) { for (const auto& link : metric.links()) { Metric2Condition mc; diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h index bfb2de36fad4..e828dddcbb18 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ b/cmds/statsd/src/metrics/EventMetricProducer.h @@ -36,7 +36,7 @@ public: EventMetricProducer( const ConfigKey& key, const EventMetric& eventMetric, const int conditionIndex, const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, - const int64_t startTimeNs, + const uint64_t protoHash, const int64_t startTimeNs, const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {}, const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& eventDeactivationMap = {}, @@ -45,6 +45,10 @@ public: virtual ~EventMetricProducer(); + MetricType getMetricType() const override { + return METRIC_TYPE_EVENT; + } + private: void onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index 020f4b638f4d..9dda248a6d1f 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -71,13 +71,14 @@ const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 8; GaugeMetricProducer::GaugeMetricProducer( const ConfigKey& key, const GaugeMetric& metric, const int conditionIndex, const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, - const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard, - const int pullTagId, const int triggerAtomId, const int atomId, const int64_t timeBaseNs, - const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager, + const uint64_t protoHash, const int whatMatcherIndex, + const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int triggerAtomId, + const int atomId, const int64_t timeBaseNs, const int64_t startTimeNs, + const sp<StatsPullerManager>& pullerManager, const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard, - eventActivationMap, eventDeactivationMap, /*slicedStateAtoms=*/{}, + protoHash, eventActivationMap, eventDeactivationMap, /*slicedStateAtoms=*/{}, /*stateGroupMap=*/{}), mWhatMatcherIndex(whatMatcherIndex), mEventMatcherWizard(matcherWizard), diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h index ef3a24a43dcc..e933d4b19716 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -59,10 +59,11 @@ public: GaugeMetricProducer( const ConfigKey& key, const GaugeMetric& gaugeMetric, const int conditionIndex, const vector<ConditionState>& initialConditionCache, - const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex, - const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, - const int triggerAtomId, const int atomId, const int64_t timeBaseNs, - const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager, + const sp<ConditionWizard>& conditionWizard, const uint64_t protoHash, + const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard, + const int pullTagId, const int triggerAtomId, const int atomId, + const int64_t timeBaseNs, const int64_t startTimeNs, + const sp<StatsPullerManager>& pullerManager, const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {}, const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& eventDeactivationMap = {}); @@ -96,6 +97,10 @@ public: } }; + MetricType getMetricType() const override { + return METRIC_TYPE_GAUGE; + } + protected: void onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index fe143e496373..c1c1d20f00e2 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -46,13 +46,14 @@ const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE = 3; MetricProducer::MetricProducer( const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs, const int conditionIndex, const vector<ConditionState>& initialConditionCache, - const sp<ConditionWizard>& wizard, + const sp<ConditionWizard>& wizard, const uint64_t protoHash, const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap, const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& eventDeactivationMap, const vector<int>& slicedStateAtoms, const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) : mMetricId(metricId), + mProtoHash(protoHash), mConfigKey(key), mTimeBaseNs(timeBaseNs), mCurrentBucketStartTimeNs(timeBaseNs), diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index be4cd6724bb1..bb590aac54d6 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -87,6 +87,13 @@ enum BucketDropReason { NO_DATA = 9 }; +enum MetricType { + METRIC_TYPE_EVENT = 1, + METRIC_TYPE_COUNT = 2, + METRIC_TYPE_DURATION = 3, + METRIC_TYPE_GAUGE = 4, + METRIC_TYPE_VALUE = 5, +}; struct Activation { Activation(const ActivationType& activationType, const int64_t ttlNs) : ttl_ns(ttlNs), @@ -130,7 +137,7 @@ class MetricProducer : public virtual android::RefBase, public virtual StateList public: MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs, const int conditionIndex, const vector<ConditionState>& initialConditionCache, - const sp<ConditionWizard>& wizard, + const sp<ConditionWizard>& wizard, const uint64_t protoHash, const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap, const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& eventDeactivationMap, @@ -259,10 +266,16 @@ public: int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto); // Start: getters/setters - inline const int64_t& getMetricId() const { + inline int64_t getMetricId() const { return mMetricId; } + inline uint64_t getProtoHash() const { + return mProtoHash; + } + + virtual MetricType getMetricType() const = 0; + // For test only. inline int64_t getCurrentBucketNum() const { return mCurrentBucketNum; @@ -400,6 +413,10 @@ protected: const int64_t mMetricId; + // Hash of the Metric's proto bytes from StatsdConfig, including any activations. + // Used to determine if the definition of this metric has changed across a config update. + const uint64_t mProtoHash; + const ConfigKey mConfigKey; // The time when this metric producer was first created. The end time for the current bucket diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index a0c701ea4229..39806890c42d 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -80,11 +80,11 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, mConfigValid = initStatsdConfig( key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap, - mAllConditionTrackers, mConditionTrackerMap, mAllMetricProducers, mAllAnomalyTrackers, - mAllPeriodicAlarmTrackers, mConditionToMetricMap, mTrackerToMetricMap, - mTrackerToConditionMap, mActivationAtomTrackerToMetricMap, + mAllConditionTrackers, mConditionTrackerMap, mAllMetricProducers, mMetricProducerMap, + mAllAnomalyTrackers, mAllPeriodicAlarmTrackers, mConditionToMetricMap, + mTrackerToMetricMap, mTrackerToConditionMap, mActivationAtomTrackerToMetricMap, mDeactivationAtomTrackerToMetricMap, mAlertTrackerMap, mMetricIndexesWithActivation, - mNoReportMetricIds); + mStateProtoHashes, mNoReportMetricIds); mHashStringsInReport = config.hash_strings_in_metric_report(); mVersionStringsInReport = config.version_strings_in_metric_report(); diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index bd0c8161a884..27f3d51b07ce 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -248,6 +248,9 @@ private: // Maps the id of a condition tracker to its index in mAllConditionTrackers. std::unordered_map<int64_t, int> mConditionTrackerMap; + // Maps the id of a metric producer to its index in mAllMetricProducers. + std::unordered_map<int64_t, int> mMetricProducerMap; + // To make the log processing more efficient, we want to do as much filtering as possible // before we go into individual trackers and conditions to match. @@ -295,6 +298,9 @@ private: // The config is always active if any metric in the config does not have an activation signal. bool mIsAlwaysActive; + // Hashes of the States used in this config, keyed by the state id, used in config updates. + std::map<int64_t, uint64_t> mStateProtoHashes; + FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions); FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks); FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid); diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index dcfbd5ea239b..39ae9a47f2bf 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -79,16 +79,17 @@ const Value ZERO_DOUBLE((int64_t)0); ValueMetricProducer::ValueMetricProducer( const ConfigKey& key, const ValueMetric& metric, const int conditionIndex, const vector<ConditionState>& initialConditionCache, - const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex, - const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int64_t timeBaseNs, - const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager, + const sp<ConditionWizard>& conditionWizard, const uint64_t protoHash, + const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard, + const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs, + const sp<StatsPullerManager>& pullerManager, const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap, const vector<int>& slicedStateAtoms, const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, - conditionWizard, eventActivationMap, eventDeactivationMap, slicedStateAtoms, - stateGroupMap), + conditionWizard, protoHash, eventActivationMap, eventDeactivationMap, + slicedStateAtoms, stateGroupMap), mWhatMatcherIndex(whatMatcherIndex), mEventMatcherWizard(matcherWizard), mPullerManager(pullerManager), diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index 472cc33b97fa..4b2599bdb517 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -52,9 +52,9 @@ public: ValueMetricProducer( const ConfigKey& key, const ValueMetric& valueMetric, const int conditionIndex, const vector<ConditionState>& initialConditionCache, - const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex, - const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, - const int64_t timeBaseNs, const int64_t startTimeNs, + const sp<ConditionWizard>& conditionWizard, const uint64_t protoHash, + const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard, + const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager, const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {}, const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& @@ -92,6 +92,10 @@ public: void onStateChanged(int64_t eventTimeNs, int32_t atomId, const HashableDimensionKey& primaryKey, const FieldValue& oldState, const FieldValue& newState) override; + MetricType getMetricType() const override { + return METRIC_TYPE_VALUE; + } + protected: void onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp index 2e3e43413d54..3f40c90d515a 100644 --- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp @@ -38,6 +38,7 @@ #include "state/StateManager.h" #include "stats_util.h" +using google::protobuf::MessageLite; using std::set; using std::unordered_map; using std::vector; @@ -60,6 +61,20 @@ bool hasLeafNode(const FieldMatcher& matcher) { return true; } +bool getMetricProtoHash(const MessageLite& metric, const int64_t id, const bool hasActivation, + const uint64_t activationHash, uint64_t& metricHash) { + string serializedMetric; + if (!metric.SerializeToString(&serializedMetric)) { + ALOGE("Unable to serialize metric %lld", (long long)id); + return false; + } + metricHash = Hash64(serializedMetric); + if (hasActivation) { + metricHash = Hash64(to_string(metricHash).append(to_string(activationHash))); + } + return true; +} + } // namespace sp<AtomMatchingTracker> createAtomMatchingTracker(const AtomMatcher& logMatcher, const int index, @@ -228,19 +243,31 @@ bool handleMetricWithStateLink(const FieldMatcher& stateMatcher, bool handleMetricActivation( const StatsdConfig& config, const int64_t metricId, const int metricIndex, const unordered_map<int64_t, int>& metricToActivationMap, - const unordered_map<int64_t, int>& atomMatchingTrackerMap, + const unordered_map<int64_t, int>& atomMatchingTrackerMap, bool& hasActivation, unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, vector<int>& metricsWithActivation, unordered_map<int, shared_ptr<Activation>>& eventActivationMap, - unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) { + unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap, + uint64_t& activationHash) { // Check if metric has an associated activation auto itr = metricToActivationMap.find(metricId); - if (itr == metricToActivationMap.end()) return true; + if (itr == metricToActivationMap.end()) { + hasActivation = false; + return true; + } + hasActivation = true; int activationIndex = itr->second; const MetricActivation& metricActivation = config.metric_activation(activationIndex); + string serializedActivation; + if (!metricActivation.SerializeToString(&serializedActivation)) { + ALOGE("Unable to serialize metric activation for metric %lld", (long long)metricId); + return false; + } + activationHash = Hash64(serializedActivation); + for (int i = 0; i < metricActivation.event_activation_size(); i++) { const EventActivation& activation = metricActivation.event_activation(i); @@ -357,12 +384,20 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, } bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap, - unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps) { + unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, + map<int64_t, uint64_t>& stateProtoHashes) { for (int i = 0; i < config.state_size(); i++) { const State& state = config.state(i); const int64_t stateId = state.id(); stateAtomIdMap[stateId] = state.atom_id(); + string serializedState; + if (!state.SerializeToString(&serializedState)) { + ALOGE("Unable to serialize state %lld", (long long)stateId); + return false; + } + stateProtoHashes[stateId] = Hash64(serializedState); + const StateMap& stateMap = state.map(); for (auto group : stateMap.group()) { for (auto value : group.value()) { @@ -457,18 +492,25 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } } + bool hasActivation = false; unordered_map<int, shared_ptr<Activation>> eventActivationMap; unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; + uint64_t activationHash; bool success = handleMetricActivation( config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, eventActivationMap, eventDeactivationMap); + hasActivation, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation, eventActivationMap, eventDeactivationMap, activationHash); if (!success) return false; - sp<MetricProducer> countProducer = - new CountMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard, - timeBaseTimeNs, currentTimeNs, eventActivationMap, - eventDeactivationMap, slicedStateAtoms, stateGroupMap); + uint64_t metricHash; + if (!getMetricProtoHash(metric, metric.id(), hasActivation, activationHash, metricHash)) { + return false; + } + + sp<MetricProducer> countProducer = new CountMetricProducer( + key, metric, conditionIndex, initialConditionCache, wizard, metricHash, + timeBaseTimeNs, currentTimeNs, eventActivationMap, eventDeactivationMap, + slicedStateAtoms, stateGroupMap); allMetricProducers.push_back(countProducer); } @@ -566,19 +608,26 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } } + bool hasActivation = false; unordered_map<int, shared_ptr<Activation>> eventActivationMap; unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; + uint64_t activationHash; bool success = handleMetricActivation( config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, eventActivationMap, eventDeactivationMap); + hasActivation, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation, eventActivationMap, eventDeactivationMap, activationHash); if (!success) return false; + uint64_t metricHash; + if (!getMetricProtoHash(metric, metric.id(), hasActivation, activationHash, metricHash)) { + return false; + } + sp<MetricProducer> durationMetric = new DurationMetricProducer( key, metric, conditionIndex, initialConditionCache, trackerIndices[0], - trackerIndices[1], trackerIndices[2], nesting, wizard, internalDimensions, - timeBaseTimeNs, currentTimeNs, eventActivationMap, eventDeactivationMap, - slicedStateAtoms, stateGroupMap); + trackerIndices[1], trackerIndices[2], nesting, wizard, metricHash, + internalDimensions, timeBaseTimeNs, currentTimeNs, eventActivationMap, + eventDeactivationMap, slicedStateAtoms, stateGroupMap); allMetricProducers.push_back(durationMetric); } @@ -614,17 +663,24 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } } + bool hasActivation = false; unordered_map<int, shared_ptr<Activation>> eventActivationMap; unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; + uint64_t activationHash; bool success = handleMetricActivation( config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, eventActivationMap, eventDeactivationMap); + hasActivation, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation, eventActivationMap, eventDeactivationMap, activationHash); if (!success) return false; - sp<MetricProducer> eventMetric = - new EventMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard, - timeBaseTimeNs, eventActivationMap, eventDeactivationMap); + uint64_t metricHash; + if (!getMetricProtoHash(metric, metric.id(), hasActivation, activationHash, metricHash)) { + return false; + } + + sp<MetricProducer> eventMetric = new EventMetricProducer( + key, metric, conditionIndex, initialConditionCache, wizard, metricHash, + timeBaseTimeNs, eventActivationMap, eventDeactivationMap); allMetricProducers.push_back(eventMetric); } @@ -703,18 +759,26 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } } + bool hasActivation = false; unordered_map<int, shared_ptr<Activation>> eventActivationMap; unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; + uint64_t activationHash; bool success = handleMetricActivation( config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, eventActivationMap, eventDeactivationMap); + hasActivation, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation, eventActivationMap, eventDeactivationMap, activationHash); if (!success) return false; + uint64_t metricHash; + if (!getMetricProtoHash(metric, metric.id(), hasActivation, activationHash, metricHash)) { + return false; + } + sp<MetricProducer> valueProducer = new ValueMetricProducer( - key, metric, conditionIndex, initialConditionCache, wizard, trackerIndex, - matcherWizard, pullTagId, timeBaseTimeNs, currentTimeNs, pullerManager, - eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap); + key, metric, conditionIndex, initialConditionCache, wizard, metricHash, + trackerIndex, matcherWizard, pullTagId, timeBaseTimeNs, currentTimeNs, + pullerManager, eventActivationMap, eventDeactivationMap, slicedStateAtoms, + stateGroupMap); allMetricProducers.push_back(valueProducer); } @@ -799,18 +863,25 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } } + bool hasActivation = false; unordered_map<int, shared_ptr<Activation>> eventActivationMap; unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; + uint64_t activationHash; bool success = handleMetricActivation( config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, eventActivationMap, eventDeactivationMap); + hasActivation, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation, eventActivationMap, eventDeactivationMap, activationHash); if (!success) return false; + uint64_t metricHash; + if (!getMetricProtoHash(metric, metric.id(), hasActivation, activationHash, metricHash)) { + return false; + } + sp<MetricProducer> gaugeProducer = new GaugeMetricProducer( - key, metric, conditionIndex, initialConditionCache, wizard, trackerIndex, - matcherWizard, pullTagId, triggerAtomId, atomTagId, timeBaseTimeNs, currentTimeNs, - pullerManager, eventActivationMap, eventDeactivationMap); + key, metric, conditionIndex, initialConditionCache, wizard, metricHash, + trackerIndex, matcherWizard, pullTagId, triggerAtomId, atomTagId, timeBaseTimeNs, + currentTimeNs, pullerManager, eventActivationMap, eventDeactivationMap); allMetricProducers.push_back(gaugeProducer); } for (int i = 0; i < config.no_report_metric_size(); ++i) { @@ -945,6 +1016,7 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp vector<sp<ConditionTracker>>& allConditionTrackers, unordered_map<int64_t, int>& conditionTrackerMap, vector<sp<MetricProducer>>& allMetricProducers, + unordered_map<int64_t, int>& metricProducerMap, vector<sp<AnomalyTracker>>& allAnomalyTrackers, vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers, unordered_map<int, std::vector<int>>& conditionToMetricMap, @@ -953,9 +1025,9 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, unordered_map<int64_t, int>& alertTrackerMap, - vector<int>& metricsWithActivation, std::set<int64_t>& noReportMetricIds) { + vector<int>& metricsWithActivation, map<int64_t, uint64_t>& stateProtoHashes, + set<int64_t>& noReportMetricIds) { vector<ConditionState> initialConditionCache; - unordered_map<int64_t, int> metricProducerMap; unordered_map<int64_t, int> stateAtomIdMap; unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps; @@ -972,7 +1044,7 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp return false; } - if (!initStates(config, stateAtomIdMap, allStateGroupMaps)) { + if (!initStates(config, stateAtomIdMap, allStateGroupMaps, stateProtoHashes)) { ALOGE("initStates failed"); return false; } diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h index 6eabcf4971d3..4979c3051133 100644 --- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h +++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h @@ -98,8 +98,10 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, // [stateAtomIdMap]: this map should contain the mapping from state ids to atom ids // [allStateGroupMaps]: this map should contain the mapping from states ids and state // values to state group ids for all states +// [stateProtoHashes]: contains a map of state id to the hash of the State proto from the config bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap, - unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps); + unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, + std::map<int64_t, uint64_t>& stateProtoHashes); // Initialize MetricProducers. // input: @@ -146,6 +148,7 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp std::vector<sp<ConditionTracker>>& allConditionTrackers, std::unordered_map<int64_t, int>& conditionTrackerMap, std::vector<sp<MetricProducer>>& allMetricProducers, + std::unordered_map<int64_t, int>& metricProducerMap, vector<sp<AnomalyTracker>>& allAnomalyTrackers, vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers, std::unordered_map<int, std::vector<int>>& conditionToMetricMap, @@ -154,7 +157,9 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, std::unordered_map<int64_t, int>& alertTrackerMap, - vector<int>& metricsWithActivation, std::set<int64_t>& noReportMetricIds); + vector<int>& metricsWithActivation, + std::map<int64_t, uint64_t>& stateProtoHashes, + std::set<int64_t>& noReportMetricIds); } // namespace statsd } // namespace os diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp index bb8e7bfd90f4..8e2864c6fba8 100644 --- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp @@ -41,6 +41,7 @@ namespace statsd { namespace { const ConfigKey kConfigKey(0, 12345); +const uint64_t protoHash = 0x1234567890; void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) { AStatsEvent* statsEvent = AStatsEvent_obtain(); @@ -75,7 +76,7 @@ TEST(CountMetricProducerTest, TestFirstBucket) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2); + wizard, protoHash, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2); EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(10, countProducer.mCurrentBucketNum); EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs()); @@ -95,7 +96,7 @@ TEST(CountMetricProducerTest, TestNonDimensionalEvents) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, bucketStartTimeNs, bucketStartTimeNs); + wizard, protoHash, bucketStartTimeNs, bucketStartTimeNs); // 2 events in bucket 1. LogEvent event1(/*uid=*/0, /*pid=*/0); @@ -158,7 +159,7 @@ TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); CountMetricProducer countProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, - bucketStartTimeNs, bucketStartTimeNs); + protoHash, bucketStartTimeNs, bucketStartTimeNs); countProducer.onConditionChanged(true, bucketStartTimeNs); @@ -226,8 +227,8 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue)); CountMetricProducer countProducer(kConfigKey, metric, 0 /*condition tracker index*/, - {ConditionState::kUnknown}, wizard, bucketStartTimeNs, - bucketStartTimeNs); + {ConditionState::kUnknown}, wizard, protoHash, + bucketStartTimeNs, bucketStartTimeNs); countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); countProducer.flushIfNeededLocked(bucketStartTimeNs + 1); @@ -265,7 +266,7 @@ TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInCurrentBucket) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard, - bucketStartTimeNs, bucketStartTimeNs); + protoHash, bucketStartTimeNs, bucketStartTimeNs); sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor); EXPECT_TRUE(anomalyTracker != nullptr); @@ -332,7 +333,7 @@ TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInNextBucket) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard, - bucketStartTimeNs, bucketStartTimeNs); + protoHash, bucketStartTimeNs, bucketStartTimeNs); // Bucket is flushed yet. LogEvent event1(/*uid=*/0, /*pid=*/0); @@ -397,7 +398,7 @@ TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, bucketStartTimeNs, bucketStartTimeNs); + wizard, protoHash, bucketStartTimeNs, bucketStartTimeNs); sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor); @@ -459,7 +460,7 @@ TEST(CountMetricProducerTest, TestOneWeekTimeUnit) { int64_t fiveWeeksNs = 5 * 7 * oneDayNs; CountMetricProducer countProducer(kConfigKey, metric, -1 /* meaning no condition */, {}, wizard, - oneDayNs, fiveWeeksNs); + protoHash, oneDayNs, fiveWeeksNs); int64_t fiveWeeksOneDayNs = fiveWeeksNs + oneDayNs; diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp index 05cfa37b0ee1..d1f89775ed6a 100644 --- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp @@ -45,6 +45,7 @@ namespace statsd { namespace { const ConfigKey kConfigKey(0, 12345); +const uint64_t protoHash = 0x1234567890; void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) { AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, atomId); @@ -71,10 +72,10 @@ TEST(DurationMetricTrackerTest, TestFirstBucket) { FieldMatcher dimensions; - DurationMetricProducer durationProducer(kConfigKey, metric, -1 /*no condition*/, {}, - 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, - dimensions, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2); + DurationMetricProducer durationProducer( + kConfigKey, metric, -1 /*no condition*/, {}, 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, 5, + 600 * NS_PER_SEC + NS_PER_SEC / 2); EXPECT_EQ(600500000000, durationProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(10, durationProducer.mCurrentBucketNum); @@ -99,10 +100,10 @@ TEST(DurationMetricTrackerTest, TestNoCondition) { FieldMatcher dimensions; - DurationMetricProducer durationProducer(kConfigKey, metric, -1 /*no condition*/, {}, - 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, - dimensions, bucketStartTimeNs, bucketStartTimeNs); + DurationMetricProducer durationProducer( + kConfigKey, metric, -1 /*no condition*/, {}, 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, + bucketStartTimeNs, bucketStartTimeNs); durationProducer.onMatchedLogEvent(1 /* start index*/, event1); durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); @@ -145,7 +146,7 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { DurationMetricProducer durationProducer( kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown}, 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, - wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); + wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); durationProducer.mCondition = ConditionState::kFalse; EXPECT_FALSE(durationProducer.mCondition); @@ -195,7 +196,7 @@ TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) { DurationMetricProducer durationProducer( kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown}, 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, - wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); + wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition); EXPECT_FALSE(durationProducer.isConditionSliced()); @@ -238,10 +239,10 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDuration) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); FieldMatcher dimensions; - DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {}, - 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, - dimensions, bucketStartTimeNs, bucketStartTimeNs); + DurationMetricProducer durationProducer( + kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, + bucketStartTimeNs, bucketStartTimeNs); int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; LogEvent event1(/*uid=*/0, /*pid=*/0); @@ -301,10 +302,10 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationWithSplitInFollo sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); FieldMatcher dimensions; - DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {}, - 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, - dimensions, bucketStartTimeNs, bucketStartTimeNs); + DurationMetricProducer durationProducer( + kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, + bucketStartTimeNs, bucketStartTimeNs); int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; LogEvent event1(/*uid=*/0, /*pid=*/0); @@ -365,10 +366,10 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationAnomaly) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); FieldMatcher dimensions; - DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {}, - 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, - dimensions, bucketStartTimeNs, bucketStartTimeNs); + DurationMetricProducer durationProducer( + kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, + bucketStartTimeNs, bucketStartTimeNs); sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor); EXPECT_TRUE(anomalyTracker != nullptr); @@ -411,10 +412,10 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDuration) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); FieldMatcher dimensions; - DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {}, - 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, - dimensions, bucketStartTimeNs, bucketStartTimeNs); + DurationMetricProducer durationProducer( + kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, + bucketStartTimeNs, bucketStartTimeNs); int64_t startTimeNs = bucketStartTimeNs + 1; LogEvent event1(/*uid=*/0, /*pid=*/0); @@ -465,10 +466,10 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextB sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); FieldMatcher dimensions; - DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {}, - 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, - dimensions, bucketStartTimeNs, bucketStartTimeNs); + DurationMetricProducer durationProducer( + kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, + bucketStartTimeNs, bucketStartTimeNs); int64_t startTimeNs = bucketStartTimeNs + 1; LogEvent event1(/*uid=*/0, /*pid=*/0); diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp index dfbb9da568b0..4bbbd2cb36ad 100644 --- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp @@ -36,9 +36,11 @@ namespace android { namespace os { namespace statsd { -const ConfigKey kConfigKey(0, 12345); namespace { +const ConfigKey kConfigKey(0, 12345); +const uint64_t protoHash = 0x1234567890; + void makeLogEvent(LogEvent* logEvent, int32_t atomId, int64_t timestampNs, string str) { AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, atomId); @@ -66,7 +68,7 @@ TEST(EventMetricProducerTest, TestNoCondition) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, bucketStartTimeNs); + wizard, protoHash, bucketStartTimeNs); eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1); eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2); @@ -102,7 +104,8 @@ TEST(EventMetricProducerTest, TestEventsWithNonSlicedCondition) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); EventMetricProducer eventProducer(kConfigKey, metric, 0 /*condition index*/, - {ConditionState::kUnknown}, wizard, bucketStartTimeNs); + {ConditionState::kUnknown}, wizard, protoHash, + bucketStartTimeNs); eventProducer.onConditionChanged(true /*condition*/, bucketStartTimeNs); eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1); @@ -157,7 +160,8 @@ TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) { EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue)); EventMetricProducer eventProducer(kConfigKey, metric, 0 /*condition index*/, - {ConditionState::kUnknown}, wizard, bucketStartTimeNs); + {ConditionState::kUnknown}, wizard, protoHash, + bucketStartTimeNs); eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1); eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2); diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp index ba919f1e0ad8..10606810d806 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -47,6 +47,7 @@ namespace { const ConfigKey kConfigKey(0, 12345); const int tagId = 1; const int64_t metricId = 123; +const uint64_t protoHash = 0x123456789; const int logEventMatcherIndex = 0; const int64_t bucketStartTimeNs = 10 * NS_PER_SEC; const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; @@ -101,8 +102,9 @@ TEST(GaugeMetricProducerTest, TestFirstBucket) { // statsd started long ago. // The metric starts in the middle of the bucket GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, -1, -1, - tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager); + wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, + -1, -1, tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2, + pullerManager); gaugeProducer.prepareFirstBucket(); EXPECT_EQ(600500000000, gaugeProducer.mCurrentBucketStartTimeNs); @@ -139,8 +141,9 @@ TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) { })); GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1, - tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); + wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, + tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, + pullerManager); gaugeProducer.prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; @@ -213,7 +216,7 @@ TEST_P(GaugeMetricProducerTest_PartialBucket, TestPushedEvents) { createEventMatcherWizard(tagId, logEventMatcherIndex); GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, + wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, -1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); gaugeProducer.prepareFirstBucket(); @@ -306,8 +309,9 @@ TEST_P(GaugeMetricProducerTest_PartialBucket, TestPulled) { })); GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1, - tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); + wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, + tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, + pullerManager); gaugeProducer.prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; @@ -373,8 +377,9 @@ TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) { .WillOnce(Return(false)); GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1, - tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); + wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, + tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, + pullerManager); gaugeProducer.prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; @@ -426,9 +431,9 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) { })); GaugeMetricProducer gaugeProducer(kConfigKey, metric, 0 /*condition index*/, - {ConditionState::kUnknown}, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); + {ConditionState::kUnknown}, wizard, protoHash, + logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); gaugeProducer.prepareFirstBucket(); gaugeProducer.onConditionChanged(true, conditionChangeNs); @@ -509,9 +514,9 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) { })); GaugeMetricProducer gaugeProducer(kConfigKey, metric, 0 /*condition index*/, - {ConditionState::kUnknown}, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); + {ConditionState::kUnknown}, wizard, protoHash, + logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); gaugeProducer.prepareFirstBucket(); gaugeProducer.onSlicedConditionMayChange(true, sliceConditionChangeNs); @@ -554,8 +559,9 @@ TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) { createEventMatcherWizard(tagId, logEventMatcherIndex); GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1, - tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); + wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, + tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, + pullerManager); gaugeProducer.prepareFirstBucket(); Alert alert; @@ -649,8 +655,8 @@ TEST(GaugeMetricProducerTest, TestPullOnTrigger) { int triggerId = 5; GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, tagId, - triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, + wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, + tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); gaugeProducer.prepareFirstBucket(); @@ -724,8 +730,8 @@ TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) { int triggerId = 5; GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, tagId, - triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, + wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, + tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); gaugeProducer.prepareFirstBucket(); @@ -783,8 +789,8 @@ TEST(GaugeMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) { int triggerId = 5; GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, tagId, - triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, + wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, + tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); gaugeProducer.prepareFirstBucket(); diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index 8790fe428d19..b166cc1fe04e 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -46,6 +46,7 @@ namespace { const ConfigKey kConfigKey(0, 12345); const int tagId = 1; const int64_t metricId = 123; +const uint64_t protoHash = 0x1234567890; const int logEventMatcherIndex = 0; const int64_t bucketStartTimeNs = 10000000000; const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; @@ -108,8 +109,8 @@ public: sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); + wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, + tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer->prepareFirstBucket(); return valueProducer; } @@ -127,7 +128,7 @@ public: sp<ValueMetricProducer> valueProducer = new ValueMetricProducer( kConfigKey, metric, 0 /*condition index*/, {ConditionState::kUnknown}, wizard, - logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, + protoHash, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer->prepareFirstBucket(); valueProducer->mCondition = conditionAfterFirstBucketPrepared; @@ -147,9 +148,9 @@ public: .WillRepeatedly(Return()); sp<ValueMetricProducer> valueProducer = new ValueMetricProducer( - kConfigKey, metric, -1 /* no condition */, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager, {}, - {}, slicedStateAtoms, stateGroupMap); + kConfigKey, metric, -1 /* no condition */, {}, wizard, protoHash, + logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, + bucketStartTimeNs, pullerManager, {}, {}, slicedStateAtoms, stateGroupMap); valueProducer->prepareFirstBucket(); return valueProducer; } @@ -169,8 +170,9 @@ public: sp<ValueMetricProducer> valueProducer = new ValueMetricProducer( kConfigKey, metric, 0 /* condition tracker index */, {ConditionState::kUnknown}, - wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager, {}, {}, slicedStateAtoms, stateGroupMap); + wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager, {}, {}, slicedStateAtoms, + stateGroupMap); valueProducer->prepareFirstBucket(); valueProducer->mCondition = conditionAfterFirstBucketPrepared; return valueProducer; @@ -228,8 +230,8 @@ TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime) { // statsd started long ago. // The metric starts in the middle of the bucket ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, -1, - startTimeBase, 22, pullerManager); + wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, + -1, startTimeBase, 22, pullerManager); valueProducer.prepareFirstBucket(); EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10)); @@ -254,8 +256,8 @@ TEST(ValueMetricProducerTest, TestFirstBucket) { // statsd started long ago. // The metric starts in the middle of the bucket ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, -1, 5, - 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager); + wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, + -1, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager); valueProducer.prepareFirstBucket(); EXPECT_EQ(600500000000, valueProducer.mCurrentBucketStartTimeNs); @@ -414,9 +416,10 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { return true; })); - sp<ValueMetricProducer> valueProducer = new ValueMetricProducer( - kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = + new ValueMetricProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, wizard, + protoHash, logEventMatcherIndex, eventMatcherWizard, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer->prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; @@ -679,9 +682,9 @@ TEST_P(ValueMetricProducerTest_PartialBucket, TestPushedEvents) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, + logEventMatcherIndex, eventMatcherWizard, -1, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); LogEvent event1(/*uid=*/0, /*pid=*/0); @@ -749,9 +752,9 @@ TEST_P(ValueMetricProducerTest_PartialBucket, TestPulledValue) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, + logEventMatcherIndex, eventMatcherWizard, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; @@ -797,9 +800,9 @@ TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) { EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _)) .WillOnce(Return(true)); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, + logEventMatcherIndex, eventMatcherWizard, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; @@ -869,9 +872,9 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, + logEventMatcherIndex, eventMatcherWizard, -1, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); LogEvent event1(/*uid=*/0, /*pid=*/0); @@ -911,7 +914,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) { sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); ValueMetricProducer valueProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, - logEventMatcherIndex, eventMatcherWizard, -1, + protoHash, logEventMatcherIndex, eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); valueProducer.mCondition = ConditionState::kFalse; @@ -978,7 +981,7 @@ TEST(ValueMetricProducerTest, TestAnomalyDetection) { sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, + wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, -1 /*not pulled*/, bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); @@ -1321,9 +1324,9 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMin) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, + logEventMatcherIndex, eventMatcherWizard, -1, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); LogEvent event1(/*uid=*/0, /*pid=*/0); @@ -1361,9 +1364,9 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMax) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, + logEventMatcherIndex, eventMatcherWizard, -1, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); LogEvent event1(/*uid=*/0, /*pid=*/0); @@ -1400,9 +1403,9 @@ TEST(ValueMetricProducerTest, TestPushedAggregateAvg) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, + logEventMatcherIndex, eventMatcherWizard, -1, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); LogEvent event1(/*uid=*/0, /*pid=*/0); @@ -1444,9 +1447,9 @@ TEST(ValueMetricProducerTest, TestPushedAggregateSum) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, + logEventMatcherIndex, eventMatcherWizard, -1, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); LogEvent event1(/*uid=*/0, /*pid=*/0); @@ -1484,9 +1487,9 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, + logEventMatcherIndex, eventMatcherWizard, -1, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); LogEvent event1(/*uid=*/0, /*pid=*/0); @@ -1553,9 +1556,9 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, + logEventMatcherIndex, eventMatcherWizard, -1, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); LogEvent event1(/*uid=*/0, /*pid=*/0); @@ -2089,7 +2092,7 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) { EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return()); ValueMetricProducer valueProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, - logEventMatcherIndex, eventMatcherWizard, tagId, + protoHash, logEventMatcherIndex, eventMatcherWizard, tagId, bucket2StartTimeNs, bucket2StartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); valueProducer.mCondition = ConditionState::kFalse; @@ -2922,9 +2925,9 @@ TEST(ValueMetricProducerTest, TestPullNeededFastDump) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, + logEventMatcherIndex, eventMatcherWizard, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); ProtoOutputStream output; @@ -2956,9 +2959,9 @@ TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, + logEventMatcherIndex, eventMatcherWizard, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; @@ -3005,9 +3008,9 @@ TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); + ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash, + logEventMatcherIndex, eventMatcherWizard, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); valueProducer.prepareFirstBucket(); ProtoOutputStream output; diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp index 890884bc5d83..843d836a2c0b 100644 --- a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp +++ b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp @@ -58,6 +58,7 @@ unordered_map<int64_t, int> oldAtomMatchingTrackerMap; vector<sp<ConditionTracker>> oldConditionTrackers; unordered_map<int64_t, int> oldConditionTrackerMap; vector<sp<MetricProducer>> oldMetricProducers; +unordered_map<int64_t, int> oldMetricProducerMap; std::vector<sp<AnomalyTracker>> oldAnomalyTrackers; std::vector<sp<AlarmTracker>> oldAlarmTrackers; unordered_map<int, std::vector<int>> conditionToMetricMap; @@ -67,6 +68,7 @@ unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; +map<int64_t, uint64_t> oldStateHashes; std::set<int64_t> noReportMetricIds; class ConfigUpdateTest : public ::testing::Test { @@ -81,6 +83,7 @@ public: oldConditionTrackers.clear(); oldConditionTrackerMap.clear(); oldMetricProducers.clear(); + oldMetricProducerMap.clear(); oldAnomalyTrackers.clear(); oldAlarmTrackers.clear(); conditionToMetricMap.clear(); @@ -90,6 +93,7 @@ public: deactivationAtomTrackerToMetricMap.clear(); alertTrackerMap.clear(); metricsWithActivation.clear(); + oldStateHashes.clear(); noReportMetricIds.clear(); } }; @@ -98,10 +102,11 @@ bool initConfig(const StatsdConfig& config) { return initStatsdConfig( key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseNs, timeBaseNs, allTagIds, oldAtomMatchingTrackers, oldAtomMatchingTrackerMap, - oldConditionTrackers, oldConditionTrackerMap, oldMetricProducers, oldAnomalyTrackers, - oldAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, - metricsWithActivation, noReportMetricIds); + oldConditionTrackers, oldConditionTrackerMap, oldMetricProducers, oldMetricProducerMap, + oldAnomalyTrackers, oldAlarmTrackers, conditionToMetricMap, trackerToMetricMap, + trackerToConditionMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, + oldStateHashes, noReportMetricIds); } } // anonymous namespace diff --git a/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp index e6583c9686ec..0d0a8960043e 100644 --- a/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp +++ b/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp @@ -14,6 +14,7 @@ #include "src/metrics/parsing_utils/metrics_manager_util.h" +#include <gmock/gmock.h> #include <gtest/gtest.h> #include <private/android_filesystem_config.h> #include <stdio.h> @@ -388,6 +389,7 @@ TEST(MetricsManagerTest, TestInitialConditions) { vector<sp<ConditionTracker>> allConditionTrackers; unordered_map<int64_t, int> conditionTrackerMap; vector<sp<MetricProducer>> allMetricProducers; + unordered_map<int64_t, int> metricProducerMap; std::vector<sp<AnomalyTracker>> allAnomalyTrackers; std::vector<sp<AlarmTracker>> allAlarmTrackers; unordered_map<int, std::vector<int>> conditionToMetricMap; @@ -397,15 +399,17 @@ TEST(MetricsManagerTest, TestInitialConditions) { unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; + map<int64_t, uint64_t> stateProtoHashes; std::set<int64_t> noReportMetricIds; EXPECT_TRUE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, - allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers, - allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, - metricsWithActivation, noReportMetricIds)); + allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap, + allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, + trackerToConditionMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, + stateProtoHashes, noReportMetricIds)); ASSERT_EQ(4u, allMetricProducers.size()); ASSERT_EQ(5u, allConditionTrackers.size()); @@ -438,6 +442,7 @@ TEST(MetricsManagerTest, TestGoodConfig) { vector<sp<ConditionTracker>> allConditionTrackers; unordered_map<int64_t, int> conditionTrackerMap; vector<sp<MetricProducer>> allMetricProducers; + unordered_map<int64_t, int> metricProducerMap; std::vector<sp<AnomalyTracker>> allAnomalyTrackers; std::vector<sp<AlarmTracker>> allAlarmTrackers; unordered_map<int, std::vector<int>> conditionToMetricMap; @@ -447,16 +452,19 @@ TEST(MetricsManagerTest, TestGoodConfig) { unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; + map<int64_t, uint64_t> stateProtoHashes; std::set<int64_t> noReportMetricIds; EXPECT_TRUE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, - allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers, - allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, - metricsWithActivation, noReportMetricIds)); + allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap, + allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, + trackerToConditionMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, + stateProtoHashes, noReportMetricIds)); ASSERT_EQ(1u, allMetricProducers.size()); + EXPECT_THAT(metricProducerMap, UnorderedElementsAre(Pair(config.count_metric(0).id(), 0))); ASSERT_EQ(1u, allAnomalyTrackers.size()); ASSERT_EQ(1u, noReportMetricIds.size()); ASSERT_EQ(1u, alertTrackerMap.size()); @@ -476,6 +484,7 @@ TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { vector<sp<ConditionTracker>> allConditionTrackers; unordered_map<int64_t, int> conditionTrackerMap; vector<sp<MetricProducer>> allMetricProducers; + unordered_map<int64_t, int> metricProducerMap; std::vector<sp<AnomalyTracker>> allAnomalyTrackers; std::vector<sp<AlarmTracker>> allAlarmTrackers; unordered_map<int, std::vector<int>> conditionToMetricMap; @@ -485,15 +494,17 @@ TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; + map<int64_t, uint64_t> stateProtoHashes; std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, - allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers, - allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, - metricsWithActivation, noReportMetricIds)); + allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap, + allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, + trackerToConditionMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, + stateProtoHashes, noReportMetricIds)); } TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { @@ -508,6 +519,7 @@ TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { vector<sp<ConditionTracker>> allConditionTrackers; unordered_map<int64_t, int> conditionTrackerMap; vector<sp<MetricProducer>> allMetricProducers; + unordered_map<int64_t, int> metricProducerMap; std::vector<sp<AnomalyTracker>> allAnomalyTrackers; std::vector<sp<AlarmTracker>> allAlarmTrackers; unordered_map<int, std::vector<int>> conditionToMetricMap; @@ -517,15 +529,17 @@ TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; + map<int64_t, uint64_t> stateProtoHashes; std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, - allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers, - allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, - metricsWithActivation, noReportMetricIds)); + allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap, + allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, + trackerToConditionMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, + stateProtoHashes, noReportMetricIds)); } TEST(MetricsManagerTest, TestMissingMatchers) { @@ -540,6 +554,7 @@ TEST(MetricsManagerTest, TestMissingMatchers) { vector<sp<ConditionTracker>> allConditionTrackers; unordered_map<int64_t, int> conditionTrackerMap; vector<sp<MetricProducer>> allMetricProducers; + unordered_map<int64_t, int> metricProducerMap; std::vector<sp<AnomalyTracker>> allAnomalyTrackers; std::vector<sp<AlarmTracker>> allAlarmTrackers; unordered_map<int, std::vector<int>> conditionToMetricMap; @@ -549,14 +564,16 @@ TEST(MetricsManagerTest, TestMissingMatchers) { unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; + map<int64_t, uint64_t> stateProtoHashes; std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, - allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers, - allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, - metricsWithActivation, noReportMetricIds)); + allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap, + allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, + trackerToConditionMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, + stateProtoHashes, noReportMetricIds)); } TEST(MetricsManagerTest, TestMissingPredicate) { @@ -571,6 +588,7 @@ TEST(MetricsManagerTest, TestMissingPredicate) { vector<sp<ConditionTracker>> allConditionTrackers; unordered_map<int64_t, int> conditionTrackerMap; vector<sp<MetricProducer>> allMetricProducers; + unordered_map<int64_t, int> metricProducerMap; std::vector<sp<AnomalyTracker>> allAnomalyTrackers; std::vector<sp<AlarmTracker>> allAlarmTrackers; unordered_map<int, std::vector<int>> conditionToMetricMap; @@ -580,14 +598,16 @@ TEST(MetricsManagerTest, TestMissingPredicate) { unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; + map<int64_t, uint64_t> stateProtoHashes; std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, - allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers, - allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, - metricsWithActivation, noReportMetricIds)); + allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap, + allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, + trackerToConditionMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, + stateProtoHashes, noReportMetricIds)); } TEST(MetricsManagerTest, TestCirclePredicateDependency) { @@ -602,6 +622,7 @@ TEST(MetricsManagerTest, TestCirclePredicateDependency) { vector<sp<ConditionTracker>> allConditionTrackers; unordered_map<int64_t, int> conditionTrackerMap; vector<sp<MetricProducer>> allMetricProducers; + unordered_map<int64_t, int> metricProducerMap; std::vector<sp<AnomalyTracker>> allAnomalyTrackers; std::vector<sp<AlarmTracker>> allAlarmTrackers; unordered_map<int, std::vector<int>> conditionToMetricMap; @@ -611,15 +632,17 @@ TEST(MetricsManagerTest, TestCirclePredicateDependency) { unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; + map<int64_t, uint64_t> stateProtoHashes; std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, - allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers, - allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, - metricsWithActivation, noReportMetricIds)); + allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap, + allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, + trackerToConditionMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, + stateProtoHashes, noReportMetricIds)); } TEST(MetricsManagerTest, testAlertWithUnknownMetric) { @@ -634,6 +657,7 @@ TEST(MetricsManagerTest, testAlertWithUnknownMetric) { vector<sp<ConditionTracker>> allConditionTrackers; unordered_map<int64_t, int> conditionTrackerMap; vector<sp<MetricProducer>> allMetricProducers; + unordered_map<int64_t, int> metricProducerMap; std::vector<sp<AnomalyTracker>> allAnomalyTrackers; std::vector<sp<AlarmTracker>> allAlarmTrackers; unordered_map<int, std::vector<int>> conditionToMetricMap; @@ -643,15 +667,17 @@ TEST(MetricsManagerTest, testAlertWithUnknownMetric) { unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; + map<int64_t, uint64_t> stateProtoHashes; std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, - allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers, - allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, - metricsWithActivation, noReportMetricIds)); + allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap, + allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, + trackerToConditionMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, + stateProtoHashes, noReportMetricIds)); } TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerInvalidMatcher) { diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 6737972dc34e..1ff48f6baa6a 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -207,7 +207,7 @@ public class Notification implements Parcelable * <p> * Avoids spamming the system with overly large strings such as full e-mails. */ - private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024; + private static final int MAX_CHARSEQUENCE_LENGTH = 1024; /** * Maximum entries of reply text that are accepted by Builder and friends. @@ -7863,7 +7863,7 @@ public class Notification implements Parcelable */ public Message(@NonNull CharSequence text, long timestamp, @Nullable Person sender, boolean remoteInputHistory) { - mText = text; + mText = safeCharSequence(text); mTimestamp = timestamp; mSender = sender; mRemoteInputHistory = remoteInputHistory; @@ -7977,7 +7977,7 @@ public class Notification implements Parcelable bundle.putLong(KEY_TIMESTAMP, mTimestamp); if (mSender != null) { // Legacy listeners need this - bundle.putCharSequence(KEY_SENDER, mSender.getName()); + bundle.putCharSequence(KEY_SENDER, safeCharSequence(mSender.getName())); bundle.putParcelable(KEY_SENDER_PERSON, mSender); } if (mDataMimeType != null) { diff --git a/core/java/android/os/image/DynamicSystemManager.java b/core/java/android/os/image/DynamicSystemManager.java index 17851adac51b..7f01cad940ec 100644 --- a/core/java/android/os/image/DynamicSystemManager.java +++ b/core/java/android/os/image/DynamicSystemManager.java @@ -155,6 +155,19 @@ public class DynamicSystemManager { } } /** + * Complete the current partition installation. + * + * @return true if the partition installation completes without error. + */ + @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM) + public boolean closePartition() { + try { + return mService.closePartition(); + } catch (RemoteException e) { + throw new RuntimeException(e.toString()); + } + } + /** * Finish a previously started installation. Installations without a cooresponding * finishInstallation() will be cleaned up during device boot. */ diff --git a/core/java/android/os/image/IDynamicSystemService.aidl b/core/java/android/os/image/IDynamicSystemService.aidl index a1f927266de3..df0a69b47225 100644 --- a/core/java/android/os/image/IDynamicSystemService.aidl +++ b/core/java/android/os/image/IDynamicSystemService.aidl @@ -39,6 +39,13 @@ interface IDynamicSystemService boolean createPartition(@utf8InCpp String name, long size, boolean readOnly); /** + * Complete the current partition installation. + * + * @return true if the partition installation completes without error. + */ + boolean closePartition(); + + /** * Finish a previously started installation. Installations without * a cooresponding finishInstallation() will be cleaned up during device boot. */ diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 6e17ac9960a8..03071cd5e814 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -53,6 +53,9 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APPEARANCE_CO import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FIT_INSETS_CONTROLLED; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; @@ -1439,11 +1442,10 @@ public final class ViewRootImpl implements ViewParent, } // Don't lose the mode we last auto-computed. - if ((attrs.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) + if ((attrs.softInputMode & SOFT_INPUT_MASK_ADJUST) == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) { mWindowAttributes.softInputMode = (mWindowAttributes.softInputMode - & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) - | (oldSoftInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST); + & ~SOFT_INPUT_MASK_ADJUST) | (oldSoftInputMode & SOFT_INPUT_MASK_ADJUST); } if ((changes & LayoutParams.SOFT_INPUT_MODE_CHANGED) != 0) { @@ -2063,6 +2065,7 @@ public final class ViewRootImpl implements ViewParent, final int sysUiVis = inOutParams.systemUiVisibility | inOutParams.subtreeSystemUiVisibility; final int flags = inOutParams.flags; final int type = inOutParams.type; + final int adjust = inOutParams.softInputMode & SOFT_INPUT_MASK_ADJUST; if ((inOutParams.privateFlags & PRIVATE_FLAG_APPEARANCE_CONTROLLED) == 0) { inOutParams.insetsFlags.appearance = 0; @@ -2088,12 +2091,13 @@ public final class ViewRootImpl implements ViewParent, } } + inOutParams.privateFlags &= ~PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; + if ((inOutParams.privateFlags & PRIVATE_FLAG_FIT_INSETS_CONTROLLED) != 0) { return; } int types = inOutParams.getFitInsetsTypes(); - int sides = inOutParams.getFitInsetsSides(); boolean ignoreVis = inOutParams.isFitInsetsIgnoringVisibility(); if (((sysUiVis & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0 @@ -2108,10 +2112,13 @@ public final class ViewRootImpl implements ViewParent, if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT) { ignoreVis = true; } else if ((types & Type.systemBars()) == Type.systemBars()) { - types |= Type.ime(); + if (adjust == SOFT_INPUT_ADJUST_RESIZE) { + types |= Type.ime(); + } else { + inOutParams.privateFlags |= PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; + } } inOutParams.setFitInsetsTypes(types); - inOutParams.setFitInsetsSides(sides); inOutParams.setFitInsetsIgnoringVisibility(ignoreVis); // The fitting of insets are not really controlled by the clients, so we remove the flag. @@ -2481,8 +2488,7 @@ public final class ViewRootImpl implements ViewParent, if (mFirst || mAttachInfo.mViewVisibilityChanged) { mAttachInfo.mViewVisibilityChanged = false; - int resizeMode = mSoftInputMode & - WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; + int resizeMode = mSoftInputMode & SOFT_INPUT_MASK_ADJUST; // If we are in auto resize mode, then we need to determine // what mode to use now. if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) { @@ -2495,11 +2501,8 @@ public final class ViewRootImpl implements ViewParent, if (resizeMode == 0) { resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN; } - if ((lp.softInputMode & - WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) != resizeMode) { - lp.softInputMode = (lp.softInputMode & - ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) | - resizeMode; + if ((lp.softInputMode & SOFT_INPUT_MASK_ADJUST) != resizeMode) { + lp.softInputMode = (lp.softInputMode & ~SOFT_INPUT_MASK_ADJUST) | resizeMode; params = lp; } } @@ -3234,8 +3237,8 @@ public final class ViewRootImpl implements ViewParent, // Note: must be done after the focus change callbacks, // so all of the view state is set up correctly. - mImeFocusController.onPostWindowFocus(mView.findFocus(), hasWindowFocus, - mWindowAttributes); + mImeFocusController.onPostWindowFocus(mView != null ? mView.findFocus() : null, + hasWindowFocus, mWindowAttributes); if (hasWindowFocus) { // Clear the forward bit. We can just do this directly, since diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 32ee290a0f47..0d62da6bc8e3 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -2105,6 +2105,12 @@ public interface WindowManager extends ViewManager { public static final int PRIVATE_FLAG_TRUSTED_OVERLAY = 0x20000000; /** + * Flag to indicate that the parent frame of a window should be inset by IME. + * @hide + */ + public static final int PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME = 0x40000000; + + /** * An internal annotation for flags that can be specified to {@link #softInputMode}. * * @hide diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java index 51b41198e272..5e34c15c42e2 100644 --- a/core/java/com/android/internal/policy/DecorContext.java +++ b/core/java/com/android/internal/policy/DecorContext.java @@ -20,8 +20,10 @@ import android.content.AutofillOptions; import android.content.ContentCaptureOptions; import android.content.Context; import android.content.res.AssetManager; +import android.content.res.Configuration; import android.content.res.Resources; import android.view.ContextThemeWrapper; +import android.view.Display; import android.view.contentcapture.ContentCaptureManager; import com.android.internal.annotations.VisibleForTesting; @@ -47,9 +49,17 @@ public class DecorContext extends ContextThemeWrapper { public DecorContext(Context baseContext, PhoneWindow phoneWindow) { super(null /* base */, null); setPhoneWindow(phoneWindow); - final Context displayContext = baseContext.createDisplayContext( - // TODO(b/149790106): Non-activity context can be passed. - phoneWindow.getContext().getDisplayNoVerify()); + // TODO(b/149790106): Non-activity context can be passed. + final Display display = phoneWindow.getContext().getDisplayNoVerify(); + final Context displayContext; + if (display.getDisplayId() == Display.DEFAULT_DISPLAY) { + // TODO(b/166174272): Creating a display context for the default display will result + // in additional resource creation. + displayContext = baseContext.createConfigurationContext(Configuration.EMPTY); + displayContext.updateDisplay(Display.DEFAULT_DISPLAY); + } else { + displayContext = baseContext.createDisplayContext(display); + } attachBaseContext(displayContext); } diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 3f39478ffd43..5c4c5099bf4c 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -2407,6 +2407,79 @@ static jint android_media_AudioSystem_getDevicesForRoleAndStrategy(JNIEnv *env, return AUDIO_JAVA_SUCCESS; } +static jint android_media_AudioSystem_setDevicesRoleForCapturePreset( + JNIEnv *env, jobject thiz, jint capturePreset, jint role, jintArray jDeviceTypes, + jobjectArray jDeviceAddresses) { + AudioDeviceTypeAddrVector nDevices; + jint results = getVectorOfAudioDeviceTypeAddr(env, jDeviceTypes, jDeviceAddresses, nDevices); + if (results != NO_ERROR) { + return results; + } + int status = check_AudioSystem_Command( + AudioSystem::setDevicesRoleForCapturePreset((audio_source_t)capturePreset, + (device_role_t)role, nDevices)); + return (jint)status; +} + +static jint android_media_AudioSystem_addDevicesRoleForCapturePreset( + JNIEnv *env, jobject thiz, jint capturePreset, jint role, jintArray jDeviceTypes, + jobjectArray jDeviceAddresses) { + AudioDeviceTypeAddrVector nDevices; + jint results = getVectorOfAudioDeviceTypeAddr(env, jDeviceTypes, jDeviceAddresses, nDevices); + if (results != NO_ERROR) { + return results; + } + int status = check_AudioSystem_Command( + AudioSystem::addDevicesRoleForCapturePreset((audio_source_t)capturePreset, + (device_role_t)role, nDevices)); + return (jint)status; +} + +static jint android_media_AudioSystem_removeDevicesRoleForCapturePreset( + JNIEnv *env, jobject thiz, jint capturePreset, jint role, jintArray jDeviceTypes, + jobjectArray jDeviceAddresses) { + AudioDeviceTypeAddrVector nDevices; + jint results = getVectorOfAudioDeviceTypeAddr(env, jDeviceTypes, jDeviceAddresses, nDevices); + if (results != NO_ERROR) { + return results; + } + int status = check_AudioSystem_Command( + AudioSystem::removeDevicesRoleForCapturePreset((audio_source_t)capturePreset, + (device_role_t)role, nDevices)); + return (jint)status; +} + +static jint android_media_AudioSystem_clearDevicesRoleForCapturePreset(JNIEnv *env, jobject thiz, + jint capturePreset, + jint role) { + return (jint)check_AudioSystem_Command( + AudioSystem::clearDevicesRoleForCapturePreset((audio_source_t)capturePreset, + (device_role_t)role)); +} + +static jint android_media_AudioSystem_getDevicesForRoleAndCapturePreset(JNIEnv *env, jobject thiz, + jint capturePreset, + jint role, + jobject jDevices) { + AudioDeviceTypeAddrVector nDevices; + status_t status = check_AudioSystem_Command( + AudioSystem::getDevicesForRoleAndCapturePreset((audio_source_t)capturePreset, + (device_role_t)role, nDevices)); + if (status != NO_ERROR) { + return (jint)status; + } + for (const auto &device : nDevices) { + jobject jAudioDeviceAttributes = NULL; + jint jStatus = createAudioDeviceAttributesFromNative(env, &jAudioDeviceAttributes, &device); + if (jStatus != AUDIO_JAVA_SUCCESS) { + return jStatus; + } + env->CallBooleanMethod(jDevices, gListMethods.add, jAudioDeviceAttributes); + env->DeleteLocalRef(jAudioDeviceAttributes); + } + return AUDIO_JAVA_SUCCESS; +} + static jint android_media_AudioSystem_getDevicesForAttributes(JNIEnv *env, jobject thiz, jobject jaa, jobjectArray jDeviceArray) @@ -2558,6 +2631,16 @@ static const JNINativeMethod gMethods[] = (void *)android_media_AudioSystem_removeDevicesRoleForStrategy}, {"getDevicesForRoleAndStrategy", "(IILjava/util/List;)I", (void *)android_media_AudioSystem_getDevicesForRoleAndStrategy}, + {"setDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", + (void *)android_media_AudioSystem_setDevicesRoleForCapturePreset}, + {"addDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", + (void *)android_media_AudioSystem_addDevicesRoleForCapturePreset}, + {"removeDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", + (void *)android_media_AudioSystem_removeDevicesRoleForCapturePreset}, + {"clearDevicesRoleForCapturePreset", "(II)I", + (void *)android_media_AudioSystem_clearDevicesRoleForCapturePreset}, + {"getDevicesForRoleAndCapturePreset", "(IILjava/util/List;)I", + (void *)android_media_AudioSystem_getDevicesForRoleAndCapturePreset}, {"getDevicesForAttributes", "(Landroid/media/AudioAttributes;[Landroid/media/AudioDeviceAttributes;)I", (void *)android_media_AudioSystem_getDevicesForAttributes}, diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp index 54567e5010c5..8177ec6df803 100644 --- a/core/jni/android_view_KeyEvent.cpp +++ b/core/jni/android_view_KeyEvent.cpp @@ -20,6 +20,7 @@ #include <android_runtime/AndroidRuntime.h> #include <android_runtime/Log.h> +#include <attestation/HmacKeyManager.h> #include <input/Input.h> #include <nativehelper/ScopedPrimitiveArray.h> #include <nativehelper/ScopedUtfChars.h> diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp index 2e396f247979..ce8b59941a5a 100644 --- a/core/jni/android_view_MotionEvent.cpp +++ b/core/jni/android_view_MotionEvent.cpp @@ -21,12 +21,13 @@ #include <android/graphics/matrix.h> #include <android_runtime/AndroidRuntime.h> #include <android_runtime/Log.h> -#include <utils/Log.h> +#include <attestation/HmacKeyManager.h> #include <input/Input.h> #include <nativehelper/ScopedUtfChars.h> +#include <utils/Log.h> #include "android_os_Parcel.h" -#include "android_view_MotionEvent.h" #include "android_util_Binder.h" +#include "android_view_MotionEvent.h" #include "core_jni_helpers.h" diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index 5c16772488d0..4cf6715ba0ca 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -24,6 +24,7 @@ import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE; import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; @@ -117,7 +118,15 @@ public class ViewRootImplTest { final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION); ViewRootImpl.adjustLayoutParamsForCompatibility(attrs); - // A window which fits system bars must fit IME, unless its type is toast or system alert. + assertEquals(Type.systemBars(), attrs.getFitInsetsTypes()); + } + + @Test + public void adjustLayoutParamsForCompatibility_fitSystemBarsAndIme() { + final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION); + attrs.softInputMode |= SOFT_INPUT_ADJUST_RESIZE; + ViewRootImpl.adjustLayoutParamsForCompatibility(attrs); + assertEquals(Type.systemBars() | Type.ime(), attrs.getFitInsetsTypes()); } diff --git a/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java b/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java index 02870a53773e..7f4e9ada7b22 100644 --- a/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java +++ b/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java @@ -20,6 +20,9 @@ import static android.view.Display.DEFAULT_DISPLAY; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import android.app.Activity; import android.app.EmptyActivity; @@ -41,6 +44,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mockito; /** * Tests {@link DecorContext}. @@ -63,13 +67,18 @@ public final class DecorContextTest { @Test public void testDecorContextWithDefaultDisplay() { + final Context baseContext = Mockito.spy(mContext.getApplicationContext()); Display defaultDisplay = new Display(DisplayManagerGlobal.getInstance(), DEFAULT_DISPLAY, new DisplayInfo(), DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS); final Context defaultDisplayContext = mContext.createDisplayContext(defaultDisplay); final PhoneWindow window = new PhoneWindow(defaultDisplayContext); - DecorContext context = new DecorContext(mContext.getApplicationContext(), window); + DecorContext context = new DecorContext(baseContext, window); assertDecorContextDisplay(DEFAULT_DISPLAY, context); + + // TODO(b/166174272): Creating a display context for the default display will result + // in additional resource creation. + verify(baseContext, never()).createDisplayContext(any()); } @Test diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm index 1ab4c6385ac1..fe6eeeb66e40 100644 --- a/data/keyboards/Generic.kcm +++ b/data/keyboards/Generic.kcm @@ -28,12 +28,14 @@ key A { label: 'A' base: 'a' shift, capslock: 'A' + shift+capslock: 'a' } key B { label: 'B' base: 'b' shift, capslock: 'B' + shift+capslock: 'b' } key C { @@ -42,12 +44,14 @@ key C { shift, capslock: 'C' alt: '\u00e7' shift+alt: '\u00c7' + shift+capslock: 'c' } key D { label: 'D' base: 'd' shift, capslock: 'D' + shift+capslock: 'd' } key E { @@ -55,24 +59,28 @@ key E { base: 'e' shift, capslock: 'E' alt: '\u0301' + shift+capslock: 'e' } key F { label: 'F' base: 'f' shift, capslock: 'F' + shift+capslock: 'f' } key G { label: 'G' base: 'g' shift, capslock: 'G' + shift+capslock: 'g' } key H { label: 'H' base: 'h' shift, capslock: 'H' + shift+capslock: 'h' } key I { @@ -80,30 +88,35 @@ key I { base: 'i' shift, capslock: 'I' alt: '\u0302' + shift+capslock: 'i' } key J { label: 'J' base: 'j' shift, capslock: 'J' + shift+capslock: 'j' } key K { label: 'K' base: 'k' shift, capslock: 'K' + shift+capslock: 'k' } key L { label: 'L' base: 'l' shift, capslock: 'L' + shift+capslock: 'l' } key M { label: 'M' base: 'm' shift, capslock: 'M' + shift+capslock: 'm' } key N { @@ -111,30 +124,35 @@ key N { base: 'n' shift, capslock: 'N' alt: '\u0303' + shift+capslock: 'n' } key O { label: 'O' base: 'o' shift, capslock: 'O' + shift+capslock: 'o' } key P { label: 'P' base: 'p' shift, capslock: 'P' + shift+capslock: 'p' } key Q { label: 'Q' base: 'q' shift, capslock: 'Q' + shift+capslock: 'q' } key R { label: 'R' base: 'r' shift, capslock: 'R' + shift+capslock: 'r' } key S { @@ -142,12 +160,14 @@ key S { base: 's' shift, capslock: 'S' alt: '\u00df' + shift+capslock: 's' } key T { label: 'T' base: 't' shift, capslock: 'T' + shift+capslock: 't' } key U { @@ -155,36 +175,42 @@ key U { base: 'u' shift, capslock: 'U' alt: '\u0308' + shift+capslock: 'u' } key V { label: 'V' base: 'v' shift, capslock: 'V' + shift+capslock: 'v' } key W { label: 'W' base: 'w' shift, capslock: 'W' + shift+capslock: 'w' } key X { label: 'X' base: 'x' shift, capslock: 'X' + shift+capslock: 'x' } key Y { label: 'Y' base: 'y' shift, capslock: 'Y' + shift+capslock: 'y' } key Z { label: 'Z' base: 'z' shift, capslock: 'Z' + shift+capslock: 'z' } key 0 { diff --git a/packages/SystemUI/res/anim/forced_resizable_enter.xml b/libs/WindowManager/Shell/res/anim/forced_resizable_enter.xml index 01b8fdbe4437..01b8fdbe4437 100644 --- a/packages/SystemUI/res/anim/forced_resizable_enter.xml +++ b/libs/WindowManager/Shell/res/anim/forced_resizable_enter.xml diff --git a/packages/SystemUI/res/anim/forced_resizable_exit.xml b/libs/WindowManager/Shell/res/anim/forced_resizable_exit.xml index 6f316a75dbed..6f316a75dbed 100644 --- a/packages/SystemUI/res/anim/forced_resizable_exit.xml +++ b/libs/WindowManager/Shell/res/anim/forced_resizable_exit.xml diff --git a/packages/SystemUI/res/layout/divider.xml b/libs/WindowManager/Shell/res/layout/divider.xml index f1f0df054240..f1f0df054240 100644 --- a/packages/SystemUI/res/layout/divider.xml +++ b/libs/WindowManager/Shell/res/layout/divider.xml diff --git a/packages/SystemUI/res/layout/docked_stack_divider.xml b/libs/WindowManager/Shell/res/layout/docked_stack_divider.xml index 70e5451eed57..ad870252d819 100644 --- a/packages/SystemUI/res/layout/docked_stack_divider.xml +++ b/libs/WindowManager/Shell/res/layout/docked_stack_divider.xml @@ -14,7 +14,7 @@ limitations under the License. --> -<com.android.systemui.stackdivider.DividerView +<com.android.wm.shell.splitscreen.DividerView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="match_parent" android:layout_width="match_parent"> @@ -24,15 +24,15 @@ android:id="@+id/docked_divider_background" android:background="@color/docked_divider_background"/> - <com.android.systemui.stackdivider.MinimizedDockShadow + <com.android.wm.shell.splitscreen.MinimizedDockShadow style="@style/DockedDividerMinimizedShadow" android:id="@+id/minimized_dock_shadow" android:alpha="0"/>"> - <com.android.systemui.stackdivider.DividerHandleView + <com.android.wm.shell.splitscreen.DividerHandleView style="@style/DockedDividerHandle" android:id="@+id/docked_divider_handle" android:contentDescription="@string/accessibility_divider" android:background="@null"/> -</com.android.systemui.stackdivider.DividerView> +</com.android.wm.shell.splitscreen.DividerView> diff --git a/packages/SystemUI/res/layout/forced_resizable_activity.xml b/libs/WindowManager/Shell/res/layout/forced_resizable_activity.xml index 3c778c431a2e..3c778c431a2e 100644 --- a/packages/SystemUI/res/layout/forced_resizable_activity.xml +++ b/libs/WindowManager/Shell/res/layout/forced_resizable_activity.xml diff --git a/libs/WindowManager/Shell/res/values-land/dimens.xml b/libs/WindowManager/Shell/res/values-land/dimens.xml new file mode 100644 index 000000000000..77a601ddf440 --- /dev/null +++ b/libs/WindowManager/Shell/res/values-land/dimens.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ +--> +<resources> + <dimen name="docked_divider_handle_width">2dp</dimen> + <dimen name="docked_divider_handle_height">16dp</dimen> +</resources>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/values-land/styles.xml b/libs/WindowManager/Shell/res/values-land/styles.xml new file mode 100644 index 000000000000..863bb69d4034 --- /dev/null +++ b/libs/WindowManager/Shell/res/values-land/styles.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + <style name="DockedDividerBackground"> + <item name="android:layout_width">10dp</item> + <item name="android:layout_height">match_parent</item> + <item name="android:layout_gravity">center_horizontal</item> + </style> + + <style name="DockedDividerHandle"> + <item name="android:layout_gravity">center_vertical</item> + <item name="android:layout_width">48dp</item> + <item name="android:layout_height">96dp</item> + </style> + + <style name="DockedDividerMinimizedShadow"> + <item name="android:layout_width">8dp</item> + <item name="android:layout_height">match_parent</item> + </style> +</resources> + diff --git a/libs/WindowManager/Shell/res/values-sw600dp/config.xml b/libs/WindowManager/Shell/res/values-sw600dp/config.xml new file mode 100644 index 000000000000..f194532f1e0d --- /dev/null +++ b/libs/WindowManager/Shell/res/values-sw600dp/config.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2020, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<!-- These resources are around just to allow their values to be customized + for different hardware and product builds. --> +<resources> + <!-- Animation duration when using long press on recents to dock --> + <integer name="long_press_dock_anim_duration">290</integer> +</resources>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml new file mode 100644 index 000000000000..6a19083e3788 --- /dev/null +++ b/libs/WindowManager/Shell/res/values/colors.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + * Copyright 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources> + <color name="docked_divider_background">#ff000000</color> + <color name="docked_divider_handle">#ffffff</color> + <drawable name="forced_resizable_background">#59000000</drawable> + <color name="minimize_dock_shadow_start">#60000000</color> + <color name="minimize_dock_shadow_end">#00000000</color> +</resources>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml index 245c0725c2a8..39efd0768eaa 100644 --- a/libs/WindowManager/Shell/res/values/config.xml +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -26,4 +26,7 @@ <!-- Allow PIP to enable round corner, see also R.dimen.pip_corner_radius --> <bool name="config_pipEnableRoundCorner">false</bool> + + <!-- Animation duration when using long press on recents to dock --> + <integer name="long_press_dock_anim_duration">250</integer> </resources> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 1c1217681b9f..ce690281b491 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -56,4 +56,10 @@ <dimen name="pip_resize_handle_size">12dp</dimen> <dimen name="pip_resize_handle_margin">4dp</dimen> <dimen name="pip_resize_handle_padding">0dp</dimen> + + <!-- How high we lift the divider when touching --> + <dimen name="docked_stack_divider_lift_elevation">4dp</dimen> + + <dimen name="docked_divider_handle_width">16dp</dimen> + <dimen name="docked_divider_handle_height">2dp</dimen> </resources> diff --git a/libs/WindowManager/Shell/res/values/ids.xml b/libs/WindowManager/Shell/res/values/ids.xml index ed20398f309d..fb892388cf74 100644 --- a/libs/WindowManager/Shell/res/values/ids.xml +++ b/libs/WindowManager/Shell/res/values/ids.xml @@ -16,4 +16,11 @@ --> <resources> <item type="id" name="action_pip_resize" /> + + <!-- Accessibility actions for the docked stack divider --> + <item type="id" name="action_move_tl_full" /> + <item type="id" name="action_move_tl_70" /> + <item type="id" name="action_move_tl_50" /> + <item type="id" name="action_move_tl_30" /> + <item type="id" name="action_move_rb_full" /> </resources> diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml index 6752b56fcdf3..cad924771cd3 100644 --- a/libs/WindowManager/Shell/res/values/strings.xml +++ b/libs/WindowManager/Shell/res/values/strings.xml @@ -53,4 +53,39 @@ <!-- TODO Deprecated. Label for PIP the drag to dismiss hint. DO NOT TRANSLATE [CHAR LIMIT=NONE]--> <string name="pip_phone_dismiss_hint">Drag down to dismiss</string> + + <!-- Multi-Window strings --> + <!-- Text that gets shown on top of current activity to inform the user that the system force-resized the current activity to be displayed in split-screen and that things might crash/not work properly [CHAR LIMIT=NONE] --> + <string name="dock_forced_resizable">App may not work with split-screen.</string> + <!-- Warning message when we try to dock a non-resizeable task and launch it in fullscreen instead. --> + <string name="dock_non_resizeble_failed_to_dock_text">App does not support split-screen.</string> + <!-- Text that gets shown on top of current activity to inform the user that the system force-resized the current activity to be displayed on a secondary display and that things might crash/not work properly [CHAR LIMIT=NONE] --> + <string name="forced_resizable_secondary_display">App may not work on a secondary display.</string> + <!-- Warning message when we try to launch a non-resizeable activity on a secondary display and launch it on the primary instead. --> + <string name="activity_launch_on_secondary_display_failed_text">App does not support launch on secondary displays.</string> + + <!-- Accessibility label for the divider that separates the windows in split-screen mode [CHAR LIMIT=NONE] --> + <string name="accessibility_divider">Split-screen divider</string> + + <!-- Accessibility action for moving docked stack divider to make the left screen full screen [CHAR LIMIT=NONE] --> + <string name="accessibility_action_divider_left_full">Left full screen</string> + <!-- Accessibility action for moving docked stack divider to make the left screen 70% [CHAR LIMIT=NONE] --> + <string name="accessibility_action_divider_left_70">Left 70%</string> + <!-- Accessibility action for moving docked stack divider to make the left screen 50% [CHAR LIMIT=NONE] --> + <string name="accessibility_action_divider_left_50">Left 50%</string> + <!-- Accessibility action for moving docked stack divider to make the left screen 30% [CHAR LIMIT=NONE] --> + <string name="accessibility_action_divider_left_30">Left 30%</string> + <!-- Accessibility action for moving docked stack divider to make the right screen full screen [CHAR LIMIT=NONE] --> + <string name="accessibility_action_divider_right_full">Right full screen</string> + + <!-- Accessibility action for moving docked stack divider to make the top screen full screen [CHAR LIMIT=NONE] --> + <string name="accessibility_action_divider_top_full">Top full screen</string> + <!-- Accessibility action for moving docked stack divider to make the top screen 70% [CHAR LIMIT=NONE] --> + <string name="accessibility_action_divider_top_70">Top 70%</string> + <!-- Accessibility action for moving docked stack divider to make the top screen 50% [CHAR LIMIT=NONE] --> + <string name="accessibility_action_divider_top_50">Top 50%</string> + <!-- Accessibility action for moving docked stack divider to make the top screen 30% [CHAR LIMIT=NONE] --> + <string name="accessibility_action_divider_top_30">Top 30%</string> + <!-- Accessibility action for moving docked stack divider to make the bottom screen full screen [CHAR LIMIT=NONE] --> + <string name="accessibility_action_divider_bottom_full">Bottom full screen</string> </resources> diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml new file mode 100644 index 000000000000..fffcd33f7992 --- /dev/null +++ b/libs/WindowManager/Shell/res/values/styles.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + <!-- Theme used for the activity that shows when the system forced an app to be resizable --> + <style name="ForcedResizableTheme" parent="@android:style/Theme.Translucent.NoTitleBar"> + <item name="android:windowBackground">@drawable/forced_resizable_background</item> + <item name="android:statusBarColor">@android:color/transparent</item> + <item name="android:windowAnimationStyle">@style/Animation.ForcedResizable</item> + </style> + + <style name="Animation.ForcedResizable" parent="@android:style/Animation"> + <item name="android:activityOpenEnterAnimation">@anim/forced_resizable_enter</item> + + <!-- If the target stack doesn't have focus, we do a task to front animation. --> + <item name="android:taskToFrontEnterAnimation">@anim/forced_resizable_enter</item> + <item name="android:activityCloseExitAnimation">@anim/forced_resizable_exit</item> + </style> + + <style name="DockedDividerBackground"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">10dp</item> + <item name="android:layout_gravity">center_vertical</item> + </style> + + <style name="DockedDividerMinimizedShadow"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">8dp</item> + </style> + + <style name="DockedDividerHandle"> + <item name="android:layout_gravity">center_horizontal</item> + <item name="android:layout_width">96dp</item> + <item name="android:layout_height">48dp</item> + </style> +</resources> diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java index 13ed02e9513e..9cb125087cd9 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java @@ -14,8 +14,9 @@ * limitations under the License. */ -package com.android.systemui.stackdivider; +package com.android.wm.shell.common; +import android.annotation.NonNull; import android.os.Handler; import android.util.Slog; import android.view.SurfaceControl; @@ -23,17 +24,13 @@ import android.window.WindowContainerTransaction; import android.window.WindowContainerTransactionCallback; import android.window.WindowOrganizer; -import androidx.annotation.NonNull; - -import com.android.wm.shell.common.TransactionPool; - import java.util.ArrayList; /** * Helper for serializing sync-transactions and corresponding callbacks. */ -class SyncTransactionQueue { - private static final boolean DEBUG = SplitScreenController.DEBUG; +public final class SyncTransactionQueue { + private static final boolean DEBUG = false; private static final String TAG = "SyncTransactionQueue"; // Just a little longer than the sync-engine timeout of 5s @@ -58,7 +55,7 @@ class SyncTransactionQueue { } }; - SyncTransactionQueue(TransactionPool pool, Handler handler) { + public SyncTransactionQueue(TransactionPool pool, Handler handler) { mTransactionPool = pool; mHandler = handler; } @@ -66,7 +63,7 @@ class SyncTransactionQueue { /** * Queues a sync transaction to be sent serially to WM. */ - void queue(WindowContainerTransaction wct) { + public void queue(WindowContainerTransaction wct) { SyncCallback cb = new SyncCallback(wct); synchronized (mQueue) { if (DEBUG) Slog.d(TAG, "Queueing up " + wct); @@ -82,7 +79,7 @@ class SyncTransactionQueue { * Otherwise just returns without queueing. * @return {@code true} if queued, {@code false} if not. */ - boolean queueIfWaiting(WindowContainerTransaction wct) { + public boolean queueIfWaiting(WindowContainerTransaction wct) { synchronized (mQueue) { if (mQueue.isEmpty()) { if (DEBUG) Slog.d(TAG, "Nothing in queue, so skip queueing up " + wct); @@ -102,7 +99,7 @@ class SyncTransactionQueue { * Runs a runnable in sync with sync transactions (ie. when the current in-flight transaction * returns. If there are no transactions in-flight, runnable executes immediately. */ - void runInSync(TransactionRunnable runnable) { + public void runInSync(TransactionRunnable runnable) { synchronized (mQueue) { if (DEBUG) Slog.d(TAG, "Run in sync. mInFlight=" + mInFlight); if (mInFlight != null) { @@ -127,7 +124,9 @@ class SyncTransactionQueue { t.close(); } - interface TransactionRunnable { + /** Task to run with transaction. */ + public interface TransactionRunnable { + /** Runs with transaction. */ void runWithTransaction(SurfaceControl.Transaction t); } @@ -154,7 +153,7 @@ class SyncTransactionQueue { @Override public void onTransactionReady(int id, - @androidx.annotation.NonNull SurfaceControl.Transaction t) { + @NonNull SurfaceControl.Transaction t) { mHandler.post(() -> { synchronized (mQueue) { if (mId != id) { diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerHandleView.java index 1559169bd8ca..2cb1fff4cde6 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerHandleView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.stackdivider; +package com.android.wm.shell.splitscreen; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -28,43 +28,41 @@ import android.util.AttributeSet; import android.util.Property; import android.view.View; -import com.android.systemui.R; +import com.android.wm.shell.R; import com.android.wm.shell.animation.Interpolators; /** * View for the handle in the docked stack divider. */ -class DividerHandleView extends View { - - private final static Property<DividerHandleView, Integer> WIDTH_PROPERTY - = new Property<DividerHandleView, Integer>(Integer.class, "width") { - - @Override - public Integer get(DividerHandleView object) { - return object.mCurrentWidth; - } - - @Override - public void set(DividerHandleView object, Integer value) { - object.mCurrentWidth = value; - object.invalidate(); - } - }; - - private final static Property<DividerHandleView, Integer> HEIGHT_PROPERTY - = new Property<DividerHandleView, Integer>(Integer.class, "height") { - - @Override - public Integer get(DividerHandleView object) { - return object.mCurrentHeight; - } - - @Override - public void set(DividerHandleView object, Integer value) { - object.mCurrentHeight = value; - object.invalidate(); - } - }; +public class DividerHandleView extends View { + + private static final Property<DividerHandleView, Integer> WIDTH_PROPERTY = + new Property<DividerHandleView, Integer>(Integer.class, "width") { + @Override + public Integer get(DividerHandleView object) { + return object.mCurrentWidth; + } + + @Override + public void set(DividerHandleView object, Integer value) { + object.mCurrentWidth = value; + object.invalidate(); + } + }; + + private static final Property<DividerHandleView, Integer> HEIGHT_PROPERTY = + new Property<DividerHandleView, Integer>(Integer.class, "height") { + @Override + public Integer get(DividerHandleView object) { + return object.mCurrentHeight; + } + + @Override + public void set(DividerHandleView object, Integer value) { + object.mCurrentHeight = value; + object.invalidate(); + } + }; private final Paint mPaint = new Paint(); private final int mWidth; @@ -86,7 +84,7 @@ class DividerHandleView extends View { mCircleDiameter = (mWidth + mHeight) / 3; } - public void setTouching(boolean touching, boolean animate) { + void setTouching(boolean touching, boolean animate) { if (touching == mTouching) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerImeController.java index 64ee7ed5e0e0..92cee8a1a874 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerImeController.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.stackdivider; +package com.android.wm.shell.splitscreen; import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED; import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED; @@ -22,6 +22,7 @@ import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; +import android.annotation.Nullable; import android.graphics.Rect; import android.os.Handler; import android.util.Slog; @@ -31,8 +32,6 @@ import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import android.window.WindowOrganizer; -import androidx.annotation.Nullable; - import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.TransactionPool; diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerState.java index 8e79d51ee209..23d86a00d4bf 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerState.java @@ -11,15 +11,15 @@ * 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 + * limitations under the License. */ -package com.android.systemui.stackdivider; +package com.android.wm.shell.splitscreen; /** * Class to hold state of divider that needs to persist across configuration changes. */ -public class DividerState { +final class DividerState { public boolean animateAfterRecentsDrawn; public float mRatioPositionBeforeMinimized; } diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerView.java index 9bf7ea681926..79bfda92bd92 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.stackdivider; +package com.android.wm.shell.splitscreen; import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW; import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW; @@ -62,7 +62,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.policy.DividerSnapAlgorithm; import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget; import com.android.internal.policy.DockedDividerUtils; -import com.android.systemui.R; +import com.android.wm.shell.R; import com.android.wm.shell.animation.FlingAnimationUtils; import com.android.wm.shell.animation.Interpolators; @@ -76,7 +76,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, private static final String TAG = "DividerView"; private static final boolean DEBUG = SplitScreenController.DEBUG; - public interface DividerCallbacks { + interface DividerCallbacks { void onDraggingStart(); void onDraggingEnd(); } @@ -187,7 +187,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, if (snapAlgorithm.showMiddleSplitTargetForAccessibility()) { // Only show the middle target if there are more than 1 split target info.addAction(new AccessibilityAction(R.id.action_move_tl_50, - mContext.getString(R.string.accessibility_action_divider_top_50))); + mContext.getString(R.string.accessibility_action_divider_top_50))); } if (snapAlgorithm.isLastSplitTargetAvailable()) { info.addAction(new AccessibilityAction(R.id.action_move_tl_30, @@ -205,7 +205,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, if (snapAlgorithm.showMiddleSplitTargetForAccessibility()) { // Only show the middle target if there are more than 1 split target info.addAction(new AccessibilityAction(R.id.action_move_tl_50, - mContext.getString(R.string.accessibility_action_divider_left_50))); + mContext.getString(R.string.accessibility_action_divider_left_50))); } if (snapAlgorithm.isLastSplitTargetAvailable()) { info.addAction(new AccessibilityAction(R.id.action_move_tl_30, @@ -373,6 +373,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, } } + /** Gets non-minimized secondary bounds of split screen. */ public Rect getNonMinimizedSplitScreenSecondaryBounds() { mOtherTaskRect.set(mSplitLayout.mSecondary); return mOtherTaskRect; @@ -409,6 +410,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, return mSurfaceHidden; } + /** Starts dragging the divider bar. */ public boolean startDragging(boolean animate, boolean touching) { cancelFlingAnimation(); if (touching) { @@ -427,6 +429,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, return inSplitMode(); } + /** Stops dragging the divider bar. */ public void stopDragging(int position, float velocity, boolean avoidDismissStart, boolean logMetrics) { mHandle.setTouching(false, true /* animate */); @@ -889,7 +892,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, WindowManagerProxy.applyResizeSplits(midPos, mSplitLayout); } - public void setMinimizedDockStack(boolean minimized, long animDuration, + void setMinimizedDockStack(boolean minimized, long animDuration, boolean isHomeStackResizable) { if (DEBUG) Slog.d(TAG, "setMinDock: " + mDockedStackMinimized + "->" + minimized); mHomeStackResizable = isHomeStackResizable; @@ -925,7 +928,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, } } - public void setAdjustedForIme(boolean adjustedForIme, long animDuration) { + void setAdjustedForIme(boolean adjustedForIme, long animDuration) { if (mAdjustedForIme == adjustedForIme) { return; } @@ -952,8 +955,8 @@ public class DividerView extends FrameLayout implements OnTouchListener, private void saveSnapTargetBeforeMinimized(SnapTarget target) { mSnapTargetBeforeMinimized = target; - mState.mRatioPositionBeforeMinimized = (float) target.position / - (isHorizontalDivision() ? mSplitLayout.mDisplayLayout.height() + mState.mRatioPositionBeforeMinimized = (float) target.position + / (isHorizontalDivision() ? mSplitLayout.mDisplayLayout.height() : mSplitLayout.mDisplayLayout.width()); } @@ -971,8 +974,8 @@ public class DividerView extends FrameLayout implements OnTouchListener, } private void repositionSnapTargetBeforeMinimized() { - int position = (int) (mState.mRatioPositionBeforeMinimized * - (isHorizontalDivision() ? mSplitLayout.mDisplayLayout.height() + int position = (int) (mState.mRatioPositionBeforeMinimized + * (isHorizontalDivision() ? mSplitLayout.mDisplayLayout.height() : mSplitLayout.mDisplayLayout.width())); // Set the snap target before minimized but do not save until divider is attached and not @@ -1011,7 +1014,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, containingRect.right, containingRect.bottom); } - public void calculateBoundsForPosition(int position, int dockSide, Rect outRect) { + private void calculateBoundsForPosition(int position, int dockSide, Rect outRect) { DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outRect, mSplitLayout.mDisplayLayout.width(), mSplitLayout.mDisplayLayout.height(), mDividerSize); @@ -1240,8 +1243,8 @@ public class DividerView extends FrameLayout implements OnTouchListener, if (dismissTarget != null && fraction > 0f && isDismissing(splitTarget, position, dockSide)) { fraction = calculateParallaxDismissingFraction(fraction, dockSide); - int offsetPosition = (int) (start + - fraction * (dismissTarget.position - splitTarget.position)); + int offsetPosition = (int) (start + fraction + * (dismissTarget.position - splitTarget.position)); int width = taskRect.width(); int height = taskRect.height(); switch (dockSide) { diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerWindowManager.java index d869333e11a7..0b4e17c27398 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerWindowManager.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.stackdivider; +package com.android.wm.shell.splitscreen; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; @@ -37,7 +37,7 @@ import com.android.wm.shell.common.SystemWindows; /** * Manages the window parameters of the docked stack divider. */ -public class DividerWindowManager { +final class DividerWindowManager { private static final String WINDOW_TITLE = "DockedStackDivider"; @@ -45,12 +45,12 @@ public class DividerWindowManager { private WindowManager.LayoutParams mLp; private View mView; - public DividerWindowManager(SystemWindows systemWindows) { + DividerWindowManager(SystemWindows systemWindows) { mSystemWindows = systemWindows; } /** Add a divider view */ - public void add(View view, int width, int height, int displayId) { + void add(View view, int width, int height, int displayId) { mLp = new WindowManager.LayoutParams( width, height, TYPE_DOCK_DIVIDER, FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL @@ -67,14 +67,14 @@ public class DividerWindowManager { mView = view; } - public void remove() { + void remove() { if (mView != null) { mSystemWindows.removeView(mView); } mView = null; } - public void setSlippery(boolean slippery) { + void setSlippery(boolean slippery) { boolean changed = false; if (slippery && (mLp.flags & FLAG_SLIPPERY) == 0) { mLp.flags |= FLAG_SLIPPERY; @@ -88,7 +88,7 @@ public class DividerWindowManager { } } - public void setTouchable(boolean touchable) { + void setTouchable(boolean touchable) { if (mView == null) { return; } @@ -106,7 +106,7 @@ public class DividerWindowManager { } /** Sets the touch region to `touchRegion`. Use null to unset.*/ - public void setTouchRegion(Region touchRegion) { + void setTouchRegion(Region touchRegion) { if (mView == null) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ForcedResizableInfoActivity.java index 02f75050c061..7a1633530148 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ForcedResizableInfoActivity.java @@ -11,10 +11,10 @@ * 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 + * limitations under the License. */ -package com.android.systemui.stackdivider; +package com.android.wm.shell.splitscreen; import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY; import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN; @@ -29,7 +29,7 @@ import android.view.View; import android.view.View.OnTouchListener; import android.widget.TextView; -import com.android.systemui.R; +import com.android.wm.shell.R; /** * Translucent activity that gets started on top of a task in multi-window to inform the user that diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ForcedResizableInfoActivityController.java index 4c26694cc22a..1ef142dacb9e 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ForcedResizableInfoActivityController.java @@ -11,13 +11,13 @@ * 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 + * limitations under the License. */ -package com.android.systemui.stackdivider; +package com.android.wm.shell.splitscreen; -import static com.android.systemui.stackdivider.ForcedResizableInfoActivity - .EXTRA_FORCED_RESIZEABLE_REASON; + +import static com.android.wm.shell.splitscreen.ForcedResizableInfoActivity.EXTRA_FORCED_RESIZEABLE_REASON; import android.app.ActivityOptions; import android.content.Context; @@ -27,7 +27,7 @@ import android.os.UserHandle; import android.util.ArraySet; import android.widget.Toast; -import com.android.systemui.R; +import com.android.wm.shell.R; import java.util.function.Consumer; @@ -55,20 +55,20 @@ final class ForcedResizableInfoActivityController implements DividerView.Divider /** Record of force resized task that's pending to be handled. */ private class PendingTaskRecord { - int taskId; + int mTaskId; /** * {@link android.app.ITaskStackListener#FORCED_RESIZEABLE_REASON_SPLIT_SCREEN} or * {@link android.app.ITaskStackListener#FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY} */ - int reason; + int mReason; PendingTaskRecord(int taskId, int reason) { - this.taskId = taskId; - this.reason = reason; + this.mTaskId = taskId; + this.mReason = reason; } } - public ForcedResizableInfoActivityController(Context context, + ForcedResizableInfoActivityController(Context context, SplitScreenController splitScreenController) { mContext = context; splitScreenController.registerInSplitScreenListener(mDockedStackExistsListener); @@ -116,11 +116,11 @@ final class ForcedResizableInfoActivityController implements DividerView.Divider PendingTaskRecord pendingRecord = mPendingTasks.valueAt(i); Intent intent = new Intent(mContext, ForcedResizableInfoActivity.class); ActivityOptions options = ActivityOptions.makeBasic(); - options.setLaunchTaskId(pendingRecord.taskId); + options.setLaunchTaskId(pendingRecord.mTaskId); // Set as task overlay and allow to resume, so that when an app enters split-screen and // becomes paused, the overlay will still be shown. options.setTaskOverlay(true, true /* canResume */); - intent.putExtra(EXTRA_FORCED_RESIZEABLE_REASON, pendingRecord.reason); + intent.putExtra(EXTRA_FORCED_RESIZEABLE_REASON, pendingRecord.mReason); mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT); } mPendingTasks.clear(); diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/MinimizedDockShadow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MinimizedDockShadow.java index ecff54fd907d..06f4ef109193 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/MinimizedDockShadow.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MinimizedDockShadow.java @@ -11,10 +11,10 @@ * 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 + * limitations under the License. */ -package com.android.systemui.stackdivider; +package com.android.wm.shell.splitscreen; import android.annotation.Nullable; import android.content.Context; @@ -27,7 +27,7 @@ import android.util.AttributeSet; import android.view.View; import android.view.WindowManager; -import com.android.systemui.R; +import com.android.wm.shell.R; /** * Shadow for the minimized dock state on homescreen. @@ -42,7 +42,7 @@ public class MinimizedDockShadow extends View { super(context, attrs); } - public void setDockSide(int dockSide) { + void setDockSide(int dockSide) { if (dockSide != mDockSide) { mDockSide = dockSide; updatePaint(getLeft(), getTop(), getRight(), getBottom()); diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDisplayLayout.java index a34e85517953..3c0f93906795 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDisplayLayout.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.stackdivider; +package com.android.wm.shell.splitscreen; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java index 93b1f86a5dc2..184342f14d4f 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreen.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.stackdivider; +package com.android.wm.shell.splitscreen; import android.graphics.Rect; import android.window.WindowContainerToken; diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 360b49555612..d5326d4845a3 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.stackdivider; +package com.android.wm.shell.splitscreen; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; @@ -34,7 +34,7 @@ import android.window.WindowContainerTransaction; import android.window.WindowOrganizer; import com.android.internal.policy.DividerSnapAlgorithm; -import com.android.systemui.R; +import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java index 325c5597f9d8..6d28c5e17d42 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.stackdivider; +package com.android.wm.shell.splitscreen; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java index 82b10bd40b17..cd96676ad1fe 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.stackdivider; +package com.android.wm.shell.splitscreen; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; @@ -40,6 +40,7 @@ import android.window.WindowContainerTransaction; import android.window.WindowOrganizer; import com.android.internal.annotations.GuardedBy; +import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; import java.util.ArrayList; diff --git a/media/java/android/media/AudioDeviceAttributes.java b/media/java/android/media/AudioDeviceAttributes.java index 0ab62c14ab9f..5096bf616a1f 100644 --- a/media/java/android/media/AudioDeviceAttributes.java +++ b/media/java/android/media/AudioDeviceAttributes.java @@ -72,6 +72,11 @@ public final class AudioDeviceAttributes implements Parcelable { private final @Role int mRole; /** + * The internal audio device type + */ + private final int mNativeType; + + /** * @hide * Constructor from a valid {@link AudioDeviceInfo} * @param deviceInfo the connected audio device from which to obtain the device-identifying @@ -83,6 +88,7 @@ public final class AudioDeviceAttributes implements Parcelable { mRole = deviceInfo.isSink() ? ROLE_OUTPUT : ROLE_INPUT; mType = deviceInfo.getType(); mAddress = deviceInfo.getAddress(); + mNativeType = deviceInfo.getPort().type(); } /** @@ -109,12 +115,14 @@ public final class AudioDeviceAttributes implements Parcelable { mRole = role; mType = type; mAddress = address; + mNativeType = AudioSystem.DEVICE_NONE; } /*package*/ AudioDeviceAttributes(int nativeType, @NonNull String address) { mRole = (nativeType & AudioSystem.DEVICE_BIT_IN) != 0 ? ROLE_INPUT : ROLE_OUTPUT; mType = AudioDeviceInfo.convertInternalDeviceToDeviceType(nativeType); mAddress = address; + mNativeType = nativeType; } /** @@ -147,6 +155,15 @@ public final class AudioDeviceAttributes implements Parcelable { return mAddress; } + /** + * @hide + * Returns the internal device type of a device + * @return the internal device type + */ + public int getInternalType() { + return mNativeType; + } + @Override public int hashCode() { return Objects.hash(mRole, mType, mAddress); @@ -189,12 +206,14 @@ public final class AudioDeviceAttributes implements Parcelable { dest.writeInt(mRole); dest.writeInt(mType); dest.writeString(mAddress); + dest.writeInt(mNativeType); } private AudioDeviceAttributes(@NonNull Parcel in) { mRole = in.readInt(); mType = in.readInt(); mAddress = in.readString(); + mNativeType = in.readInt(); } public static final @NonNull Parcelable.Creator<AudioDeviceAttributes> CREATOR = diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java index d4fb1be56890..ba880cc58888 100644 --- a/media/java/android/media/AudioDeviceInfo.java +++ b/media/java/android/media/AudioDeviceInfo.java @@ -513,10 +513,21 @@ public final class AudioDeviceInfo { return INT_TO_EXT_DEVICE_MAPPING.get(intDevice, TYPE_UNKNOWN); } + /** @hide */ + public static int convertDeviceTypeToInternalInputDevice(int deviceType) { + return EXT_TO_INT_INPUT_DEVICE_MAPPING.get(deviceType, AudioSystem.DEVICE_NONE); + } + private static final SparseIntArray INT_TO_EXT_DEVICE_MAPPING; private static final SparseIntArray EXT_TO_INT_DEVICE_MAPPING; + /** + * EXT_TO_INT_INPUT_DEVICE_MAPPING aims at mapping external device type to internal input device + * type. + */ + private static final SparseIntArray EXT_TO_INT_INPUT_DEVICE_MAPPING; + static { INT_TO_EXT_DEVICE_MAPPING = new SparseIntArray(); INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_EARPIECE, TYPE_BUILTIN_EARPIECE); @@ -601,6 +612,32 @@ public final class AudioDeviceInfo { EXT_TO_INT_DEVICE_MAPPING.put(TYPE_REMOTE_SUBMIX, AudioSystem.DEVICE_OUT_REMOTE_SUBMIX); EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_HEADSET, AudioSystem.DEVICE_OUT_BLE_HEADSET); EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_SPEAKER, AudioSystem.DEVICE_OUT_BLE_SPEAKER); + + // privileges mapping to input device + EXT_TO_INT_INPUT_DEVICE_MAPPING = new SparseIntArray(); + EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_BUILTIN_MIC, AudioSystem.DEVICE_IN_BUILTIN_MIC); + EXT_TO_INT_INPUT_DEVICE_MAPPING.put( + TYPE_BLUETOOTH_SCO, AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET); + EXT_TO_INT_INPUT_DEVICE_MAPPING.put( + TYPE_WIRED_HEADSET, AudioSystem.DEVICE_IN_WIRED_HEADSET); + EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_HDMI, AudioSystem.DEVICE_IN_HDMI); + EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_TELEPHONY, AudioSystem.DEVICE_IN_TELEPHONY_RX); + EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_DOCK, AudioSystem.DEVICE_IN_ANLG_DOCK_HEADSET); + EXT_TO_INT_INPUT_DEVICE_MAPPING.put( + TYPE_USB_ACCESSORY, AudioSystem.DEVICE_IN_USB_ACCESSORY); + EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_USB_DEVICE, AudioSystem.DEVICE_IN_USB_DEVICE); + EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_USB_HEADSET, AudioSystem.DEVICE_IN_USB_HEADSET); + EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_FM_TUNER, AudioSystem.DEVICE_IN_FM_TUNER); + EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_TV_TUNER, AudioSystem.DEVICE_IN_TV_TUNER); + EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_LINE_ANALOG, AudioSystem.DEVICE_IN_LINE); + EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_LINE_DIGITAL, AudioSystem.DEVICE_IN_SPDIF); + EXT_TO_INT_INPUT_DEVICE_MAPPING.put( + TYPE_BLUETOOTH_A2DP, AudioSystem.DEVICE_IN_BLUETOOTH_A2DP); + EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_IP, AudioSystem.DEVICE_IN_IP); + EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_BUS, AudioSystem.DEVICE_IN_BUS); + EXT_TO_INT_INPUT_DEVICE_MAPPING.put( + TYPE_REMOTE_SUBMIX, AudioSystem.DEVICE_IN_REMOTE_SUBMIX); + EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_BLE_HEADSET, AudioSystem.DEVICE_IN_BLE_HEADSET); } } diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index a16e063fe969..aa2ff17a307b 100755 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1949,6 +1949,349 @@ public class AudioManager { } //==================================================================== + // Audio Capture Preset routing + + /** + * @hide + * Set the preferred device for a given capture preset, i.e. the audio routing to be used by + * this capture preset. Note that the device may not be available at the time the preferred + * device is set, but it will be used once made available. + * <p>Use {@link #clearPreferredDevicesForCapturePreset(int)} to cancel setting this preference + * for this capture preset.</p> + * @param capturePreset the audio capture preset whose routing will be affected + * @param device the audio device to route to when available + * @return true if the operation was successful, false otherwise + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public boolean setPreferredDeviceForCapturePreset(int capturePreset, + @NonNull AudioDeviceAttributes device) { + return setPreferredDevicesForCapturePreset(capturePreset, Arrays.asList(device)); + } + + /** + * @hide + * Remove all the preferred audio devices previously set + * @param capturePreset the audio capture preset whose routing will be affected + * @return true if the operation was successful, false otherwise (invalid capture preset, or no + * device set for example) + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public boolean clearPreferredDevicesForCapturePreset(int capturePreset) { + if (!MediaRecorder.isValidAudioSource(capturePreset)) { + return false; + } + try { + final int status = getService().clearPreferredDevicesForCapturePreset(capturePreset); + return status == AudioSystem.SUCCESS; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + * Return the preferred devices for an audio capture preset, previously set with + * {@link #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)} + * @param capturePreset the capture preset to query + * @return a list that contains preferred devices for that capture preset. + */ + @NonNull + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int capturePreset) { + if (!MediaRecorder.isValidAudioSource(capturePreset)) { + return new ArrayList<AudioDeviceAttributes>(); + } + try { + return getService().getPreferredDevicesForCapturePreset(capturePreset); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + private boolean setPreferredDevicesForCapturePreset( + int capturePreset, @NonNull List<AudioDeviceAttributes> devices) { + Objects.requireNonNull(devices); + if (!MediaRecorder.isValidAudioSource(capturePreset)) { + return false; + } + if (devices.size() != 1) { + throw new IllegalArgumentException( + "Only support setting one preferred devices for capture preset"); + } + for (AudioDeviceAttributes device : devices) { + Objects.requireNonNull(device); + } + try { + final int status = + getService().setPreferredDevicesForCapturePreset(capturePreset, devices); + return status == AudioSystem.SUCCESS; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + * Interface to be notified of changes in the preferred audio devices set for a given capture + * preset. + * <p>Note that this listener will only be invoked whenever + * {@link #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)} or + * {@link #clearPreferredDevicesForCapturePreset(int)} causes a change in + * preferred device. It will not be invoked directly after registration with + * {@link #addOnPreferredDevicesForCapturePresetChangedListener( + * Executor, OnPreferredDevicesForCapturePresetChangedListener)} + * to indicate which strategies had preferred devices at the time of registration.</p> + * @see #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes) + * @see #clearPreferredDevicesForCapturePreset(int) + * @see #getPreferredDevicesForCapturePreset(int) + */ + @SystemApi + public interface OnPreferredDevicesForCapturePresetChangedListener { + /** + * Called on the listener to indicate that the preferred audio devices for the given + * capture preset has changed. + * @param capturePreset the capture preset whose preferred device changed + * @param devices a list of newly set preferred audio devices + */ + void onPreferredDevicesForCapturePresetChanged( + int capturePreset, @NonNull List<AudioDeviceAttributes> devices); + } + + /** + * @hide + * Adds a listener for being notified of changes to the capture-preset-preferred audio device. + * @param executor + * @param listener + * @throws SecurityException if the caller doesn't hold the required permission + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public void addOnPreferredDevicesForCapturePresetChangedListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull OnPreferredDevicesForCapturePresetChangedListener listener) + throws SecurityException { + Objects.requireNonNull(executor); + Objects.requireNonNull(listener); + int status = addOnDevRoleForCapturePresetChangedListener( + executor, listener, AudioSystem.DEVICE_ROLE_PREFERRED); + if (status == AudioSystem.ERROR) { + // This must not happen + throw new RuntimeException("Unknown error happened"); + } + if (status == AudioSystem.BAD_VALUE) { + throw new IllegalArgumentException( + "attempt to call addOnPreferredDevicesForCapturePresetChangedListener() " + + "on a previously registered listener"); + } + } + + /** + * @hide + * Removes a previously added listener of changes to the capture-preset-preferred audio device. + * @param listener + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public void removeOnPreferredDevicesForCapturePresetChangedListener( + @NonNull OnPreferredDevicesForCapturePresetChangedListener listener) { + Objects.requireNonNull(listener); + int status = removeOnDevRoleForCapturePresetChangedListener( + listener, AudioSystem.DEVICE_ROLE_PREFERRED); + if (status == AudioSystem.ERROR) { + // This must not happen + throw new RuntimeException("Unknown error happened"); + } + if (status == AudioSystem.BAD_VALUE) { + throw new IllegalArgumentException( + "attempt to call removeOnPreferredDevicesForCapturePresetChangedListener() " + + "on an unregistered listener"); + } + } + + private <T> int addOnDevRoleForCapturePresetChangedListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull T listener, int deviceRole) { + Objects.requireNonNull(executor); + Objects.requireNonNull(listener); + DevRoleListeners<T> devRoleListeners = + (DevRoleListeners<T>) mDevRoleForCapturePresetListeners.get(deviceRole); + if (devRoleListeners == null) { + return AudioSystem.ERROR; + } + synchronized (devRoleListeners.mDevRoleListenersLock) { + if (devRoleListeners.hasDevRoleListener(listener)) { + return AudioSystem.BAD_VALUE; + } + // lazy initialization of the list of device role listener + if (devRoleListeners.mListenerInfos == null) { + devRoleListeners.mListenerInfos = new ArrayList<>(); + } + final int oldCbCount = devRoleListeners.mListenerInfos.size(); + devRoleListeners.mListenerInfos.add(new DevRoleListenerInfo<T>(executor, listener)); + if (oldCbCount == 0 && devRoleListeners.mListenerInfos.size() > 0) { + // register binder for callbacks + synchronized (mDevRoleForCapturePresetListenersLock) { + int deviceRoleListenerStatus = mDeviceRoleListenersStatus; + mDeviceRoleListenersStatus |= (1 << deviceRole); + if (deviceRoleListenerStatus != 0) { + // There are already device role changed listeners active. + return AudioSystem.SUCCESS; + } + if (mDevicesRoleForCapturePresetDispatcherStub == null) { + mDevicesRoleForCapturePresetDispatcherStub = + new CapturePresetDevicesRoleDispatcherStub(); + } + try { + getService().registerCapturePresetDevicesRoleDispatcher( + mDevicesRoleForCapturePresetDispatcherStub); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + } + return AudioSystem.SUCCESS; + } + + private <T> int removeOnDevRoleForCapturePresetChangedListener( + @NonNull T listener, int deviceRole) { + Objects.requireNonNull(listener); + DevRoleListeners<T> devRoleListeners = + (DevRoleListeners<T>) mDevRoleForCapturePresetListeners.get(deviceRole); + if (devRoleListeners == null) { + return AudioSystem.ERROR; + } + synchronized (devRoleListeners.mDevRoleListenersLock) { + if (!devRoleListeners.removeDevRoleListener(listener)) { + return AudioSystem.BAD_VALUE; + } + if (devRoleListeners.mListenerInfos.size() == 0) { + // unregister binder for callbacks + synchronized (mDevRoleForCapturePresetListenersLock) { + mDeviceRoleListenersStatus ^= (1 << deviceRole); + if (mDeviceRoleListenersStatus != 0) { + // There are some other device role changed listeners active. + return AudioSystem.SUCCESS; + } + try { + getService().unregisterCapturePresetDevicesRoleDispatcher( + mDevicesRoleForCapturePresetDispatcherStub); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + } + return AudioSystem.SUCCESS; + } + + private final Map<Integer, Object> mDevRoleForCapturePresetListeners = new HashMap<>(){{ + put(AudioSystem.DEVICE_ROLE_PREFERRED, + new DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener>()); + }}; + + private class DevRoleListenerInfo<T> { + final @NonNull Executor mExecutor; + final @NonNull T mListener; + DevRoleListenerInfo(Executor executor, T listener) { + mExecutor = executor; + mListener = listener; + } + } + + private class DevRoleListeners<T> { + private final Object mDevRoleListenersLock = new Object(); + @GuardedBy("mDevRoleListenersLock") + private @Nullable ArrayList<DevRoleListenerInfo<T>> mListenerInfos; + + @GuardedBy("mDevRoleListenersLock") + private @Nullable DevRoleListenerInfo<T> getDevRoleListenerInfo(T listener) { + if (mListenerInfos == null) { + return null; + } + for (DevRoleListenerInfo<T> listenerInfo : mListenerInfos) { + if (listenerInfo.mListener == listener) { + return listenerInfo; + } + } + return null; + } + + @GuardedBy("mDevRoleListenersLock") + private boolean hasDevRoleListener(T listener) { + return getDevRoleListenerInfo(listener) != null; + } + + @GuardedBy("mDevRoleListenersLock") + private boolean removeDevRoleListener(T listener) { + final DevRoleListenerInfo<T> infoToRemove = getDevRoleListenerInfo(listener); + if (infoToRemove != null) { + mListenerInfos.remove(infoToRemove); + return true; + } + return false; + } + } + + private final Object mDevRoleForCapturePresetListenersLock = new Object(); + /** + * Record if there is a listener added for device role change. If there is a listener added for + * a specified device role change, the bit at position `1 << device_role` is set. + */ + @GuardedBy("mDevRoleForCapturePresetListenersLock") + private int mDeviceRoleListenersStatus = 0; + @GuardedBy("mDevRoleForCapturePresetListenersLock") + private CapturePresetDevicesRoleDispatcherStub mDevicesRoleForCapturePresetDispatcherStub; + + private final class CapturePresetDevicesRoleDispatcherStub + extends ICapturePresetDevicesRoleDispatcher.Stub { + + @Override + public void dispatchDevicesRoleChanged( + int capturePreset, int role, List<AudioDeviceAttributes> devices) { + final Object listenersObj = mDevRoleForCapturePresetListeners.get(role); + if (listenersObj == null) { + return; + } + switch (role) { + case AudioSystem.DEVICE_ROLE_PREFERRED: { + final DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener> + listeners = + (DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener>) + listenersObj; + final ArrayList<DevRoleListenerInfo< + OnPreferredDevicesForCapturePresetChangedListener>> prefDevListeners; + synchronized (listeners.mDevRoleListenersLock) { + if (listeners.mListenerInfos.isEmpty()) { + return; + } + prefDevListeners = (ArrayList<DevRoleListenerInfo< + OnPreferredDevicesForCapturePresetChangedListener>>) + listeners.mListenerInfos.clone(); + } + final long ident = Binder.clearCallingIdentity(); + try { + for (DevRoleListenerInfo< + OnPreferredDevicesForCapturePresetChangedListener> info : + prefDevListeners) { + info.mExecutor.execute(() -> + info.mListener.onPreferredDevicesForCapturePresetChanged( + capturePreset, devices)); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } break; + default: + break; + } + } + } + + //==================================================================== // Offload query /** * Returns whether offloaded playback of an audio format is supported on the device. diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 243ec1f1fcd0..279ba0a55be0 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -27,6 +27,7 @@ import android.media.audiofx.AudioEffect; import android.media.audiopolicy.AudioMix; import android.telephony.TelephonyManager; import android.util.Log; +import android.util.Pair; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -524,6 +525,7 @@ public class AudioSystem /** @hide Media server died. see ErrorCallback */ public static final int AUDIO_STATUS_SERVER_DIED = 100; + // all accesses must be synchronized (AudioSystem.class) private static ErrorCallback sErrorCallback; /** @hide @@ -560,11 +562,9 @@ public class AudioSystem @UnsupportedAppUsage private static void errorCallbackFromNative(int error) { - ErrorCallback errorCallback = null; + ErrorCallback errorCallback; synchronized (AudioSystem.class) { - if (sErrorCallback != null) { - errorCallback = sErrorCallback; - } + errorCallback = sErrorCallback; } if (errorCallback != null) { errorCallback.onError(error); @@ -584,6 +584,7 @@ public class AudioSystem //keep in sync with include/media/AudioPolicy.h private final static int DYNAMIC_POLICY_EVENT_MIX_STATE_UPDATE = 0; + // all accesses must be synchronized (AudioSystem.class) private static DynamicPolicyCallback sDynPolicyCallback; /** @hide */ @@ -598,11 +599,9 @@ public class AudioSystem @UnsupportedAppUsage private static void dynamicPolicyCallbackFromNative(int event, String regId, int val) { - DynamicPolicyCallback cb = null; + DynamicPolicyCallback cb; synchronized (AudioSystem.class) { - if (sDynPolicyCallback != null) { - cb = sDynPolicyCallback; - } + cb = sDynPolicyCallback; } if (cb != null) { switch(event) { @@ -646,6 +645,7 @@ public class AudioSystem int activeSource, String packName); } + // all accesses must be synchronized (AudioSystem.class) private static AudioRecordingCallback sRecordingCallback; /** @hide */ @@ -678,7 +678,7 @@ public class AudioSystem int source, int portId, boolean silenced, int[] recordingFormat, AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] effects, int activeSource) { - AudioRecordingCallback cb = null; + AudioRecordingCallback cb; synchronized (AudioSystem.class) { cb = sRecordingCallback; } @@ -1756,6 +1756,134 @@ public class AudioSystem public static native int getDevicesForRoleAndStrategy( int strategy, int role, @NonNull List<AudioDeviceAttributes> devices); + // use case routing by capture preset + + private static Pair<int[], String[]> populateInputDevicesTypeAndAddress( + @NonNull List<AudioDeviceAttributes> devices) { + int[] types = new int[devices.size()]; + String[] addresses = new String[devices.size()]; + for (int i = 0; i < devices.size(); ++i) { + types[i] = devices.get(i).getInternalType(); + if (types[i] == AudioSystem.DEVICE_NONE) { + types[i] = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice( + devices.get(i).getType()); + } + addresses[i] = devices.get(i).getAddress(); + } + return new Pair<int[], String[]>(types, addresses); + } + + /** + * @hide + * Set devices as role for capture preset. + * @param capturePreset the capture preset to configure + * @param role the role of the devices + * @param devices the list of devices to be set as role for the given capture preset + * @return {@link #SUCCESS} if successfully set + */ + public static int setDevicesRoleForCapturePreset( + int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) { + if (devices.isEmpty()) { + return BAD_VALUE; + } + Pair<int[], String[]> typeAddresses = populateInputDevicesTypeAndAddress(devices); + return setDevicesRoleForCapturePreset( + capturePreset, role, typeAddresses.first, typeAddresses.second); + } + + /** + * @hide + * Set devices as role for capture preset. + * @param capturePreset the capture preset to configure + * @param role the role of the devices + * @param types all device types + * @param addresses all device addresses + * @return {@link #SUCCESS} if successfully set + */ + private static native int setDevicesRoleForCapturePreset( + int capturePreset, int role, @NonNull int[] types, @NonNull String[] addresses); + + /** + * @hide + * Add devices as role for capture preset. + * @param capturePreset the capture preset to configure + * @param role the role of the devices + * @param devices the list of devices to be added as role for the given capture preset + * @return {@link #SUCCESS} if successfully add + */ + public static int addDevicesRoleForCapturePreset( + int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) { + if (devices.isEmpty()) { + return BAD_VALUE; + } + Pair<int[], String[]> typeAddresses = populateInputDevicesTypeAndAddress(devices); + return addDevicesRoleForCapturePreset( + capturePreset, role, typeAddresses.first, typeAddresses.second); + } + + /** + * @hide + * Add devices as role for capture preset. + * @param capturePreset the capture preset to configure + * @param role the role of the devices + * @param types all device types + * @param addresses all device addresses + * @return {@link #SUCCESS} if successfully set + */ + private static native int addDevicesRoleForCapturePreset( + int capturePreset, int role, @NonNull int[] types, @NonNull String[] addresses); + + /** + * @hide + * Remove devices as role for the capture preset + * @param capturePreset the capture preset to configure + * @param role the role of the devices + * @param devices the devices to be removed + * @return {@link #SUCCESS} if successfully removed + */ + public static int removeDevicesRoleForCapturePreset( + int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) { + if (devices.isEmpty()) { + return BAD_VALUE; + } + Pair<int[], String[]> typeAddresses = populateInputDevicesTypeAndAddress(devices); + return removeDevicesRoleForCapturePreset( + capturePreset, role, typeAddresses.first, typeAddresses.second); + } + + /** + * @hide + * Remove devices as role for capture preset. + * @param capturePreset the capture preset to configure + * @param role the role of the devices + * @param types all device types + * @param addresses all device addresses + * @return {@link #SUCCESS} if successfully set + */ + private static native int removeDevicesRoleForCapturePreset( + int capturePreset, int role, @NonNull int[] types, @NonNull String[] addresses); + + /** + * @hide + * Remove all devices as role for the capture preset + * @param capturePreset the capture preset to configure + * @param role the role of the devices + * @return {@link #SUCCESS} if successfully removed + */ + public static native int clearDevicesRoleForCapturePreset(int capturePreset, int role); + + /** + * @hide + * Query previously set devices as role for a capture preset + * @param capturePreset the capture preset to query for + * @param role the role of the devices + * @param devices a list that will contain the devices of role + * @return {@link #SUCCESS} if there is a preferred device and it was successfully retrieved + * and written to the array + */ + public static native int getDevicesForRoleAndCapturePreset( + int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices); + // Items shared with audio service /** diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index ef8b0edb1fe5..85fb67df82d4 100755 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -26,6 +26,7 @@ import android.media.AudioRoutesInfo; import android.media.IAudioFocusDispatcher; import android.media.IAudioRoutesObserver; import android.media.IAudioServerStateDispatcher; +import android.media.ICapturePresetDevicesRoleDispatcher; import android.media.IPlaybackConfigDispatcher; import android.media.IRecordingConfigDispatcher; import android.media.IRingtonePlayer; @@ -307,4 +308,16 @@ interface IAudioService { // code via IAudioManager.h need to be added to the top section. oneway void setMultiAudioFocusEnabled(in boolean enabled); + + int setPreferredDevicesForCapturePreset( + in int capturePreset, in List<AudioDeviceAttributes> devices); + + int clearPreferredDevicesForCapturePreset(in int capturePreset); + + List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(in int capturePreset); + + void registerCapturePresetDevicesRoleDispatcher(ICapturePresetDevicesRoleDispatcher dispatcher); + + oneway void unregisterCapturePresetDevicesRoleDispatcher( + ICapturePresetDevicesRoleDispatcher dispatcher); } diff --git a/media/java/android/media/ICapturePresetDevicesRoleDispatcher.aidl b/media/java/android/media/ICapturePresetDevicesRoleDispatcher.aidl new file mode 100644 index 000000000000..5e03e632c4ff --- /dev/null +++ b/media/java/android/media/ICapturePresetDevicesRoleDispatcher.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.media.AudioDeviceAttributes; + +/** + * AIDL for AudioService to signal devices role for capture preset updates. + * + * {@hide} + */ +oneway interface ICapturePresetDevicesRoleDispatcher { + + void dispatchDevicesRoleChanged( + int capturePreset, int role, in List<AudioDeviceAttributes> devices); + +} diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 4198d7917932..1db02beaea1a 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -404,6 +404,32 @@ public class MediaRecorder implements AudioRouting, } } + /** + * @hide + * @param source An audio source to test + * @return true if the source is a valid one + */ + public static boolean isValidAudioSource(int source) { + switch(source) { + case AudioSource.MIC: + case AudioSource.VOICE_UPLINK: + case AudioSource.VOICE_DOWNLINK: + case AudioSource.VOICE_CALL: + case AudioSource.CAMCORDER: + case AudioSource.VOICE_RECOGNITION: + case AudioSource.VOICE_COMMUNICATION: + case AudioSource.REMOTE_SUBMIX: + case AudioSource.UNPROCESSED: + case AudioSource.VOICE_PERFORMANCE: + case AudioSource.ECHO_REFERENCE: + case AudioSource.RADIO_TUNER: + case AudioSource.HOTWORD: + return true; + default: + return false; + } + } + /** @hide */ public static final String toLogFriendlyAudioSource(int source) { switch(source) { diff --git a/media/tests/MediaTranscodingTest/build_and_run_unit_tests.sh b/media/tests/MediaTranscodingTest/build_and_run_unit_tests.sh index 43db3530a16b..c8fb3a63fe2c 100644 --- a/media/tests/MediaTranscodingTest/build_and_run_unit_tests.sh +++ b/media/tests/MediaTranscodingTest/build_and_run_unit_tests.sh @@ -31,6 +31,9 @@ do adb push --sync $file /data/user/0/com.android.mediatranscodingtest/cache/ done -echo "[==========] running real transcoding tests" +echo "[==========] running MediaTranscodeManagerTest" adb shell am instrument -e class com.android.mediatranscodingtest.MediaTranscodeManagerTest -w com.android.mediatranscodingtest/.MediaTranscodingTestRunner +echo "[==========] running MediaTranscodeManagerDiedTest" +adb shell am instrument -e class com.android.mediatranscodingtest.MediaTranscodeManagerDiedTest -w com.android.mediatranscodingtest/.MediaTranscodingTestRunner + diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerDiedTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerDiedTest.java new file mode 100644 index 000000000000..6ea573ea7d3c --- /dev/null +++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerDiedTest.java @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mediatranscodingtest; + +import android.content.ContentResolver; +import android.content.Context; +import android.media.MediaFormat; +import android.media.MediaTranscodeManager; +import android.media.MediaTranscodeManager.TranscodingJob; +import android.media.MediaTranscodeManager.TranscodingRequest; +import android.net.Uri; +import android.os.Bundle; +import android.os.FileUtils; +import android.os.ParcelFileDescriptor; +import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; + +import androidx.test.InstrumentationRegistry; +import androidx.test.uiautomator.UiDevice; + +import org.junit.Test; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +/* + * Service died tests for MediaTranscodeManager in the media framework. + * + * To run this test suite: + make frameworks/base/media/tests/MediaTranscodingTest + make mediatranscodingtest + + adb install -r testcases/mediatranscodingtest/arm64/mediatranscodingtest.apk + + adb shell am instrument -e class \ + com.android.mediatranscodingtest.MediaTranscodeManagerDiedTest \ + -w com.android.mediatranscodingtest/.MediaTranscodingTestRunner + * + */ +public class MediaTranscodeManagerDiedTest + extends ActivityInstrumentationTestCase2<MediaTranscodingTest> { + private static final String TAG = "MediaTranscodeManagerDiedTest"; + /** The time to wait for the transcode operation to complete before failing the test. */ + private static final int TRANSCODE_TIMEOUT_SECONDS = 10; + + /** Maximum number of retry to connect to the service. */ + private static final int CONNECT_SERVICE_RETRY_COUNT = 100; + + /** Interval between trying to reconnect to the service. */ + private static final int INTERVAL_CONNECT_SERVICE_RETRY_MS = 40; + + private Context mContext; + private MediaTranscodeManager mMediaTranscodeManager = null; + private Uri mSourceHEVCVideoUri = null; + private Uri mSourceAVCVideoUri = null; + private Uri mDestinationUri = null; + + // Setting for transcoding to H.264. + private static final String MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC; + private static final int BIT_RATE = 20000000; // 20Mbps + private static final int WIDTH = 1920; + private static final int HEIGHT = 1080; + + public MediaTranscodeManagerDiedTest() { + super("com.android.MediaTranscodeManagerTest", MediaTranscodingTest.class); + } + + // Copy the resource to cache. + private Uri resourceToUri(Context context, int resId, String name) throws IOException { + Uri resUri = new Uri.Builder() + .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) + .authority(context.getResources().getResourcePackageName(resId)) + .appendPath(context.getResources().getResourceTypeName(resId)) + .appendPath(context.getResources().getResourceEntryName(resId)) + .build(); + + Uri cacheUri = Uri.parse(ContentResolver.SCHEME_FILE + "://" + + mContext.getCacheDir().getAbsolutePath() + "/" + name); + + InputStream is = mContext.getResources().openRawResource(resId); + OutputStream os = mContext.getContentResolver().openOutputStream(cacheUri); + + FileUtils.copy(is, os); + + return cacheUri; + } + + private static Uri generateNewUri(Context context, String filename) { + File outFile = new File(context.getExternalCacheDir(), filename); + return Uri.fromFile(outFile); + } + + /** + * Creates a MediaFormat with the basic set of values. + */ + private static MediaFormat createMediaFormat() { + MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, WIDTH, HEIGHT); + format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE); + return format; + } + + private MediaTranscodeManager getManager() { + for (int count = 1; count <= CONNECT_SERVICE_RETRY_COUNT; count++) { + Log.d(TAG, "Trying to connect to service. Try count: " + count); + MediaTranscodeManager manager = mContext.getSystemService(MediaTranscodeManager.class); + if (manager != null) { + return manager; + } + try { + // Sleep a bit before retry. + Thread.sleep(INTERVAL_CONNECT_SERVICE_RETRY_MS); + } catch (InterruptedException ie) { + /* ignore */ + } + } + + throw new UnsupportedOperationException("Failed to acquire MediaTranscodeManager"); + } + + @Override + public void setUp() throws Exception { + Log.d(TAG, "setUp"); + super.setUp(); + + mContext = getInstrumentation().getContext(); + mMediaTranscodeManager = getManager(); + assertNotNull(mMediaTranscodeManager); + androidx.test.InstrumentationRegistry.registerInstance(getInstrumentation(), new Bundle()); + + // Setup source HEVC file uri. + mSourceHEVCVideoUri = resourceToUri(mContext, R.raw.VideoOnlyHEVC, "VideoOnlyHEVC.mp4"); + + // Setup source AVC file uri. + mSourceAVCVideoUri = resourceToUri(mContext, R.raw.VideoOnlyAVC, + "VideoOnlyAVC.mp4"); + + // Setup destination file. + mDestinationUri = generateNewUri(mContext, "transcoded.mp4"); + } + + @Override + public void tearDown() throws Exception { + super.tearDown(); + } + + // [[ $(adb shell whoami) == "root" ]] + private boolean checkIfRoot() { + try (ParcelFileDescriptor result = getInstrumentation().getUiAutomation() + .executeShellCommand("whoami"); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader( + new FileInputStream(result.getFileDescriptor())))) { + String line; + while ((line = bufferedReader.readLine()) != null) { + if (line.contains("root")) { + return true; + } + } + } catch (IOException ie) { + return false; + } + return false; + } + + private String executeShellCommand(String cmd) throws Exception { + return UiDevice.getInstance( + InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd); + } + + @Test + public void testHandleTranscoderServiceDied() throws Exception { + if (!checkIfRoot()) { + throw new AssertionError("must be root to run this test; try adb root?"); + } + + Semaphore transcodeCompleteSemaphore = new Semaphore(0); + Semaphore jobStartedSemaphore = new Semaphore(0); + + // Transcode a 15 seconds video, so that the transcoding is not finished when we kill the + // service. + Uri srcUri = Uri.parse(ContentResolver.SCHEME_FILE + "://" + + mContext.getCacheDir().getAbsolutePath() + "/longtest_15s.mp4"); + Uri destinationUri = Uri.parse(ContentResolver.SCHEME_FILE + "://" + + mContext.getCacheDir().getAbsolutePath() + "/HevcTranscode.mp4"); + + TranscodingRequest request = + new TranscodingRequest.Builder() + .setSourceUri(mSourceHEVCVideoUri) + .setDestinationUri(destinationUri) + .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) + .setPriority(MediaTranscodeManager.PRIORITY_REALTIME) + .setVideoTrackFormat(createMediaFormat()) + .build(); + Executor listenerExecutor = Executors.newSingleThreadExecutor(); + + Log.i(TAG, "transcoding to " + createMediaFormat()); + + TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor, + transcodingJob -> { + Log.d(TAG, "Transcoding completed with result: " + transcodingJob.getResult()); + transcodeCompleteSemaphore.release(); + }); + assertNotNull(job); + + AtomicInteger progressUpdateCount = new AtomicInteger(0); + + // Set progress update executor and use the same executor as result listener. + job.setOnProgressUpdateListener(listenerExecutor, + new TranscodingJob.OnProgressUpdateListener() { + @Override + public void onProgressUpdate(int newProgress) { + if (newProgress > 0) { + jobStartedSemaphore.release(); + } + } + }); + + // Wait for progress update so the job is in running state. + jobStartedSemaphore.tryAcquire(TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + assertTrue("Job is not running", job.getStatus() == TranscodingJob.STATUS_RUNNING); + + // Kills the service and expects receiving failure of the job. + executeShellCommand("pkill -f media.transcoding"); + + Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode result."); + boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire( + TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + assertTrue("Invalid job status", job.getStatus() == TranscodingJob.STATUS_FINISHED); + assertTrue("Invalid job result", job.getResult()== TranscodingJob.RESULT_ERROR); + } +} + diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java index a707c7a7955b..a54655d16dd3 100644 --- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java +++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java @@ -16,12 +16,15 @@ package com.android.mediatranscodingtest; +import static org.testng.Assert.assertThrows; + import android.content.ContentResolver; import android.content.Context; import android.media.MediaFormat; import android.media.MediaTranscodeManager; import android.media.MediaTranscodeManager.TranscodingJob; import android.media.MediaTranscodeManager.TranscodingRequest; +import android.media.TranscodingTestConfig; import android.net.Uri; import android.os.Bundle; import android.os.FileUtils; @@ -180,6 +183,227 @@ public class MediaTranscodeManagerTest super.tearDown(); } + + /** + * Verify that setting null destination uri will throw exception. + */ + @Test + public void testCreateTranscodingRequestWithNullDestinationUri() throws Exception { + assertThrows(IllegalArgumentException.class, () -> { + TranscodingRequest request = + new TranscodingRequest.Builder() + .setSourceUri(mSourceHEVCVideoUri) + .setDestinationUri(null) + .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) + .setPriority(MediaTranscodeManager.PRIORITY_REALTIME) + .setVideoTrackFormat(createMediaFormat()) + .build(); + }); + } + + /** + * Verify that setting null source uri will throw exception. + */ + @Test + public void testCreateTranscodingRequestWithNullSourceUri() throws Exception { + assertThrows(IllegalArgumentException.class, () -> { + TranscodingRequest request = + new TranscodingRequest.Builder() + .setSourceUri(null) + .setDestinationUri(mDestinationUri) + .setPriority(MediaTranscodeManager.PRIORITY_REALTIME) + .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) + .build(); + }); + } + + /** + * Verify that not setting source uri will throw exception. + */ + @Test + public void testCreateTranscodingRequestWithoutSourceUri() throws Exception { + assertThrows(UnsupportedOperationException.class, () -> { + TranscodingRequest request = + new TranscodingRequest.Builder() + .setDestinationUri(mDestinationUri) + .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) + .setPriority(MediaTranscodeManager.PRIORITY_REALTIME) + .setVideoTrackFormat(createMediaFormat()) + .build(); + }); + } + + /** + * Verify that not setting destination uri will throw exception. + */ + @Test + public void testCreateTranscodingRequestWithoutDestinationUri() throws Exception { + assertThrows(UnsupportedOperationException.class, () -> { + TranscodingRequest request = + new TranscodingRequest.Builder() + .setSourceUri(mSourceHEVCVideoUri) + .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) + .setPriority(MediaTranscodeManager.PRIORITY_REALTIME) + .setVideoTrackFormat(createMediaFormat()) + .build(); + }); + } + + /** + * Verify that setting image transcoding mode will throw exception. + */ + @Test + public void testCreateTranscodingRequestWithUnsupportedMode() throws Exception { + assertThrows(UnsupportedOperationException.class, () -> { + TranscodingRequest request = + new TranscodingRequest.Builder() + .setSourceUri(mSourceHEVCVideoUri) + .setDestinationUri(mDestinationUri) + .setType(MediaTranscodeManager.TRANSCODING_TYPE_IMAGE) + .setPriority(MediaTranscodeManager.PRIORITY_REALTIME) + .setVideoTrackFormat(createMediaFormat()) + .build(); + }); + } + + /** + * Verify that setting video transcoding without setting video format will throw exception. + */ + @Test + public void testCreateTranscodingRequestWithoutVideoFormat() throws Exception { + assertThrows(UnsupportedOperationException.class, () -> { + TranscodingRequest request = + new TranscodingRequest.Builder() + .setSourceUri(mSourceHEVCVideoUri) + .setDestinationUri(mDestinationUri) + .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) + .setPriority(MediaTranscodeManager.PRIORITY_REALTIME) + .build(); + }); + } + + void testTranscodingWithExpectResult(Uri srcUri, Uri dstUri, int expectedResult) + throws Exception { + Semaphore transcodeCompleteSemaphore = new Semaphore(0); + TranscodingTestConfig testConfig = new TranscodingTestConfig(); + testConfig.passThroughMode = true; + testConfig.processingTotalTimeMs = 300; // minimum time spent on transcoding. + + TranscodingRequest request = + new TranscodingRequest.Builder() + .setSourceUri(srcUri) + .setDestinationUri(dstUri) + .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) + .setPriority(MediaTranscodeManager.PRIORITY_REALTIME) + .setVideoTrackFormat(createMediaFormat()) + .setTestConfig(testConfig) + .build(); + Executor listenerExecutor = Executors.newSingleThreadExecutor(); + + TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor, + transcodingJob -> { + Log.d(TAG, "Transcoding completed with result: " + transcodingJob.getResult()); + assertTrue("Transcoding should failed.", + transcodingJob.getResult() == expectedResult); + transcodeCompleteSemaphore.release(); + }); + assertNotNull(job); + + if (job != null) { + Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to complete."); + boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire( + TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + assertTrue("Transcode failed to complete in time.", finishedOnTime); + } + + if (expectedResult == TranscodingJob.RESULT_SUCCESS) { + // Checks the destination file get generated. + File file = new File(dstUri.getPath()); + assertTrue("Failed to create destination file", file.exists()); + + // Removes the file. + file.delete(); + } + } + + // Tests transcoding from invalid a invalid and expects failure. + @Test + public void testTranscodingInvalidSrcUri() throws Exception { + Log.d(TAG, "Starting: testMediaTranscodeManager"); + + Uri invalidSrcUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + + mContext.getPackageName() + "/source.mp4"); + Log.d(TAG, "Transcoding from source: " + invalidSrcUri); + + // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/cache/temp.mp4 + // The full path of this file is: + // /data/user/0/com.android.mediatranscodingtest/cache/temp.mp4 + Uri destinationUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + + mContext.getPackageName() + "/temp.mp4"); + Log.d(TAG, "Transcoding to destination: " + destinationUri); + + testTranscodingWithExpectResult(invalidSrcUri, destinationUri, TranscodingJob.RESULT_ERROR); + } + + // Tests transcoding to a uri in res folder and expects failure as we could not write to res + // folder. + @Test + public void testTranscodingToResFolder() throws Exception { + Log.d(TAG, "Starting: testMediaTranscodeManager"); + + // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/cache/temp.mp4 + // The full path of this file is: + // /data/user/0/com.android.mediatranscodingtest/cache/temp.mp4 + Uri destinationUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + + mContext.getPackageName() + "/temp.mp4"); + Log.d(TAG, "Transcoding to destination: " + destinationUri); + + testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri, + TranscodingJob.RESULT_ERROR); + } + + // Tests transcoding to a uri in internal storage folder and expects success. + @Test + public void testTranscodingToCacheDir() throws Exception { + Log.d(TAG, "Starting: testMediaTranscodeManager"); + + // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/cache/temp.mp4 + // The full path of this file is: + // /data/user/0/com.android.mediatranscodingtest/cache/temp.mp4 + Uri destinationUri = Uri.parse(ContentResolver.SCHEME_FILE + "://" + + mContext.getCacheDir().getAbsolutePath() + "/temp.mp4"); + + testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri, + TranscodingJob.RESULT_SUCCESS); + } + + // Tests transcoding to a uri in internal files directory and expects success. + @Test + public void testTranscodingToInternalFilesDir() throws Exception { + Log.d(TAG, "Starting: testMediaTranscodeManager"); + + // Create a file Uri: + // file:///storage/emulated/0/Android/data/com.android.mediatranscodingtest/files/temp.mp4 + Uri destinationUri = Uri.fromFile(new File(mContext.getFilesDir(), "temp.mp4")); + Log.i(TAG, "Transcoding to files dir: " + destinationUri); + + testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri, + TranscodingJob.RESULT_SUCCESS); + } + + // Tests transcoding to a uri in external files directory and expects success. + @Test + public void testTranscodingToExternalFilesDir() throws Exception { + Log.d(TAG, "Starting: testMediaTranscodeManager"); + + // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/files/temp.mp4 + Uri destinationUri = Uri.fromFile(new File(mContext.getExternalFilesDir(null), "temp.mp4")); + Log.i(TAG, "Transcoding to files dir: " + destinationUri); + + testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri, + TranscodingJob.RESULT_SUCCESS); + } + @Test public void testTranscodingFromHevcToAvc() throws Exception { Semaphore transcodeCompleteSemaphore = new Semaphore(0); @@ -354,73 +578,5 @@ public class MediaTranscodeManagerTest return UiDevice.getInstance( InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd); } - - @Test - public void testHandleTranscoderServiceDied() throws Exception { - try { - if (!checkIfRoot()) { - throw new AssertionError("must be root to run this test; try adb root?"); - } else { - Log.i(TAG, "Device is root"); - } - } catch (IOException e) { - throw new AssertionError(e); - } - - Semaphore transcodeCompleteSemaphore = new Semaphore(0); - Semaphore jobStartedSemaphore = new Semaphore(0); - - // Transcode a 15 seconds video, so that the transcoding is not finished when we kill the - // service. - Uri srcUri = Uri.parse(ContentResolver.SCHEME_FILE + "://" - + mContext.getCacheDir().getAbsolutePath() + "/longtest_15s.mp4"); - Uri destinationUri = Uri.parse(ContentResolver.SCHEME_FILE + "://" - + mContext.getCacheDir().getAbsolutePath() + "/HevcTranscode.mp4"); - - TranscodingRequest request = - new TranscodingRequest.Builder() - .setSourceUri(mSourceHEVCVideoUri) - .setDestinationUri(destinationUri) - .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) - .setPriority(MediaTranscodeManager.PRIORITY_REALTIME) - .setVideoTrackFormat(createMediaFormat()) - .build(); - Executor listenerExecutor = Executors.newSingleThreadExecutor(); - - Log.i(TAG, "transcoding to " + createMediaFormat()); - - TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor, - transcodingJob -> { - Log.d(TAG, "Transcoding completed with result: " + transcodingJob.getResult()); - assertEquals(transcodingJob.getResult(), TranscodingJob.RESULT_ERROR); - transcodeCompleteSemaphore.release(); - }); - assertNotNull(job); - - AtomicInteger progressUpdateCount = new AtomicInteger(0); - - // Set progress update executor and use the same executor as result listener. - job.setOnProgressUpdateListener(listenerExecutor, - new TranscodingJob.OnProgressUpdateListener() { - @Override - public void onProgressUpdate(int newProgress) { - if (newProgress > 0) { - jobStartedSemaphore.release(); - } - } - }); - - // Wait for progress update so the job is in running state. - jobStartedSemaphore.tryAcquire(TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS); - assertTrue("Job is not running", job.getStatus() == TranscodingJob.STATUS_RUNNING); - - // Kills the service and expects receiving failure of the job. - executeShellCommand("pkill -f media.transcoding"); - - Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode result."); - boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire( - TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS); - assertTrue("Invalid job status", job.getStatus() == TranscodingJob.STATUS_FINISHED); - } } diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerWithMockServiceTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerWithMockServiceTest.java deleted file mode 100644 index 167e474eb143..000000000000 --- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerWithMockServiceTest.java +++ /dev/null @@ -1,503 +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.mediatranscodingtest; - -import static org.testng.Assert.assertThrows; - -import android.content.ContentResolver; -import android.content.Context; -import android.media.IMediaTranscodingService; -import android.media.ITranscodingClient; -import android.media.ITranscodingClientCallback; -import android.media.MediaFormat; -import android.media.MediaTranscodeManager; -import android.media.MediaTranscodeManager.TranscodingJob; -import android.media.MediaTranscodeManager.TranscodingRequest; -import android.media.TranscodingJobParcel; -import android.media.TranscodingRequestParcel; -import android.media.TranscodingResultParcel; -import android.media.TranscodingTestConfig; -import android.net.Uri; -import android.os.ParcelFileDescriptor; -import android.os.RemoteException; -import android.test.ActivityInstrumentationTestCase2; -import android.util.Log; - -import org.junit.Test; - -import java.io.File; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -/* - * Functional tests for MediaTranscodeManager in the media framework. - * The test uses a mock Transcoding service as backend to test the API functionality. - * - * To run this test suite: - make frameworks/base/media/tests/MediaTranscodingTest - make mediatranscodingtest - - adb install -r testcases/mediatranscodingtest/arm64/mediatranscodingtest.apk - - adb shell am instrument -e class \ - com.android.mediatranscodingtest.MediaTranscodeManagerWithMockServiceTest \ - -w com.android.mediatranscodingtest/.MediaTranscodingTestRunner - * - */ -public class MediaTranscodeManagerWithMockServiceTest - extends ActivityInstrumentationTestCase2<MediaTranscodingTest> { - private static final String TAG = "MediaTranscodeManagerWithMockServiceTest"; - /** The time to wait for the transcode operation to complete before failing the test. */ - private static final int TRANSCODE_TIMEOUT_SECONDS = 2; - private Context mContext; - private MediaTranscodeManager mMediaTranscodeManager = null; - private Uri mSourceHEVCVideoUri = null; - private Uri mDestinationUri = null; - // Use mock transcoding service for testing the api. - private MockTranscodingService mTranscodingService = null; - - // Setting for transcoding to H.264. - private static final String MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC; - private static final int BIT_RATE = 2000000; // 2Mbps - private static final int WIDTH = 1920; - private static final int HEIGHT = 1080; - - // A mock transcoding service that will take constant 300ms to process each transcoding job. - // Instead of doing real transcoding, it will return the dst uri directly. - class MockTranscodingService extends IMediaTranscodingService.Stub { - private final ScheduledExecutorService mJobScheduler = Executors.newScheduledThreadPool(1); - private int mNumOfClients = 0; - private AtomicInteger mJobId = new AtomicInteger(); - - // A runnable that will process the job. - private class ProcessingJobRunnable implements Runnable { - private TranscodingJobParcel mJob; - private ITranscodingClientCallback mCallback; - private ConcurrentMap<Integer, ScheduledFuture<?>> mJobMap; - - ProcessingJobRunnable(ITranscodingClientCallback callback, - TranscodingJobParcel job, - ConcurrentMap<Integer, ScheduledFuture<?>> jobMap) { - mJob = job; - mCallback = callback; - mJobMap = jobMap; - } - - @Override - public void run() { - Log.d(TAG, "Start to process job " + mJob.jobId); - TranscodingResultParcel result = new TranscodingResultParcel(); - try { - // Try to open the source fd. - ParcelFileDescriptor sourceFd = mCallback.openFileDescriptor( - mJob.request.sourceFilePath, "r"); - if (sourceFd == null) { - Log.d(TAG, "Failed to open sourceFd"); - // TODO(hkuang): Pass the correct error code. - mCallback.onTranscodingFailed(mJob.jobId, 1); - return; - } else { - Log.d(TAG, "Successfully open sourceFd"); - } - - // Try to write to the destination fd. - ParcelFileDescriptor destinationFd = mCallback.openFileDescriptor( - mJob.request.destinationFilePath, "w"); - if (destinationFd == null) { - Log.d(TAG, "Failed to open destinationFd"); - // TODO(hkuang): Pass the correct error code. - mCallback.onTranscodingFailed(mJob.jobId, 1); - return; - } else { - Log.d(TAG, "Successfully open destinationFd"); - } - - mCallback.onTranscodingFinished(mJob.jobId, result); - // Removes the job from job map. - mJobMap.remove(mJob.jobId); - } catch (RemoteException re) { - Log.e(TAG, "Failed to callback to client"); - } - } - } - - @Override - public ITranscodingClient registerClient(ITranscodingClientCallback callback, - String clientName, String opPackageName, int clientUid, int clientPid) - throws RemoteException { - Log.d(TAG, "MockTranscodingService creates one client"); - - ITranscodingClient client = new ITranscodingClient.Stub() { - private final ConcurrentMap<Integer, ScheduledFuture<?>> mPendingTranscodingJobs = - new ConcurrentHashMap<Integer, ScheduledFuture<?>>(); - - @Override - public boolean submitRequest(TranscodingRequestParcel inRequest, - TranscodingJobParcel outjob) { - Log.d(TAG, "Mock client gets submitRequest"); - try { - outjob.request = inRequest; - outjob.jobId = mJobId.getAndIncrement(); - Log.i(TAG, "Generate new job " + outjob.jobId); - Log.i(TAG, "Source Uri " + inRequest.sourceFilePath); - Log.i(TAG, "Destination Uri " + inRequest.destinationFilePath); - - // Schedules the job to run after inRequest.processingDelayMs. - ScheduledFuture<?> transcodingFuture = mJobScheduler.schedule( - new ProcessingJobRunnable(callback, outjob, - mPendingTranscodingJobs), - inRequest.testConfig == null ? 0 - : inRequest.testConfig.processingTotalTimeMs, - TimeUnit.MILLISECONDS); - mPendingTranscodingJobs.put(outjob.jobId, transcodingFuture); - } catch (RejectedExecutionException e) { - Log.e(TAG, "Failed to schedule transcoding job: " + e); - return false; - } - - return true; - } - - @Override - public boolean cancelJob(int jobId) throws RemoteException { - Log.d(TAG, "Mock client gets cancelJob " + jobId); - // Cancels the job is still in the mPendingTranscodingJobs. - if (mPendingTranscodingJobs.containsKey(jobId)) { - // Cancel the future task for transcoding. - mPendingTranscodingJobs.get(jobId).cancel(true); - - // Remove the job from the mPendingTranscodingJobs. - mPendingTranscodingJobs.remove(jobId); - return true; - } - return false; - } - - @Override - public boolean getJobWithId(int jobId, TranscodingJobParcel job) - throws RemoteException { - // This will be implemented this if needed in the test. - return true; - } - - @Override - public void unregister() throws RemoteException { - Log.d(TAG, "Mock client gets unregister"); - // This will be implemented this if needed in the test. - mNumOfClients--; - } - }; - mNumOfClients++; - return client; - } - - @Override - public int getNumOfClients() throws RemoteException { - return mNumOfClients; - } - } - - public MediaTranscodeManagerWithMockServiceTest() { - super("com.android.MediaTranscodeManagerWithMockServiceTest", MediaTranscodingTest.class); - } - - private static Uri resourceToUri(Context context, int resId) { - Uri uri = new Uri.Builder() - .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) - .authority(context.getResources().getResourcePackageName(resId)) - .appendPath(context.getResources().getResourceTypeName(resId)) - .appendPath(context.getResources().getResourceEntryName(resId)) - .build(); - return uri; - } - - private static Uri generateNewUri(Context context, String filename) { - File outFile = new File(context.getExternalCacheDir(), filename); - return Uri.fromFile(outFile); - } - - // Generates a invalid uri which will let the mock service return transcoding failure. - private static Uri generateInvalidTranscodingUri(Context context) { - File outFile = new File(context.getExternalCacheDir(), "InvalidUri.mp4"); - return Uri.fromFile(outFile); - } - - /** - * Creates a MediaFormat with the basic set of values. - */ - private static MediaFormat createMediaFormat() { - MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, WIDTH, HEIGHT); - format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE); - return format; - } - - @Override - public void setUp() throws Exception { - Log.d(TAG, "setUp"); - super.setUp(); - mTranscodingService = new MockTranscodingService(); - mContext = getInstrumentation().getContext(); - mMediaTranscodeManager = mContext.getSystemService(MediaTranscodeManager.class); - assertNotNull(mMediaTranscodeManager); - - // Setup source HEVC file uri. - mSourceHEVCVideoUri = resourceToUri(mContext, R.raw.VideoOnlyHEVC); - - // Setup destination file. - mDestinationUri = generateNewUri(mContext, "transcoded.mp4"); - } - - @Override - public void tearDown() throws Exception { - super.tearDown(); - } - - /** - * Verify that setting null destination uri will throw exception. - */ - @Test - public void testCreateTranscodingRequestWithNullDestinationUri() throws Exception { - assertThrows(IllegalArgumentException.class, () -> { - TranscodingRequest request = - new TranscodingRequest.Builder() - .setSourceUri(mSourceHEVCVideoUri) - .setDestinationUri(null) - .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) - .setPriority(MediaTranscodeManager.PRIORITY_REALTIME) - .setVideoTrackFormat(createMediaFormat()) - .build(); - }); - } - - /** - * Verify that setting null source uri will throw exception. - */ - @Test - public void testCreateTranscodingRequestWithNullSourceUri() throws Exception { - assertThrows(IllegalArgumentException.class, () -> { - TranscodingRequest request = - new TranscodingRequest.Builder() - .setSourceUri(null) - .setDestinationUri(mDestinationUri) - .setPriority(MediaTranscodeManager.PRIORITY_REALTIME) - .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) - .build(); - }); - } - - /** - * Verify that not setting source uri will throw exception. - */ - @Test - public void testCreateTranscodingRequestWithoutSourceUri() throws Exception { - assertThrows(UnsupportedOperationException.class, () -> { - TranscodingRequest request = - new TranscodingRequest.Builder() - .setDestinationUri(mDestinationUri) - .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) - .setPriority(MediaTranscodeManager.PRIORITY_REALTIME) - .setVideoTrackFormat(createMediaFormat()) - .build(); - }); - } - - /** - * Verify that not setting destination uri will throw exception. - */ - @Test - public void testCreateTranscodingRequestWithoutDestinationUri() throws Exception { - assertThrows(UnsupportedOperationException.class, () -> { - TranscodingRequest request = - new TranscodingRequest.Builder() - .setSourceUri(mSourceHEVCVideoUri) - .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) - .setPriority(MediaTranscodeManager.PRIORITY_REALTIME) - .setVideoTrackFormat(createMediaFormat()) - .build(); - }); - } - - /** - * Verify that setting image transcoding mode will throw exception. - */ - @Test - public void testCreateTranscodingRequestWithUnsupportedMode() throws Exception { - assertThrows(UnsupportedOperationException.class, () -> { - TranscodingRequest request = - new TranscodingRequest.Builder() - .setSourceUri(mSourceHEVCVideoUri) - .setDestinationUri(mDestinationUri) - .setType(MediaTranscodeManager.TRANSCODING_TYPE_IMAGE) - .setPriority(MediaTranscodeManager.PRIORITY_REALTIME) - .setVideoTrackFormat(createMediaFormat()) - .build(); - }); - } - - /** - * Verify that setting video transcoding without setting video format will throw exception. - */ - @Test - public void testCreateTranscodingRequestWithoutVideoFormat() throws Exception { - assertThrows(UnsupportedOperationException.class, () -> { - TranscodingRequest request = - new TranscodingRequest.Builder() - .setSourceUri(mSourceHEVCVideoUri) - .setDestinationUri(mDestinationUri) - .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) - .setPriority(MediaTranscodeManager.PRIORITY_REALTIME) - .build(); - }); - } - - void testTranscodingWithExpectResult(Uri srcUri, Uri dstUri, int expectedResult) - throws Exception { - Semaphore transcodeCompleteSemaphore = new Semaphore(0); - TranscodingTestConfig testConfig = new TranscodingTestConfig(); - testConfig.passThroughMode = true; - testConfig.processingTotalTimeMs = 300; // minimum time spent on transcoding. - - TranscodingRequest request = - new TranscodingRequest.Builder() - .setSourceUri(srcUri) - .setDestinationUri(dstUri) - .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) - .setPriority(MediaTranscodeManager.PRIORITY_REALTIME) - .setVideoTrackFormat(createMediaFormat()) - .setTestConfig(testConfig) - .build(); - Executor listenerExecutor = Executors.newSingleThreadExecutor(); - - TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor, - transcodingJob -> { - Log.d(TAG, "Transcoding completed with result: " + transcodingJob.getResult()); - assertTrue("Transcoding should failed.", - transcodingJob.getResult() == expectedResult); - transcodeCompleteSemaphore.release(); - }); - assertNotNull(job); - - if (job != null) { - Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to complete."); - boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire( - TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS); - assertTrue("Transcode failed to complete in time.", finishedOnTime); - } - - if (expectedResult == TranscodingJob.RESULT_SUCCESS) { - // Checks the destination file get generated. - File file = new File(dstUri.getPath()); - assertTrue("Failed to create destination file", file.exists()); - - // Removes the file. - file.delete(); - } - } - - // Tests transcoding from invalid a invalid and expects failure. - @Test - public void testTranscodingInvalidSrcUri() throws Exception { - Log.d(TAG, "Starting: testMediaTranscodeManager"); - - Uri invalidSrcUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" - + mContext.getPackageName() + "/source.mp4"); - Log.d(TAG, "Transcoding from source: " + invalidSrcUri); - - // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/cache/temp.mp4 - // The full path of this file is: - // /data/user/0/com.android.mediatranscodingtest/cache/temp.mp4 - Uri destinationUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" - + mContext.getPackageName() + "/temp.mp4"); - Log.d(TAG, "Transcoding to destination: " + destinationUri); - - testTranscodingWithExpectResult(invalidSrcUri, destinationUri, TranscodingJob.RESULT_ERROR); - } - - // Tests transcoding to a uri in res folder and expects failure as we could not write to res - // folder. - @Test - public void testTranscodingToResFolder() throws Exception { - Log.d(TAG, "Starting: testMediaTranscodeManager"); - - // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/cache/temp.mp4 - // The full path of this file is: - // /data/user/0/com.android.mediatranscodingtest/cache/temp.mp4 - Uri destinationUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" - + mContext.getPackageName() + "/temp.mp4"); - Log.d(TAG, "Transcoding to destination: " + destinationUri); - - testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri, - TranscodingJob.RESULT_ERROR); - } - - // Tests transcoding to a uri in internal storage folder and expects success. - @Test - public void testTranscodingToCacheDir() throws Exception { - Log.d(TAG, "Starting: testMediaTranscodeManager"); - - // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/cache/temp.mp4 - // The full path of this file is: - // /data/user/0/com.android.mediatranscodingtest/cache/temp.mp4 - Uri destinationUri = Uri.parse(ContentResolver.SCHEME_FILE + "://" - + mContext.getCacheDir().getAbsolutePath() + "/temp.mp4"); - - testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri, - TranscodingJob.RESULT_SUCCESS); - } - - // Tests transcoding to a uri in internal files directory and expects success. - @Test - public void testTranscodingToInternalFilesDir() throws Exception { - Log.d(TAG, "Starting: testMediaTranscodeManager"); - - // Create a file Uri: - // file:///storage/emulated/0/Android/data/com.android.mediatranscodingtest/files/temp.mp4 - Uri destinationUri = Uri.fromFile(new File(mContext.getFilesDir(), "temp.mp4")); - Log.i(TAG, "Transcoding to files dir: " + destinationUri); - - testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri, - TranscodingJob.RESULT_SUCCESS); - } - - // Tests transcoding to a uri in external files directory and expects success. - @Test - public void testTranscodingToExternalFilesDir() throws Exception { - Log.d(TAG, "Starting: testMediaTranscodeManager"); - - // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/files/temp.mp4 - Uri destinationUri = Uri.fromFile(new File(mContext.getExternalFilesDir(null), "temp.mp4")); - Log.i(TAG, "Transcoding to files dir: " + destinationUri); - - testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri, - TranscodingJob.RESULT_SUCCESS); - } - - @Test - public void testTranscodingOneVideo() throws Exception { - Log.d(TAG, "Starting: testMediaTranscodeManager"); - testTranscodingWithExpectResult(mSourceHEVCVideoUri, mDestinationUri, - TranscodingJob.RESULT_SUCCESS); - } -} diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java index 53b23927fc64..bd1551f352f4 100644 --- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java +++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java @@ -36,8 +36,8 @@ public class MediaTranscodingTestRunner extends InstrumentationTestRunner { @Override public TestSuite getAllTests() { TestSuite suite = new InstrumentationTestSuite(this); + suite.addTestSuite(MediaTranscodeManagerDiedTest.class); suite.addTestSuite(MediaTranscodeManagerTest.class); - suite.addTestSuite(MediaTranscodeManagerWithMockServiceTest.class); suite.addTestSuite(MediaTranscodingBenchmark.class); return suite; } diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt index f64be2b1ff03..39ba60c23e7a 100644 --- a/non-updatable-api/system-current.txt +++ b/non-updatable-api/system-current.txt @@ -4139,8 +4139,10 @@ package android.media { public class AudioManager { method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException; + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForCapturePresetChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener) throws java.lang.SecurityException; method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener) throws java.lang.SecurityException; method public void clearAudioServerStateCallback(); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean clearPreferredDevicesForCapturePreset(int); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy); method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioProductStrategy> getAudioProductStrategies(); @@ -4151,6 +4153,7 @@ package android.media { method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDeviceAttributes getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy); + method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getSupportedSystemUsages(); method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); @@ -4159,6 +4162,7 @@ package android.media { method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy); method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForCapturePresetChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean removePreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException; @@ -4168,6 +4172,7 @@ package android.media { method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForCapturePreset(int, @NonNull android.media.AudioDeviceAttributes); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]); @@ -4197,6 +4202,10 @@ package android.media { method @Deprecated public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDeviceAttributes); } + public static interface AudioManager.OnPreferredDevicesForCapturePresetChangedListener { + method public void onPreferredDevicesForCapturePresetChanged(int, @NonNull java.util.List<android.media.AudioDeviceAttributes>); + } + public static interface AudioManager.OnPreferredDevicesForStrategyChangedListener { method public void onPreferredDevicesForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>); } diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java index 616e56288392..3def945dd03c 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java @@ -28,7 +28,6 @@ import com.android.systemui.car.window.SystemUIOverlayWindowManager; import com.android.systemui.globalactions.GlobalActionsComponent; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.dagger.KeyguardModule; -import com.android.systemui.onehanded.OneHandedUI; import com.android.systemui.power.PowerUI; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsModule; @@ -94,12 +93,6 @@ public abstract class CarSystemUIBinder { @ClassKey(LatencyTester.class) public abstract SystemUI bindLatencyTester(LatencyTester sysui); - /** Inject into OneHandedUI. */ - @Binds - @IntoMap - @ClassKey(OneHandedUI.class) - public abstract SystemUI bindOneHandedUI(OneHandedUI sysui); - /** Inject into PowerUI. */ @Binds @IntoMap diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java index f8952ace3cb3..4d31ce97e8b7 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java @@ -334,6 +334,11 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog throw new IOException( "Failed to start installation with requested size: " + mUserdataSize); } + // Reset installation session and verify that installation completes successfully. + mInstallationSession = null; + if (!mDynSystem.closePartition()) { + throw new IOException("Failed to complete partition installation: userdata"); + } } private void installImages() @@ -503,6 +508,12 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog imageValidationThrowOrWarning(new KeyRevokedException(publicKey)); } } + + // Reset installation session and verify that installation completes successfully. + mInstallationSession = null; + if (!mDynSystem.closePartition()) { + throw new IOException("Failed to complete partition installation: " + partitionName); + } } private static String toHexString(byte[] bytes) { diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index b2808061586b..2fd46d94d5cc 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -13,6 +13,7 @@ import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; +import android.graphics.Canvas; import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; @@ -29,6 +30,10 @@ import android.telephony.AccessNetworkConstants; import android.telephony.NetworkRegistrationInfo; import android.telephony.ServiceState; +import androidx.annotation.NonNull; +import androidx.core.graphics.drawable.RoundedBitmapDrawable; +import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; + import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.UserIcons; import com.android.launcher3.icons.IconFactory; @@ -504,4 +509,25 @@ public class Utils { == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING); return !isInIwlan; } + + /** + * Returns a bitmap with rounded corner. + * + * @param context application context. + * @param source bitmap to apply round corner. + * @param cornerRadius corner radius value. + */ + public static Bitmap convertCornerRadiusBitmap(@NonNull Context context, + @NonNull Bitmap source, @NonNull float cornerRadius) { + final Bitmap roundedBitmap = Bitmap.createBitmap(source.getWidth(), source.getHeight(), + Bitmap.Config.ARGB_8888); + final RoundedBitmapDrawable drawable = + RoundedBitmapDrawableFactory.create(context.getResources(), source); + drawable.setAntiAlias(true); + drawable.setCornerRadius(cornerRadius); + final Canvas canvas = new Canvas(roundedBitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + return roundedBitmap; + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java index 68f72896c251..8f8f859d1ada 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java @@ -18,6 +18,7 @@ import android.util.Log; import android.util.Pair; import androidx.annotation.DrawableRes; +import androidx.core.graphics.drawable.IconCompat; import com.android.settingslib.R; import com.android.settingslib.widget.AdaptiveIcon; @@ -216,6 +217,23 @@ public class BluetoothUtils { } /** + * Create an Icon pointing to a drawable. + */ + public static IconCompat createIconWithDrawable(Drawable drawable) { + Bitmap bitmap; + if (drawable instanceof BitmapDrawable) { + bitmap = ((BitmapDrawable) drawable).getBitmap(); + } else { + final int width = drawable.getIntrinsicWidth(); + final int height = drawable.getIntrinsicHeight(); + bitmap = createBitmap(drawable, + width > 0 ? width : 1, + height > 0 ? height : 1); + } + return IconCompat.createWithBitmap(bitmap); + } + + /** * Build device icon with advanced outline */ public static Drawable buildAdvancedDrawable(Context context, Drawable drawable) { diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 6db3d0b8dff2..e3f75e3e0b34 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -390,7 +390,7 @@ </activity-alias> <activity - android:name=".stackdivider.ForcedResizableInfoActivity" + android:name="com.android.wm.shell.splitscreen.ForcedResizableInfoActivity" android:theme="@style/ForcedResizableTheme" android:excludeFromRecents="true" android:stateNotNeeded="true" diff --git a/packages/SystemUI/README.md b/packages/SystemUI/README.md index bd93c396aa16..ee8d02301d5d 100644 --- a/packages/SystemUI/README.md +++ b/packages/SystemUI/README.md @@ -111,10 +111,6 @@ Plays ringtones. Shows UI for keyboard shortcuts (triggered by keyboard shortcut). -### [com.android.systemui.onehanded.OneHandedUI](/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java) - -Shows the overlay controls when One handed is triggered. - ### [com.android.systemui.shortcut.ShortcutKeyDispatcher](/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java) Dispatches shortcut to System UI components. diff --git a/packages/SystemUI/res/drawable-mcc310-mnc004/ic_5g_plus_mobiledata.xml b/packages/SystemUI/res/drawable-mcc310-mnc004/ic_5g_plus_mobiledata.xml new file mode 100644 index 000000000000..998db3b44b19 --- /dev/null +++ b/packages/SystemUI/res/drawable-mcc310-mnc004/ic_5g_plus_mobiledata.xml @@ -0,0 +1,36 @@ +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="22" + android:viewportHeight="17" + android:width="22dp" + android:height="17dp"> + <group> + <group> + <path android:fillColor="#FF000000" + android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/> + <path android:fillColor="#FF000000" + android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/> + </group> + <group> + <path android:fillColor="#FF000000" + android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/> + </group> + <path android:fillColor="#FF000000" + android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/> + </group> +</vector> + diff --git a/packages/SystemUI/res/drawable-mcc311-mnc480/ic_5g_plus_mobiledata.xml b/packages/SystemUI/res/drawable-mcc311-mnc480/ic_5g_plus_mobiledata.xml new file mode 100644 index 000000000000..998db3b44b19 --- /dev/null +++ b/packages/SystemUI/res/drawable-mcc311-mnc480/ic_5g_plus_mobiledata.xml @@ -0,0 +1,36 @@ +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="22" + android:viewportHeight="17" + android:width="22dp" + android:height="17dp"> + <group> + <group> + <path android:fillColor="#FF000000" + android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/> + <path android:fillColor="#FF000000" + android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/> + </group> + <group> + <path android:fillColor="#FF000000" + android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/> + </group> + <path android:fillColor="#FF000000" + android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/> + </group> +</vector> + diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml index ed870f8bb2ef..170f2c4e0bea 100644 --- a/packages/SystemUI/res/layout/media_view.xml +++ b/packages/SystemUI/res/layout/media_view.xml @@ -166,8 +166,7 @@ android:layout_height="wrap_content" android:clickable="true" android:maxHeight="@dimen/qs_media_enabled_seekbar_height" - android:paddingTop="16dp" - android:paddingBottom="16dp" + android:paddingVertical="@dimen/qs_media_enabled_seekbar_vertical_padding" android:thumbTint="@color/media_primary_text" android:progressTint="@color/media_seekbar_progress" android:progressBackgroundTint="@color/media_disabled" diff --git a/packages/SystemUI/res/values-mcc310-mnc004/strings.xml b/packages/SystemUI/res/values-mcc310-mnc004/strings.xml new file mode 100644 index 000000000000..f8ed0c01fa83 --- /dev/null +++ b/packages/SystemUI/res/values-mcc310-mnc004/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] --> + <string name="data_connection_5g_plus" translatable="false">5G UW</string> +</resources> diff --git a/packages/SystemUI/res/values-mcc311-mnc480/strings.xml b/packages/SystemUI/res/values-mcc311-mnc480/strings.xml new file mode 100644 index 000000000000..f8ed0c01fa83 --- /dev/null +++ b/packages/SystemUI/res/values-mcc311-mnc480/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] --> + <string name="data_connection_5g_plus" translatable="false">5G UW</string> +</resources> diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml index 5d20564a3ab8..be66320975d9 100644 --- a/packages/SystemUI/res/values-sw600dp/config.xml +++ b/packages/SystemUI/res/values-sw600dp/config.xml @@ -29,9 +29,6 @@ <!-- Nav bar button default ordering/layout --> <string name="config_navBarLayout" translatable="false">left;back,home,recent;right</string> - <!-- Animation duration when using long press on recents to dock --> - <integer name="long_press_dock_anim_duration">290</integer> - <!-- orientation of the dead zone when touches have recently occurred elsewhere on screen --> <integer name="navigation_bar_deadzone_orientation">0</integer> diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml index 4a94038f4533..66304013da46 100644 --- a/packages/SystemUI/res/values-television/config.xml +++ b/packages/SystemUI/res/values-television/config.xml @@ -39,7 +39,6 @@ <item>com.android.systemui.SizeCompatModeActivityController</item> <item>com.android.systemui.statusbar.notification.InstantAppNotifier</item> <item>com.android.systemui.toast.ToastUI</item> - <item>com.android.systemui.onehanded.OneHandedUI</item> <item>com.android.systemui.wmshell.WMShell</item> </string-array> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 7faa2a44bb6c..1c3fba2abacd 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -161,9 +161,6 @@ <!-- The number of milliseconds to extend ambient pulse by when prompted (e.g. on touch) --> <integer name="ambient_notification_extension_time">10000</integer> - <!-- Animation duration when using long press on recents to dock --> - <integer name="long_press_dock_anim_duration">250</integer> - <!-- Whether to enable KeyguardService or not --> <bool name="config_enableKeyguardService">true</bool> @@ -320,7 +317,6 @@ <item>com.android.systemui.accessibility.WindowMagnification</item> <item>com.android.systemui.accessibility.SystemActions</item> <item>com.android.systemui.toast.ToastUI</item> - <item>com.android.systemui.onehanded.OneHandedUI</item> <item>com.android.systemui.wmshell.WMShell</item> </string-array> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 7b80f00e8333..76c61fb6e1e5 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -954,12 +954,6 @@ <dimen name="fab_elevation">12dp</dimen> <dimen name="fab_press_translation_z">9dp</dimen> - <!-- How high we lift the divider when touching --> - <dimen name="docked_stack_divider_lift_elevation">4dp</dimen> - - <dimen name="docked_divider_handle_width">16dp</dimen> - <dimen name="docked_divider_handle_height">2dp</dimen> - <dimen name="battery_detail_graph_space_top">27dp</dimen> <dimen name="battery_detail_graph_space_bottom">27dp</dimen> @@ -1261,6 +1255,8 @@ <dimen name="qs_footer_horizontal_margin">22dp</dimen> <dimen name="qs_media_disabled_seekbar_height">1dp</dimen> <dimen name="qs_media_enabled_seekbar_height">3dp</dimen> + <dimen name="qs_media_enabled_seekbar_vertical_padding">15dp</dimen> + <dimen name="qs_media_disabled_seekbar_vertical_padding">16dp</dimen> <!-- Window magnification --> <dimen name="magnification_border_drag_size">35dp</dimen> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index a56f6f56836a..2f018b9239b5 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -103,13 +103,6 @@ <item type="id" name="contains_transformed_view" /> <item type="id" name="is_clicked_heads_up_tag" /> - <!-- Accessibility actions for the docked stack divider --> - <item type="id" name="action_move_tl_full" /> - <item type="id" name="action_move_tl_70" /> - <item type="id" name="action_move_tl_50" /> - <item type="id" name="action_move_tl_30" /> - <item type="id" name="action_move_rb_full" /> - <item type="id" name="bottom_roundess_animator_tag"/> <item type="id" name="bottom_roundess_animator_start_tag"/> <item type="id" name="bottom_roundess_animator_end_tag"/> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index bfa7532349aa..e58bf3bad795 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2260,31 +2260,6 @@ <!-- SysUI Tuner: Other section --> <string name="other">Other</string> - <!-- Accessibility label for the divider that separates the windows in split-screen mode [CHAR LIMIT=NONE] --> - <string name="accessibility_divider">Split-screen divider</string> - - <!-- Accessibility action for moving docked stack divider to make the left screen full screen [CHAR LIMIT=NONE] --> - <string name="accessibility_action_divider_left_full">Left full screen</string> - <!-- Accessibility action for moving docked stack divider to make the left screen 70% [CHAR LIMIT=NONE] --> - <string name="accessibility_action_divider_left_70">Left 70%</string> - <!-- Accessibility action for moving docked stack divider to make the left screen 50% [CHAR LIMIT=NONE] --> - <string name="accessibility_action_divider_left_50">Left 50%</string> - <!-- Accessibility action for moving docked stack divider to make the left screen 30% [CHAR LIMIT=NONE] --> - <string name="accessibility_action_divider_left_30">Left 30%</string> - <!-- Accessibility action for moving docked stack divider to make the right screen full screen [CHAR LIMIT=NONE] --> - <string name="accessibility_action_divider_right_full">Right full screen</string> - - <!-- Accessibility action for moving docked stack divider to make the top screen full screen [CHAR LIMIT=NONE] --> - <string name="accessibility_action_divider_top_full">Top full screen</string> - <!-- Accessibility action for moving docked stack divider to make the top screen 70% [CHAR LIMIT=NONE] --> - <string name="accessibility_action_divider_top_70">Top 70%</string> - <!-- Accessibility action for moving docked stack divider to make the top screen 50% [CHAR LIMIT=NONE] --> - <string name="accessibility_action_divider_top_50">Top 50%</string> - <!-- Accessibility action for moving docked stack divider to make the top screen 30% [CHAR LIMIT=NONE] --> - <string name="accessibility_action_divider_top_30">Top 30%</string> - <!-- Accessibility action for moving docked stack divider to make the bottom screen full screen [CHAR LIMIT=NONE] --> - <string name="accessibility_action_divider_bottom_full">Bottom full screen</string> - <!-- Accessibility description of a QS tile while editing positions [CHAR LIMIT=NONE] --> <string name="accessibility_qs_edit_tile_label">Position <xliff:g id="position" example="2">%1$d</xliff:g>, <xliff:g id="tile_name" example="Wi-Fi">%2$s</xliff:g>. Double tap to edit.</string> @@ -2312,16 +2287,6 @@ <!-- Label for button that reports a touch that was wrongly rejected by the lockscreen. For debugging only. [CHAR LIMIT=NONE] --> <string name="report_rejected_touch" translatable="false">Report rejected touch</string> - <!-- Multi-Window strings --> - <!-- Text that gets shown on top of current activity to inform the user that the system force-resized the current activity to be displayed in split-screen and that things might crash/not work properly [CHAR LIMIT=NONE] --> - <string name="dock_forced_resizable">App may not work with split-screen.</string> - <!-- Warning message when we try to dock a non-resizeable task and launch it in fullscreen instead. --> - <string name="dock_non_resizeble_failed_to_dock_text">App does not support split-screen.</string> - <!-- Text that gets shown on top of current activity to inform the user that the system force-resized the current activity to be displayed on a secondary display and that things might crash/not work properly [CHAR LIMIT=NONE] --> - <string name="forced_resizable_secondary_display">App may not work on a secondary display.</string> - <!-- Warning message when we try to launch a non-resizeable activity on a secondary display and launch it on the primary instead. --> - <string name="activity_launch_on_secondary_display_failed_text">App does not support launch on secondary displays.</string> - <!-- accessibility label for button to open settings [CHAR LIMIT=NONE] --> <string name="accessibility_quick_settings_settings">Open settings.</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index ee07e613a0c5..58563f49dce4 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -24,21 +24,6 @@ <item name="android:layout_marginBottom">0dp</item> </style> - <!-- Theme used for the activity that shows when the system forced an app to be resizable --> - <style name="ForcedResizableTheme" parent="@android:style/Theme.Translucent.NoTitleBar"> - <item name="android:windowBackground">@drawable/forced_resizable_background</item> - <item name="android:statusBarColor">@*android:color/transparent</item> - <item name="android:windowAnimationStyle">@style/Animation.ForcedResizable</item> - </style> - - <style name="Animation.ForcedResizable" parent="@android:style/Animation"> - <item name="android:activityOpenEnterAnimation">@anim/forced_resizable_enter</item> - - <!-- If the target stack doesn't have focus, we do a task to front animation. --> - <item name="android:taskToFrontEnterAnimation">@anim/forced_resizable_enter</item> - <item name="android:activityCloseExitAnimation">@anim/forced_resizable_exit</item> - </style> - <style name="PipPhoneOverlayControlTheme" parent="@android:style/Theme.Material"> <item name="android:windowIsTranslucent">true</item> <item name="android:windowNoTitle">true</item> @@ -483,23 +468,6 @@ <item name="android:background">@drawable/btn_borderless_rect</item> </style> - <style name="DockedDividerBackground"> - <item name="android:layout_width">match_parent</item> - <item name="android:layout_height">10dp</item> - <item name="android:layout_gravity">center_vertical</item> - </style> - - <style name="DockedDividerMinimizedShadow"> - <item name="android:layout_width">match_parent</item> - <item name="android:layout_height">8dp</item> - </style> - - <style name="DockedDividerHandle"> - <item name="android:layout_gravity">center_horizontal</item> - <item name="android:layout_width">96dp</item> - <item name="android:layout_height">48dp</item> - </style> - <style name="TunerSettings" parent="@android:style/Theme.DeviceDefault.Settings"> <item name="android:windowActionBar">false</item> <item name="preferenceTheme">@style/TunerPreferenceTheme</item> diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index 0dd9488f1d3e..ac01ba1539b3 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -65,7 +65,6 @@ import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.shared.plugins.PluginManagerImpl; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.DevicePolicyManagerWrapper; -import com.android.systemui.stackdivider.SplitScreen; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.phone.AutoHideController; @@ -78,6 +77,7 @@ import com.android.systemui.statusbar.policy.DataSaverController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.util.leak.LeakDetector; +import com.android.wm.shell.splitscreen.SplitScreen; import java.util.Optional; import java.util.concurrent.Executor; diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java index c331bd377eec..3a5ce4d82540 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java @@ -28,7 +28,6 @@ import com.android.systemui.bubbles.dagger.BubbleModule; import com.android.systemui.globalactions.GlobalActionsComponent; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.dagger.KeyguardModule; -import com.android.systemui.onehanded.OneHandedUI; import com.android.systemui.power.PowerUI; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsModule; @@ -90,12 +89,6 @@ public abstract class SystemUIBinder { @ClassKey(LatencyTester.class) public abstract SystemUI bindLatencyTester(LatencyTester sysui); - /** Inject into OneHandedUI. */ - @Binds - @IntoMap - @ClassKey(OneHandedUI.class) - public abstract SystemUI bindOneHandedUI(OneHandedUI sysui); - /** Inject into PowerUI. */ @Binds @IntoMap diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt index 1ae54d60d3fa..d789501ffdef 100644 --- a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt @@ -28,10 +28,14 @@ import com.android.systemui.R */ class SeekBarObserver(private val holder: PlayerViewHolder) : Observer<SeekBarViewModel.Progress> { - val seekBarDefaultMaxHeight = holder.seekBar.context.resources + val seekBarEnabledMaxHeight = holder.seekBar.context.resources .getDimensionPixelSize(R.dimen.qs_media_enabled_seekbar_height) val seekBarDisabledHeight = holder.seekBar.context.resources .getDimensionPixelSize(R.dimen.qs_media_disabled_seekbar_height) + val seekBarEnabledVerticalPadding = holder.seekBar.context.resources + .getDimensionPixelSize(R.dimen.qs_media_enabled_seekbar_vertical_padding) + val seekBarDisabledVerticalPadding = holder.seekBar.context.resources + .getDimensionPixelSize(R.dimen.qs_media_disabled_seekbar_vertical_padding) /** Updates seek bar views when the data model changes. */ @UiThread @@ -39,6 +43,7 @@ class SeekBarObserver(private val holder: PlayerViewHolder) : Observer<SeekBarVi if (!data.enabled) { if (holder.seekBar.maxHeight != seekBarDisabledHeight) { holder.seekBar.maxHeight = seekBarDisabledHeight + setVerticalPadding(seekBarDisabledVerticalPadding) } holder.seekBar.setEnabled(false) holder.seekBar.getThumb().setAlpha(0) @@ -51,8 +56,9 @@ class SeekBarObserver(private val holder: PlayerViewHolder) : Observer<SeekBarVi holder.seekBar.getThumb().setAlpha(if (data.seekAvailable) 255 else 0) holder.seekBar.setEnabled(data.seekAvailable) - if (holder.seekBar.maxHeight != seekBarDefaultMaxHeight) { - holder.seekBar.maxHeight = seekBarDefaultMaxHeight + if (holder.seekBar.maxHeight != seekBarEnabledMaxHeight) { + holder.seekBar.maxHeight = seekBarEnabledMaxHeight + setVerticalPadding(seekBarEnabledVerticalPadding) } data.duration?.let { @@ -67,4 +73,11 @@ class SeekBarObserver(private val holder: PlayerViewHolder) : Observer<SeekBarVi it / DateUtils.SECOND_IN_MILLIS)) } } + + @UiThread + fun setVerticalPadding(padding: Int) { + val leftPadding = holder.seekBar.paddingLeft + val rightPadding = holder.seekBar.paddingRight + holder.seekBar.setPadding(leftPadding, padding, rightPadding, padding) + } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index c7e78174f474..4e0df214d884 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -122,7 +122,6 @@ import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.recents.Recents; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; -import com.android.systemui.stackdivider.SplitScreen; import com.android.systemui.statusbar.AutoHideUiElement; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue.Callbacks; @@ -137,6 +136,7 @@ import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.wm.shell.splitscreen.SplitScreen; import java.io.PrintWriter; import java.util.List; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java index 9b9dc6dec583..339e504a3cf1 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java @@ -55,7 +55,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.recents.Recents; import com.android.systemui.shared.system.ActivityManagerWrapper; -import com.android.systemui.stackdivider.SplitScreen; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.NotificationRemoteInputManager; @@ -65,6 +64,7 @@ import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.wm.shell.splitscreen.SplitScreen; import java.io.FileDescriptor; import java.io.PrintWriter; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java index 8a468f6eb709..13b9a552d75c 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java @@ -87,12 +87,12 @@ import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.shared.system.WindowManagerWrapper; -import com.android.systemui.stackdivider.SplitScreen; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.AutoHideController; import com.android.systemui.statusbar.phone.LightBarTransitionsController; import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.wm.shell.splitscreen.SplitScreen; import java.io.PrintWriter; import java.util.function.Consumer; diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHanded.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHanded.java new file mode 100644 index 000000000000..b7c6262b07e0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHanded.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.onehanded; + +import androidx.annotation.NonNull; + +import com.android.systemui.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback; + +import java.io.PrintWriter; + +/** + * Interface to engage one handed feature. + */ +public interface OneHanded { + /** + * Return whether the device has one handed feature or not. + */ + boolean hasOneHandedFeature(); + + /** + * Return one handed settings enabled or not. + */ + boolean isOneHandedEnabled(); + + /** + * Return swipe to notification settings enabled or not. + */ + boolean isSwipeToNotificationEnabled(); + + /** + * Enters one handed mode. + */ + void startOneHanded(); + + /** + * Exits one handed mode. + */ + void stopOneHanded(); + + /** + * Exits one handed mode with {@link OneHandedEvents}. + */ + void stopOneHanded(int event); + + /** + * Set navigation 3 button mode enabled or disabled by users. + */ + void setThreeButtonModeEnabled(boolean enabled); + + /** + * Register callback to be notified after {@link OneHandedDisplayAreaOrganizer} + * transition start or finish + */ + void registerTransitionCallback(OneHandedTransitionCallback callback); + + /** + * Register callback for one handed gesture, this gesture callbcak will be activated on + * 3 button navigation mode only + */ + void registerGestureCallback(OneHandedGestureEventCallback callback); + + /** + * Dump one handed status. + */ + void dump(@NonNull PrintWriter pw); +} diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedController.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedController.java index bb59449d114d..90adf838440c 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedController.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedController.java @@ -16,195 +16,262 @@ package com.android.systemui.onehanded; +import static android.os.UserHandle.USER_CURRENT; import static android.view.Display.DEFAULT_DISPLAY; -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE; - -import android.content.ComponentName; import android.content.Context; +import android.content.om.IOverlayManager; +import android.content.om.OverlayInfo; +import android.database.ContentObserver; import android.graphics.Point; -import android.graphics.Rect; +import android.os.Handler; +import android.os.Looper; +import android.os.RemoteException; +import android.os.ServiceManager; import android.os.SystemProperties; -import android.view.KeyEvent; +import android.provider.Settings; +import android.util.Log; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; -import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.Dumpable; -import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.model.SysUiState; -import com.android.systemui.navigationbar.NavigationModeController; -import com.android.systemui.shared.system.ActivityManagerWrapper; -import com.android.systemui.shared.system.TaskStackChangeListener; -import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback; import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; -import java.io.FileDescriptor; import java.io.PrintWriter; -import javax.inject.Inject; - /** * Manages and manipulates the one handed states, transitions, and gesture for phones. */ -@SysUISingleton -public class OneHandedController implements Dumpable { - private static final String TAG = "OneHandedManager"; +public class OneHandedController implements OneHanded { + private static final String TAG = "OneHandedController"; + private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE = "persist.debug.one_handed_offset_percentage"; + private static final String ONE_HANDED_MODE_GESTURAL_OVERLAY = + "com.android.internal.systemui.onehanded.gestural"; + + static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode"; + private final boolean mHasOneHandedFeature; private boolean mIsOneHandedEnabled; private boolean mIsSwipeToNotificationEnabled; private boolean mTaskChangeToExit; private float mOffSetFraction; - private final CommandQueue mCommandQueue; + private final Context mContext; private final DisplayController mDisplayController; private final OneHandedGestureHandler mGestureHandler; private final OneHandedTimeoutHandler mTimeoutHandler; private final OneHandedTouchHandler mTouchHandler; private final OneHandedTutorialHandler mTutorialHandler; - private final SysUiState mSysUiFlagContainer; + private final IOverlayManager mOverlayManager; + private final Handler mMainHandler = new Handler(Looper.getMainLooper()); private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer; /** - * Handler for system task stack changes, exit when user lunch new task or bring task to front + * Handle rotation based on OnDisplayChangingListener callback */ - private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() { + private final DisplayChangeController.OnDisplayChangingListener mRotationController = + (display, fromRotation, toRotation, wct) -> { + if (mDisplayAreaOrganizer != null) { + mDisplayAreaOrganizer.onRotateDisplay(fromRotation, toRotation); + } + }; + + private final ContentObserver mEnabledObserver = new ContentObserver(mMainHandler) { @Override - public void onTaskCreated(int taskId, ComponentName componentName) { - if (!mIsOneHandedEnabled || !mDisplayAreaOrganizer.isInOneHanded()) { - return; - } - OneHandedEvents.writeEvent(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT); - stopOneHanded(); + public void onChange(boolean selfChange) { + final boolean enabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( + mContext.getContentResolver()); + OneHandedEvents.writeEvent(enabled + ? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_ON + : OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_OFF); + + setOneHandedEnabled(enabled); + + // Also checks swipe to notification settings since they all need gesture overlay. + setEnabledGesturalOverlay( + enabled || OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( + mContext.getContentResolver())); } + }; + private final ContentObserver mTimeoutObserver = new ContentObserver(mMainHandler) { @Override - public void onTaskMovedToFront(int taskId) { - if (!mIsOneHandedEnabled || !mDisplayAreaOrganizer.isInOneHanded()) { - return; + public void onChange(boolean selfChange) { + final int newTimeout = OneHandedSettingsUtil.getSettingsOneHandedModeTimeout( + mContext.getContentResolver()); + int metricsId = OneHandedEvents.OneHandedSettingsTogglesEvent.INVALID.getId(); + switch (newTimeout) { + case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER: + metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_NEVER; + break; + case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS: + metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_4; + break; + case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS: + metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_8; + break; + case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_LONG_IN_SECONDS: + metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_12; + break; + default: + // do nothing + break; + } + OneHandedEvents.writeEvent(metricsId); + + if (mTimeoutHandler != null) { + mTimeoutHandler.setTimeout(newTimeout); } - OneHandedEvents.writeEvent(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT); - stopOneHanded(); } }; - /** - * Handle rotation based on OnDisplayChangingListener callback - */ - private final DisplayChangeController.OnDisplayChangingListener mRotationController = - (display, fromRotation, toRotation, wct) -> { - if (mDisplayAreaOrganizer != null) { - mDisplayAreaOrganizer.onRotateDisplay(fromRotation, toRotation); + private final ContentObserver mTaskChangeExitObserver = new ContentObserver(mMainHandler) { + @Override + public void onChange(boolean selfChange) { + final boolean enabled = OneHandedSettingsUtil.getSettingsTapsAppToExit( + mContext.getContentResolver()); + OneHandedEvents.writeEvent(enabled + ? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_ON + : OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_OFF); + + setTaskChangeToExit(enabled); + } + }; + + private final ContentObserver mSwipeToNotificationEnabledObserver = + new ContentObserver(mMainHandler) { + @Override + public void onChange(boolean selfChange) { + final boolean enabled = + OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( + mContext.getContentResolver()); + setSwipeToNotificationEnabled(enabled); + + // Also checks one handed mode settings since they all need gesture overlay. + setEnabledGesturalOverlay( + enabled || OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( + mContext.getContentResolver())); } }; /** - * Constructor of OneHandedManager + * The static constructor method to create OneHnadedController. */ - @Inject - public OneHandedController(Context context, - CommandQueue commandQueue, - DisplayController displayController, - NavigationModeController navigationModeController, - SysUiState sysUiState) { - mCommandQueue = commandQueue; - mDisplayController = displayController; - mDisplayController.addDisplayChangingController(mRotationController); - mSysUiFlagContainer = sysUiState; - mOffSetFraction = SystemProperties.getInt(ONE_HANDED_MODE_OFFSET_PERCENTAGE, 50) / 100.0f; - - mIsOneHandedEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( - context.getContentResolver()); - mIsSwipeToNotificationEnabled = OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( - context.getContentResolver()); - mTimeoutHandler = OneHandedTimeoutHandler.get(); - mTouchHandler = new OneHandedTouchHandler(); - mTutorialHandler = new OneHandedTutorialHandler(context); - mDisplayAreaOrganizer = new OneHandedDisplayAreaOrganizer(context, displayController, - new OneHandedAnimationController(context), mTutorialHandler); - mGestureHandler = new OneHandedGestureHandler( - context, displayController, navigationModeController); - updateOneHandedEnabled(); - setupGestures(); + public static OneHandedController create( + Context context, DisplayController displayController) { + OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context); + OneHandedAnimationController animationController = + new OneHandedAnimationController(context); + OneHandedTouchHandler touchHandler = new OneHandedTouchHandler(); + OneHandedGestureHandler gestureHandler = new OneHandedGestureHandler( + context, displayController); + OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer( + context, displayController, animationController, tutorialHandler); + return new OneHandedController(context, displayController, organizer, touchHandler, + tutorialHandler, gestureHandler); } - /** - * Constructor of OneHandedManager for testing - */ - // TODO(b/161980408): Should remove extra constructor. @VisibleForTesting OneHandedController(Context context, - CommandQueue commandQueue, DisplayController displayController, OneHandedDisplayAreaOrganizer displayAreaOrganizer, OneHandedTouchHandler touchHandler, OneHandedTutorialHandler tutorialHandler, - OneHandedGestureHandler gestureHandler, - SysUiState sysUiState) { - mCommandQueue = commandQueue; + OneHandedGestureHandler gestureHandler) { + mHasOneHandedFeature = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false); + if (!mHasOneHandedFeature) { + Log.i(TAG, "Device config SUPPORT_ONE_HANDED_MODE off"); + mContext = null; + mDisplayAreaOrganizer = null; + mDisplayController = null; + mTouchHandler = null; + mTutorialHandler = null; + mGestureHandler = null; + mTimeoutHandler = null; + mOverlayManager = null; + return; + } + + mContext = context; mDisplayAreaOrganizer = displayAreaOrganizer; mDisplayController = displayController; - mDisplayController.addDisplayChangingController(mRotationController); - mSysUiFlagContainer = sysUiState; - mOffSetFraction = SystemProperties.getInt(ONE_HANDED_MODE_OFFSET_PERCENTAGE, 50) / 100.0f; + mTouchHandler = touchHandler; + mTutorialHandler = tutorialHandler; + mGestureHandler = gestureHandler; + mOverlayManager = IOverlayManager.Stub.asInterface( + ServiceManager.getService(Context.OVERLAY_SERVICE)); + mOffSetFraction = SystemProperties.getInt(ONE_HANDED_MODE_OFFSET_PERCENTAGE, 50) / 100.0f; mIsOneHandedEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( context.getContentResolver()); mIsSwipeToNotificationEnabled = OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( context.getContentResolver()); mTimeoutHandler = OneHandedTimeoutHandler.get(); - mTouchHandler = touchHandler; - mTutorialHandler = tutorialHandler; - mGestureHandler = gestureHandler; - updateOneHandedEnabled(); - setupGestures(); + + mDisplayController.addDisplayChangingController(mRotationController); + + setupCallback(); + setupSettingObservers(); + setupTimeoutListener(); + setupGesturalOverlay(); + updateSettings(); } /** - * Set one handed enabled or disabled by OneHanded UI when user update settings + * Set one handed enabled or disabled when user update settings */ - public void setOneHandedEnabled(boolean enabled) { + void setOneHandedEnabled(boolean enabled) { mIsOneHandedEnabled = enabled; updateOneHandedEnabled(); } /** - * Set one handed enabled or disabled by OneHanded UI when user update settings + * Set one handed enabled or disabled by when user update settings */ - public void setTaskChangeToExit(boolean enabled) { - if (mTaskChangeToExit == enabled) { - return; - } + void setTaskChangeToExit(boolean enabled) { mTaskChangeToExit = enabled; - updateOneHandedEnabled(); } /** * Sets whether to enable swipe bottom to notification gesture when user update settings. */ - public void setSwipeToNotificationEnabled(boolean enabled) { + void setSwipeToNotificationEnabled(boolean enabled) { mIsSwipeToNotificationEnabled = enabled; updateOneHandedEnabled(); } - /** - * Enters one handed mode. - */ + @Override + public boolean hasOneHandedFeature() { + return mHasOneHandedFeature; + } + + @Override + public boolean isOneHandedEnabled() { + return mIsOneHandedEnabled; + } + + @Override + public boolean isSwipeToNotificationEnabled() { + return mIsSwipeToNotificationEnabled; + } + + @Override public void startOneHanded() { if (!mDisplayAreaOrganizer.isInOneHanded()) { final int yOffSet = Math.round(getDisplaySize().y * mOffSetFraction); mDisplayAreaOrganizer.scheduleOffset(0, yOffSet); mTimeoutHandler.resetTimer(); + + OneHandedEvents.writeEvent(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_IN); } } - /** - * Exits one handed mode. - */ + @Override public void stopOneHanded() { if (mDisplayAreaOrganizer.isInOneHanded()) { mDisplayAreaOrganizer.scheduleOffset(0, 0); @@ -212,64 +279,72 @@ public class OneHandedController implements Dumpable { } } - private void setupGestures() { - mTouchHandler.registerTouchEventListener( - new OneHandedTouchHandler.OneHandedTouchEventCallback() { - @Override - public void onStart() { - if (mIsOneHandedEnabled) { - startOneHanded(); - } - } - - @Override - public void onStop() { - if (mIsOneHandedEnabled) { - stopOneHanded(); - } - } - }); - - mGestureHandler.setGestureEventListener( - new OneHandedGestureHandler.OneHandedGestureEventCallback() { - @Override - public void onStart() { - if (mIsOneHandedEnabled) { - startOneHanded(); - } else if (mIsSwipeToNotificationEnabled) { - mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN); - } - } - - @Override - public void onStop() { - if (mIsOneHandedEnabled) { - stopOneHanded(); - } else if (mIsSwipeToNotificationEnabled) { - mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP); - } - } - }); - - mDisplayAreaOrganizer.registerTransitionCallback(new OneHandedTransitionCallback() { - @Override - public void onStartFinished(Rect bounds) { - mSysUiFlagContainer.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE, - true).commitUpdate(DEFAULT_DISPLAY); - } + @Override + public void stopOneHanded(int event) { + if (!mTaskChangeToExit && event == OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT) { + //Task change exit not enable, do nothing and return here. + return; + } - @Override - public void onStopFinished(Rect bounds) { - mSysUiFlagContainer.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE, - false).commitUpdate(DEFAULT_DISPLAY); - } - }); + if (mDisplayAreaOrganizer.isInOneHanded()) { + OneHandedEvents.writeEvent(event); + } + + stopOneHanded(); + } + + @Override + public void setThreeButtonModeEnabled(boolean enabled) { + mGestureHandler.onThreeButtonModeEnabled(enabled); + } + + @Override + public void registerTransitionCallback(OneHandedTransitionCallback callback) { + mDisplayAreaOrganizer.registerTransitionCallback(callback); + } + + @Override + public void registerGestureCallback(OneHandedGestureEventCallback callback) { + mGestureHandler.setGestureEventListener(callback); + } + private void setupCallback() { + mTouchHandler.registerTouchEventListener(() -> + stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_OVERSPACE_OUT)); mDisplayAreaOrganizer.registerTransitionCallback(mTouchHandler); mDisplayAreaOrganizer.registerTransitionCallback(mGestureHandler); mDisplayAreaOrganizer.registerTransitionCallback(mTutorialHandler); } + private void setupSettingObservers() { + OneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_ENABLED, + mContext.getContentResolver(), mEnabledObserver); + OneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_TIMEOUT, + mContext.getContentResolver(), mTimeoutObserver); + OneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.TAPS_APP_TO_EXIT, + mContext.getContentResolver(), mTaskChangeExitObserver); + OneHandedSettingsUtil.registerSettingsKeyObserver( + Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, + mContext.getContentResolver(), mSwipeToNotificationEnabledObserver); + } + + private void updateSettings() { + setOneHandedEnabled(OneHandedSettingsUtil + .getSettingsOneHandedModeEnabled(mContext.getContentResolver())); + mTimeoutHandler.setTimeout(OneHandedSettingsUtil + .getSettingsOneHandedModeTimeout(mContext.getContentResolver())); + setTaskChangeToExit(OneHandedSettingsUtil + .getSettingsTapsAppToExit(mContext.getContentResolver())); + setSwipeToNotificationEnabled(OneHandedSettingsUtil + .getSettingsSwipeToNotificationEnabled(mContext.getContentResolver())); + } + + private void setupTimeoutListener() { + mTimeoutHandler.registerTimeoutListener(timeoutTime -> { + stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_TIMEOUT_OUT); + }); + } + /** * Query the current display real size from {@link DisplayController} * @@ -293,25 +368,73 @@ public class OneHandedController implements Dumpable { mDisplayAreaOrganizer.registerOrganizer( OneHandedDisplayAreaOrganizer.FEATURE_ONE_HANDED); } - ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener); - if (mTaskChangeToExit) { - ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); - } mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled); mGestureHandler.onOneHandedEnabled(mIsOneHandedEnabled || mIsSwipeToNotificationEnabled); } + private void setupGesturalOverlay() { + if (!OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(mContext.getContentResolver())) { + return; + } + + OverlayInfo info = null; + try { + // TODO(b/157958539) migrate new RRO config file after S+ + mOverlayManager.setHighestPriority(ONE_HANDED_MODE_GESTURAL_OVERLAY, USER_CURRENT); + info = mOverlayManager.getOverlayInfo(ONE_HANDED_MODE_GESTURAL_OVERLAY, USER_CURRENT); + } catch (RemoteException e) { /* Do nothing */ } + + if (info != null && !info.isEnabled()) { + // Enable the default gestural one handed overlay. + setEnabledGesturalOverlay(true); + } + } + + @androidx.annotation.VisibleForTesting + private void setEnabledGesturalOverlay(boolean enabled) { + try { + mOverlayManager.setEnabled(ONE_HANDED_MODE_GESTURAL_OVERLAY, enabled, USER_CURRENT); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + @Override - public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { + public void dump(@NonNull PrintWriter pw) { final String innerPrefix = " "; pw.println(TAG + "states: "); - pw.print(innerPrefix + "mSysUiFlagContainer="); - pw.println(mSysUiFlagContainer.getFlags()); pw.print(innerPrefix + "mOffSetFraction="); pw.println(mOffSetFraction); if (mDisplayAreaOrganizer != null) { - mDisplayAreaOrganizer.dump(fd, pw, args); + mDisplayAreaOrganizer.dump(pw); + } + + if (mTouchHandler != null) { + mTouchHandler.dump(pw); + } + + if (mTimeoutHandler != null) { + mTimeoutHandler.dump(pw); + } + + if (mTutorialHandler != null) { + mTutorialHandler.dump(pw); + } + + OneHandedSettingsUtil.dump(pw, innerPrefix, mContext.getContentResolver()); + + if (mOverlayManager != null) { + OverlayInfo info = null; + try { + info = mOverlayManager.getOverlayInfo(ONE_HANDED_MODE_GESTURAL_OVERLAY, + USER_CURRENT); + } catch (RemoteException e) { /* Do nothing */ } + + if (info != null && !info.isEnabled()) { + pw.print(innerPrefix + "OverlayInfo="); + pw.println(info); + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java index ad9f7ea4e945..ec40bad06b71 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java @@ -38,10 +38,8 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.internal.os.SomeArgs; -import com.android.systemui.Dumpable; import com.android.wm.shell.common.DisplayController; -import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; @@ -58,7 +56,7 @@ import java.util.Objects; * * This class is also responsible for translating one handed operations within SysUI component */ -public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implements Dumpable { +public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { private static final String TAG = "OneHandedDisplayAreaOrganizer"; private static final String ONE_HANDED_MODE_TRANSLATE_ANIMATION_DURATION = "persist.debug.one_handed_translate_animation_duration"; @@ -353,8 +351,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen return args; } - @Override - public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { + void dump(@NonNull PrintWriter pw) { final String innerPrefix = " "; pw.println(TAG + "states: "); pw.print(innerPrefix + "mIsInOneHanded="); diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java index f3be699ab821..4a493ba800ba 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java @@ -17,7 +17,6 @@ package com.android.systemui.onehanded; import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import android.annotation.Nullable; import android.content.Context; @@ -40,7 +39,6 @@ import android.window.WindowContainerTransaction; import androidx.annotation.VisibleForTesting; import com.android.systemui.R; -import com.android.systemui.navigationbar.NavigationModeController; import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; @@ -49,7 +47,6 @@ import com.android.wm.shell.common.DisplayController; * others(e.g, 2-button, full gesture mode) are handled by Launcher quick steps. */ public class OneHandedGestureHandler implements OneHandedTransitionCallback, - NavigationModeController.ModeChangedListener, DisplayChangeController.OnDisplayChangingListener { private static final String TAG = "OneHandedGestureHandler"; private static final boolean DEBUG_GESTURE = false; @@ -66,7 +63,7 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, private boolean mAllowGesture; private boolean mIsEnabled; private int mNavGestureHeight; - private boolean mIsThreeButtonModeEnable; + private boolean mIsThreeButtonModeEnabled; private int mRotation = Surface.ROTATION_0; @VisibleForTesting @@ -85,14 +82,10 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, * * @param context {@link Context} * @param displayController {@link DisplayController} - * @param navigationModeController {@link NavigationModeController} */ - public OneHandedGestureHandler(Context context, DisplayController displayController, - NavigationModeController navigationModeController) { + public OneHandedGestureHandler(Context context, DisplayController displayController) { mDisplayController = displayController; displayController.addDisplayChangingController(this); - final int NavBarMode = navigationModeController.addListener(this); - mIsThreeButtonModeEnable = (NavBarMode == NAV_BAR_MODE_3BUTTON); mNavGestureHeight = context.getResources().getDimensionPixelSize( com.android.internal.R.dimen.navigation_bar_gesture_height); mDragDistThreshold = context.getResources().getDimensionPixelSize( @@ -115,6 +108,11 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, updateIsEnabled(); } + void onThreeButtonModeEnabled(boolean isEnabled) { + mIsThreeButtonModeEnabled = isEnabled; + updateIsEnabled(); + } + /** * Register {@link OneHandedGestureEventCallback} to receive onStart(), onStop() callback */ @@ -199,7 +197,7 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, private void updateIsEnabled() { disposeInputChannel(); - if (mIsEnabled && mIsThreeButtonModeEnable) { + if (mIsEnabled && mIsThreeButtonModeEnabled) { final Point displaySize = new Point(); if (mDisplayController != null) { final Display display = mDisplayController.getDisplay(DEFAULT_DISPLAY); @@ -224,15 +222,6 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, } @Override - public void onNavigationModeChanged(int mode) { - if (DEBUG_GESTURE) { - Log.d(TAG, "onNavigationModeChanged, mode =" + mode); - } - mIsThreeButtonModeEnable = (mode == NAV_BAR_MODE_3BUTTON); - updateIsEnabled(); - } - - @Override public void onRotateDisplay(int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) { mRotation = toRotation; diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTimeoutHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTimeoutHandler.java index 6bed30425c55..21329ea1b0e6 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTimeoutHandler.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTimeoutHandler.java @@ -25,9 +25,6 @@ import android.os.Message; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; -import com.android.systemui.Dumpable; - -import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -36,7 +33,7 @@ import java.util.concurrent.TimeUnit; /** * Timeout handler for stop one handed mode operations. */ -public class OneHandedTimeoutHandler implements Dumpable { +public class OneHandedTimeoutHandler { private static final String TAG = "OneHandedTimeoutHandler"; private static boolean sIsDragging = false; // Default timeout is ONE_HANDED_TIMEOUT_MEDIUM @@ -150,8 +147,7 @@ public class OneHandedTimeoutHandler implements Dumpable { } } - @Override - public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { + void dump(@NonNull PrintWriter pw) { final String innerPrefix = " "; pw.println(TAG + "states: "); pw.print(innerPrefix + "sTimeout="); diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java index 8265da6a5f14..3d28a426f4f8 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java @@ -30,9 +30,6 @@ import android.view.MotionEvent; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; -import com.android.systemui.Dumpable; - -import java.io.FileDescriptor; import java.io.PrintWriter; /** @@ -40,7 +37,7 @@ import java.io.PrintWriter; * to exit, reset timer when user is in one-handed mode. * Refer {@link OneHandedGestureHandler} to see start and stop one handed gesture */ -public class OneHandedTouchHandler implements OneHandedTransitionCallback, Dumpable { +public class OneHandedTouchHandler implements OneHandedTransitionCallback { private static final String TAG = "OneHandedTouchHandler"; private final Rect mLastUpdatedBounds = new Rect(); @@ -146,8 +143,7 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback, Dumpa mIsOnStopTransitioning = false; } - @Override - public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { + void dump(@NonNull PrintWriter pw) { final String innerPrefix = " "; pw.println(TAG + "states: "); pw.print(innerPrefix + "mLastUpdatedBounds="); @@ -170,11 +166,6 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback, Dumpa */ public interface OneHandedTouchEventCallback { /** - * Handle the start event. - */ - void onStart(); - - /** * Handle the exit event. */ void onStop(); diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java index 8ef9b092bc00..beccf3dbc8de 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java @@ -33,10 +33,8 @@ import android.widget.FrameLayout; import androidx.annotation.NonNull; -import com.android.systemui.Dumpable; import com.android.systemui.R; -import java.io.FileDescriptor; import java.io.PrintWriter; /** @@ -45,7 +43,7 @@ import java.io.PrintWriter; * Refer {@link OneHandedGestureHandler} and {@link OneHandedTouchHandler} to see start and stop * one handed gesture */ -public class OneHandedTutorialHandler implements OneHandedTransitionCallback, Dumpable { +public class OneHandedTutorialHandler implements OneHandedTransitionCallback { private static final String TAG = "OneHandedTutorialHandler"; private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE = "persist.debug.one_handed_offset_percentage"; @@ -170,8 +168,7 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, Du return lp; } - @Override - public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { + void dump(@NonNull PrintWriter pw) { final String innerPrefix = " "; pw.println(TAG + "states: "); pw.print(innerPrefix + "mLastUpdatedBounds="); diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java deleted file mode 100644 index 3348a06d5cac..000000000000 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.onehanded; - -import static android.os.UserHandle.USER_CURRENT; -import static android.view.Display.DEFAULT_DISPLAY; - -import android.content.Context; -import android.content.om.IOverlayManager; -import android.content.om.OverlayInfo; -import android.database.ContentObserver; -import android.inputmethodservice.InputMethodService; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.SystemProperties; -import android.provider.Settings; -import android.util.Log; - -import androidx.annotation.VisibleForTesting; - -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.KeyguardUpdateMonitorCallback; -import com.android.systemui.Dependency; -import com.android.systemui.Dumpable; -import com.android.systemui.SystemUI; -import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.keyguard.ScreenLifecycle; -import com.android.systemui.statusbar.CommandQueue; - -import java.io.FileDescriptor; -import java.io.PrintWriter; - -import javax.inject.Inject; - -/** - * A service that controls UI of the one handed mode function. - */ -@SysUISingleton -public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dumpable { - private static final String TAG = "OneHandedUI"; - private static final String ONE_HANDED_MODE_GESTURAL_OVERLAY = - "com.android.internal.systemui.onehanded.gestural"; - private static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode"; - - private final OneHandedController mOneHandedController; - private final CommandQueue mCommandQueue; - private final Handler mMainHandler = new Handler(Looper.getMainLooper()); - private final IOverlayManager mOverlayManager; - private final OneHandedTimeoutHandler mTimeoutHandler; - private final ScreenLifecycle mScreenLifecycle; - - private final ContentObserver mEnabledObserver = new ContentObserver(mMainHandler) { - @Override - public void onChange(boolean selfChange) { - final boolean enabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( - mContext.getContentResolver()); - OneHandedEvents.writeEvent(enabled - ? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_ON - : OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_OFF); - if (mOneHandedController != null) { - mOneHandedController.setOneHandedEnabled(enabled); - } - - // Also checks swipe to notification settings since they all need gesture overlay. - setEnabledGesturalOverlay( - enabled || OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( - mContext.getContentResolver())); - } - }; - - private final ContentObserver mTimeoutObserver = new ContentObserver(mMainHandler) { - @Override - public void onChange(boolean selfChange) { - final int newTimeout = OneHandedSettingsUtil.getSettingsOneHandedModeTimeout( - mContext.getContentResolver()); - int metricsId = OneHandedEvents.OneHandedSettingsTogglesEvent.INVALID.getId(); - switch (newTimeout) { - case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER: - metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_NEVER; - break; - case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS: - metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_4; - break; - case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS: - metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_8; - break; - case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_LONG_IN_SECONDS: - metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_12; - break; - default: - // do nothing - break; - } - OneHandedEvents.writeEvent(metricsId); - - if (mTimeoutHandler != null) { - mTimeoutHandler.setTimeout(newTimeout); - } - } - }; - - private final ContentObserver mTaskChangeExitObserver = new ContentObserver(mMainHandler) { - @Override - public void onChange(boolean selfChange) { - final boolean enabled = OneHandedSettingsUtil.getSettingsTapsAppToExit( - mContext.getContentResolver()); - OneHandedEvents.writeEvent(enabled - ? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_ON - : OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_OFF); - - if (mOneHandedController != null) { - mOneHandedController.setTaskChangeToExit(enabled); - } - } - }; - - private final ContentObserver mSwipeToNotificationEnabledObserver = - new ContentObserver(mMainHandler) { - @Override - public void onChange(boolean selfChange) { - final boolean enabled = - OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( - mContext.getContentResolver()); - if (mOneHandedController != null) { - mOneHandedController.setSwipeToNotificationEnabled(enabled); - } - - // Also checks one handed mode settings since they all need gesture overlay. - setEnabledGesturalOverlay( - enabled || OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( - mContext.getContentResolver())); - } - }; - - @Inject - public OneHandedUI(Context context, - CommandQueue commandQueue, - OneHandedController oneHandedController, - ScreenLifecycle screenLifecycle) { - super(context); - - if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) { - Log.i(TAG, "Device config SUPPORT_ONE_HANDED_MODE off"); - mCommandQueue = null; - mOneHandedController = null; - mOverlayManager = null; - mTimeoutHandler = null; - mScreenLifecycle = null; - return; - } - - mCommandQueue = commandQueue; - mOneHandedController = oneHandedController; - mTimeoutHandler = OneHandedTimeoutHandler.get(); - mScreenLifecycle = screenLifecycle; - mOverlayManager = IOverlayManager.Stub.asInterface( - ServiceManager.getService(Context.OVERLAY_SERVICE)); - } - - @Override - public void start() { - if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) { - return; - } - mCommandQueue.addCallback(this); - setupKeyguardUpdateMonitor(); - setupScreenObserver(); - setupSettingObservers(); - setupTimeoutListener(); - setupGesturalOverlay(); - updateSettings(); - } - - private void setupGesturalOverlay() { - if (!OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(mContext.getContentResolver())) { - return; - } - - OverlayInfo info = null; - try { - // TODO(b/157958539) migrate new RRO config file after S+ - mOverlayManager.setHighestPriority(ONE_HANDED_MODE_GESTURAL_OVERLAY, USER_CURRENT); - info = mOverlayManager.getOverlayInfo(ONE_HANDED_MODE_GESTURAL_OVERLAY, USER_CURRENT); - } catch (RemoteException e) { /* Do nothing */ } - - if (info != null && !info.isEnabled()) { - // Enable the default gestural one handed overlay. - setEnabledGesturalOverlay(true); - } - } - - private void setupTimeoutListener() { - mTimeoutHandler.registerTimeoutListener(timeoutTime -> { - OneHandedEvents.writeEvent(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_TIMEOUT_OUT); - stopOneHanded(); - }); - } - - private void setupKeyguardUpdateMonitor() { - final KeyguardUpdateMonitorCallback keyguardCallback = - new KeyguardUpdateMonitorCallback() { - @Override - public void onKeyguardBouncerChanged(boolean bouncer) { - if (bouncer) { - stopOneHanded(); - } - } - - @Override - public void onKeyguardVisibilityChanged(boolean showing) { - stopOneHanded(); - } - }; - Dependency.get(KeyguardUpdateMonitor.class).registerCallback(keyguardCallback); - } - - @Override - public void onCameraLaunchGestureDetected(int source) { - stopOneHanded(); - } - - private void setupScreenObserver() { - final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() { - @Override - public void onScreenTurningOff() { - OneHandedEvents.writeEvent(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_SCREEN_OFF_OUT); - stopOneHanded(); - } - }; - mScreenLifecycle.addObserver(mScreenObserver); - } - - private void setupSettingObservers() { - OneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_ENABLED, - mContext.getContentResolver(), mEnabledObserver); - OneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_TIMEOUT, - mContext.getContentResolver(), mTimeoutObserver); - OneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.TAPS_APP_TO_EXIT, - mContext.getContentResolver(), mTaskChangeExitObserver); - OneHandedSettingsUtil.registerSettingsKeyObserver( - Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, - mContext.getContentResolver(), mSwipeToNotificationEnabledObserver); - } - - private void updateSettings() { - mOneHandedController.setOneHandedEnabled(OneHandedSettingsUtil - .getSettingsOneHandedModeEnabled(mContext.getContentResolver())); - mTimeoutHandler.setTimeout(OneHandedSettingsUtil - .getSettingsOneHandedModeTimeout(mContext.getContentResolver())); - mOneHandedController.setTaskChangeToExit(OneHandedSettingsUtil - .getSettingsTapsAppToExit(mContext.getContentResolver())); - mOneHandedController.setSwipeToNotificationEnabled(OneHandedSettingsUtil - .getSettingsSwipeToNotificationEnabled(mContext.getContentResolver())); - } - - @Override - public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, - boolean showImeSwitcher) { - if (displayId != DEFAULT_DISPLAY) { - return; - } - if ((vis & InputMethodService.IME_VISIBLE) != 0) { - OneHandedEvents.writeEvent(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_POP_IME_OUT); - stopOneHanded(); - } - } - - @VisibleForTesting - private void setEnabledGesturalOverlay(boolean enabled) { - try { - mOverlayManager.setEnabled(ONE_HANDED_MODE_GESTURAL_OVERLAY, enabled, USER_CURRENT); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Trigger one handed more - */ - public void startOneHanded() { - mOneHandedController.startOneHanded(); - OneHandedEvents.writeEvent(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_IN); - } - - /** - * Dismiss one handed more - */ - public void stopOneHanded() { - mOneHandedController.stopOneHanded(); - OneHandedEvents.writeEvent(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT); - } - - /** - * Dump all one handed data of states - */ - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - final String innerPrefix = " "; - pw.println(TAG + "one handed states: "); - - if (mOneHandedController != null) { - mOneHandedController.dump(fd, pw, args); - } - - if (mTimeoutHandler != null) { - mTimeoutHandler.dump(fd, pw, args); - } - - OneHandedSettingsUtil.dump(pw, innerPrefix, mContext.getContentResolver()); - - if (mOverlayManager != null) { - OverlayInfo info = null; - try { - info = mOverlayManager.getOverlayInfo(ONE_HANDED_MODE_GESTURAL_OVERLAY, - USER_CURRENT); - } catch (RemoteException e) { /* Do nothing */ } - - if (info != null && !info.isEnabled()) { - pw.print(innerPrefix + "OverlayInfo="); - pw.println(info); - } - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index 6f44090d2744..862405609de8 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -63,10 +63,10 @@ import com.android.internal.os.SomeArgs; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.pip.phone.PipMenuActivityController; import com.android.systemui.pip.phone.PipUpdateThread; -import com.android.systemui.stackdivider.SplitScreen; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.splitscreen.SplitScreen; import java.io.PrintWriter; import java.util.ArrayList; diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java index 1a805daef77e..6998e90b3a7c 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java @@ -280,6 +280,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac PackageManager pm = context.getPackageManager(); boolean supportsPip = pm.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE); if (!supportsPip) { + Log.w(TAG, "Device not support PIP feature"); return; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java index 1a7e2290e044..47002683c6b9 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java @@ -38,8 +38,8 @@ import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; -import com.android.systemui.stackdivider.SplitScreen; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.wm.shell.splitscreen.SplitScreen; import java.util.Optional; diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 62b35f9f5e2d..cba938f5e1a6 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -16,6 +16,7 @@ package com.android.systemui.recents; +import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_DOWN; @@ -74,7 +75,8 @@ import com.android.systemui.navigationbar.NavigationBar; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.navigationbar.NavigationModeController; -import com.android.systemui.onehanded.OneHandedUI; +import com.android.systemui.onehanded.OneHanded; +import com.android.systemui.onehanded.OneHandedEvents; import com.android.systemui.pip.Pip; import com.android.systemui.pip.PipAnimationController; import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; @@ -86,12 +88,12 @@ import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.InputMonitorCompat; import com.android.systemui.shared.system.QuickStepContract; -import com.android.systemui.stackdivider.SplitScreen; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarWindowCallback; import com.android.systemui.statusbar.policy.CallbackController; +import com.android.wm.shell.splitscreen.SplitScreen; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -134,7 +136,7 @@ public class OverviewProxyService extends CurrentUserTracker implements private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>(); private final Intent mQuickStepIntent; private final ScreenshotHelper mScreenshotHelper; - private final OneHandedUI mOneHandedUI; + private final Optional<OneHanded> mOneHandedOptional; private final CommandQueue mCommandQueue; private Region mActiveNavBarRegion; @@ -143,6 +145,7 @@ public class OverviewProxyService extends CurrentUserTracker implements private int mConnectionBackoffAttempts; private boolean mBound; private boolean mIsEnabled; + private boolean mHasPipFeature; private int mCurrentBoundedUserId = -1; private float mNavBarButtonAlpha; private boolean mInputFocusTransferStarted; @@ -383,7 +386,9 @@ public class OverviewProxyService extends CurrentUserTracker implements @Override public void setShelfHeight(boolean visible, int shelfHeight) { - if (!verifyCaller("setShelfHeight")) { + if (!verifyCaller("setShelfHeight") || !mHasPipFeature) { + Log.w(TAG_OPS, + "ByPass setShelfHeight, FEATURE_PICTURE_IN_PICTURE:" + mHasPipFeature); return; } long token = Binder.clearCallingIdentity(); @@ -409,7 +414,9 @@ public class OverviewProxyService extends CurrentUserTracker implements @Override public void notifySwipeToHomeFinished() { - if (!verifyCaller("notifySwipeToHomeFinished")) { + if (!verifyCaller("notifySwipeToHomeFinished") || !mHasPipFeature) { + Log.w(TAG_OPS, "ByPass notifySwipeToHomeFinished, FEATURE_PICTURE_IN_PICTURE:" + + mHasPipFeature); return; } long token = Binder.clearCallingIdentity(); @@ -424,7 +431,9 @@ public class OverviewProxyService extends CurrentUserTracker implements @Override public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) { - if (!verifyCaller("setPinnedStackAnimationListener")) { + if (!verifyCaller("setPinnedStackAnimationListener") || !mHasPipFeature) { + Log.w(TAG_OPS, "ByPass setPinnedStackAnimationListener, FEATURE_PICTURE_IN_PICTURE:" + + mHasPipFeature); return; } long token = Binder.clearCallingIdentity(); @@ -456,9 +465,7 @@ public class OverviewProxyService extends CurrentUserTracker implements } long token = Binder.clearCallingIdentity(); try { - if (mOneHandedUI != null) { - mOneHandedUI.startOneHanded(); - } + mOneHandedOptional.ifPresent(oneHanded -> oneHanded.startOneHanded()); } finally { Binder.restoreCallingIdentity(token); } @@ -471,9 +478,8 @@ public class OverviewProxyService extends CurrentUserTracker implements } long token = Binder.clearCallingIdentity(); try { - if (mOneHandedUI != null) { - mOneHandedUI.stopOneHanded(); - } + mOneHandedOptional.ifPresent(oneHanded -> oneHanded.stopOneHanded( + OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT)); } finally { Binder.restoreCallingIdentity(token); } @@ -612,11 +618,13 @@ public class OverviewProxyService extends CurrentUserTracker implements NotificationShadeWindowController statusBarWinController, SysUiState sysUiState, Optional<Pip> pipOptional, Optional<SplitScreen> splitScreenOptional, - Optional<Lazy<StatusBar>> statusBarOptionalLazy, OneHandedUI oneHandedUI, + Optional<Lazy<StatusBar>> statusBarOptionalLazy, + Optional<OneHanded> oneHandedOptional, BroadcastDispatcher broadcastDispatcher) { super(broadcastDispatcher); mContext = context; mPipOptional = pipOptional; + mHasPipFeature = mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE); mStatusBarOptionalLazy = statusBarOptionalLazy; mHandler = new Handler(); mNavBarControllerLazy = navBarControllerLazy; @@ -631,7 +639,7 @@ public class OverviewProxyService extends CurrentUserTracker implements .supportsRoundedCornersOnWindows(mContext.getResources()); mSysUiState = sysUiState; mSysUiState.addCallback(this::notifySystemUiStateFlags); - mOneHandedUI = oneHandedUI; + mOneHandedOptional = oneHandedOptional; // Assumes device always starts with back button until launcher tells it that it does not mNavBarButtonAlpha = 1.0f; diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java index 0d92d1e198e7..b9b4f42a66e1 100644 --- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java +++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java @@ -30,8 +30,8 @@ import com.android.internal.policy.DividerSnapAlgorithm; import com.android.systemui.SystemUI; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.recents.Recents; -import com.android.systemui.stackdivider.DividerView; -import com.android.systemui.stackdivider.SplitScreen; +import com.android.wm.shell.splitscreen.DividerView; +import com.android.wm.shell.splitscreen.SplitScreen; import java.util.Optional; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index c01bdc4c2f28..8bf134d9c5b9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -21,6 +21,7 @@ import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_LOCKSCREEN_W import static com.android.systemui.statusbar.phone.StatusBar.SHOW_LOCKSCREEN_MEDIA_ARTWORK; import android.annotation.MainThread; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Notification; import android.content.Context; @@ -57,6 +58,7 @@ import com.android.systemui.statusbar.dagger.StatusBarModule; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.LockscreenWallpaper; @@ -233,8 +235,17 @@ public class NotificationMediaManager implements Dumpable { NotificationVisibility visibility, boolean removedByUser, int reason) { - onNotificationRemoved(entry.getKey()); - mediaDataManager.onNotificationRemoved(entry.getKey()); + removeEntry(entry); + } + }); + + // Pending entries are never inflated, and will never generate a call to onEntryRemoved(). + // This can happen when notifications are added and canceled before inflation. Add this + // separate listener for cleanup, since media inflation occurs onPendingEntryAdded(). + notificationEntryManager.addCollectionListener(new NotifCollectionListener() { + @Override + public void onEntryCleanUp(@NonNull NotificationEntry entry) { + removeEntry(entry); } }); @@ -247,6 +258,11 @@ public class NotificationMediaManager implements Dumpable { mPropertiesChangedListener); } + private void removeEntry(NotificationEntry entry) { + onNotificationRemoved(entry.getKey()); + mMediaDataManager.onNotificationRemoved(entry.getKey()); + } + /** * Check if a state should be considered actively playing * @param state a PlaybackState diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java index 838cf0c9a6d0..6f7b32b3ac74 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java @@ -55,10 +55,10 @@ import com.android.systemui.R; import com.android.systemui.SystemUI; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.UiBackground; -import com.android.systemui.stackdivider.SplitScreen; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.NotificationChannels; +import com.android.wm.shell.splitscreen.SplitScreen; import java.util.List; import java.util.Optional; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index 05d9fe757dfd..1d72557c6a89 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -145,13 +145,6 @@ public class ExpandableNotificationRowController implements NodeController { mOnUserInteractionCallback ); - mStatusBarStateController.addCallback(new StatusBarStateController.StateListener() { - @Override - public void onStateChanged(int newState) { - mView.setOnKeyguard(newState == KEYGUARD); - } - }); - mView.setOnKeyguard(mStatusBarStateController.getState() == KEYGUARD); mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); if (mAllowLongPress) { mView.setLongPressListener((v, x, y, item) -> { @@ -172,15 +165,26 @@ public class ExpandableNotificationRowController implements NodeController { mView.getEntry().setInitializationTime(mClock.elapsedRealtime()); mPluginManager.addPluginListener(mView, NotificationMenuRowPlugin.class, false /* Allow multiple */); + mView.setOnKeyguard(mStatusBarStateController.getState() == KEYGUARD); + mStatusBarStateController.addCallback(mStatusBarStateListener); } @Override public void onViewDetachedFromWindow(View v) { mPluginManager.removePluginListener(mView); + mStatusBarStateController.removeCallback(mStatusBarStateListener); } }); } + private final StatusBarStateController.StateListener mStatusBarStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onStateChanged(int newState) { + mView.setOnKeyguard(newState == KEYGUARD); + } + }; + private void logNotificationExpansion(String key, boolean userAction, boolean expanded) { mNotificationLogger.onExpansionChanged(key, userAction, expanded); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 298672769b56..31c1a5e5a9aa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -177,7 +177,6 @@ import com.android.systemui.recents.Recents; import com.android.systemui.recents.ScreenPinningRequest; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.shared.system.WindowManagerWrapper; -import com.android.systemui.stackdivider.SplitScreen; import com.android.systemui.statusbar.AutoHideUiElement; import com.android.systemui.statusbar.BackDropView; import com.android.systemui.statusbar.CommandQueue; @@ -231,6 +230,7 @@ import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.volume.VolumeComponent; +import com.android.wm.shell.splitscreen.SplitScreen; import java.io.FileDescriptor; import java.io.PrintWriter; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java index 16c3dc460a9c..b7f83145f477 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java @@ -46,7 +46,6 @@ import com.android.systemui.plugins.PluginDependencyProvider; import com.android.systemui.recents.Recents; import com.android.systemui.recents.ScreenPinningRequest; import com.android.systemui.shared.plugins.PluginManager; -import com.android.systemui.stackdivider.SplitScreen; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -99,6 +98,7 @@ import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.volume.VolumeComponent; +import com.android.wm.shell.splitscreen.SplitScreen; import java.util.Optional; import java.util.concurrent.Executor; diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java index d8cb280bc7e0..0869cf739d02 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java @@ -31,13 +31,13 @@ import com.android.systemui.pip.PipUiEventLogger; import com.android.systemui.pip.tv.PipController; import com.android.systemui.pip.tv.PipNotification; import com.android.systemui.pip.tv.dagger.TvPipComponent; -import com.android.systemui.stackdivider.SplitScreen; -import com.android.systemui.stackdivider.SplitScreenController; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.splitscreen.SplitScreen; +import com.android.wm.shell.splitscreen.SplitScreenController; import java.util.Optional; diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 27af5f91c9e4..c7a9af3642e5 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -16,27 +16,43 @@ package com.android.systemui.wmshell; +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; + +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE; import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import android.app.ActivityManager; +import android.content.ComponentName; import android.content.Context; +import android.graphics.Rect; +import android.inputmethodservice.InputMethodService; +import android.os.IBinder; +import android.view.KeyEvent; import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.SystemUI; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.keyguard.ScreenLifecycle; +import com.android.systemui.model.SysUiState; +import com.android.systemui.navigationbar.NavigationModeController; +import com.android.systemui.onehanded.OneHanded; +import com.android.systemui.onehanded.OneHandedEvents; +import com.android.systemui.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback; +import com.android.systemui.onehanded.OneHandedTransitionCallback; import com.android.systemui.pip.Pip; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.shared.tracing.ProtoTraceable; -import com.android.systemui.stackdivider.SplitScreen; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.tracing.nano.SystemUiTraceProto; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.nano.WmShellTraceProto; import com.android.wm.shell.protolog.ShellProtoLogImpl; +import com.android.wm.shell.splitscreen.SplitScreen; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -54,8 +70,12 @@ public final class WMShell extends SystemUI implements ProtoTraceable<SystemUiTr private final DisplayImeController mDisplayImeController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final ActivityManagerWrapper mActivityManagerWrapper; + private final NavigationModeController mNavigationModeController; + private final ScreenLifecycle mScreenLifecycle; + private final SysUiState mSysUiState; private final Optional<Pip> mPipOptional; private final Optional<SplitScreen> mSplitScreenOptional; + private final Optional<OneHanded> mOneHandedOptional; private final ProtoTracer mProtoTracer; @Inject @@ -63,16 +83,24 @@ public final class WMShell extends SystemUI implements ProtoTraceable<SystemUiTr KeyguardUpdateMonitor keyguardUpdateMonitor, ActivityManagerWrapper activityManagerWrapper, DisplayImeController displayImeController, + NavigationModeController navigationModeController, + ScreenLifecycle screenLifecycle, + SysUiState sysUiState, Optional<Pip> pipOptional, Optional<SplitScreen> splitScreenOptional, + Optional<OneHanded> oneHandedOptional, ProtoTracer protoTracer) { super(context); mCommandQueue = commandQueue; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mActivityManagerWrapper = activityManagerWrapper; mDisplayImeController = displayImeController; + mNavigationModeController = navigationModeController; + mScreenLifecycle = screenLifecycle; + mSysUiState = sysUiState; mPipOptional = pipOptional; mSplitScreenOptional = splitScreenOptional; + mOneHandedOptional = oneHandedOptional; mProtoTracer = protoTracer; mProtoTracer.add(this); } @@ -83,9 +111,9 @@ public final class WMShell extends SystemUI implements ProtoTraceable<SystemUiTr // constructor. And make sure the initialization of DisplayImeController won't depend on // specific feature anymore. mDisplayImeController.startMonitorDisplays(); - mPipOptional.ifPresent(this::initPip); mSplitScreenOptional.ifPresent(this::initSplitScreen); + mOneHandedOptional.ifPresent(this::initOneHanded); } @VisibleForTesting @@ -145,6 +173,104 @@ public final class WMShell extends SystemUI implements ProtoTraceable<SystemUiTr }); } + @VisibleForTesting + void initOneHanded(OneHanded oneHanded) { + if (!oneHanded.hasOneHandedFeature()) { + return; + } + + int currentMode = mNavigationModeController.addListener(mode -> + oneHanded.setThreeButtonModeEnabled(mode == NAV_BAR_MODE_3BUTTON)); + oneHanded.setThreeButtonModeEnabled(currentMode == NAV_BAR_MODE_3BUTTON); + + oneHanded.registerTransitionCallback(new OneHandedTransitionCallback() { + @Override + public void onStartFinished(Rect bounds) { + mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE, + true).commitUpdate(DEFAULT_DISPLAY); + } + + @Override + public void onStopFinished(Rect bounds) { + mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE, + false).commitUpdate(DEFAULT_DISPLAY); + } + }); + + oneHanded.registerGestureCallback(new OneHandedGestureEventCallback() { + @Override + public void onStart() { + if (oneHanded.isOneHandedEnabled()) { + oneHanded.startOneHanded(); + } else if (oneHanded.isSwipeToNotificationEnabled()) { + mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN); + } + } + + @Override + public void onStop() { + if (oneHanded.isOneHandedEnabled()) { + oneHanded.stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT); + } else if (oneHanded.isSwipeToNotificationEnabled()) { + mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP); + } + } + }); + + mKeyguardUpdateMonitor.registerCallback(new KeyguardUpdateMonitorCallback() { + @Override + public void onKeyguardBouncerChanged(boolean bouncer) { + if (bouncer) { + oneHanded.stopOneHanded(); + } + } + + @Override + public void onKeyguardVisibilityChanged(boolean showing) { + oneHanded.stopOneHanded(); + } + }); + + mScreenLifecycle.addObserver(new ScreenLifecycle.Observer() { + @Override + public void onScreenTurningOff() { + oneHanded.stopOneHanded( + OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_SCREEN_OFF_OUT); + } + }); + + mCommandQueue.addCallback(new CommandQueue.Callbacks() { + @Override + public void onCameraLaunchGestureDetected(int source) { + oneHanded.stopOneHanded(); + } + + @Override + public void setImeWindowStatus(int displayId, IBinder token, int vis, + int backDisposition, boolean showImeSwitcher) { + if (displayId != DEFAULT_DISPLAY && (vis & InputMethodService.IME_VISIBLE) == 0) { + return; + } + oneHanded.stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_POP_IME_OUT); + } + }); + + mActivityManagerWrapper.registerTaskStackListener( + new TaskStackChangeListener() { + @Override + public void onTaskCreated(int taskId, ComponentName componentName) { + oneHanded.stopOneHanded( + OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT); + } + + @Override + public void onTaskMovedToFront(int taskId) { + oneHanded.stopOneHanded( + OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT); + } + }); + } + @Override public void writeToProto(SystemUiTraceProto proto) { if (proto.wmShell == null) { diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index e63c6c317392..adb9186d6705 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -24,10 +24,10 @@ import android.view.IWindowManager; import com.android.internal.logging.UiEventLogger; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.onehanded.OneHanded; import com.android.systemui.pip.Pip; import com.android.systemui.pip.PipSurfaceTransactionHelper; import com.android.systemui.pip.PipUiEventLogger; -import com.android.systemui.stackdivider.SplitScreen; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.FloatingContentCoordinator; @@ -36,6 +36,7 @@ import com.android.wm.shell.animation.FlingAnimationUtils; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.splitscreen.SplitScreen; import dagger.BindsOptionalOf; import dagger.Module; @@ -101,16 +102,19 @@ public abstract class WMShellBaseModule { return organizer; } - @BindsOptionalOf - abstract Pip optionalPip(); - - @BindsOptionalOf - abstract SplitScreen optionalSplitScreen(); - @SysUISingleton @Provides static FlingAnimationUtils.Builder provideFlingAnimationUtilsBuilder( DisplayMetrics displayMetrics) { return new FlingAnimationUtils.Builder(displayMetrics); } + + @BindsOptionalOf + abstract Pip optionalPip(); + + @BindsOptionalOf + abstract SplitScreen optionalSplitScreen(); + + @BindsOptionalOf + abstract OneHanded optionalOneHanded(); } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index 1870b7605567..3a249d68d969 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -24,14 +24,14 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.model.SysUiState; +import com.android.systemui.onehanded.OneHanded; +import com.android.systemui.onehanded.OneHandedController; import com.android.systemui.pip.Pip; import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.pip.PipSurfaceTransactionHelper; import com.android.systemui.pip.PipTaskOrganizer; import com.android.systemui.pip.PipUiEventLogger; import com.android.systemui.pip.phone.PipController; -import com.android.systemui.stackdivider.SplitScreen; -import com.android.systemui.stackdivider.SplitScreenController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.FloatingContentCoordinator; @@ -40,6 +40,8 @@ import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.splitscreen.SplitScreen; +import com.android.wm.shell.splitscreen.SplitScreenController; import java.util.Optional; @@ -109,4 +111,10 @@ public class WMShellModule { pipUiEventLogger, shellTaskOrganizer); } + @SysUISingleton + @Provides + static OneHanded provideOneHandedController(Context context, + DisplayController displayController) { + return OneHandedController.create(context, displayController); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java index 57dbac5bfb10..ad5f987b59e7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java @@ -52,7 +52,6 @@ import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.recents.Recents; -import com.android.systemui.stackdivider.SplitScreen; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.phone.ShadeController; @@ -60,6 +59,7 @@ import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.wm.shell.splitscreen.SplitScreen; import org.junit.After; import org.junit.Before; diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java index 389c5a08b7c6..f308e9e479e1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java @@ -68,7 +68,6 @@ import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.recents.Recents; -import com.android.systemui.stackdivider.SplitScreen; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.phone.ShadeController; @@ -77,6 +76,7 @@ import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.utils.leaks.LeakCheckedTest; +import com.android.wm.shell.splitscreen.SplitScreen; import org.junit.Before; import org.junit.Rule; diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedControllerTest.java index 02d587d90655..e42cf529373e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedControllerTest.java @@ -22,21 +22,20 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.Display; import androidx.test.filters.SmallTest; -import com.android.systemui.model.SysUiState; -import com.android.systemui.statusbar.CommandQueue; import com.android.wm.shell.common.DisplayController; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -52,8 +51,6 @@ public class OneHandedControllerTest extends OneHandedTestCase { OneHandedTimeoutHandler mTimeoutHandler; @Mock - CommandQueue mCommandQueue; - @Mock DisplayController mMockDisplayController; @Mock OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer; @@ -64,21 +61,20 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Mock OneHandedGestureHandler mMockGestureHandler; @Mock - SysUiState mMockSysUiState; + OneHandedTimeoutHandler mMockTimeoutHandler; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mDisplay = mContext.getDisplay(); - mOneHandedController = new OneHandedController( - getContext(), - mCommandQueue, + OneHandedController oneHandedController = new OneHandedController( + mContext, mMockDisplayController, mMockDisplayAreaOrganizer, mMockTouchHandler, mMockTutorialHandler, - mMockGestureHandler, - mMockSysUiState); + mMockGestureHandler); + mOneHandedController = Mockito.spy(oneHandedController); mTimeoutHandler = Mockito.spy(OneHandedTimeoutHandler.get()); when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay); @@ -97,7 +93,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Test public void testRegisterOrganizer() { - verify(mMockDisplayAreaOrganizer).registerOrganizer(anyInt()); + verify(mMockDisplayAreaOrganizer, atLeastOnce()).registerOrganizer(anyInt()); } @Test @@ -132,7 +128,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { final boolean enabled = true; mOneHandedController.setOneHandedEnabled(enabled); - verify(mMockTouchHandler, times(2)).onOneHandedEnabled(enabled); + verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(enabled); } @Test @@ -140,6 +136,44 @@ public class OneHandedControllerTest extends OneHandedTestCase { final boolean enabled = true; mOneHandedController.setSwipeToNotificationEnabled(enabled); - verify(mMockTouchHandler, times(2)).onOneHandedEnabled(enabled); + verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(enabled); + } + + @Ignore("b/161980408, fix it after migration finished") + @Test + public void tesSettingsObserver_updateTapAppToExit() { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.TAPS_APP_TO_EXIT, 1); + + verify(mOneHandedController).setTaskChangeToExit(true); + } + + @Ignore("b/161980408, fix it after migration finished") + @Test + public void tesSettingsObserver_updateEnabled() { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.ONE_HANDED_MODE_ENABLED, 1); + + verify(mOneHandedController).setOneHandedEnabled(true); + } + + @Ignore("b/161980408, fix it after migration finished") + @Test + public void tesSettingsObserver_updateTimeout() { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.ONE_HANDED_MODE_TIMEOUT, + OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS); + + verify(mMockTimeoutHandler).setTimeout( + OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS); + } + + @Ignore("b/161980408, fix it after migration finished") + @Test + public void tesSettingsObserver_updateSwipeToNotification() { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1); + + verify(mOneHandedController).setSwipeToNotificationEnabled(true); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java index 756382a6c630..41af53b1c522 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java @@ -16,12 +16,10 @@ package com.android.systemui.onehanded; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON; - import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.times; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.verify; import android.testing.AndroidTestingRunner; @@ -29,9 +27,6 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; -import com.android.systemui.model.SysUiState; -import com.android.systemui.navigationbar.NavigationModeController; -import com.android.systemui.statusbar.CommandQueue; import com.android.wm.shell.common.DisplayController; import org.junit.Before; @@ -50,52 +45,55 @@ public class OneHandedGestureHandlerTest extends OneHandedTestCase { OneHandedGestureHandler mGestureHandler; OneHandedController mOneHandedController; @Mock - CommandQueue mCommandQueue; - @Mock DisplayController mMockDisplayController; @Mock OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer; - @Mock - SysUiState mMockSysUiState; - @Mock - NavigationModeController mMockNavigationModeController; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mTouchHandler = new OneHandedTouchHandler(); mTutorialHandler = new OneHandedTutorialHandler(mContext); - mGestureHandler = Mockito.spy(new OneHandedGestureHandler( - mContext, mMockDisplayController, mMockNavigationModeController)); + mGestureHandler = Mockito.spy( + new OneHandedGestureHandler(mContext, mMockDisplayController)); mOneHandedController = new OneHandedController( getContext(), - mCommandQueue, mMockDisplayController, mMockDisplayAreaOrganizer, mTouchHandler, mTutorialHandler, - mGestureHandler, - mMockSysUiState); + mGestureHandler); + mOneHandedController.setThreeButtonModeEnabled(true); } @Test public void testOneHandedManager_registerForDisplayAreaOrganizer() { - verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mGestureHandler); + verify(mMockDisplayAreaOrganizer, atLeastOnce()) + .registerTransitionCallback(mGestureHandler); } @Test public void testOneHandedManager_setGestureEventListener() { - verify(mGestureHandler).setGestureEventListener(any()); - - assertThat(mGestureHandler.mGestureEventCallback).isNotNull(); + OneHandedGestureHandler.OneHandedGestureEventCallback callback = + new OneHandedGestureHandler.OneHandedGestureEventCallback() { + @Override + public void onStart() {} + + @Override + public void onStop() {} + }; + mOneHandedController.registerGestureCallback(callback); + + verify(mGestureHandler).setGestureEventListener(callback); + assertThat(mGestureHandler.mGestureEventCallback).isEqualTo(callback); } @Test public void testReceiveNewConfig_whenSetOneHandedEnabled() { // 1st called at init - verify(mGestureHandler).onOneHandedEnabled(true); + verify(mGestureHandler, atLeastOnce()).onOneHandedEnabled(true); mOneHandedController.setOneHandedEnabled(true); // 2nd called by setOneHandedEnabled() - verify(mGestureHandler, times(2)).onOneHandedEnabled(true); + verify(mGestureHandler, atLeast(2)).onOneHandedEnabled(true); } @Test @@ -108,14 +106,14 @@ public class OneHandedGestureHandlerTest extends OneHandedTestCase { } @Test - public void testChangeNavBarTo2Button_shouldDisposeInputChannel() { + public void testChangeNavBarToNon3Button_shouldDisposeInputChannel() { // 1st called at init - verify(mGestureHandler).onOneHandedEnabled(true); + verify(mGestureHandler, atLeastOnce()).onOneHandedEnabled(true); mOneHandedController.setOneHandedEnabled(true); // 2nd called by setOneHandedEnabled() - verify(mGestureHandler, times(2)).onOneHandedEnabled(true); + verify(mGestureHandler, atLeast(2)).onOneHandedEnabled(true); - mGestureHandler.onNavigationModeChanged(NAV_BAR_MODE_2BUTTON); + mGestureHandler.onThreeButtonModeEnabled(false); assertThat(mGestureHandler.mInputMonitor).isNull(); assertThat(mGestureHandler.mInputEventReceiver).isNull(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTestCase.java index 04ebf25e1b49..f111c4896458 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTestCase.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTestCase.java @@ -16,8 +16,12 @@ package com.android.systemui.onehanded; +import static com.android.systemui.onehanded.OneHandedController.SUPPORT_ONE_HANDED_MODE; import static com.android.systemui.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS; +import static org.junit.Assume.assumeTrue; + +import android.os.SystemProperties; import android.provider.Settings; import com.android.systemui.SysuiTestCase; @@ -54,6 +58,11 @@ public abstract class OneHandedTestCase extends SysuiTestCase { Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1); } + @Before + public void assumeOneHandedModeSupported() { + assumeTrue(SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)); + } + @After public void restoreSettings() { Settings.Secure.putInt(getContext().getContentResolver(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java index 3c3ace052e47..1e408313a36e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java @@ -19,7 +19,8 @@ package com.android.systemui.onehanded; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.times; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.verify; import android.testing.AndroidTestingRunner; @@ -27,9 +28,6 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; -import com.android.systemui.model.SysUiState; -import com.android.systemui.navigationbar.NavigationModeController; -import com.android.systemui.statusbar.CommandQueue; import com.android.wm.shell.common.DisplayController; import org.junit.Before; @@ -48,31 +46,22 @@ public class OneHandedTouchHandlerTest extends OneHandedTestCase { OneHandedGestureHandler mGestureHandler; OneHandedController mOneHandedController; @Mock - CommandQueue mCommandQueue; - @Mock DisplayController mMockDisplayController; @Mock - NavigationModeController mMockNavigationModeController; - @Mock OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer; - @Mock - SysUiState mMockSysUiState; @Before public void setUp() { MockitoAnnotations.initMocks(this); mTouchHandler = Mockito.spy(new OneHandedTouchHandler()); - mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController, - mMockNavigationModeController); + mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController); mOneHandedController = new OneHandedController( getContext(), - mCommandQueue, mMockDisplayController, mMockDisplayAreaOrganizer, mTouchHandler, mTutorialHandler, - mGestureHandler, - mMockSysUiState); + mGestureHandler); } @Test @@ -102,10 +91,10 @@ public class OneHandedTouchHandlerTest extends OneHandedTestCase { @Test public void testReceiveNewConfig_whenSetOneHandedEnabled() { - // 1st called at init - verify(mTouchHandler).onOneHandedEnabled(true); + // Called at init + verify(mTouchHandler, atLeastOnce()).onOneHandedEnabled(true); mOneHandedController.setOneHandedEnabled(true); - // 2nd called by setOneHandedEnabled() - verify(mTouchHandler, times(2)).onOneHandedEnabled(true); + // Called by setOneHandedEnabled() + verify(mTouchHandler, atLeast(2)).onOneHandedEnabled(true); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java index 1bffbf7eb8dd..8ea5524eb7e6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java @@ -23,9 +23,6 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; -import com.android.systemui.model.SysUiState; -import com.android.systemui.navigationbar.NavigationModeController; -import com.android.systemui.statusbar.CommandQueue; import com.android.wm.shell.common.DisplayController; import org.junit.Before; @@ -44,32 +41,23 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { OneHandedGestureHandler mGestureHandler; OneHandedController mOneHandedController; @Mock - CommandQueue mCommandQueue; - @Mock DisplayController mMockDisplayController; @Mock - NavigationModeController mMockNavigationModeController; - @Mock OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer; - @Mock - SysUiState mMockSysUiState; @Before public void setUp() { MockitoAnnotations.initMocks(this); mTouchHandler = new OneHandedTouchHandler(); mTutorialHandler = Mockito.spy(new OneHandedTutorialHandler(mContext)); - mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController, - mMockNavigationModeController); + mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController); mOneHandedController = new OneHandedController( getContext(), - mCommandQueue, mMockDisplayController, mMockDisplayAreaOrganizer, mTouchHandler, mTutorialHandler, - mGestureHandler, - mMockSysUiState); + mGestureHandler); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java deleted file mode 100644 index ae3df5db30bc..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.onehanded; - -import static org.junit.Assume.assumeTrue; -import static org.mockito.Mockito.verify; - -import android.os.SystemProperties; -import android.provider.Settings; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; - -import androidx.test.filters.SmallTest; - -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.keyguard.ScreenLifecycle; -import com.android.systemui.statusbar.CommandQueue; - -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper -public class OneHandedUITest extends OneHandedTestCase { - private static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode"; - - CommandQueue mCommandQueue; - KeyguardUpdateMonitor mKeyguardUpdateMonitor; - OneHandedUI mOneHandedUI; - ScreenLifecycle mScreenLifecycle; - @Mock - OneHandedController mOneHandedController; - @Mock - OneHandedTimeoutHandler mMockTimeoutHandler; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - mCommandQueue = new CommandQueue(mContext); - mScreenLifecycle = new ScreenLifecycle(); - mOneHandedUI = new OneHandedUI(mContext, - mCommandQueue, - mOneHandedController, - mScreenLifecycle); - mOneHandedUI.start(); - mKeyguardUpdateMonitor = mDependency.injectMockDependency(KeyguardUpdateMonitor.class); - } - - @Before - public void assumeOneHandedModeSupported() { - assumeTrue(SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)); - } - - @Test - public void testStartOneHanded() { - mOneHandedUI.startOneHanded(); - - verify(mOneHandedController).startOneHanded(); - } - - @Test - public void testStopOneHanded() { - mOneHandedUI.stopOneHanded(); - - verify(mOneHandedController).stopOneHanded(); - } - - @Test - public void tesSettingsObserver_updateTapAppToExit() { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.TAPS_APP_TO_EXIT, 1); - - verify(mOneHandedController).setTaskChangeToExit(true); - } - - @Test - public void tesSettingsObserver_updateEnabled() { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.ONE_HANDED_MODE_ENABLED, 1); - - verify(mOneHandedController).setOneHandedEnabled(true); - } - - @Test - public void tesSettingsObserver_updateTimeout() { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.ONE_HANDED_MODE_TIMEOUT, - OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS); - - verify(mMockTimeoutHandler).setTimeout( - OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS); - } - - @Test - public void tesSettingsObserver_updateSwipeToNotification() { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1); - - verify(mOneHandedController).setSwipeToNotificationEnabled(true); - } - - @Ignore("Clarifying do not receive callback") - @Test - public void testKeyguardBouncerShowing_shouldStopOneHanded() { - mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true); - - verify(mOneHandedController).stopOneHanded(); - } - - @Test - public void testScreenTurningOff_shouldStopOneHanded() { - mScreenLifecycle.dispatchScreenTurningOff(); - - verify(mOneHandedController).stopOneHanded(); - } - -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 5143596f0214..7d8a62607395 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -96,7 +96,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.Recents; import com.android.systemui.recents.ScreenPinningRequest; import com.android.systemui.shared.plugins.PluginManager; -import com.android.systemui.stackdivider.SplitScreen; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.NotificationListener; @@ -143,6 +142,7 @@ import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.volume.VolumeComponent; +import com.android.wm.shell.splitscreen.SplitScreen; import org.junit.Before; import org.junit.Test; diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java index ac9346f675be..51cc5f175444 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java @@ -18,6 +18,7 @@ package com.android.systemui.wmshell; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.test.suitebuilder.annotation.SmallTest; @@ -26,13 +27,19 @@ import androidx.test.runner.AndroidJUnit4; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.SysuiTestCase; +import com.android.systemui.keyguard.ScreenLifecycle; +import com.android.systemui.model.SysUiState; +import com.android.systemui.navigationbar.NavigationModeController; +import com.android.systemui.onehanded.OneHanded; +import com.android.systemui.onehanded.OneHandedGestureHandler; +import com.android.systemui.onehanded.OneHandedTransitionCallback; import com.android.systemui.pip.Pip; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.TaskStackChangeListener; -import com.android.systemui.stackdivider.SplitScreen; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.tracing.ProtoTracer; import com.android.wm.shell.common.DisplayImeController; +import com.android.wm.shell.splitscreen.SplitScreen; import org.junit.Before; import org.junit.Test; @@ -51,16 +58,21 @@ public class WMShellTest extends SysuiTestCase { @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Mock ActivityManagerWrapper mActivityManagerWrapper; @Mock DisplayImeController mDisplayImeController; + @Mock NavigationModeController mNavigationModeController; + @Mock ScreenLifecycle mScreenLifecycle; + @Mock SysUiState mSysUiState; @Mock Pip mPip; @Mock SplitScreen mSplitScreen; + @Mock OneHanded mOneHanded; @Mock ProtoTracer mProtoTracer; @Before public void setUp() { MockitoAnnotations.initMocks(this); mWMShell = new WMShell(mContext, mCommandQueue, mKeyguardUpdateMonitor, - mActivityManagerWrapper, mDisplayImeController, Optional.of(mPip), - Optional.of(mSplitScreen), mProtoTracer); + mActivityManagerWrapper, mDisplayImeController, mNavigationModeController, + mScreenLifecycle, mSysUiState, Optional.of(mPip), Optional.of(mSplitScreen), + Optional.of(mOneHanded), mProtoTracer); } @Test @@ -85,4 +97,22 @@ public class WMShellTest extends SysuiTestCase { verify(mActivityManagerWrapper).registerTaskStackListener( any(TaskStackChangeListener.class)); } + + @Test + public void initOneHanded_registersCallbacks() { + when(mOneHanded.hasOneHandedFeature()).thenReturn(true); + mWMShell.initOneHanded(mOneHanded); + + verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class)); + verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks.class)); + verify(mScreenLifecycle).addObserver(any(ScreenLifecycle.Observer.class)); + verify(mNavigationModeController).addListener( + any(NavigationModeController.ModeChangedListener.class)); + verify(mActivityManagerWrapper).registerTaskStackListener( + any(TaskStackChangeListener.class)); + + verify(mOneHanded).registerGestureCallback(any( + OneHandedGestureHandler.OneHandedGestureEventCallback.class)); + verify(mOneHanded).registerTransitionCallback(any(OneHandedTransitionCallback.class)); + } } diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java index b09b2605a791..500e768372f5 100644 --- a/services/core/java/com/android/server/DynamicSystemService.java +++ b/services/core/java/com/android/server/DynamicSystemService.java @@ -126,6 +126,16 @@ public class DynamicSystemService extends IDynamicSystemService.Stub { } @Override + public boolean closePartition() throws RemoteException { + IGsiService service = getGsiService(); + if (service.closePartition() != 0) { + Slog.i(TAG, "Partition installation completes with error"); + return false; + } + return true; + } + + @Override public boolean finishInstallation() throws RemoteException { IGsiService service = getGsiService(); if (service.closeInstall() != 0) { diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index cd3e21052efb..87898d80bd46 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -1528,15 +1528,18 @@ public final class ProcessList { && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY && proc.lastCachedPss >= 4000) { // Turn this condition on to cause killing to happen regularly, for testing. - if (proc.baseProcessTracker != null) { - proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss); - for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) { - ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg); - FrameworkStatsLog.write(FrameworkStatsLog.CACHED_KILL_REPORTED, - proc.info.uid, - holder.state.getName(), - holder.state.getPackage(), - proc.lastCachedPss, holder.appVersion); + synchronized (mService.mProcessStats.mLock) { + if (proc.baseProcessTracker != null) { + proc.baseProcessTracker.reportCachedKill( + proc.pkgList.mPkgList, proc.lastCachedPss); + for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) { + ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg); + FrameworkStatsLog.write(FrameworkStatsLog.CACHED_KILL_REPORTED, + proc.info.uid, + holder.state.getName(), + holder.state.getPackage(), + proc.lastCachedPss, holder.appVersion); + } } } proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", @@ -1549,16 +1552,18 @@ public final class ProcessList { if (DEBUG_PSS) Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + proc .lastCachedPss); if (proc.lastCachedPss >= getCachedRestoreThresholdKb()) { - if (proc.baseProcessTracker != null) { - proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, - proc.lastCachedPss); - for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) { - ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg); - FrameworkStatsLog.write(FrameworkStatsLog.CACHED_KILL_REPORTED, - proc.info.uid, - holder.state.getName(), - holder.state.getPackage(), - proc.lastCachedPss, holder.appVersion); + synchronized (mService.mProcessStats.mLock) { + if (proc.baseProcessTracker != null) { + proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, + proc.lastCachedPss); + for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) { + ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg); + FrameworkStatsLog.write(FrameworkStatsLog.CACHED_KILL_REPORTED, + proc.info.uid, + holder.state.getName(), + holder.state.getPackage(), + proc.lastCachedPss, holder.appVersion); + } } } proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 5447605a36d1..1615998f7787 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -28,6 +28,7 @@ import android.media.AudioDeviceAttributes; import android.media.AudioRoutesInfo; import android.media.AudioSystem; import android.media.IAudioRoutesObserver; +import android.media.ICapturePresetDevicesRoleDispatcher; import android.media.IStrategyPreferredDevicesDispatcher; import android.media.MediaMetrics; import android.os.Binder; @@ -546,6 +547,25 @@ import java.util.concurrent.atomic.AtomicBoolean; mDeviceInventory.unregisterStrategyPreferredDevicesDispatcher(dispatcher); } + /*package*/ int setPreferredDevicesForCapturePresetSync(int capturePreset, + @NonNull List<AudioDeviceAttributes> devices) { + return mDeviceInventory.setPreferredDevicesForCapturePresetSync(capturePreset, devices); + } + + /*package*/ int clearPreferredDevicesForCapturePresetSync(int capturePreset) { + return mDeviceInventory.clearPreferredDevicesForCapturePresetSync(capturePreset); + } + + /*package*/ void registerCapturePresetDevicesRoleDispatcher( + @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) { + mDeviceInventory.registerCapturePresetDevicesRoleDispatcher(dispatcher); + } + + /*package*/ void unregisterCapturePresetDevicesRoleDispatcher( + @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) { + mDeviceInventory.unregisterCapturePresetDevicesRoleDispatcher(dispatcher); + } + //--------------------------------------------------------------------- // Communication with (to) AudioService //TODO check whether the AudioService methods are candidates to move here @@ -694,6 +714,17 @@ import java.util.concurrent.atomic.AtomicBoolean; sendIMsgNoDelay(MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY, SENDMSG_QUEUE, strategy); } + /*package*/ void postSaveSetPreferredDevicesForCapturePreset( + int capturePreset, List<AudioDeviceAttributes> devices) { + sendILMsgNoDelay( + MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET, SENDMSG_QUEUE, capturePreset, devices); + } + + /*package*/ void postSaveClearPreferredDevicesForCapturePreset(int capturePreset) { + sendIMsgNoDelay( + MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET, SENDMSG_QUEUE, capturePreset); + } + //--------------------------------------------------------------------- // Method forwarding between the helper classes (BtHelper, AudioDeviceInventory) // only call from a "handle"* method or "on"* method @@ -1098,6 +1129,17 @@ import java.util.concurrent.atomic.AtomicBoolean; case MSG_CHECK_MUTE_MUSIC: checkMessagesMuteMusic(0); break; + case MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET: { + final int capturePreset = msg.arg1; + final List<AudioDeviceAttributes> devices = + (List<AudioDeviceAttributes>) msg.obj; + mDeviceInventory.onSaveSetPreferredDevicesForCapturePreset( + capturePreset, devices); + } break; + case MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET: { + final int capturePreset = msg.arg1; + mDeviceInventory.onSaveClearPreferredDevicesForCapturePreset(capturePreset); + } break; default: Log.wtf(TAG, "Invalid message " + msg.what); } @@ -1174,6 +1216,9 @@ import java.util.concurrent.atomic.AtomicBoolean; private static final int MSG_CHECK_MUTE_MUSIC = 36; private static final int MSG_REPORT_NEW_ROUTES_A2DP = 37; + private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET = 38; + private static final int MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET = 39; + private static boolean isMessageHandledUnderWakelock(int msgId) { switch(msgId) { diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index fbf07cc591ff..33a8a30243de 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -31,6 +31,7 @@ import android.media.AudioPort; import android.media.AudioRoutesInfo; import android.media.AudioSystem; import android.media.IAudioRoutesObserver; +import android.media.ICapturePresetDevicesRoleDispatcher; import android.media.IStrategyPreferredDevicesDispatcher; import android.media.MediaMetrics; import android.os.Binder; @@ -140,6 +141,10 @@ public class AudioDeviceInventory { private final ArrayMap<Integer, List<AudioDeviceAttributes>> mPreferredDevices = new ArrayMap<>(); + // List of preferred devices of capture preset + private final ArrayMap<Integer, List<AudioDeviceAttributes>> mPreferredDevicesForCapturePreset = + new ArrayMap<>(); + // the wrapper for AudioSystem static methods, allows us to spy AudioSystem private final @NonNull AudioSystemAdapter mAudioSystem; @@ -154,6 +159,10 @@ public class AudioDeviceInventory { final RemoteCallbackList<IStrategyPreferredDevicesDispatcher> mPrefDevDispatchers = new RemoteCallbackList<IStrategyPreferredDevicesDispatcher>(); + // Monitoring of devices for role and capture preset + final RemoteCallbackList<ICapturePresetDevicesRoleDispatcher> mDevRoleCapturePresetDispatchers = + new RemoteCallbackList<ICapturePresetDevicesRoleDispatcher>(); + /*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) { mDeviceBroker = broker; mAudioSystem = AudioSystemAdapter.getDefaultAdapter(); @@ -243,6 +252,9 @@ public class AudioDeviceInventory { pw.println(" " + prefix + " type:0x" + Integer.toHexString(keyType) + " (" + AudioSystem.getDeviceName(keyType) + ") addr:" + valueAddress); }); + mPreferredDevicesForCapturePreset.forEach((capturePreset, devices) -> { + pw.println(" " + prefix + "capturePreset:" + capturePreset + + " devices:" + devices); }); } //------------------------------------------------------------ @@ -270,6 +282,9 @@ public class AudioDeviceInventory { mAudioSystem.setDevicesRoleForStrategy( strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices); }); } + synchronized (mPreferredDevicesForCapturePreset) { + // TODO: call audiosystem to restore + } } // only public for mocking/spying @@ -613,6 +628,20 @@ public class AudioDeviceInventory { dispatchPreferredDevice(strategy, new ArrayList<AudioDeviceAttributes>()); } + /*package*/ void onSaveSetPreferredDevicesForCapturePreset( + int capturePreset, @NonNull List<AudioDeviceAttributes> devices) { + mPreferredDevicesForCapturePreset.put(capturePreset, devices); + dispatchDevicesRoleForCapturePreset( + capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices); + } + + /*package*/ void onSaveClearPreferredDevicesForCapturePreset(int capturePreset) { + mPreferredDevicesForCapturePreset.remove(capturePreset); + dispatchDevicesRoleForCapturePreset( + capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, + new ArrayList<AudioDeviceAttributes>()); + } + //------------------------------------------------------------ // @@ -651,6 +680,41 @@ public class AudioDeviceInventory { mPrefDevDispatchers.unregister(dispatcher); } + /*package*/ int setPreferredDevicesForCapturePresetSync( + int capturePreset, @NonNull List<AudioDeviceAttributes> devices) { + final long identity = Binder.clearCallingIdentity(); + final int status = mAudioSystem.setDevicesRoleForCapturePreset( + capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices); + Binder.restoreCallingIdentity(identity); + + if (status == AudioSystem.SUCCESS) { + mDeviceBroker.postSaveSetPreferredDevicesForCapturePreset(capturePreset, devices); + } + return status; + } + + /*package*/ int clearPreferredDevicesForCapturePresetSync(int capturePreset) { + final long identity = Binder.clearCallingIdentity(); + final int status = mAudioSystem.clearDevicesRoleForCapturePreset( + capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED); + Binder.restoreCallingIdentity(identity); + + if (status == AudioSystem.SUCCESS) { + mDeviceBroker.postSaveClearPreferredDevicesForCapturePreset(capturePreset); + } + return status; + } + + /*package*/ void registerCapturePresetDevicesRoleDispatcher( + @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) { + mDevRoleCapturePresetDispatchers.register(dispatcher); + } + + /*package*/ void unregisterCapturePresetDevicesRoleDispatcher( + @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) { + mDevRoleCapturePresetDispatchers.unregister(dispatcher); + } + /** * Implements the communication with AudioSystem to (dis)connect a device in the native layers * @param connect true if connection @@ -1306,6 +1370,19 @@ public class AudioDeviceInventory { mPrefDevDispatchers.finishBroadcast(); } + private void dispatchDevicesRoleForCapturePreset( + int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) { + final int nbDispatchers = mDevRoleCapturePresetDispatchers.beginBroadcast(); + for (int i = 0; i < nbDispatchers; ++i) { + try { + mDevRoleCapturePresetDispatchers.getBroadcastItem(i).dispatchDevicesRoleChanged( + capturePreset, role, devices); + } catch (RemoteException e) { + } + } + mDevRoleCapturePresetDispatchers.finishBroadcast(); + } + //---------------------------------------------------------- // For tests only diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 4378490d19c5..5f6491093453 100755 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -84,6 +84,7 @@ import android.media.IAudioFocusDispatcher; import android.media.IAudioRoutesObserver; import android.media.IAudioServerStateDispatcher; import android.media.IAudioService; +import android.media.ICapturePresetDevicesRoleDispatcher; import android.media.IPlaybackConfigDispatcher; import android.media.IRecordingConfigDispatcher; import android.media.IRingtonePlayer; @@ -1987,6 +1988,94 @@ public class AudioService extends IAudioService.Stub mDeviceBroker.unregisterStrategyPreferredDevicesDispatcher(dispatcher); } + /** + * @see AudioManager#setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes) + */ + public int setPreferredDevicesForCapturePreset( + int capturePreset, List<AudioDeviceAttributes> devices) { + if (devices == null) { + return AudioSystem.ERROR; + } + enforceModifyAudioRoutingPermission(); + final String logString = String.format( + "setPreferredDevicesForCapturePreset u/pid:%d/%d source:%d dev:%s", + Binder.getCallingUid(), Binder.getCallingPid(), capturePreset, + devices.stream().map(e -> e.toString()).collect(Collectors.joining(","))); + sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG)); + if (devices.stream().anyMatch(device -> + device.getRole() == AudioDeviceAttributes.ROLE_OUTPUT)) { + Log.e(TAG, "Unsupported output routing in " + logString); + return AudioSystem.ERROR; + } + + final int status = mDeviceBroker.setPreferredDevicesForCapturePresetSync( + capturePreset, devices); + if (status != AudioSystem.SUCCESS) { + Log.e(TAG, String.format("Error %d in %s)", status, logString)); + } + + return status; + } + + /** @see AudioManager#clearPreferredDevicesForCapturePreset(int) */ + public int clearPreferredDevicesForCapturePreset(int capturePreset) { + enforceModifyAudioRoutingPermission(); + final String logString = String.format( + "removePreferredDeviceForCapturePreset source:%d", capturePreset); + sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG)); + + final int status = mDeviceBroker.clearPreferredDevicesForCapturePresetSync(capturePreset); + if (status != AudioSystem.SUCCESS) { + Log.e(TAG, String.format("Error %d in %s", status, logString)); + } + return status; + } + + /** + * @see AudioManager#getPreferredDevicesForCapturePreset(int) + */ + public List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int capturePreset) { + enforceModifyAudioRoutingPermission(); + List<AudioDeviceAttributes> devices = new ArrayList<>(); + final long identity = Binder.clearCallingIdentity(); + final int status = AudioSystem.getDevicesForRoleAndCapturePreset( + capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices); + Binder.restoreCallingIdentity(identity); + if (status != AudioSystem.SUCCESS) { + Log.e(TAG, String.format("Error %d in getPreferredDeviceForCapturePreset(%d)", + status, capturePreset)); + return new ArrayList<AudioDeviceAttributes>(); + } else { + return devices; + } + } + + /** + * @see AudioManager#addOnPreferredDevicesForCapturePresetChangedListener( + * Executor, OnPreferredDevicesForCapturePresetChangedListener) + */ + public void registerCapturePresetDevicesRoleDispatcher( + @Nullable ICapturePresetDevicesRoleDispatcher dispatcher) { + if (dispatcher == null) { + return; + } + enforceModifyAudioRoutingPermission(); + mDeviceBroker.registerCapturePresetDevicesRoleDispatcher(dispatcher); + } + + /** + * @see AudioManager#removeOnPreferredDevicesForCapturePresetChangedListener( + * AudioManager.OnPreferredDevicesForCapturePresetChangedListener) + */ + public void unregisterCapturePresetDevicesRoleDispatcher( + @Nullable ICapturePresetDevicesRoleDispatcher dispatcher) { + if (dispatcher == null) { + return; + } + enforceModifyAudioRoutingPermission(); + mDeviceBroker.unregisterCapturePresetDevicesRoleDispatcher(dispatcher); + } + /** @see AudioManager#getDevicesForAttributes(AudioAttributes) */ public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes( @NonNull AudioAttributes attributes) { diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java index a0e1ca78a5e7..ae64990fd8d0 100644 --- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java +++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java @@ -101,6 +101,40 @@ public class AudioSystemAdapter { } /** + * Same as (@link AudioSystem#setDevicesRoleForCapturePreset(int, List)) + * @param capturePreset + * @param role + * @param devices + * @return + */ + public int setDevicesRoleForCapturePreset(int capturePreset, int role, + @NonNull List<AudioDeviceAttributes> devices) { + return AudioSystem.setDevicesRoleForCapturePreset(capturePreset, role, devices); + } + + /** + * Same as {@link AudioSystem#removeDevicesRoleForCapturePreset(int, int)} + * @param capturePreset + * @param role + * @param devicesToRemove + * @return + */ + public int removeDevicesRoleForCapturePreset( + int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devicesToRemove) { + return AudioSystem.removeDevicesRoleForCapturePreset(capturePreset, role, devicesToRemove); + } + + /** + * Same as {@link AudioSystem#} + * @param capturePreset + * @param role + * @return + */ + public int clearDevicesRoleForCapturePreset(int capturePreset, int role) { + return AudioSystem.clearDevicesRoleForCapturePreset(capturePreset, role); + } + + /** * Same as {@link AudioSystem#setParameters(String)} * @param keyValuePairs * @return diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 2c632d96e738..4a12ee71adbe 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -83,6 +83,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.text.TextUtils; +import android.util.EventLog; import android.util.IntArray; import android.util.Pair; import android.util.Slog; @@ -283,6 +284,7 @@ public final class DisplayManagerService extends SystemService { // Temporary display info, used for comparing display configurations. private final DisplayInfo mTempDisplayInfo = new DisplayInfo(); + private final DisplayInfo mTempNonOverrideDisplayInfo = new DisplayInfo(); // Temporary viewports, used when sending new viewport information to the // input system. May be used outside of the lock but only on the handler thread. @@ -507,7 +509,8 @@ public final class DisplayManagerService extends SystemService { mDisplayTransactionListeners.remove(listener); } - private void setDisplayInfoOverrideFromWindowManagerInternal( + @VisibleForTesting + void setDisplayInfoOverrideFromWindowManagerInternal( int displayId, DisplayInfo info) { synchronized (mSyncRoot) { LogicalDisplay display = mLogicalDisplays.get(displayId); @@ -935,7 +938,8 @@ public final class DisplayManagerService extends SystemService { adapter.registerLocked(); } - private void handleDisplayDeviceAdded(DisplayDevice device) { + @VisibleForTesting + void handleDisplayDeviceAdded(DisplayDevice device) { synchronized (mSyncRoot) { handleDisplayDeviceAddedLocked(device); } @@ -947,7 +951,6 @@ public final class DisplayManagerService extends SystemService { Slog.w(TAG, "Attempted to add already added display device: " + info); return; } - Slog.i(TAG, "Display device added: " + info); device.mDebugLastLoggedDeviceInfo = info; @@ -960,7 +963,8 @@ public final class DisplayManagerService extends SystemService { scheduleTraversalLocked(false); } - private void handleDisplayDeviceChanged(DisplayDevice device) { + @VisibleForTesting + void handleDisplayDeviceChanged(DisplayDevice device) { synchronized (mSyncRoot) { DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); if (!mDisplayDevices.contains(device)) { @@ -1234,6 +1238,7 @@ public final class DisplayManagerService extends SystemService { LogicalDisplay display = mLogicalDisplays.valueAt(i); mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked()); + display.getNonOverrideDisplayInfoLocked(mTempNonOverrideDisplayInfo); display.updateLocked(mDisplayDevices); if (!display.isValidLocked()) { mLogicalDisplays.removeAt(i); @@ -1242,6 +1247,15 @@ public final class DisplayManagerService extends SystemService { } else if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) { handleLogicalDisplayChanged(displayId, display); changed = true; + } else { + // While applications shouldn't know nor care about the non-overridden info, we + // still need to let WindowManager know so it can update its own internal state for + // things like display cutouts. + display.getNonOverrideDisplayInfoLocked(mTempDisplayInfo); + if (!mTempNonOverrideDisplayInfo.equals(mTempDisplayInfo)) { + handleLogicalDisplayChanged(displayId, display); + changed = true; + } } } return changed; @@ -2176,6 +2190,8 @@ public final class DisplayManagerService extends SystemService { if (callingUid != Process.SYSTEM_UID && (flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) { if (!checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) { + EventLog.writeEvent(0x534e4554, "162627132", callingUid, + "Attempt to create a trusted display without holding permission!"); throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to " + "create a trusted virtual display."); } diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java index 817902d9d566..b61c6a7ca569 100644 --- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java +++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java @@ -205,8 +205,13 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { } if (DEBUG_INTEGRITY_COMPONENT) { - Slog.i(TAG, String.format("Successfully pushed rule set: %s", version)); + Slog.i( + TAG, + String.format( + "Successfully pushed rule set to version '%s' from '%s'", + version, ruleProvider)); } + FrameworkStatsLog.write( FrameworkStatsLog.INTEGRITY_RULES_PUSHED, success, @@ -324,13 +329,12 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { + getAllowedInstallers(packageInfo)); } IntegrityCheckResult result = mEvaluationEngine.evaluate(appInstallMetadata); - if (DEBUG_INTEGRITY_COMPONENT) { + if (!result.getMatchedRules().isEmpty() || DEBUG_INTEGRITY_COMPONENT) { Slog.i( TAG, - "Integrity check result: " - + result.getEffect() - + " due to " - + result.getMatchedRules()); + String.format( + "Integrity check of %s result: %s due to %s", + packageName, result.getEffect(), result.getMatchedRules())); } FrameworkStatsLog.write( @@ -673,8 +677,10 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { // Obtain the system apps that are whitelisted in config_integrityRuleProviderPackages. List<String> allowedRuleProviders = getAllowedRuleProviderSystemApps(); if (DEBUG_INTEGRITY_COMPONENT) { - Slog.i(TAG, String.format( - "Rule provider system app list contains: %s", allowedRuleProviders)); + Slog.i( + TAG, + String.format( + "Rule provider system app list contains: %s", allowedRuleProviders)); } // Identify the package names in the caller list. @@ -730,9 +736,9 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { private boolean integrityCheckIncludesRuleProvider() { return Settings.Global.getInt( - mContext.getContentResolver(), - Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, - 0) + mContext.getContentResolver(), + Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, + 0) == 1; } diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 534533f2fc97..f4d0a6254318 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -697,7 +697,8 @@ public class LocationManagerService extends ILocationManager.Stub { return null; } - Location location = manager.getLastLocation(request, identity, permissionLevel); + Location location = manager.getLastLocation(identity, permissionLevel, + request.isLocationSettingsIgnored()); // lastly - note app ops if (!mInjector.getAppOpsHelper().noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel), @@ -740,13 +741,9 @@ public class LocationManagerService extends ILocationManager.Stub { return null; } - // create a location request that works in almost all circumstances - LocationRequest request = LocationRequest.createFromDeprecatedProvider(GPS_PROVIDER, 0, - 0, true); - - // use our own identity rather than the caller - CallerIdentity identity = CallerIdentity.fromContext(mContext); - Location location = gpsManager.getLastLocation(request, identity, PERMISSION_FINE); + // use fine permission level to avoid creating unnecessary coarse locations + Location location = gpsManager.getLastLocationUnsafe(UserHandle.USER_ALL, + PERMISSION_FINE, false); if (location == null) { return null; } diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java index 66245a279666..1815a8554705 100644 --- a/services/core/java/com/android/server/location/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/LocationProviderManager.java @@ -434,18 +434,17 @@ class LocationProviderManager extends } LocationRequest newRequest = calculateProviderLocationRequest(); - if (!mProviderLocationRequest.equals(newRequest)) { - LocationRequest oldRequest = mProviderLocationRequest; - mProviderLocationRequest = newRequest; - onHighPowerUsageChanged(); - updateService(); - - // if location settings ignored has changed then the active state may have changed - return oldRequest.isLocationSettingsIgnored() - != newRequest.isLocationSettingsIgnored(); + if (mProviderLocationRequest.equals(newRequest)) { + return false; } - return false; + LocationRequest oldRequest = mProviderLocationRequest; + mProviderLocationRequest = newRequest; + onHighPowerUsageChanged(); + updateService(); + + // if location settings ignored has changed then the active state may have changed + return oldRequest.isLocationSettingsIgnored() != newRequest.isLocationSettingsIgnored(); } private LocationRequest calculateProviderLocationRequest() { @@ -1229,10 +1228,8 @@ class LocationProviderManager extends } @Nullable - public Location getLastLocation(LocationRequest request, CallerIdentity identity, - @PermissionLevel int permissionLevel) { - Preconditions.checkArgument(mName.equals(request.getProvider())); - + public Location getLastLocation(CallerIdentity identity, @PermissionLevel int permissionLevel, + boolean ignoreLocationSettings) { if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(), identity.getPackageName())) { return null; @@ -1240,12 +1237,12 @@ class LocationProviderManager extends if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) { return null; } - if (!request.isLocationSettingsIgnored() && !isEnabled(identity.getUserId())) { + if (!ignoreLocationSettings && !isEnabled(identity.getUserId())) { return null; } - Location location = getLastLocation(identity.getUserId(), permissionLevel, - request.isLocationSettingsIgnored()); + Location location = getLastLocationUnsafe(identity.getUserId(), permissionLevel, + ignoreLocationSettings); // we don't note op here because we don't know what the client intends to do with the // location, the client is responsible for noting if necessary @@ -1259,9 +1256,30 @@ class LocationProviderManager extends } } + /** + * This function does not perform any permissions or safety checks, by calling it you are + * committing to performing all applicable checks yourself. Prefer + * {@link #getLastLocation(CallerIdentity, int, boolean)} where possible. + */ @Nullable - private Location getLastLocation(int userId, @PermissionLevel int permissionLevel, + public Location getLastLocationUnsafe(int userId, @PermissionLevel int permissionLevel, boolean ignoreLocationSettings) { + if (userId == UserHandle.USER_ALL) { + Location lastLocation = null; + final int[] runningUserIds = mUserInfoHelper.getRunningUserIds(); + for (int i = 0; i < runningUserIds.length; i++) { + Location next = getLastLocationUnsafe(runningUserIds[i], permissionLevel, + ignoreLocationSettings); + if (lastLocation == null || (next != null && next.getElapsedRealtimeNanos() + > lastLocation.getElapsedRealtimeNanos())) { + lastLocation = next; + } + } + return lastLocation; + } + + Preconditions.checkArgument(userId >= 0); + synchronized (mLock) { LastLocation lastLocation = mLastLocations.get(userId); if (lastLocation == null) { @@ -1273,7 +1291,7 @@ class LocationProviderManager extends public void injectLastLocation(Location location, int userId) { synchronized (mLock) { - if (getLastLocation(userId, PERMISSION_FINE, false) == null) { + if (getLastLocationUnsafe(userId, PERMISSION_FINE, false) == null) { setLastLocation(location, userId); } } @@ -1322,7 +1340,22 @@ class LocationProviderManager extends permissionLevel); synchronized (mLock) { - Location lastLocation = getLastLocation(request, callerIdentity, permissionLevel); + if (mSettingsHelper.isLocationPackageBlacklisted(callerIdentity.getUserId(), + callerIdentity.getPackageName())) { + registration.deliverLocation(null); + return; + } + if (!mUserInfoHelper.isCurrentUserId(callerIdentity.getUserId())) { + registration.deliverLocation(null); + return; + } + if (!request.isLocationSettingsIgnored() && !isEnabled(callerIdentity.getUserId())) { + registration.deliverLocation(null); + return; + } + + Location lastLocation = getLastLocationUnsafe(callerIdentity.getUserId(), + permissionLevel, request.isLocationSettingsIgnored()); if (lastLocation != null) { long locationAgeMs = NANOSECONDS.toMillis( SystemClock.elapsedRealtimeNanos() @@ -1346,17 +1379,17 @@ class LocationProviderManager extends } finally { Binder.restoreCallingIdentity(identity); } + } - CancellationSignal cancellationSignal = CancellationSignal.fromTransport( - cancellationTransport); - if (cancellationSignal != null) { - cancellationSignal.setOnCancelListener( - () -> { - synchronized (mLock) { - removeRegistration(callback.asBinder(), registration); - } - }); - } + CancellationSignal cancellationSignal = CancellationSignal.fromTransport( + cancellationTransport); + if (cancellationSignal != null) { + cancellationSignal.setOnCancelListener(SingleUseCallback.wrap( + () -> { + synchronized (mLock) { + removeRegistration(callback.asBinder(), registration); + } + })); } } @@ -1934,7 +1967,8 @@ class LocationProviderManager extends ipw.println("user " + userId + ":"); ipw.increaseIndent(); } - ipw.println("last location=" + getLastLocation(userId, PERMISSION_FINE, false)); + ipw.println( + "last location=" + getLastLocationUnsafe(userId, PERMISSION_FINE, false)); ipw.println("enabled=" + isEnabled(userId)); if (userIds.length != 1) { ipw.decreaseIndent(); @@ -2007,7 +2041,7 @@ class LocationProviderManager extends } // update last coarse interval only if enough time has passed long timeDeltaMs = NANOSECONDS.toMillis(newCoarse.getElapsedRealtimeNanos()) - - NANOSECONDS.toMillis(oldCoarse.getElapsedRealtimeNanos()); + - NANOSECONDS.toMillis(oldCoarse.getElapsedRealtimeNanos()); if (timeDeltaMs > FASTEST_COARSE_INTERVAL_MS) { return newCoarse; } else { @@ -2016,10 +2050,11 @@ class LocationProviderManager extends } } - private static class SingleUseCallback extends IRemoteCallback.Stub { + private static class SingleUseCallback extends IRemoteCallback.Stub implements Runnable, + CancellationSignal.OnCancelListener { @Nullable - public static IRemoteCallback wrap(@Nullable Runnable callback) { + public static SingleUseCallback wrap(@Nullable Runnable callback) { return callback == null ? null : new SingleUseCallback(callback); } @@ -2032,6 +2067,16 @@ class LocationProviderManager extends @Override public void sendResult(Bundle data) { + run(); + } + + @Override + public void onCancel() { + run(); + } + + @Override + public void run() { Runnable callback; synchronized (this) { callback = mCallback; diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 9f68d627622c..56e404c8f409 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -7786,42 +7786,40 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A /** Gets the horizontal centered container bounds for size compatibility mode. */ void getContainerBounds(Rect outAppBounds, Rect outBounds, int rotation, int orientation, boolean orientationRequested, boolean canChangeOrientation) { + getFrameByOrientation(outBounds, orientation); if (mIsFloating) { - getFrameByOrientation(outBounds, orientation); outAppBounds.set(outBounds); return; } - if (canChangeOrientation) { - getBoundsByRotation(outBounds, rotation); - if (orientationRequested) { - getFrameByOrientation(outAppBounds, orientation); - } else { - outAppBounds.set(outBounds); - } - } else { - if (orientationRequested) { - getFrameByOrientation(outBounds, orientation); - if ((outBounds.width() > outBounds.height()) != (mWidth > mHeight)) { - // The orientation is mismatched but the display cannot rotate. The bounds - // will fit to the short side of display. - if (orientation == ORIENTATION_LANDSCAPE) { - outBounds.bottom = (int) ((float) mWidth * mWidth / mHeight); - outBounds.right = mWidth; - } else { - outBounds.bottom = mHeight; - outBounds.right = (int) ((float) mHeight * mHeight / mWidth); - } - outBounds.offset( - getHorizontalCenterOffset(mWidth, outBounds.width()), 0 /* dy */); - } + getBoundsByRotation(outAppBounds, rotation); + final int dW = outAppBounds.width(); + final int dH = outAppBounds.height(); + final boolean isOrientationMismatched = + ((outBounds.width() > outBounds.height()) != (dW > dH)); + + if (isOrientationMismatched && !canChangeOrientation && orientationRequested) { + // The orientation is mismatched but the display cannot rotate. The bounds will fit + // to the short side of container. + if (orientation == ORIENTATION_LANDSCAPE) { + outBounds.bottom = (int) ((float) dW * dW / dH); + outBounds.right = dW; } else { - outBounds.set(0, 0, mWidth, mHeight); + outBounds.bottom = dH; + outBounds.right = (int) ((float) dH * dH / dW); } - outAppBounds.set(outBounds); - } - - if (rotation != ROTATION_UNDEFINED) { + outBounds.offset(getHorizontalCenterOffset(mWidth, outBounds.width()), 0 /* dy */); + } + outAppBounds.set(outBounds); + + if (isOrientationMismatched) { + // One side of container is smaller than the requested size, then it will be scaled + // and the final position will be calculated according to the parent container and + // scale, so the original size shouldn't be shrunk by insets. + final Rect insets = mNonDecorInsets[rotation]; + outBounds.offset(insets.left, insets.top); + outAppBounds.offset(insets.left, insets.top); + } else if (rotation != ROTATION_UNDEFINED) { // Ensure the app bounds won't overlap with insets. Task.intersectWithInsetsIfFits(outAppBounds, outBounds, mNonDecorInsets[rotation]); } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index abfbf2eb8b67..4998624b9097 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -29,6 +29,7 @@ import static android.view.InsetsState.ITYPE_BOTTOM_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES; import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT; import static android.view.InsetsState.ITYPE_CAPTION_BAR; +import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_LEFT_GESTURES; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; @@ -63,6 +64,7 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; @@ -2055,6 +2057,7 @@ public class DisplayPolicy { final int type = attrs.type; final int fl = PolicyControl.getWindowFlags(win, attrs); + final int pfl = attrs.privateFlags; final int sim = attrs.softInputMode; displayFrames = win.getDisplayFrames(displayFrames); @@ -2102,6 +2105,13 @@ public class DisplayPolicy { df.set(dfu.left + left, dfu.top + top, dfu.right - right, dfu.bottom - bottom); if (attached == null) { pf.set(df); + if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) { + final InsetsSource source = mDisplayContent.getInsetsPolicy() + .getInsetsForDispatch(win).peekSource(ITYPE_IME); + if (source != null) { + pf.inset(source.calculateInsets(pf, false /* ignoreVisibility */)); + } + } vf.set(adjust != SOFT_INPUT_ADJUST_NOTHING ? displayFrames.mCurrent : displayFrames.mDock); } else { @@ -2118,8 +2128,8 @@ public class DisplayPolicy { if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) { final boolean attachedInParent = attached != null && !layoutInScreen; final InsetsState requestedInsetsState = win.getRequestedInsetsState(); - final boolean requestedFullscreen = - !requestedInsetsState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR); + final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0 + || !requestedInsetsState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR); final boolean requestedHideNavigation = !requestedInsetsState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 19179a808d7c..017747f03ca0 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1410,7 +1410,8 @@ public class WindowManagerService extends IWindowManager.Stub if (!displayContent.hasAccess(session.mUid)) { ProtoLog.w(WM_ERROR, "Attempted to add window to a display for which the application " - + "does not have access: %d. Aborting.", displayId); + + "does not have access: %d. Aborting.", + displayContent.getDisplayId()); return WindowManagerGlobal.ADD_INVALID_DISPLAY; } diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java index d6894cf2a4e8..80ad0a838bbb 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java @@ -274,60 +274,55 @@ public class LocationProviderManagerTest { @Test public void testGetLastLocation_Fine() { - LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull(); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull(); Location loc = createLocation(NAME, mRandom); mProvider.setProviderLocation(loc); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc); } @Test public void testGetLastLocation_Coarse() { - LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull(); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull(); Location loc = createLocation(NAME, mRandom); mProvider.setProviderLocation(loc); - Location coarse = mManager.getLastLocation(request, IDENTITY, PERMISSION_COARSE); + Location coarse = mManager.getLastLocation(IDENTITY, PERMISSION_COARSE, false); assertThat(coarse).isNotEqualTo(loc); assertThat(coarse).isNearby(loc, 5000); } @Test public void testGetLastLocation_Bypass() { - LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false); - LocationRequest bypassRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, - false).setLocationSettingsIgnored(true); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull(); - assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isNull(); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull(); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isNull(); Location loc = createLocation(NAME, mRandom); mProvider.setProviderLocation(loc); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc); - assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo( + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isEqualTo( loc); mProvider.setProviderAllowed(false); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull(); - assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo( + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull(); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isEqualTo( loc); loc = createLocation(NAME, mRandom); mProvider.setProviderLocation(loc); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull(); - assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo( + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull(); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isEqualTo( loc); mProvider.setProviderAllowed(true); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull(); - assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo( + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull(); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isEqualTo( loc); loc = createLocation(NAME, mRandom); mProvider.setProviderLocation(loc); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc); - assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo( + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isEqualTo( loc); } @@ -337,13 +332,12 @@ public class LocationProviderManagerTest { mockProvider.setAllowed(true); mManager.setMockProvider(mockProvider); - LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false); Location loc = createLocation(NAME, mRandom); mockProvider.setProviderLocation(loc); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc); mManager.setMockProvider(null); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull(); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull(); } @Test @@ -351,13 +345,12 @@ public class LocationProviderManagerTest { Location loc1 = createLocation(NAME, mRandom); mManager.injectLastLocation(loc1, CURRENT_USER); - LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc1); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc1); Location loc2 = createLocation(NAME, mRandom); mManager.injectLastLocation(loc2, CURRENT_USER); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc1); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc1); } @Test @@ -381,9 +374,7 @@ public class LocationProviderManagerTest { Location loc = createLocation(NAME, mRandom); mProvider.setProviderLocation(loc); - LocationRequest request = LocationRequest.createFromDeprecatedProvider(PASSIVE_PROVIDER, 0, - 0, false); - assertThat(mPassive.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc); + assertThat(mPassive.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc); } @Test diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java index 609af8d5bf4d..8d706cb960e9 100644 --- a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java +++ b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java @@ -79,6 +79,23 @@ public class NoOpAudioSystemAdapter extends AudioSystemAdapter { } @Override + public int setDevicesRoleForCapturePreset(int capturePreset, int role, + @NonNull List<AudioDeviceAttributes> devices) { + return AudioSystem.AUDIO_STATUS_OK; + } + + @Override + public int removeDevicesRoleForCapturePreset( + int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devicesToRemove) { + return AudioSystem.AUDIO_STATUS_OK; + } + + @Override + public int clearDevicesRoleForCapturePreset(int capturePreset, int role) { + return AudioSystem.AUDIO_STATUS_OK; + } + + @Override public int setParameters(String keyValuePairs) { return AudioSystem.AUDIO_STATUS_OK; } diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java index b1f3871274ac..73dda0736d2f 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -19,6 +19,7 @@ package com.android.server.display; import static com.android.server.display.VirtualDisplayAdapter.UNIQUE_ID_PREFIX; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -28,18 +29,23 @@ import static org.mockito.Mockito.when; import android.app.PropertyInvalidatedCache; import android.content.Context; +import android.graphics.Insets; +import android.graphics.Rect; import android.hardware.display.BrightnessConfiguration; import android.hardware.display.Curve; import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.DisplayViewport; import android.hardware.display.DisplayedContentSample; import android.hardware.display.DisplayedContentSamplingAttributes; +import android.hardware.display.IDisplayManagerCallback; import android.hardware.display.IVirtualDisplayCallback; import android.hardware.display.VirtualDisplayConfig; import android.hardware.input.InputManagerInternal; import android.os.Handler; import android.os.IBinder; import android.view.Display; +import android.view.DisplayCutout; import android.view.DisplayInfo; import android.view.Surface; import android.view.SurfaceControl; @@ -282,6 +288,68 @@ public class DisplayManagerServiceTest { } /** + * Tests that there should be a display change notification to WindowManager to update its own + * internal state for things like display cutout when nonOverrideDisplayInfo is changed. + */ + @Test + public void testShouldNotifyChangeWhenNonOverrideDisplayInfoChanged() throws Exception { + DisplayManagerService displayManager = + new DisplayManagerService(mContext, mShortMockedInjector); + registerDefaultDisplays(displayManager); + displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY); + + // Add the FakeDisplayDevice + FakeDisplayDevice displayDevice = new FakeDisplayDevice(); + DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo(); + displayDeviceInfo.width = 100; + displayDeviceInfo.height = 200; + final Rect zeroRect = new Rect(); + displayDeviceInfo.displayCutout = new DisplayCutout( + Insets.of(0, 10, 0, 0), + zeroRect, new Rect(0, 0, 10, 10), zeroRect, zeroRect); + displayDeviceInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY; + displayDevice.setDisplayDeviceInfo(displayDeviceInfo); + displayManager.handleDisplayDeviceAdded(displayDevice); + + // Find the display id of the added FakeDisplayDevice + DisplayManagerService.BinderService bs = displayManager.new BinderService(); + final int[] displayIds = bs.getDisplayIds(); + assertTrue(displayIds.length > 0); + int displayId = Display.INVALID_DISPLAY; + for (int i = 0; i < displayIds.length; i++) { + DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayIds[i]); + if (displayDeviceInfo.equals(ddi)) { + displayId = displayIds[i]; + break; + } + } + assertFalse(displayId == Display.INVALID_DISPLAY); + + // Setup override DisplayInfo + DisplayInfo overrideInfo = bs.getDisplayInfo(displayId); + displayManager.setDisplayInfoOverrideFromWindowManagerInternal(displayId, overrideInfo); + + Handler handler = displayManager.getDisplayHandler(); + handler.runWithScissors(() -> { + }, 0 /* now */); + + // register display listener callback + FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback(displayId); + bs.registerCallback(callback); + + // Simulate DisplayDevice change + DisplayDeviceInfo displayDeviceInfo2 = new DisplayDeviceInfo(); + displayDeviceInfo2.copyFrom(displayDeviceInfo); + displayDeviceInfo2.displayCutout = null; + displayDevice.setDisplayDeviceInfo(displayDeviceInfo2); + displayManager.handleDisplayDeviceChanged(displayDevice); + + handler.runWithScissors(() -> { + }, 0 /* now */); + assertTrue(callback.mCalled); + } + + /** * Tests that we get a Runtime exception when we cannot initialize the default display. */ @Test @@ -512,4 +580,42 @@ public class DisplayManagerServiceTest { // flush the handler handler.runWithScissors(() -> {}, 0 /* now */); } + + private class FakeDisplayManagerCallback extends IDisplayManagerCallback.Stub { + int mDisplayId; + boolean mCalled = false; + + FakeDisplayManagerCallback(int displayId) { + mDisplayId = displayId; + } + + @Override + public void onDisplayEvent(int displayId, int event) { + if (displayId == mDisplayId && event == DisplayManagerGlobal.EVENT_DISPLAY_CHANGED) { + mCalled = true; + } + } + } + + private class FakeDisplayDevice extends DisplayDevice { + private DisplayDeviceInfo mDisplayDeviceInfo; + + FakeDisplayDevice() { + super(null, null, ""); + } + + public void setDisplayDeviceInfo(DisplayDeviceInfo displayDeviceInfo) { + mDisplayDeviceInfo = displayDeviceInfo; + } + + @Override + public boolean hasStableUniqueId() { + return false; + } + + @Override + public DisplayDeviceInfo getDisplayDeviceInfoLocked() { + return mDisplayDeviceInfo; + } + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index a5585c0f02b1..94e40413f9f8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -34,6 +34,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; @@ -369,6 +370,24 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { } @Test + public void layoutWindowLw_insetParentFrameByIme() { + final InsetsState state = + mDisplayContent.getInsetsStateController().getRawInsetsState(); + state.getSource(InsetsState.ITYPE_IME).setVisible(true); + state.getSource(InsetsState.ITYPE_IME).setFrame( + 0, DISPLAY_HEIGHT - IME_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT); + mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; + mWindow.mBehindIme = true; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, IME_HEIGHT); + } + + @Test public void layoutWindowLw_fitDisplayCutout() { addDisplayCutout(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java index b4e1c375993d..af8cb02a86fe 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java @@ -62,7 +62,7 @@ public class DisplayPolicyTestsBase extends WindowTestsBase { static final int STATUS_BAR_HEIGHT = 10; static final int NAV_BAR_HEIGHT = 15; static final int DISPLAY_CUTOUT_HEIGHT = 8; - static final int INPUT_METHOD_WINDOW_TOP = 585; + static final int IME_HEIGHT = 415; DisplayPolicy mDisplayPolicy; diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 6c648a894821..468b2c35ffc2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -35,7 +35,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doCallRealMethod; import android.app.ActivityManager; import android.app.ActivityManagerInternal; @@ -216,22 +218,50 @@ public class SizeCompatTests extends WindowTestsBase { final Rect origBounds = new Rect(mActivity.getBounds()); final Rect currentBounds = mActivity.getWindowConfiguration().getBounds(); + final DisplayContent display = mActivity.mDisplayContent; // Change the size of current display. - resizeDisplay(mStack.mDisplayContent, 1000, 2000); - + resizeDisplay(display, 1000, 2000); + // The bounds should be [100, 0 - 1100, 2500]. assertEquals(origBounds.width(), currentBounds.width()); assertEquals(origBounds.height(), currentBounds.height()); assertScaled(); + // The scale is 2000/2500=0.8. The horizontal centered offset is (1000-(1000*0.8))/2=100. + final float scale = (float) display.mBaseDisplayHeight / currentBounds.height(); + final int offsetX = (int) (display.mBaseDisplayWidth - (origBounds.width() * scale)) / 2; + assertEquals(offsetX, currentBounds.left); + // The position of configuration bounds should be the same as compat bounds. assertEquals(mActivity.getBounds().left, currentBounds.left); assertEquals(mActivity.getBounds().top, currentBounds.top); // Change display size to a different orientation - resizeDisplay(mStack.mDisplayContent, 2000, 1000); + resizeDisplay(display, 2000, 1000); + // The bounds should be [800, 0 - 1800, 2500]. assertEquals(origBounds.width(), currentBounds.width()); assertEquals(origBounds.height(), currentBounds.height()); + assertEquals(Configuration.ORIENTATION_LANDSCAPE, display.getConfiguration().orientation); + assertEquals(Configuration.ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation); + + // The previous resize operation doesn't consider the rotation change after size changed. + // These setups apply the requested orientation to rotation as real case that the top fixed + // portrait activity will determine the display rotation. + final DisplayRotation displayRotation = display.getDisplayRotation(); + doCallRealMethod().when(displayRotation).updateRotationUnchecked(anyBoolean()); + // Skip unrelated layout procedures. + mAtm.deferWindowLayout(); + display.reconfigureDisplayLocked(); + displayRotation.updateOrientation(display.getOrientation(), true /* forceUpdate */); + display.sendNewConfiguration(); + + assertEquals(Configuration.ORIENTATION_PORTRAIT, display.getConfiguration().orientation); + assertEquals(Configuration.ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation); + // The size should still be in portrait [100, 0 - 1100, 2500] = 1000x2500. + assertEquals(origBounds.width(), currentBounds.width()); + assertEquals(origBounds.height(), currentBounds.height()); + assertEquals(offsetX, currentBounds.left); + assertScaled(); } @Test diff --git a/tests/SilkFX/Android.bp b/tests/SilkFX/Android.bp index ca0a091e65bb..92e3efa7fd55 100644 --- a/tests/SilkFX/Android.bp +++ b/tests/SilkFX/Android.bp @@ -19,4 +19,10 @@ android_test { srcs: ["**/*.java", "**/*.kt"], platform_apis: true, certificate: "platform", + static_libs: [ + "androidx.core_core", + "androidx.appcompat_appcompat", + "com.google.android.material_material", + "androidx-constraintlayout_constraintlayout", + ], } diff --git a/tests/SilkFX/AndroidManifest.xml b/tests/SilkFX/AndroidManifest.xml index ca9550a9eeab..050e9c33aeac 100644 --- a/tests/SilkFX/AndroidManifest.xml +++ b/tests/SilkFX/AndroidManifest.xml @@ -39,5 +39,8 @@ <activity android:name=".hdr.GlowActivity" android:label="Glow Examples"/> + <activity android:name=".materials.GlassActivity" + android:label="Glass Examples"/> + </application> </manifest> diff --git a/tests/SilkFX/res/drawable-hdpi/background1.jpeg b/tests/SilkFX/res/drawable-hdpi/background1.jpeg Binary files differnew file mode 100644 index 000000000000..dcdfa7b850bc --- /dev/null +++ b/tests/SilkFX/res/drawable-hdpi/background1.jpeg diff --git a/tests/SilkFX/res/drawable-hdpi/background2.jpeg b/tests/SilkFX/res/drawable-hdpi/background2.jpeg Binary files differnew file mode 100644 index 000000000000..dc7ce84e6784 --- /dev/null +++ b/tests/SilkFX/res/drawable-hdpi/background2.jpeg diff --git a/tests/SilkFX/res/drawable-hdpi/background3.jpeg b/tests/SilkFX/res/drawable-hdpi/background3.jpeg Binary files differnew file mode 100644 index 000000000000..12b3429e3920 --- /dev/null +++ b/tests/SilkFX/res/drawable-hdpi/background3.jpeg diff --git a/tests/SilkFX/res/drawable-hdpi/noise.png b/tests/SilkFX/res/drawable-hdpi/noise.png Binary files differnew file mode 100644 index 000000000000..053995dad760 --- /dev/null +++ b/tests/SilkFX/res/drawable-hdpi/noise.png diff --git a/tests/SilkFX/res/layout/activity_glass.xml b/tests/SilkFX/res/layout/activity_glass.xml new file mode 100644 index 000000000000..85dab9315197 --- /dev/null +++ b/tests/SilkFX/res/layout/activity_glass.xml @@ -0,0 +1,246 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** Copyright 2012, 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. +--> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".MainActivity"> + + <ImageView + android:id="@+id/background" + android:layout_width="0dp" + android:layout_height="0dp" + android:scaleType="matrix" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:srcCompat="@drawable/background1" /> + + <com.android.test.silkfx.materials.GlassView + android:id="@+id/materialView" + android:layout_width="0dp" + android:layout_height="180dp" + android:layout_marginEnd="64dp" + android:layout_marginStart="64dp" + app:layout_constraintBottom_toTopOf="@+id/bottomPanel" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/bottomPanel" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:background="?android:attr/colorBackground" + android:paddingTop="24dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintBottom_toBottomOf="parent"> + + <SeekBar + android:id="@+id/materialOpacity" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="12dp" + android:layout_marginEnd="12dp" + android:layout_marginBottom="16dp" + android:max="100" + android:progress="12" + app:layout_constraintBottom_toTopOf="@+id/scrimOpacityTitle" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="1.0" + app:layout_constraintStart_toStartOf="parent" /> + + <SeekBar + android:id="@+id/blurRadius" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginBottom="16dp" + android:layout_marginEnd="12dp" + android:layout_marginStart="12dp" + android:max="150" + android:progress="50" + app:layout_constraintBottom_toTopOf="@+id/materialOpacityTitle" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="1.0" + app:layout_constraintStart_toStartOf="parent" /> + + <SeekBar + android:id="@+id/scrimOpacity" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="12dp" + android:layout_marginEnd="12dp" + android:layout_marginBottom="16dp" + android:max="100" + android:progress="50" + app:layout_constraintBottom_toTopOf="@+id/noiseOpacityTitle" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="1.0" + app:layout_constraintStart_toStartOf="parent" /> + + <SeekBar + android:id="@+id/noiseOpacity" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="12dp" + android:layout_marginEnd="12dp" + android:layout_marginBottom="24dp" + android:max="100" + android:progress="5" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.0" + app:layout_constraintStart_toStartOf="parent" /> + + <TextView + android:id="@+id/scrimOpacityTitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="24dp" + android:layout_marginBottom="8dp" + android:text="Scrim Opacity" + android:textColor="@android:color/white" + app:layout_constraintBottom_toTopOf="@+id/scrimOpacity" + app:layout_constraintStart_toStartOf="parent" /> + + <TextView + android:id="@+id/materialOpacityTitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="24dp" + android:layout_marginBottom="8dp" + android:text="Material Opacity" + android:textColor="@android:color/white" + app:layout_constraintBottom_toTopOf="@+id/materialOpacity" + app:layout_constraintStart_toStartOf="parent" /> + + <TextView + android:id="@+id/blurRadiusTitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="24dp" + android:layout_marginBottom="8dp" + android:text="Blur Radius" + android:textColor="@android:color/white" + app:layout_constraintBottom_toTopOf="@+id/blurRadius" + app:layout_constraintStart_toStartOf="parent" /> + + <TextView + android:id="@+id/noiseOpacityTitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="24dp" + android:layout_marginBottom="8dp" + android:textColor="@android:color/white" + android:text="Noise Opacity" + app:layout_constraintBottom_toTopOf="@+id/noiseOpacity" + app:layout_constraintStart_toStartOf="parent" /> + + <ImageView + android:id="@+id/background1" + android:layout_width="64dp" + android:layout_height="64dp" + android:layout_marginStart="24dp" + android:layout_marginBottom="16dp" + android:foreground="?android:attr/selectableItemBackgroundBorderless" + android:clickable="true" + android:onClick="onBackgroundClick" + android:scaleType="centerCrop" + app:layout_constraintBottom_toTopOf="@+id/lightMaterialSwitch" + app:layout_constraintStart_toStartOf="parent" + android:src="@drawable/background1" /> + + <ImageView + android:id="@+id/background2" + android:layout_width="64dp" + android:layout_height="64dp" + android:layout_marginStart="8dp" + android:foreground="?android:attr/selectableItemBackgroundBorderless" + android:clickable="true" + android:onClick="onBackgroundClick" + android:scaleType="centerCrop" + app:layout_constraintBottom_toBottomOf="@+id/background1" + app:layout_constraintStart_toEndOf="@+id/background1" + android:src="@drawable/background2" /> + + <ImageView + android:id="@+id/background3" + android:layout_width="64dp" + android:layout_height="64dp" + android:layout_marginStart="8dp" + android:scaleType="centerCrop" + android:foreground="?android:attr/selectableItemBackgroundBorderless" + android:clickable="true" + android:onClick="onBackgroundClick" + app:layout_constraintBottom_toBottomOf="@+id/background1" + app:layout_constraintStart_toEndOf="@+id/background2" + android:src="@drawable/background3" /> + + <Switch + android:id="@+id/lightMaterialSwitch" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="24dp" + android:layout_marginBottom="8dp" + android:text="Light Material" + app:layout_constraintBottom_toTopOf="@+id/blurRadiusTitle" + app:layout_constraintStart_toStartOf="parent" /> + + <TextView + android:id="@+id/blurRadiusValue" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="TextView" + android:layout_marginLeft="8dp" + app:layout_constraintBottom_toBottomOf="@+id/blurRadiusTitle" + app:layout_constraintStart_toEndOf="@+id/blurRadiusTitle" /> + + <TextView + android:id="@+id/materialOpacityValue" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="TextView" + android:layout_marginLeft="8dp" + app:layout_constraintBottom_toBottomOf="@+id/materialOpacityTitle" + app:layout_constraintStart_toEndOf="@+id/materialOpacityTitle" /> + + <TextView + android:id="@+id/noiseOpacityValue" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="TextView" + android:layout_marginLeft="8dp" + app:layout_constraintBottom_toBottomOf="@+id/noiseOpacityTitle" + app:layout_constraintStart_toEndOf="@+id/noiseOpacityTitle" /> + + + <TextView + android:id="@+id/scrimOpacityValue" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="TextView" + android:layout_marginLeft="8dp" + app:layout_constraintBottom_toBottomOf="@+id/scrimOpacityTitle" + app:layout_constraintStart_toEndOf="@+id/scrimOpacityTitle" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + + +</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file diff --git a/tests/SilkFX/src/com/android/test/silkfx/Main.kt b/tests/SilkFX/src/com/android/test/silkfx/Main.kt index 76e62a6c8cff..9ed8d2f5edf7 100644 --- a/tests/SilkFX/src/com/android/test/silkfx/Main.kt +++ b/tests/SilkFX/src/com/android/test/silkfx/Main.kt @@ -29,6 +29,7 @@ import com.android.test.silkfx.app.CommonDemoActivity import com.android.test.silkfx.app.EXTRA_LAYOUT import com.android.test.silkfx.app.EXTRA_TITLE import com.android.test.silkfx.hdr.GlowActivity +import com.android.test.silkfx.materials.GlassActivity import kotlin.reflect.KClass class Demo(val name: String, val makeIntent: (Context) -> Intent) { @@ -48,6 +49,9 @@ private val AllDemos = listOf( DemoGroup("HDR", listOf( Demo("Glow", GlowActivity::class), Demo("Blingy Notifications", R.layout.bling_notifications) + )), + DemoGroup("Materials", listOf( + Demo("Glass", GlassActivity::class) )) ) diff --git a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassActivity.kt b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassActivity.kt new file mode 100644 index 000000000000..6f5ddacb2733 --- /dev/null +++ b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassActivity.kt @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.test.silkfx.materials + +import android.app.Activity +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.Color +import android.os.Bundle +import android.util.TypedValue +import android.view.View +import android.widget.ImageView +import android.widget.SeekBar +import android.widget.Switch +import android.widget.TextView +import com.android.test.silkfx.R + +class GlassActivity : Activity(), SeekBar.OnSeekBarChangeListener { + + lateinit var backgroundButton1: ImageView + lateinit var backgroundButton2: ImageView + lateinit var backgroundButton3: ImageView + lateinit var backgroundView: ImageView + lateinit var materialView: GlassView + lateinit var lightMaterialSwitch: Switch + lateinit var noiseOpacitySeekBar: SeekBar + lateinit var materialOpacitySeekBar: SeekBar + lateinit var scrimOpacitySeekBar: SeekBar + lateinit var blurRadiusSeekBar: SeekBar + lateinit var noiseOpacityValue: TextView + lateinit var materialOpacityValue: TextView + lateinit var scrimOpacityValue: TextView + lateinit var blurRadiusValue: TextView + + lateinit var background: Bitmap + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_glass) + backgroundButton1 = requireViewById(R.id.background1) + backgroundButton2 = requireViewById(R.id.background2) + backgroundButton3 = requireViewById(R.id.background3) + backgroundView = requireViewById(R.id.background) + lightMaterialSwitch = requireViewById(R.id.lightMaterialSwitch) + materialView = requireViewById(R.id.materialView) + materialOpacitySeekBar = requireViewById(R.id.materialOpacity) + blurRadiusSeekBar = requireViewById(R.id.blurRadius) + noiseOpacitySeekBar = requireViewById(R.id.noiseOpacity) + scrimOpacitySeekBar = requireViewById(R.id.scrimOpacity) + noiseOpacityValue = requireViewById(R.id.noiseOpacityValue) + materialOpacityValue = requireViewById(R.id.materialOpacityValue) + scrimOpacityValue = requireViewById(R.id.scrimOpacityValue) + blurRadiusValue = requireViewById(R.id.blurRadiusValue) + + background = BitmapFactory.decodeResource(resources, R.drawable.background1) + backgroundView.setImageBitmap(background) + materialView.backgroundBitmap = background + + blurRadiusSeekBar.setOnSeekBarChangeListener(this) + materialOpacitySeekBar.setOnSeekBarChangeListener(this) + noiseOpacitySeekBar.setOnSeekBarChangeListener(this) + scrimOpacitySeekBar.setOnSeekBarChangeListener(this) + + arrayOf(blurRadiusSeekBar, materialOpacitySeekBar, noiseOpacitySeekBar, + scrimOpacitySeekBar).forEach { + it.setOnSeekBarChangeListener(this) + onProgressChanged(it, it.progress, fromUser = false) + } + + lightMaterialSwitch.setOnCheckedChangeListener { _, isChecked -> + materialView.color = if (isChecked) Color.WHITE else Color.BLACK + } + } + + override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { + when (seekBar) { + blurRadiusSeekBar -> { + materialView.blurRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + progress.toFloat(), resources.displayMetrics) + blurRadiusValue.text = progress.toString() + } + materialOpacitySeekBar -> { + materialView.materialOpacity = progress / seekBar.max.toFloat() + materialOpacityValue.text = progress.toString() + } + noiseOpacitySeekBar -> { + materialView.noiseOpacity = progress / seekBar.max.toFloat() + noiseOpacityValue.text = progress.toString() + } + scrimOpacitySeekBar -> { + materialView.scrimOpacity = progress / seekBar.max.toFloat() + scrimOpacityValue.text = progress.toString() + } + else -> throw IllegalArgumentException("Unknown seek bar") + } + } + + override fun onStartTrackingTouch(seekBar: SeekBar?) {} + override fun onStopTrackingTouch(seekBar: SeekBar?) {} + + fun onBackgroundClick(view: View) { + val resource = when (view) { + backgroundButton1 -> R.drawable.background1 + backgroundButton2 -> R.drawable.background2 + backgroundButton3 -> R.drawable.background3 + else -> throw IllegalArgumentException("Invalid button") + } + + background = BitmapFactory.decodeResource(resources, resource) + backgroundView.setImageBitmap(background) + materialView.backgroundBitmap = background + } +}
\ No newline at end of file diff --git a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt new file mode 100644 index 000000000000..e100959908c3 --- /dev/null +++ b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.test.silkfx.materials + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.BitmapShader +import android.graphics.BlendMode +import android.graphics.BlurShader +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Outline +import android.graphics.Paint +import android.graphics.Rect +import android.graphics.Shader +import android.util.AttributeSet +import android.view.View +import android.view.ViewOutlineProvider +import com.android.test.silkfx.R + +class GlassView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet) { + + var noise = BitmapFactory.decodeResource(resources, R.drawable.noise) + var materialPaint = Paint() + var scrimPaint = Paint() + var noisePaint = Paint() + var blurPaint = Paint() + + val src = Rect() + val dst = Rect() + + var backgroundBitmap: Bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888) + set(value) { + field = value + invalidate() + } + + var noiseOpacity = 0.0f + set(value) { + field = value + noisePaint.alpha = (value * 255).toInt() + invalidate() + } + + var materialOpacity = 0.0f + set(value) { + field = value + materialPaint.alpha = (value * 255).toInt() + invalidate() + } + + var scrimOpacity = 0.5f + set(value) { + field = value + scrimPaint.alpha = (value * 255).toInt() + invalidate() + } + + var color = Color.BLACK + set(value) { + field = value + var alpha = materialPaint.alpha + materialPaint.color = color + materialPaint.alpha = alpha + + alpha = scrimPaint.alpha + scrimPaint.color = color + scrimPaint.alpha = alpha + invalidate() + } + + var blurRadius = 150f + set(value) { + field = value + blurPaint.shader = BlurShader(value, value, null) + invalidate() + } + + init { + materialPaint.blendMode = BlendMode.SOFT_LIGHT + noisePaint.blendMode = BlendMode.SOFT_LIGHT + noisePaint.shader = BitmapShader(noise, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT) + scrimPaint.alpha = (scrimOpacity * 255).toInt() + noisePaint.alpha = (noiseOpacity * 255).toInt() + materialPaint.alpha = (materialOpacity * 255).toInt() + blurPaint.shader = BlurShader(blurRadius, blurRadius, null) + outlineProvider = object : ViewOutlineProvider() { + override fun getOutline(view: View?, outline: Outline?) { + outline?.setRoundRect(Rect(0, 0, width, height), 100f) + } + } + clipToOutline = true + } + + override fun onDraw(canvas: Canvas?) { + src.set(left, top, right, bottom) + dst.set(0, 0, width, height) + canvas?.drawBitmap(backgroundBitmap, src, dst, blurPaint) + canvas?.drawRect(dst, materialPaint) + canvas?.drawRect(dst, noisePaint) + canvas?.drawRect(dst, scrimPaint) + } +}
\ No newline at end of file |