diff options
34 files changed, 1394 insertions, 574 deletions
diff --git a/Android.bp b/Android.bp index bef32513d18b..31dbaadfd332 100644 --- a/Android.bp +++ b/Android.bp @@ -931,7 +931,7 @@ stubs_defaults { "sdk-dir", "api-versions-jars-dir", ], - previous_api: ":last-released-public-api", + previous_api: ":last-released-public-api-for-metalava-annotations", merge_annotations_dirs: [ "metalava-manual", "ojluni-annotated-sdk-stubs", @@ -988,7 +988,7 @@ stubs_defaults { local_sourcepaths: frameworks_base_subdirs, installable: false, annotations_enabled: true, - previous_api: ":last-released-public-api", + previous_api: ":last-released-public-api-for-metalava-annotations", merge_annotations_dirs: [ "metalava-manual", "ojluni-annotated-sdk-stubs", @@ -1324,7 +1324,7 @@ droidstubs { installable: false, sdk_version: "core_platform", annotations_enabled: true, - previous_api: ":last-released-public-api", + previous_api: ":last-released-public-api-for-metalava-annotations", merge_annotations_dirs: [ "metalava-manual", "ojluni-annotated-sdk-stubs", diff --git a/api/current.txt b/api/current.txt index b4d110e13261..d688220fd17b 100644 --- a/api/current.txt +++ b/api/current.txt @@ -29463,7 +29463,8 @@ package android.net.rtp { } public class AudioGroup { - ctor public AudioGroup(); + ctor @Deprecated public AudioGroup(); + ctor public AudioGroup(@Nullable android.content.Context); method public void clear(); method public int getMode(); method public android.net.rtp.AudioStream[] getStreams(); diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 43058d538552..05ff49045b17 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -99,6 +99,8 @@ cc_defaults { "src/shell/shell_config.proto", "src/shell/ShellSubscriber.cpp", "src/socket/StatsSocketListener.cpp", + "src/state/StateManager.cpp", + "src/state/StateTracker.cpp", "src/stats_log_util.cpp", "src/statscompanion_util.cpp", "src/statsd_config.proto", @@ -253,6 +255,7 @@ cc_test { "tests/metrics/ValueMetricProducer_test.cpp", "tests/MetricsManager_test.cpp", "tests/shell/ShellSubscriber_test.cpp", + "tests/state/StateTracker_test.cpp", "tests/statsd_test_util.cpp", "tests/StatsLogProcessor_test.cpp", "tests/StatsService_test.cpp", diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp index af8b3af6ea61..5e156bb26caa 100644 --- a/cmds/statsd/src/HashableDimensionKey.cpp +++ b/cmds/statsd/src/HashableDimensionKey.cpp @@ -59,6 +59,16 @@ android::hash_t hashDimension(const HashableDimensionKey& value) { return JenkinsHashWhiten(hash); } +bool filterValues(const Matcher& matcherField, const vector<FieldValue>& values, Value* output) { + for (const auto& value : values) { + if (value.mField.matches(matcherField)) { + (*output) = value.mValue; + return true; + } + } + return false; +} + bool filterValues(const vector<Matcher>& matcherFields, const vector<FieldValue>& values, HashableDimensionKey* output) { size_t num_matches = 0; diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h index 6f4941f717ee..a12385057585 100644 --- a/cmds/statsd/src/HashableDimensionKey.h +++ b/cmds/statsd/src/HashableDimensionKey.h @@ -120,6 +120,13 @@ class MetricDimensionKey { android::hash_t hashDimension(const HashableDimensionKey& key); /** + * Returns true if a FieldValue field matches the matcher field. + * The value of the FieldValue is output. + */ +bool filterValues(const Matcher& matcherField, const std::vector<FieldValue>& values, + Value* output); + +/** * Creating HashableDimensionKeys from FieldValues using matcher. * * This function may make modifications to the Field if the matcher has Position=FIRST,LAST or ALL @@ -169,4 +176,4 @@ struct hash<MetricDimensionKey> { return android::JenkinsHashWhiten(hash); } }; -} // namespace std
\ No newline at end of file +} // namespace std diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 1c7180ffbde1..b665a8b99fbd 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -266,7 +266,9 @@ status_t StatsService::onTransact(uint32_t code, const Parcel& data, Parcel* rep IResultReceiver::asInterface(data.readStrongBinder()); err = command(in, out, err, args, resultReceiver); - resultReceiver->send(err); + if (resultReceiver != nullptr) { + resultReceiver->send(err); + } return NO_ERROR; } default: { return BnStatsManager::onTransact(code, data, reply, flags); } @@ -411,13 +413,20 @@ status_t StatsService::command(int in, int out, int err, Vector<String8>& args, return cmd_trigger_active_config_broadcast(out, args); } if (!args[0].compare(String8("data-subscribe"))) { - if (mShellSubscriber == nullptr) { - mShellSubscriber = new ShellSubscriber(mUidMap, mPullerManager); + { + std::lock_guard<std::mutex> lock(mShellSubscriberMutex); + if (mShellSubscriber == nullptr) { + mShellSubscriber = new ShellSubscriber(mUidMap, mPullerManager); + } } int timeoutSec = -1; if (argCount >= 2) { timeoutSec = atoi(args[1].c_str()); } + if (resultReceiver == nullptr) { + ALOGI("Null resultReceiver given, no subscription will be started"); + return UNEXPECTED_NULL; + } mShellSubscriber->startNewSubscription(in, out, resultReceiver, timeoutSec); return NO_ERROR; } diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 53b6ce989195..949094871936 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -432,6 +432,10 @@ private: sp<ShellSubscriber> mShellSubscriber; + /** + * Mutex for setting the shell subscriber + */ + mutable mutex mShellSubscriberMutex; std::shared_ptr<LogEventQueue> mEventQueue; FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 78a6609e5e0d..b71a86b87d49 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -340,7 +340,7 @@ message Atom { } // Pulled events will start at field 10000. - // Next: 10064 + // Next: 10065 oneof pulled { WifiBytesTransfer wifi_bytes_transfer = 10000; WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001; @@ -406,6 +406,7 @@ message Atom { ProcessSystemIonHeapSize process_system_ion_heap_size = 10061; SurfaceflingerStatsGlobalInfo surfaceflinger_stats_global_info = 10062; SurfaceflingerStatsLayerInfo surfaceflinger_stats_layer_info = 10063; + ProcessMemorySnapshot process_memory_snapshot = 10064; } // DO NOT USE field numbers above 100,000 in AOSP. @@ -3069,9 +3070,9 @@ message PictureInPictureStateChanged { * services/core/java/com/android/server/wm/Session.java */ message OverlayStateChanged { - optional int32 uid = 1 [(is_uid) = true]; + optional int32 uid = 1 [(state_field_option).option = PRIMARY, (is_uid) = true]; - optional string package_name = 2; + optional string package_name = 2 [(state_field_option).option = PRIMARY]; optional bool using_alert_window = 3; @@ -3079,7 +3080,7 @@ message OverlayStateChanged { ENTERED = 1; EXITED = 2; } - optional State state = 4; + optional State state = 4 [(state_field_option).option = EXCLUSIVE]; } /* @@ -4108,6 +4109,46 @@ message ProcessMemoryHighWaterMark { } /* + * Logs the memory stats for a process. + * + * Pulled from StatsCompanionService for all managed processes (from ActivityManagerService) + * and for selected native processes. + */ +message ProcessMemorySnapshot { + // The uid if available. -1 means not available. + optional int32 uid = 1 [(is_uid) = true]; + + // The process name. + // Usually package name or process cmdline. + // Provided by ActivityManagerService or read from /proc/PID/cmdline. + optional string process_name = 2; + + // The pid of the process. + // Allows to disambiguate instances of the process. + optional int32 pid = 3; + + // The current OOM score adjustment value. + // Read from ProcessRecord for managed processes. + // Placeholder -1001 (OOM_SCORE_ADJ_MIN - 1, outside of allowed range) for native ones. + optional int32 oom_score_adj = 4; + + // The current RSS of the process. + // VmRSS from /proc/pid/status. + optional int32 rss_in_kilobytes = 5; + + // The current anon RSS of the process. + // RssAnon from /proc/pid/status. + optional int32 anon_rss_in_kilobytes = 6; + + // The current swap size of the process. + // VmSwap from /proc/pid/status. + optional int32 swap_in_kilobytes = 7; + + // The sum of rss_in_kilobytes and swap_in_kilobytes. + optional int32 anon_rss_and_swap_in_kilobytes = 8; +} + +/* * Elapsed real time from SystemClock. */ message SystemElapsedRealtime { diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 475f18a9b0b8..f69e2d09ad23 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -153,6 +153,9 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { {.additiveFields = {3}, .puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}}, + // process_memory_snapshot + {android::util::PROCESS_MEMORY_SNAPSHOT, + {.puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_SNAPSHOT)}}, // system_ion_heap_size {android::util::SYSTEM_ION_HEAP_SIZE, {.puller = new StatsCompanionServicePuller(android::util::SYSTEM_ION_HEAP_SIZE)}}, diff --git a/cmds/statsd/src/state/StateListener.h b/cmds/statsd/src/state/StateListener.h new file mode 100644 index 000000000000..a31690a102ed --- /dev/null +++ b/cmds/statsd/src/state/StateListener.h @@ -0,0 +1,52 @@ +/* + * 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. + */ +#pragma once + +#include <utils/RefBase.h> + +#include "HashableDimensionKey.h" + +namespace android { +namespace os { +namespace statsd { + +class StateListener : public virtual RefBase { +public: + StateListener(){}; + + virtual ~StateListener(){}; + + /** + * Interface for handling a state change. + * + * The old and new state values map to the original state values. + * StateTrackers only track the original state values and are unaware + * of higher-level state groups. MetricProducers hold information on + * state groups and are responsible for mapping original state values to + * the correct state group. + * + * [atomId]: The id of the state atom + * [primaryKey]: The primary field values of the state atom + * [oldState]: Previous state value before state change + * [newState]: Current state value after state change + */ + virtual void onStateChanged(int atomId, const HashableDimensionKey& primaryKey, int oldState, + int newState) = 0; +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/state/StateManager.cpp b/cmds/statsd/src/state/StateManager.cpp new file mode 100644 index 000000000000..a3059c5b52ac --- /dev/null +++ b/cmds/statsd/src/state/StateManager.cpp @@ -0,0 +1,92 @@ +/* + * 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. + */ + +#define DEBUG false // STOPSHIP if true +#include "Log.h" + +#include "StateManager.h" + +namespace android { +namespace os { +namespace statsd { + +StateManager& StateManager::getInstance() { + static StateManager sStateManager; + return sStateManager; +} + +void StateManager::onLogEvent(const LogEvent& event) { + std::lock_guard<std::mutex> lock(mMutex); + if (mStateTrackers.find(event.GetTagId()) != mStateTrackers.end()) { + mStateTrackers[event.GetTagId()]->onLogEvent(event); + } +} + +bool StateManager::registerListener(int stateAtomId, wp<StateListener> listener) { + std::lock_guard<std::mutex> lock(mMutex); + + // Check if state tracker already exists + if (mStateTrackers.find(stateAtomId) == mStateTrackers.end()) { + // Create a new state tracker iff atom is a state atom + auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(stateAtomId); + if (it != android::util::AtomsInfo::kStateAtomsFieldOptions.end()) { + mStateTrackers[stateAtomId] = new StateTracker(stateAtomId, it->second); + } else { + ALOGE("StateManager cannot register listener, Atom %d is not a state atom", + stateAtomId); + return false; + } + } + mStateTrackers[stateAtomId]->registerListener(listener); + return true; +} + +void StateManager::unregisterListener(int stateAtomId, wp<StateListener> listener) { + std::unique_lock<std::mutex> lock(mMutex); + + // Hold the sp<> until the lock is released so that ~StateTracker() is + // not called while the lock is held. + sp<StateTracker> toRemove; + + // Unregister listener from correct StateTracker + auto it = mStateTrackers.find(stateAtomId); + if (it != mStateTrackers.end()) { + it->second->unregisterListener(listener); + + // Remove the StateTracker if it has no listeners + if (it->second->getListenersCount() == 0) { + toRemove = it->second; + mStateTrackers.erase(it); + } + } else { + ALOGE("StateManager cannot unregister listener, StateTracker for atom %d does not exist", + stateAtomId); + } + lock.unlock(); +} + +int StateManager::getState(int stateAtomId, const HashableDimensionKey& key) { + std::lock_guard<std::mutex> lock(mMutex); + if (mStateTrackers.find(stateAtomId) != mStateTrackers.end()) { + return mStateTrackers[stateAtomId]->getState(key); + } + + return StateTracker::kStateUnknown; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/state/StateManager.h b/cmds/statsd/src/state/StateManager.h new file mode 100644 index 000000000000..ce60f1482be7 --- /dev/null +++ b/cmds/statsd/src/state/StateManager.h @@ -0,0 +1,77 @@ +/* + * 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. + */ +#pragma once + +//#include <utils/Log.h> +#include <utils/RefBase.h> +#include "HashableDimensionKey.h" + +#include "state/StateListener.h" +#include "state/StateTracker.h" + +namespace android { +namespace os { +namespace statsd { + +class StateManager : public virtual RefBase { +public: + StateManager(){}; + + ~StateManager(){}; + + // Returns a pointer to the single, shared StateManager object. + static StateManager& getInstance(); + + // Notifies the correct StateTracker of an event. + void onLogEvent(const LogEvent& event); + + // Returns true if stateAtomId is the id of a state atom and notifies the + // correct StateTracker to register the listener. If the correct + // StateTracker does not exist, a new StateTracker is created. + bool registerListener(int stateAtomId, wp<StateListener> listener); + + // Notifies the correct StateTracker to unregister a listener + // and removes the tracker if it no longer has any listeners. + void unregisterListener(int stateAtomId, wp<StateListener> listener); + + // Queries the correct StateTracker for the state that is mapped to the given + // query key. + // If the StateTracker doesn't exist, returns StateTracker::kStateUnknown. + int getState(int stateAtomId, const HashableDimensionKey& queryKey); + + inline int getStateTrackersCount() { + std::lock_guard<std::mutex> lock(mMutex); + return mStateTrackers.size(); + } + + inline int getListenersCount(int stateAtomId) { + std::lock_guard<std::mutex> lock(mMutex); + if (mStateTrackers.find(stateAtomId) != mStateTrackers.end()) { + return mStateTrackers[stateAtomId]->getListenersCount(); + } + return -1; + } + +private: + mutable std::mutex mMutex; + + // Maps state atom ids to StateTrackers + std::unordered_map<int, sp<StateTracker>> mStateTrackers; +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp new file mode 100644 index 000000000000..5a91950b9f8b --- /dev/null +++ b/cmds/statsd/src/state/StateTracker.cpp @@ -0,0 +1,159 @@ +/* + * 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. + */ + +#define DEBUG true // STOPSHIP if true +#include "Log.h" + +#include "stats_util.h" + +#include "StateTracker.h" + +namespace android { +namespace os { +namespace statsd { + +StateTracker::StateTracker(const int atomId, + const util::StateAtomFieldOptions& stateAtomInfo) + : mAtomId(atomId), + mStateField(getSimpleMatcher(atomId, stateAtomInfo.exclusiveField)) { + // create matcher for each primary field + // TODO(tsaichristine): handle when primary field is first uid in chain + for (const auto& primary : stateAtomInfo.primaryFields) { + Matcher matcher = getSimpleMatcher(atomId, primary); + mPrimaryFields.push_back(matcher); + } + + // TODO(tsaichristine): set default state, reset state, and nesting +} + +void StateTracker::onLogEvent(const LogEvent& event) { + // parse event for primary field values i.e. primary key + HashableDimensionKey primaryKey; + if (mPrimaryFields.size() > 0) { + if (!filterValues(mPrimaryFields, event.getValues(), &primaryKey) || + primaryKey.getValues().size() != mPrimaryFields.size()) { + ALOGE("StateTracker error extracting primary key from log event."); + handleReset(); + return; + } + } else { + // atom has no primary fields + primaryKey = DEFAULT_DIMENSION_KEY; + } + + // parse event for state value + Value state; + int32_t stateValue; + if (!filterValues(mStateField, event.getValues(), &state) || state.getType() != INT) { + ALOGE("StateTracker error extracting state from log event. Type: %d", state.getType()); + handlePartialReset(primaryKey); + return; + } + stateValue = state.int_value; + + if (stateValue == mResetState) { + VLOG("StateTracker Reset state: %s", state.toString().c_str()); + handleReset(); + } + + // track and update state + int32_t oldState = 0; + int32_t newState = 0; + updateState(primaryKey, stateValue, &oldState, &newState); + + // notify all listeners if state has changed + if (oldState != newState) { + VLOG("StateTracker updated state"); + for (auto listener : mListeners) { + auto sListener = listener.promote(); // safe access to wp<> + if (sListener != nullptr) { + sListener->onStateChanged(mAtomId, primaryKey, oldState, newState); + } + } + } else { + VLOG("StateTracker NO updated state"); + } +} + +void StateTracker::registerListener(wp<StateListener> listener) { + mListeners.insert(listener); +} + +void StateTracker::unregisterListener(wp<StateListener> listener) { + mListeners.erase(listener); +} + +int StateTracker::getState(const HashableDimensionKey& queryKey) const { + if (queryKey.getValues().size() == mPrimaryFields.size()) { + auto it = mStateMap.find(queryKey); + if (it != mStateMap.end()) { + return it->second.state; + } + } else if (queryKey.getValues().size() > mPrimaryFields.size()) { + ALOGE("StateTracker query key size > primary key size is illegal"); + } else { + ALOGE("StateTracker query key size < primary key size is not supported"); + } + return mDefaultState; +} + +void StateTracker::handleReset() { + VLOG("StateTracker handle reset"); + for (const auto pair : mStateMap) { + for (auto l : mListeners) { + auto sl = l.promote(); + if (sl != nullptr) { + sl->onStateChanged(mAtomId, pair.first, pair.second.state, mDefaultState); + } + } + } + mStateMap.clear(); +} + +void StateTracker::handlePartialReset(const HashableDimensionKey& primaryKey) { + VLOG("StateTracker handle partial reset"); + if (mStateMap.find(primaryKey) != mStateMap.end()) { + mStateMap.erase(primaryKey); + } +} + +void StateTracker::updateState(const HashableDimensionKey& primaryKey, const int32_t eventState, + int32_t* oldState, int32_t* newState) { + // get old state (either current state in map or default state) + auto it = mStateMap.find(primaryKey); + if (it != mStateMap.end()) { + *oldState = it->second.state; + } else { + *oldState = mDefaultState; + } + + // update state map + if (eventState == mDefaultState) { + // remove (key, state) pair if state returns to default state + VLOG("\t StateTracker changed to default state") + mStateMap.erase(primaryKey); + } else { + mStateMap[primaryKey].state = eventState; + mStateMap[primaryKey].count = 1; + } + *newState = eventState; + + // TODO(tsaichristine): support atoms with nested counting +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/state/StateTracker.h b/cmds/statsd/src/state/StateTracker.h new file mode 100644 index 000000000000..f22706c8418d --- /dev/null +++ b/cmds/statsd/src/state/StateTracker.h @@ -0,0 +1,95 @@ +/* + * 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. + */ +#pragma once + +#include <statslog.h> +#include <utils/RefBase.h> +#include "HashableDimensionKey.h" +#include "logd/LogEvent.h" + +#include "state/StateListener.h" + +#include <unordered_map> + +namespace android { +namespace os { +namespace statsd { + +class StateTracker : public virtual RefBase { +public: + StateTracker(const int atomId, const util::StateAtomFieldOptions& stateAtomInfo); + + virtual ~StateTracker(){}; + + // Updates state map and notifies all listeners if a state change occurs. + // Checks if a state change has occurred by getting the state value from + // the log event and comparing the old and new states. + void onLogEvent(const LogEvent& event); + + // Adds new listeners to set of StateListeners. If a listener is already + // registered, it is ignored. + void registerListener(wp<StateListener> listener); + + void unregisterListener(wp<StateListener> listener); + + // Returns the state value mapped to the given query key. + // If the key isn't mapped to a state or the key size doesn't match the + // primary key size, the default state is returned. + int getState(const HashableDimensionKey& queryKey) const; + + inline int getListenersCount() const { + return mListeners.size(); + } + + const static int kStateUnknown = -1; + +private: + struct StateValueInfo { + int32_t state; // state value + int count; // nested count (only used for binary states) + }; + + const int32_t mAtomId; // id of the state atom being tracked + + Matcher mStateField; // matches the atom's exclusive state field + + std::vector<Matcher> mPrimaryFields; // matches the atom's primary fields + + int32_t mDefaultState = kStateUnknown; + + int32_t mResetState; + + // Maps primary key to state value info + std::unordered_map<HashableDimensionKey, StateValueInfo> mStateMap; + + // Set of all StateListeners (objects listening for state changes) + std::set<wp<StateListener>> mListeners; + + // Reset all state values in map to default state + void handleReset(); + + // Reset only the state value mapped to primary key to default state + void handlePartialReset(const HashableDimensionKey& primaryKey); + + // Update the StateMap based on the received state value. + // Store the old and new states. + void updateState(const HashableDimensionKey& primaryKey, const int32_t eventState, + int32_t* oldState, int32_t* newState); +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp new file mode 100644 index 000000000000..c89ffea85709 --- /dev/null +++ b/cmds/statsd/tests/state/StateTracker_test.cpp @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <gtest/gtest.h> +#include "state/StateManager.h" +#include "state/StateTracker.h" +#include "state/StateListener.h" + +#include "tests/statsd_test_util.h" + +#ifdef __ANDROID__ + +namespace android { +namespace os { +namespace statsd { + +/** + * Mock StateListener class for testing. + * Stores primary key and state pairs. + */ +class TestStateListener : public virtual StateListener { +public: + TestStateListener(){}; + + virtual ~TestStateListener(){}; + + struct Update { + Update(const HashableDimensionKey& key, int state) : mKey(key), mState(state){}; + HashableDimensionKey mKey; + int mState; + }; + + std::vector<Update> updates; + + void onStateChanged(int stateAtomId, const HashableDimensionKey& primaryKey, int oldState, + int newState) { + updates.emplace_back(primaryKey, newState); + } +}; + +// START: build event functions. +// State with no primary fields - ScreenStateChanged +std::shared_ptr<LogEvent> buildScreenEvent(int state) { + std::shared_ptr<LogEvent> event = + std::make_shared<LogEvent>(android::util::SCREEN_STATE_CHANGED, 1000 /*timestamp*/); + event->write((int32_t)state); + event->init(); + return event; +} + +// State with one primary field - UidProcessStateChanged +std::shared_ptr<LogEvent> buildUidProcessEvent(int uid, int state) { + std::shared_ptr<LogEvent> event = + std::make_shared<LogEvent>(android::util::UID_PROCESS_STATE_CHANGED, 1000 /*timestamp*/); + event->write((int32_t)uid); + event->write((int32_t)state); + event->init(); + return event; +} + +// State with multiple primary fields - OverlayStateChanged +std::shared_ptr<LogEvent> buildOverlayEvent(int uid, const std::string& packageName, int state) { + std::shared_ptr<LogEvent> event = + std::make_shared<LogEvent>(android::util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/); + event->write((int32_t)uid); + event->write(packageName); + event->write(true); // using_alert_window + event->write((int32_t)state); + event->init(); + return event; +} + +std::shared_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName, int state) { + std::shared_ptr<LogEvent> event = + std::make_shared<LogEvent>(android::util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/); + event->write((int32_t)uid); + event->write(packageName); + event->write((int32_t)state); + event->init(); + return event; +} +// END: build event functions. + +// START: get primary key functions +void getUidProcessKey(int uid, HashableDimensionKey* key) { + int pos1[] = {1, 0, 0}; + Field field1(27 /* atom id */, pos1, 0 /* depth */); + Value value1((int32_t)uid); + + key->addValue(FieldValue(field1, value1)); +} + +void getOverlayKey(int uid, string packageName, HashableDimensionKey* key) { + int pos1[] = {1, 0, 0}; + int pos2[] = {2, 0, 0}; + + Field field1(59 /* atom id */, pos1, 0 /* depth */); + Field field2(59 /* atom id */, pos2, 0 /* depth */); + + Value value1((int32_t)uid); + Value value2(packageName); + + key->addValue(FieldValue(field1, value1)); + key->addValue(FieldValue(field2, value2)); +} +// END: get primary key functions + +TEST(StateListenerTest, TestStateListenerWeakPointer) { + sp<TestStateListener> listener = new TestStateListener(); + wp<TestStateListener> wListener = listener; + listener = nullptr; // let go of listener + EXPECT_TRUE(wListener.promote() == nullptr); +} + +TEST(StateManagerTest, TestStateManagerGetInstance) { + sp<TestStateListener> listener1 = new TestStateListener(); + StateManager& mgr = StateManager::getInstance(); + + mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1); + EXPECT_EQ(1, mgr.getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); +} + +/** + * Test registering listeners to StateTrackers + * + * - StateManager will create a new StateTracker if it doesn't already exist + * and then register the listener to the StateTracker. + * - If a listener is already registered to a StateTracker, it is not added again. + * - StateTrackers are only created for atoms that are state atoms. + */ +TEST(StateTrackerTest, TestRegisterListener) { + sp<TestStateListener> listener1 = new TestStateListener(); + sp<TestStateListener> listener2 = new TestStateListener(); + StateManager mgr; + + // Register listener to non-existing StateTracker + EXPECT_EQ(0, mgr.getStateTrackersCount()); + mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1); + EXPECT_EQ(1, mgr.getStateTrackersCount()); + EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED)); + + // Register listener to existing StateTracker + mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2); + EXPECT_EQ(1, mgr.getStateTrackersCount()); + EXPECT_EQ(2, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED)); + + // Register already registered listener to existing StateTracker + mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2); + EXPECT_EQ(1, mgr.getStateTrackersCount()); + EXPECT_EQ(2, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED)); + + // Register listener to non-state atom + mgr.registerListener(android::util::BATTERY_LEVEL_CHANGED, listener2); + EXPECT_EQ(1, mgr.getStateTrackersCount()); +} + +/** + * Test unregistering listeners from StateTrackers + * + * - StateManager will unregister listeners from a StateTracker only if the + * StateTracker exists and the listener is registered to the StateTracker. + * - Once all listeners are removed from a StateTracker, the StateTracker + * is also removed. + */ +TEST(StateTrackerTest, TestUnregisterListener) { + sp<TestStateListener> listener1 = new TestStateListener(); + sp<TestStateListener> listener2 = new TestStateListener(); + StateManager mgr; + + // Unregister listener from non-existing StateTracker + EXPECT_EQ(0, mgr.getStateTrackersCount()); + mgr.unregisterListener(android::util::SCREEN_STATE_CHANGED, listener1); + EXPECT_EQ(0, mgr.getStateTrackersCount()); + EXPECT_EQ(-1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED)); + + // Unregister non-registered listener from existing StateTracker + mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1); + EXPECT_EQ(1, mgr.getStateTrackersCount()); + EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED)); + mgr.unregisterListener(android::util::SCREEN_STATE_CHANGED, listener2); + EXPECT_EQ(1, mgr.getStateTrackersCount()); + EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED)); + + // Unregister second-to-last listener from StateTracker + mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2); + mgr.unregisterListener(android::util::SCREEN_STATE_CHANGED, listener1); + EXPECT_EQ(1, mgr.getStateTrackersCount()); + EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED)); + + // Unregister last listener from StateTracker + mgr.unregisterListener(android::util::SCREEN_STATE_CHANGED, listener2); + EXPECT_EQ(0, mgr.getStateTrackersCount()); + EXPECT_EQ(-1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED)); +} + +/** + * Test StateManager's onLogEvent and StateListener's onStateChanged correctly + * updates listener for states without primary keys. + */ +TEST(StateTrackerTest, TestStateChangeNoPrimaryFields) { + sp<TestStateListener> listener1 = new TestStateListener(); + StateManager mgr; + mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1); + + // log event + std::shared_ptr<LogEvent> event = + buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON); + mgr.onLogEvent(*event); + + // check listener was updated + EXPECT_EQ(1, listener1->updates.size()); + EXPECT_EQ(DEFAULT_DIMENSION_KEY, listener1->updates[0].mKey); + EXPECT_EQ(2, listener1->updates[0].mState); + + // check StateTracker was updated by querying for state + HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY; + EXPECT_EQ(2, mgr.getState(android::util::SCREEN_STATE_CHANGED, queryKey)); +} + +/** + * Test StateManager's onLogEvent and StateListener's onStateChanged correctly + * updates listener for states with primary keys. + */ +TEST(StateTrackerTest, TestStateChangeOnePrimaryField) { + sp<TestStateListener> listener1 = new TestStateListener(); + StateManager mgr; + mgr.registerListener(android::util::UID_PROCESS_STATE_CHANGED, listener1); + + // log event + std::shared_ptr<LogEvent> event = buildUidProcessEvent( + 1000, + android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002 + mgr.onLogEvent(*event); + + // check listener was updated + EXPECT_EQ(1, listener1->updates.size()); + EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value); + EXPECT_EQ(1002, listener1->updates[0].mState); + + // check StateTracker was updated by querying for state + HashableDimensionKey queryKey; + getUidProcessKey(1000, &queryKey); + EXPECT_EQ(1002, mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey)); +} + +TEST(StateTrackerTest, TestStateChangeMultiplePrimaryFields) { + sp<TestStateListener> listener1 = new TestStateListener(); + StateManager mgr; + mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener1); + + // log event + std::shared_ptr<LogEvent> event = buildOverlayEvent(1000, "package1", 1); // state: ENTERED + mgr.onLogEvent(*event); + + // check listener update + EXPECT_EQ(1, listener1->updates.size()); + EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value); + EXPECT_EQ(1, listener1->updates[0].mState); +} + +/** + * Test StateManager's onLogEvent and StateListener's onStateChanged + * when there is an error extracting state from log event. Listener is not + * updated of state change. + */ +TEST(StateTrackerTest, TestStateChangeEventError) { + sp<TestStateListener> listener1 = new TestStateListener(); + StateManager mgr; + mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener1); + + // log event + std::shared_ptr<LogEvent> event = + buildIncorrectOverlayEvent(1000, "package1", 1); // state: ENTERED + mgr.onLogEvent(*event); + + // check listener update + EXPECT_EQ(0, listener1->updates.size()); +} + +TEST(StateTrackerTest, TestStateQuery) { + sp<TestStateListener> listener1 = new TestStateListener(); + sp<TestStateListener> listener2 = new TestStateListener(); + sp<TestStateListener> listener3 = new TestStateListener(); + StateManager mgr; + mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1); + mgr.registerListener(android::util::UID_PROCESS_STATE_CHANGED, listener2); + mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener3); + + std::shared_ptr<LogEvent> event1 = buildUidProcessEvent( + 1000, + android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002 + std::shared_ptr<LogEvent> event2 = buildUidProcessEvent( + 1001, + android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE); // state value: 1003 + std::shared_ptr<LogEvent> event3 = buildUidProcessEvent( + 1002, + android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT); // state value: 1000 + std::shared_ptr<LogEvent> event4 = buildUidProcessEvent( + 1001, + android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002 + std::shared_ptr<LogEvent> event5 = + buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON); // state value: + std::shared_ptr<LogEvent> event6 = buildOverlayEvent(1000, "package1", 1); + std::shared_ptr<LogEvent> event7 = buildOverlayEvent(1000, "package2", 2); + + mgr.onLogEvent(*event1); + mgr.onLogEvent(*event2); + mgr.onLogEvent(*event3); + mgr.onLogEvent(*event5); + mgr.onLogEvent(*event5); + mgr.onLogEvent(*event6); + mgr.onLogEvent(*event7); + + // Query for UidProcessState of uid 1001 + HashableDimensionKey queryKey1; + getUidProcessKey(1001, &queryKey1); + EXPECT_EQ(1003, mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey1)); + + // Query for UidProcessState of uid 1004 - not in state map + HashableDimensionKey queryKey2; + getUidProcessKey(1004, &queryKey2); + EXPECT_EQ(-1, + mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey2)); // default state + + // Query for UidProcessState of uid 1001 - after change in state + mgr.onLogEvent(*event4); + EXPECT_EQ(1002, mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey1)); + + // Query for ScreenState + EXPECT_EQ(2, mgr.getState(android::util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY)); + + // Query for OverlayState of uid 1000, package name "package2" + HashableDimensionKey queryKey3; + getOverlayKey(1000, "package2", &queryKey3); + EXPECT_EQ(2, mgr.getState(android::util::OVERLAY_STATE_CHANGED, queryKey3)); +} + +} // namespace statsd +} // namespace os +} // namespace android +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 563174bb9534..1649e8be8b3a 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -853,12 +853,14 @@ public class AppOpsManager { public static final int OP_ACCESS_ACCESSIBILITY = 88; /** @hide Read the device identifiers (IMEI / MEID, IMSI, SIM / Build serial) */ public static final int OP_READ_DEVICE_IDENTIFIERS = 89; + /** @hide Read location metadata from media */ + public static final int OP_ACCESS_MEDIA_LOCATION = 90; /** @hide Query all apps on device, regardless of declarations in the calling app manifest */ - public static final int OP_QUERY_ALL_PACKAGES = 90; + public static final int OP_QUERY_ALL_PACKAGES = 91; /** @hide */ @UnsupportedAppUsage - public static final int _NUM_OP = 91; + public static final int _NUM_OP = 92; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -1134,6 +1136,8 @@ public class AppOpsManager { /** @hide Has a legacy (non-isolated) view of storage. */ @SystemApi @TestApi public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage"; + /** @hide Read location metadata from media */ + public static final String OPSTR_ACCESS_MEDIA_LOCATION = "android:access_media_location"; /** @hide Interact with accessibility. */ @SystemApi @@ -1180,6 +1184,7 @@ public class AppOpsManager { // Storage OP_READ_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE, + OP_ACCESS_MEDIA_LOCATION, // Location OP_COARSE_LOCATION, OP_FINE_LOCATION, @@ -1322,6 +1327,7 @@ public class AppOpsManager { OP_LEGACY_STORAGE, // LEGACY_STORAGE OP_ACCESS_ACCESSIBILITY, // ACCESS_ACCESSIBILITY OP_READ_DEVICE_IDENTIFIERS, // READ_DEVICE_IDENTIFIERS + OP_ACCESS_MEDIA_LOCATION, // ACCESS_MEDIA_LOCATION OP_QUERY_ALL_PACKAGES, // QUERY_ALL_PACKAGES }; @@ -1419,6 +1425,7 @@ public class AppOpsManager { OPSTR_LEGACY_STORAGE, OPSTR_ACCESS_ACCESSIBILITY, OPSTR_READ_DEVICE_IDENTIFIERS, + OPSTR_ACCESS_MEDIA_LOCATION, OPSTR_QUERY_ALL_PACKAGES, }; @@ -1517,6 +1524,7 @@ public class AppOpsManager { "LEGACY_STORAGE", "ACCESS_ACCESSIBILITY", "READ_DEVICE_IDENTIFIERS", + "ACCESS_MEDIA_LOCATION", "QUERY_ALL_PACKAGES", }; @@ -1616,6 +1624,7 @@ public class AppOpsManager { null, // no permission for OP_LEGACY_STORAGE null, // no permission for OP_ACCESS_ACCESSIBILITY null, // no direct permission for OP_READ_DEVICE_IDENTIFIERS + Manifest.permission.ACCESS_MEDIA_LOCATION, null, // no permission for OP_QUERY_ALL_PACKAGES }; @@ -1715,6 +1724,7 @@ public class AppOpsManager { null, // LEGACY_STORAGE null, // ACCESS_ACCESSIBILITY null, // READ_DEVICE_IDENTIFIERS + null, // ACCESS_MEDIA_LOCATION null, // QUERY_ALL_PACKAGES }; @@ -1813,6 +1823,7 @@ public class AppOpsManager { false, // LEGACY_STORAGE false, // ACCESS_ACCESSIBILITY false, // READ_DEVICE_IDENTIFIERS + false, // ACCESS_MEDIA_LOCATION false, // QUERY_ALL_PACKAGES }; @@ -1910,6 +1921,7 @@ public class AppOpsManager { AppOpsManager.MODE_DEFAULT, // LEGACY_STORAGE AppOpsManager.MODE_ALLOWED, // ACCESS_ACCESSIBILITY AppOpsManager.MODE_ERRORED, // READ_DEVICE_IDENTIFIERS + AppOpsManager.MODE_ALLOWED, // ALLOW_MEDIA_LOCATION AppOpsManager.MODE_DEFAULT, // QUERY_ALL_PACKAGES }; @@ -2011,6 +2023,7 @@ public class AppOpsManager { false, // LEGACY_STORAGE false, // ACCESS_ACCESSIBILITY false, // READ_DEVICE_IDENTIFIERS + false, // ACCESS_MEDIA_LOCATION false, // QUERY_ALL_PACKAGES }; diff --git a/core/java/android/app/AsyncNotedAppOp.java b/core/java/android/app/AsyncNotedAppOp.java index 64f886aa2f1d..df6533a340f2 100644 --- a/core/java/android/app/AsyncNotedAppOp.java +++ b/core/java/android/app/AsyncNotedAppOp.java @@ -238,7 +238,7 @@ public final class AsyncNotedAppOp implements Parcelable { time = 1566503083973L, codegenVersion = "1.0.0", sourceFile = "frameworks/base/core/java/android/app/AsyncNotedAppOp.java", - inputSignatures = "private final @android.annotation.IntRange(from=0L, to=90L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mNotingPackageName\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)") + inputSignatures = "private final @android.annotation.IntRange(from=0L, to=91L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mNotingPackageName\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)") @Deprecated private void __metadata() {} diff --git a/data/etc/platform.xml b/data/etc/platform.xml index d66930abd30b..2ab7845a0981 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -206,6 +206,10 @@ targetSdk="29"> <new-permission name="android.permission.ACCESS_BACKGROUND_LOCATION" /> </split-permission> + <split-permission name="android.permission.READ_EXTERNAL_STORAGE" + targetSdk="29"> + <new-permission name="android.permission.ACCESS_MEDIA_LOCATION" /> + </split-permission> <!-- This is a list of all the libraries available for application code to link against. --> diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java index b9945cc735d8..96ac0f9b38b5 100644 --- a/graphics/java/android/graphics/drawable/GradientDrawable.java +++ b/graphics/java/android/graphics/drawable/GradientDrawable.java @@ -637,7 +637,7 @@ public class GradientDrawable extends Drawable { * @see #setOrientation(Orientation) */ public Orientation getOrientation() { - return mGradientState.getOrientation(); + return mGradientState.mOrientation; } /** @@ -653,7 +653,7 @@ public class GradientDrawable extends Drawable { * @see #getOrientation() */ public void setOrientation(Orientation orientation) { - mGradientState.setOrientation(orientation); + mGradientState.mOrientation = orientation; mGradientIsDirty = true; invalidateSelf(); } @@ -1270,7 +1270,7 @@ public class GradientDrawable extends Drawable { if (st.mGradient == LINEAR_GRADIENT) { final float level = st.mUseLevel ? getLevel() / 10000.0f : 1.0f; - switch (st.getOrientation()) { + switch (st.mOrientation) { case TOP_BOTTOM: x0 = r.left; y0 = r.top; x1 = x0; y1 = level * r.bottom; @@ -1759,6 +1759,33 @@ public class GradientDrawable extends Drawable { int angle = (int) a.getFloat(R.styleable.GradientDrawableGradient_angle, st.mAngle); st.mAngle = ((angle % 360) + 360) % 360; // offset negative angle measures + switch (st.mAngle) { + case 0: + st.mOrientation = Orientation.LEFT_RIGHT; + break; + case 45: + st.mOrientation = Orientation.BL_TR; + break; + case 90: + st.mOrientation = Orientation.BOTTOM_TOP; + break; + case 135: + st.mOrientation = Orientation.BR_TL; + break; + case 180: + st.mOrientation = Orientation.RIGHT_LEFT; + break; + case 225: + st.mOrientation = Orientation.TR_BL; + break; + case 270: + st.mOrientation = Orientation.TOP_BOTTOM; + break; + case 315: + st.mOrientation = Orientation.TL_BR; + break; + } + final TypedValue tv = a.peekValue(R.styleable.GradientDrawableGradient_gradientRadius); if (tv != null) { final float radius; @@ -1981,7 +2008,7 @@ public class GradientDrawable extends Drawable { int[] mAttrPadding; public GradientState(Orientation orientation, int[] gradientColors) { - setOrientation(orientation); + mOrientation = orientation; setGradientColors(gradientColors); } @@ -2184,93 +2211,11 @@ public class GradientDrawable extends Drawable { mCenterY = y; } - public void setOrientation(Orientation orientation) { - // Update the angle here so that subsequent attempts to obtain the orientation - // from the angle overwrite previously configured values during inflation - mAngle = getAngleFromOrientation(orientation); - mOrientation = orientation; - } - @NonNull public Orientation getOrientation() { - updateGradientStateOrientation(); return mOrientation; } - /** - * Update the orientation of the gradient based on the given angle only if the type is - * {@link #LINEAR_GRADIENT} - */ - private void updateGradientStateOrientation() { - if (mGradient == LINEAR_GRADIENT) { - int angle = mAngle; - if (angle % 45 != 0) { - throw new IllegalArgumentException("Linear gradient requires 'angle' attribute " - + "to be a multiple of 45"); - } - - Orientation orientation; - switch (angle) { - case 0: - orientation = Orientation.LEFT_RIGHT; - break; - case 45: - orientation = Orientation.BL_TR; - break; - case 90: - orientation = Orientation.BOTTOM_TOP; - break; - case 135: - orientation = Orientation.BR_TL; - break; - case 180: - orientation = Orientation.RIGHT_LEFT; - break; - case 225: - orientation = Orientation.TR_BL; - break; - case 270: - orientation = Orientation.TOP_BOTTOM; - break; - case 315: - orientation = Orientation.TL_BR; - break; - default: - // Should not get here as exception is thrown above if angle is not multiple - // of 45 degrees - orientation = Orientation.LEFT_RIGHT; - break; - } - mOrientation = orientation; - } - } - - private int getAngleFromOrientation(@Nullable Orientation orientation) { - if (orientation != null) { - switch (orientation) { - default: - case LEFT_RIGHT: - return 0; - case BL_TR: - return 45; - case BOTTOM_TOP: - return 90; - case BR_TL: - return 135; - case RIGHT_LEFT: - return 180; - case TR_BL: - return 225; - case TOP_BOTTOM: - return 270; - case TL_BR: - return 315; - } - } else { - return 0; - } - } - public void setGradientColors(@Nullable int[] colors) { mGradientColors = colors; mSolidColors = null; diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 67c181b452bb..3010206cdc5b 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -569,6 +569,7 @@ void SkiaPipeline::renderOverdraw(const LayerUpdateQueue& layers, const SkRect& // Set up the overdraw canvas. SkImageInfo offscreenInfo = SkImageInfo::MakeA8(surface->width(), surface->height()); sk_sp<SkSurface> offscreen = surface->makeSurface(offscreenInfo); + LOG_ALWAYS_FATAL_IF(!offscreen, "Failed to create offscreen SkSurface for overdraw viz."); SkOverdrawCanvas overdrawCanvas(offscreen->getCanvas()); // Fake a redraw to replay the draw commands. This will increment the alpha channel diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java index bb8c7f1dabf8..4d3dc70f651c 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java @@ -34,6 +34,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.sensors.AsyncSensorManager; +import com.android.systemui.util.sensors.ProximitySensor; import com.android.systemui.util.wakelock.DelayedWakeLock; import com.android.systemui.util.wakelock.WakeLock; @@ -97,7 +98,8 @@ public class DozeFactory { DockManager dockManager) { boolean allowPulseTriggers = true; return new DozeTriggers(context, machine, host, alarmManager, config, params, - sensorManager, handler, wakeLock, allowPulseTriggers, dockManager); + sensorManager, handler, wakeLock, allowPulseTriggers, dockManager, + new ProximitySensor(context, sensorManager)); } private DozeMachine.Part createDozeUi(Context context, DozeHost host, WakeLock wakeLock, diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index 67eefc5588e3..dfd83a552bf3 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -42,9 +42,7 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; -import com.android.systemui.Dependency; import com.android.systemui.plugins.SensorManagerPlugin; -import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.sensors.AsyncSensorManager; import com.android.systemui.util.sensors.ProximitySensor; @@ -147,8 +145,7 @@ public class DozeSensors { false /* touchscreen */, mConfig.getWakeLockScreenDebounce()), }; - mProximitySensor = new ProximitySensor( - context, sensorManager, Dependency.get(PluginManager.class)); + mProximitySensor = new ProximitySensor(context, sensorManager); mProximitySensor.register( proximityEvent -> mProxCallback.accept(!proximityEvent.getNear())); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 80d4b631314a..8eed71c21ce4 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -72,7 +72,6 @@ public class DozeTriggers implements DozeMachine.Part { private final AmbientDisplayConfiguration mConfig; private final DozeParameters mDozeParameters; private final AsyncSensorManager mSensorManager; - private final Handler mHandler; private final WakeLock mWakeLock; private final boolean mAllowPulseTriggers; private final UiModeManager mUiModeManager; @@ -89,14 +88,14 @@ public class DozeTriggers implements DozeMachine.Part { public DozeTriggers(Context context, DozeMachine machine, DozeHost dozeHost, AlarmManager alarmManager, AmbientDisplayConfiguration config, DozeParameters dozeParameters, AsyncSensorManager sensorManager, Handler handler, - WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager) { + WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager, + ProximitySensor proximitySensor) { mContext = context; mMachine = machine; mDozeHost = dozeHost; mConfig = config; mDozeParameters = dozeParameters; mSensorManager = sensorManager; - mHandler = handler; mWakeLock = wakeLock; mAllowPulseTriggers = allowPulseTriggers; mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters, @@ -104,9 +103,7 @@ public class DozeTriggers implements DozeMachine.Part { dozeParameters.getPolicy()); mUiModeManager = mContext.getSystemService(UiModeManager.class); mDockManager = dockManager; - mProxCheck = new ProximitySensor.ProximityCheck( - new ProximitySensor(mContext, mSensorManager, null), - mHandler); + mProxCheck = new ProximitySensor.ProximityCheck(proximitySensor, handler); } private void onNotification(Runnable onPulseSuppressedListener) { @@ -182,7 +179,7 @@ public class DozeTriggers implements DozeMachine.Part { } } else { proximityCheckThenCall((result) -> { - if (result) { + if (result != null && result) { // In pocket, drop event. return; } @@ -271,7 +268,7 @@ public class DozeTriggers implements DozeMachine.Part { if (wake) { proximityCheckThenCall((result) -> { - if (result) { + if (result != null && result) { // In pocket, drop event. return; } @@ -380,7 +377,7 @@ public class DozeTriggers implements DozeMachine.Part { mPulsePending = true; proximityCheckThenCall((result) -> { - if (result) { + if (result != null && result) { // in pocket, abort pulse DozeLog.tracePulseDropped(mContext, "inPocket"); mPulsePending = false; diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java index c48bdde6adef..cce5bcadb509 100644 --- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java @@ -24,8 +24,8 @@ import android.hardware.SensorManager; import android.os.Handler; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; -import com.android.systemui.shared.plugins.PluginManager; import java.util.ArrayList; import java.util.List; @@ -47,7 +47,7 @@ public class ProximitySensor { private final float mMaxRange; private List<ProximitySensorListener> mListeners = new ArrayList<>(); private String mTag = null; - private ProximityEvent mLastEvent; + @VisibleForTesting ProximityEvent mLastEvent; private int mSensorDelay = SensorManager.SENSOR_DELAY_NORMAL; private boolean mPaused; private boolean mRegistered; @@ -64,8 +64,7 @@ public class ProximitySensor { }; @Inject - public ProximitySensor( - Context context, AsyncSensorManager sensorManager, PluginManager pluginManager) { + public ProximitySensor(Context context, AsyncSensorManager sensorManager) { mSensorManager = sensorManager; Sensor sensor = findBrightnessSensor(context); @@ -146,17 +145,17 @@ public class ProximitySensor { return false; } - logDebug("Using brightness sensor? " + mUsingBrightnessSensor); mListeners.add(listener); registerInternal(); return true; } - private void registerInternal() { + protected void registerInternal() { if (mRegistered || mPaused || mListeners.isEmpty()) { return; } + logDebug("Using brightness sensor? " + mUsingBrightnessSensor); logDebug("Registering sensor listener"); mRegistered = true; mSensorManager.registerListener(mSensorEventListener, mSensor, mSensorDelay); @@ -175,7 +174,7 @@ public class ProximitySensor { } } - private void unregisterInternal() { + protected void unregisterInternal() { if (!mRegistered) { return; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index f7cd69643fc6..b0e3969bf8a4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -41,7 +41,9 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.dock.DockManager; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.sensors.AsyncSensorManager; +import com.android.systemui.util.sensors.FakeProximitySensor; import com.android.systemui.util.sensors.FakeSensorManager; +import com.android.systemui.util.sensors.ProximitySensor; import com.android.systemui.util.wakelock.WakeLock; import com.android.systemui.util.wakelock.WakeLockFake; @@ -60,6 +62,7 @@ public class DozeTriggersTest extends SysuiTestCase { private FakeSensorManager mSensors; private Sensor mTapSensor; private DockManager mDockManagerFake; + private FakeProximitySensor mProximitySensor; @BeforeClass public static void setupSuite() { @@ -80,10 +83,11 @@ public class DozeTriggersTest extends SysuiTestCase { mDockManagerFake = mock(DockManager.class); AsyncSensorManager asyncSensorManager = new AsyncSensorManager(mSensors, null, new Handler()); + mProximitySensor = new FakeProximitySensor(getContext(), asyncSensorManager); mTriggers = new DozeTriggers(mContext, mMachine, mHost, alarmManager, config, parameters, asyncSensorManager, Handler.createAsync(Looper.myLooper()), wakeLock, true, - mDockManagerFake); + mDockManagerFake, mProximitySensor); waitForSensorManager(); } @@ -95,15 +99,17 @@ public class DozeTriggersTest extends SysuiTestCase { mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE); clearInvocations(mMachine); + mProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(true, 1)); mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */); - mSensors.getFakeProximitySensor().sendProximityResult(false); /* Near */ + mProximitySensor.alertListeners(); verify(mMachine, never()).requestState(any()); verify(mMachine, never()).requestPulse(anyInt()); mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */); waitForSensorManager(); - mSensors.getFakeProximitySensor().sendProximityResult(true); /* Far */ + mProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(false, 2)); + mProximitySensor.alertListeners(); verify(mMachine).requestPulse(anyInt()); } @@ -139,6 +145,14 @@ public class DozeTriggersTest extends SysuiTestCase { verify(mDockManagerFake).removeListener(any()); } + @Test + public void testProximitySensorNotAvailablel() { + mProximitySensor.setSensorAvailable(false); + mTriggers.onSensor(DozeLog.PULSE_REASON_SENSOR_LONG_PRESS, 100, 100, null); + mTriggers.onSensor(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, 100, 100, new float[]{1}); + mTriggers.onSensor(DozeLog.REASON_SENSOR_TAP, 100, 100, null); + } + private void waitForSensorManager() { TestableLooper.get(this).processAllMessages(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java new file mode 100644 index 000000000000..d7df96dfdf24 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.sensors; + +import android.content.Context; + +public class FakeProximitySensor extends ProximitySensor { + private boolean mAvailable; + private boolean mPaused; + + public FakeProximitySensor(Context context, AsyncSensorManager sensorManager) { + super(context, sensorManager); + + mAvailable = true; + } + + public void setSensorAvailable(boolean available) { + mAvailable = available; + } + + public void setLastEvent(ProximityEvent event) { + mLastEvent = event; + } + + @Override + public boolean getSensorAvailable() { + return mAvailable; + } + + @Override + protected void registerInternal() { + // no-op + } + + @Override + protected void unregisterInternal() { + // no-op + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java index 6d13408058cc..fa943e344977 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java @@ -47,7 +47,7 @@ public class ProximitySensorTest extends SysuiTestCase { AsyncSensorManager asyncSensorManager = new AsyncSensorManager( sensorManager, null, new Handler()); mFakeProximitySensor = sensorManager.getFakeProximitySensor(); - mProximitySensor = new ProximitySensor(getContext(), asyncSensorManager, null); + mProximitySensor = new ProximitySensor(getContext(), asyncSensorManager); } @Test diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 587862ad1963..d03caef1ce95 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -581,16 +581,6 @@ public class PackageManagerService extends IPackageManager.Stub private static final String PACKAGE_SCHEME = "package"; - private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay"; - - private static final String PRODUCT_OVERLAY_DIR = "/product/overlay"; - - private static final String SYSTEM_EXT_OVERLAY_DIR = "/system_ext/overlay"; - - private static final String ODM_OVERLAY_DIR = "/odm/overlay"; - - private static final String OEM_OVERLAY_DIR = "/oem/overlay"; - /** Canonical intent used to identify what counts as a "web browser" app */ private static final Intent sBrowserIntent; static { @@ -756,6 +746,26 @@ public class PackageManagerService extends IPackageManager.Stub private final Injector mInjector; /** + * The list of all system partitions that may contain packages in ascending order of + * specificity (the more generic, the earlier in the list a partition appears). + */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final List<SystemPartition> SYSTEM_PARTITIONS = Collections.unmodifiableList( + Arrays.asList( + new SystemPartition(Environment.getRootDirectory(), 0 /* scanFlag */, + true /* hasPriv */, false /* hasOverlays */), + new SystemPartition(Environment.getVendorDirectory(), SCAN_AS_VENDOR, + true /* hasPriv */, true /* hasOverlays */), + new SystemPartition(Environment.getOdmDirectory(), SCAN_AS_ODM, + true /* hasPriv */, true /* hasOverlays */), + new SystemPartition(Environment.getOemDirectory(), SCAN_AS_OEM, + false /* hasPriv */, true /* hasOverlays */), + new SystemPartition(Environment.getProductDirectory(), SCAN_AS_PRODUCT, + true /* hasPriv */, true /* hasOverlays */), + new SystemPartition(Environment.getSystemExtDirectory(), SCAN_AS_SYSTEM_EXT, + true /* hasPriv */, true /* hasOverlays */))); + + /** * Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors. * * NOTE: All getters should return the same instance for every call. @@ -2552,6 +2562,51 @@ public class PackageManagerService extends IPackageManager.Stub } } + @VisibleForTesting(visibility = Visibility.PRIVATE) + static class SystemPartition { + public final File folder; + public final int scanFlag; + public final File appFolder; + @Nullable + public final File privAppFolder; + @Nullable + public final File overlayFolder; + + private SystemPartition(File folder, int scanFlag, boolean hasPrivApps, + boolean hasOverlays) { + this.folder = folder; + this.scanFlag = scanFlag; + this.appFolder = toCanonical(new File(folder, "app")); + this.privAppFolder = hasPrivApps ? toCanonical(new File(folder, "priv-app")) : null; + this.overlayFolder = hasOverlays ? toCanonical(new File(folder, "overlay")) : null; + } + + public boolean containsPrivApp(File scanFile) { + return FileUtils.contains(privAppFolder, scanFile); + } + + public boolean containsApp(File scanFile) { + return FileUtils.contains(appFolder, scanFile); + } + + public boolean containsPath(String path) { + return path.startsWith(folder.getPath() + "/"); + } + + public boolean containsPrivPath(String path) { + return privAppFolder != null && path.startsWith(privAppFolder.getPath() + "/"); + } + + private static File toCanonical(File dir) { + try { + return dir.getCanonicalFile(); + } catch (IOException e) { + // failed to look up canonical path, continue with original one + return dir; + } + } + } + public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) { final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing", Trace.TRACE_TAG_PACKAGE_MANAGER); @@ -2775,215 +2830,35 @@ public class PackageManagerService extends IPackageManager.Stub // any apps.) // For security and version matching reason, only consider overlay packages if they // reside in the right directory. - scanDirTracedLI(new File(VENDOR_OVERLAY_DIR), - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_VENDOR, - 0); - scanDirTracedLI(new File(PRODUCT_OVERLAY_DIR), - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_PRODUCT, - 0); - scanDirTracedLI(new File(SYSTEM_EXT_OVERLAY_DIR), - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_SYSTEM_EXT, - 0); - scanDirTracedLI(new File(ODM_OVERLAY_DIR), - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_ODM, - 0); - scanDirTracedLI(new File(OEM_OVERLAY_DIR), - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_OEM, - 0); + final int systemParseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR; + final int systemScanFlags = scanFlags | SCAN_AS_SYSTEM; + for (int i = SYSTEM_PARTITIONS.size() - 1; i >= 0; i--) { + final SystemPartition partition = SYSTEM_PARTITIONS.get(i); + if (partition.overlayFolder == null) { + continue; + } + scanDirTracedLI(partition.overlayFolder, systemParseFlags, + systemScanFlags | partition.scanFlag, 0); + } mParallelPackageParserCallback.findStaticOverlayPackages(); - // Find base frameworks (resource packages without code). - scanDirTracedLI(frameworkDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_NO_DEX - | SCAN_AS_SYSTEM - | SCAN_AS_PRIVILEGED, - 0); + scanDirTracedLI(frameworkDir, systemParseFlags, + systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0); if (!mPackages.containsKey("android")) { throw new IllegalStateException( "Failed to load frameworks package; check log for warnings"); } - - // Collect privileged system packages. - final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app"); - scanDirTracedLI(privilegedAppDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_PRIVILEGED, - 0); - - // Collect ordinary system packages. - final File systemAppDir = new File(Environment.getRootDirectory(), "app"); - scanDirTracedLI(systemAppDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM, - 0); - - // Collect privileged vendor packages. - File privilegedVendorAppDir = new File(Environment.getVendorDirectory(), "priv-app"); - try { - privilegedVendorAppDir = privilegedVendorAppDir.getCanonicalFile(); - } catch (IOException e) { - // failed to look up canonical path, continue with original one - } - scanDirTracedLI(privilegedVendorAppDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_VENDOR - | SCAN_AS_PRIVILEGED, - 0); - - // Collect ordinary vendor packages. - File vendorAppDir = new File(Environment.getVendorDirectory(), "app"); - try { - vendorAppDir = vendorAppDir.getCanonicalFile(); - } catch (IOException e) { - // failed to look up canonical path, continue with original one - } - scanDirTracedLI(vendorAppDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_VENDOR, - 0); - - // Collect privileged odm packages. /odm is another vendor partition - // other than /vendor. - File privilegedOdmAppDir = new File(Environment.getOdmDirectory(), - "priv-app"); - try { - privilegedOdmAppDir = privilegedOdmAppDir.getCanonicalFile(); - } catch (IOException e) { - // failed to look up canonical path, continue with original one - } - scanDirTracedLI(privilegedOdmAppDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_VENDOR - | SCAN_AS_PRIVILEGED, - 0); - - // Collect ordinary odm packages. /odm is another vendor partition - // other than /vendor. - File odmAppDir = new File(Environment.getOdmDirectory(), "app"); - try { - odmAppDir = odmAppDir.getCanonicalFile(); - } catch (IOException e) { - // failed to look up canonical path, continue with original one - } - scanDirTracedLI(odmAppDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_VENDOR, - 0); - - // Collect all OEM packages. - final File oemAppDir = new File(Environment.getOemDirectory(), "app"); - scanDirTracedLI(oemAppDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_OEM, - 0); - - // Collected privileged /product packages. - File privilegedProductAppDir = new File(Environment.getProductDirectory(), "priv-app"); - try { - privilegedProductAppDir = privilegedProductAppDir.getCanonicalFile(); - } catch (IOException e) { - // failed to look up canonical path, continue with original one - } - scanDirTracedLI(privilegedProductAppDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_PRODUCT - | SCAN_AS_PRIVILEGED, - 0); - - // Collect ordinary /product packages. - File productAppDir = new File(Environment.getProductDirectory(), "app"); - try { - productAppDir = productAppDir.getCanonicalFile(); - } catch (IOException e) { - // failed to look up canonical path, continue with original one - } - scanDirTracedLI(productAppDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_PRODUCT, - 0); - - // Collected privileged /system_ext packages. - File privilegedSystemExtAppDir = - new File(Environment.getSystemExtDirectory(), "priv-app"); - try { - privilegedSystemExtAppDir = - privilegedSystemExtAppDir.getCanonicalFile(); - } catch (IOException e) { - // failed to look up canonical path, continue with original one - } - scanDirTracedLI(privilegedSystemExtAppDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_SYSTEM_EXT - | SCAN_AS_PRIVILEGED, - 0); - - // Collect ordinary /system_ext packages. - File systemExtAppDir = new File(Environment.getSystemExtDirectory(), "app"); - try { - systemExtAppDir = systemExtAppDir.getCanonicalFile(); - } catch (IOException e) { - // failed to look up canonical path, continue with original one + for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) { + final SystemPartition partition = SYSTEM_PARTITIONS.get(i); + if (partition.privAppFolder != null) { + scanDirTracedLI(partition.privAppFolder, systemParseFlags, + systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0); + } + scanDirTracedLI(partition.appFolder, systemParseFlags, + systemScanFlags | partition.scanFlag, 0); } - scanDirTracedLI(systemExtAppDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_SYSTEM_EXT, - 0); + // Prune any system packages that no longer exist. final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>(); @@ -3151,89 +3026,26 @@ public class PackageManagerService extends IPackageManager.Stub logCriticalInfo(Log.WARN, "Expected better " + packageName + " but never showed up; reverting to system"); - final @ParseFlags int reparseFlags; - final @ScanFlags int rescanFlags; - if (FileUtils.contains(privilegedAppDir, scanFile)) { - reparseFlags = - mDefParseFlags | - PackageParser.PARSE_IS_SYSTEM_DIR; - rescanFlags = - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_PRIVILEGED; - } else if (FileUtils.contains(systemAppDir, scanFile)) { - reparseFlags = - mDefParseFlags | - PackageParser.PARSE_IS_SYSTEM_DIR; - rescanFlags = - scanFlags - | SCAN_AS_SYSTEM; - } else if (FileUtils.contains(privilegedVendorAppDir, scanFile) - || FileUtils.contains(privilegedOdmAppDir, scanFile)) { - reparseFlags = - mDefParseFlags | - PackageParser.PARSE_IS_SYSTEM_DIR; - rescanFlags = - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_VENDOR - | SCAN_AS_PRIVILEGED; - } else if (FileUtils.contains(vendorAppDir, scanFile) - || FileUtils.contains(odmAppDir, scanFile)) { - reparseFlags = - mDefParseFlags | - PackageParser.PARSE_IS_SYSTEM_DIR; - rescanFlags = - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_VENDOR; - } else if (FileUtils.contains(oemAppDir, scanFile)) { - reparseFlags = - mDefParseFlags | - PackageParser.PARSE_IS_SYSTEM_DIR; - rescanFlags = - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_OEM; - } else if (FileUtils.contains(privilegedProductAppDir, scanFile)) { - reparseFlags = - mDefParseFlags | - PackageParser.PARSE_IS_SYSTEM_DIR; - rescanFlags = - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_PRODUCT - | SCAN_AS_PRIVILEGED; - } else if (FileUtils.contains(productAppDir, scanFile)) { - reparseFlags = - mDefParseFlags | - PackageParser.PARSE_IS_SYSTEM_DIR; - rescanFlags = - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_PRODUCT; - } else if (FileUtils.contains(privilegedSystemExtAppDir, scanFile)) { - reparseFlags = - mDefParseFlags | - PackageParser.PARSE_IS_SYSTEM_DIR; - rescanFlags = - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_SYSTEM_EXT - | SCAN_AS_PRIVILEGED; - } else if (FileUtils.contains(systemExtAppDir, scanFile)) { - reparseFlags = - mDefParseFlags | - PackageParser.PARSE_IS_SYSTEM_DIR; - rescanFlags = - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_SYSTEM_EXT; - } else { + @ParseFlags int reparseFlags = 0; + @ScanFlags int rescanFlags = 0; + for (int i1 = 0, size = SYSTEM_PARTITIONS.size(); i1 < size; i1++) { + SystemPartition partition = SYSTEM_PARTITIONS.get(i1); + if (partition.containsPrivApp(scanFile)) { + reparseFlags = systemParseFlags; + rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED + | partition.scanFlag; + break; + } + if (partition.containsApp(scanFile)) { + reparseFlags = systemParseFlags; + rescanFlags = systemScanFlags | partition.scanFlag; + break; + } + } + if (rescanFlags == 0) { Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile); continue; } - mSettings.enableSystemPackageLPw(packageName); try { @@ -18079,70 +17891,15 @@ public class PackageManagerService extends IPackageManager.Stub } static boolean locationIsPrivileged(String path) { - try { - final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app"); - final File privilegedVendorAppDir = new File(Environment.getVendorDirectory(), "priv-app"); - final File privilegedOdmAppDir = new File(Environment.getOdmDirectory(), "priv-app"); - final File privilegedProductAppDir = new File(Environment.getProductDirectory(), "priv-app"); - final File privilegedSystemExtAppDir = - new File(Environment.getSystemExtDirectory(), "priv-app"); - return path.startsWith(privilegedAppDir.getCanonicalPath() + "/") - || path.startsWith(privilegedVendorAppDir.getCanonicalPath() + "/") - || path.startsWith(privilegedOdmAppDir.getCanonicalPath() + "/") - || path.startsWith(privilegedProductAppDir.getCanonicalPath() + "/") - || path.startsWith(privilegedSystemExtAppDir.getCanonicalPath() + "/"); - } catch (IOException e) { - Slog.e(TAG, "Unable to access code path " + path); - } - return false; - } - - static boolean locationIsOem(String path) { - try { - return path.startsWith(Environment.getOemDirectory().getCanonicalPath() + "/"); - } catch (IOException e) { - Slog.e(TAG, "Unable to access code path " + path); - } - return false; - } - - static boolean locationIsVendor(String path) { - try { - return path.startsWith(Environment.getVendorDirectory().getCanonicalPath() + "/") - || path.startsWith(Environment.getOdmDirectory().getCanonicalPath() + "/"); - } catch (IOException e) { - Slog.e(TAG, "Unable to access code path " + path); - } - return false; - } - - static boolean locationIsProduct(String path) { - try { - return path.startsWith(Environment.getProductDirectory().getCanonicalPath() + "/"); - } catch (IOException e) { - Slog.e(TAG, "Unable to access code path " + path); - } - return false; - } - - static boolean locationIsSystemExt(String path) { - try { - return path.startsWith( - Environment.getSystemExtDirectory().getCanonicalPath() + "/"); - } catch (IOException e) { - Slog.e(TAG, "Unable to access code path " + path); + for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) { + SystemPartition partition = SYSTEM_PARTITIONS.get(i); + if (partition.containsPrivPath(path)) { + return true; + } } return false; } - static boolean locationIsOdm(String path) { - try { - return path.startsWith(Environment.getOdmDirectory().getCanonicalPath() + "/"); - } catch (IOException e) { - Slog.e(TAG, "Unable to access code path " + path); - } - return false; - } /* * Tries to delete system package. @@ -18253,23 +18010,15 @@ public class PackageManagerService extends IPackageManager.Stub | PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM_DIR; @ScanFlags int scanFlags = SCAN_AS_SYSTEM; - if (locationIsPrivileged(codePathString)) { - scanFlags |= SCAN_AS_PRIVILEGED; - } - if (locationIsOem(codePathString)) { - scanFlags |= SCAN_AS_OEM; - } - if (locationIsVendor(codePathString)) { - scanFlags |= SCAN_AS_VENDOR; - } - if (locationIsProduct(codePathString)) { - scanFlags |= SCAN_AS_PRODUCT; - } - if (locationIsSystemExt(codePathString)) { - scanFlags |= SCAN_AS_SYSTEM_EXT; - } - if (locationIsOdm(codePathString)) { - scanFlags |= SCAN_AS_ODM; + for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) { + SystemPartition partition = SYSTEM_PARTITIONS.get(i); + if (partition.containsPath(codePathString)) { + scanFlags |= partition.scanFlag; + if (partition.containsPrivPath(codePathString)) { + scanFlags |= SCAN_AS_PRIVILEGED; + } + break; + } } final File codePath = new File(codePathString); diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 1cea4ca1b945..6b4ef698a8f4 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -318,7 +318,7 @@ public class StagingManager { // The APEX part of the session is activated, proceed with the installation of APKs. try { Slog.d(TAG, "Installing APK packages in session " + session.sessionId); - installApksInSession(session, /* preReboot */ false); + installApksInSession(session); } catch (PackageManagerException e) { session.setStagedSessionFailed(e.error, e.getMessage()); @@ -410,72 +410,23 @@ public class StagingManager { return apkSession; } - private void commitApkSession(@NonNull PackageInstallerSession apkSession, - PackageInstallerSession originalSession, boolean preReboot) - throws PackageManagerException { - final int errorCode = preReboot ? SessionInfo.STAGED_SESSION_VERIFICATION_FAILED - : SessionInfo.STAGED_SESSION_ACTIVATION_FAILED; - if (preReboot) { - final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync( - (Intent result) -> { - int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, - PackageInstaller.STATUS_FAILURE); - if (status != PackageInstaller.STATUS_SUCCESS) { - final String errorMessage = result.getStringExtra( - PackageInstaller.EXTRA_STATUS_MESSAGE); - Slog.e(TAG, "Failure to install APK staged session " - + originalSession.sessionId + " [" + errorMessage + "]"); - originalSession.setStagedSessionFailed(errorCode, errorMessage); - return; - } - mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete( - originalSession.sessionId); - }); - apkSession.commit(receiver.getIntentSender(), false); - return; - } - - if ((apkSession.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) { - // If rollback is available for this session, notify the rollback - // manager of the apk session so it can properly enable rollback. - final IRollbackManager rm = IRollbackManager.Stub.asInterface( - ServiceManager.getService(Context.ROLLBACK_SERVICE)); - try { - rm.notifyStagedApkSession(originalSession.sessionId, apkSession.sessionId); - } catch (RemoteException re) { - Slog.e(TAG, "Failed to notifyStagedApkSession for session: " - + originalSession.sessionId, re); - } - } - - final LocalIntentReceiverSync receiver = new LocalIntentReceiverSync(); - apkSession.commit(receiver.getIntentSender(), false); - final Intent result = receiver.getResult(); - final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, - PackageInstaller.STATUS_FAILURE); - if (status != PackageInstaller.STATUS_SUCCESS) { - final String errorMessage = result.getStringExtra( - PackageInstaller.EXTRA_STATUS_MESSAGE); - Slog.e(TAG, "Failure to install APK staged session " - + originalSession.sessionId + " [" + errorMessage + "]"); - throw new PackageManagerException(errorCode, errorMessage); - } - } - - private void installApksInSession(@NonNull PackageInstallerSession session, - boolean preReboot) throws PackageManagerException { + /** + * Extract apks in the given session into a new session. Returns {@code null} if there is no + * apks in the given session. Only parent session is returned for multi-package session. + */ + @Nullable + private PackageInstallerSession extractApksInSession(PackageInstallerSession session, + boolean preReboot) throws PackageManagerException { final int errorCode = preReboot ? SessionInfo.STAGED_SESSION_VERIFICATION_FAILED : SessionInfo.STAGED_SESSION_ACTIVATION_FAILED; if (!session.isMultiPackage() && !isApexSession(session)) { - // APK single-packaged staged session. Do a regular install. - PackageInstallerSession apkSession = createAndWriteApkSession(session, preReboot); - commitApkSession(apkSession, session, preReboot); + return createAndWriteApkSession(session, preReboot); } else if (session.isMultiPackage()) { // For multi-package staged sessions containing APKs, we identify which child sessions // contain an APK, and with those then create a new multi-package group of sessions, // carrying over all the session parameters and unmarking them as staged. On commit the // sessions will be installed atomically. - List<PackageInstallerSession> childSessions; + final List<PackageInstallerSession> childSessions; synchronized (mStagedSessions) { childSessions = Arrays.stream(session.getChildSessionIds()) @@ -487,18 +438,18 @@ public class StagingManager { } if (childSessions.isEmpty()) { // APEX-only multi-package staged session, nothing to do. - return; + return null; } - PackageInstaller.SessionParams params = session.params.copy(); + final PackageInstaller.SessionParams params = session.params.copy(); params.isStaged = false; if (preReboot) { params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK; } // TODO(b/129744602): use the userid from the original session. - int apkParentSessionId = mPi.createSession( + final int apkParentSessionId = mPi.createSession( params, session.getInstallerPackageName(), 0 /* UserHandle.SYSTEM */); - PackageInstallerSession apkParentSession = mPi.getSession(apkParentSessionId); + final PackageInstallerSession apkParentSession = mPi.getSession(apkParentSessionId); try { apkParentSession.open(); } catch (IOException e) { @@ -519,9 +470,75 @@ public class StagingManager { "Failed to add a child session " + apkChildSession.sessionId); } } - commitApkSession(apkParentSession, session, preReboot); + return apkParentSession; + } + return null; + } + + private void verifyApksInSession(PackageInstallerSession session) + throws PackageManagerException { + + final PackageInstallerSession apksToVerify = extractApksInSession( + session, /* preReboot */ true); + if (apksToVerify == null) { + return; + } + + final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync( + (Intent result) -> { + int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_FAILURE); + if (status != PackageInstaller.STATUS_SUCCESS) { + final String errorMessage = result.getStringExtra( + PackageInstaller.EXTRA_STATUS_MESSAGE); + Slog.e(TAG, "Failure to verify APK staged session " + + session.sessionId + " [" + errorMessage + "]"); + session.setStagedSessionFailed( + SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, errorMessage); + return; + } + mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete( + session.sessionId); + }); + + apksToVerify.commit(receiver.getIntentSender(), false); + } + + private void installApksInSession(@NonNull PackageInstallerSession session) + throws PackageManagerException { + + final PackageInstallerSession apksToInstall = extractApksInSession( + session, /* preReboot */ false); + if (apksToInstall == null) { + return; + } + + if ((apksToInstall.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) { + // If rollback is available for this session, notify the rollback + // manager of the apk session so it can properly enable rollback. + final IRollbackManager rm = IRollbackManager.Stub.asInterface( + ServiceManager.getService(Context.ROLLBACK_SERVICE)); + try { + rm.notifyStagedApkSession(session.sessionId, apksToInstall.sessionId); + } catch (RemoteException re) { + Slog.e(TAG, "Failed to notifyStagedApkSession for session: " + + session.sessionId, re); + } + } + + final LocalIntentReceiverSync receiver = new LocalIntentReceiverSync(); + apksToInstall.commit(receiver.getIntentSender(), false); + final Intent result = receiver.getResult(); + final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_FAILURE); + if (status != PackageInstaller.STATUS_SUCCESS) { + final String errorMessage = result.getStringExtra( + PackageInstaller.EXTRA_STATUS_MESSAGE); + Slog.e(TAG, "Failure to install APK staged session " + + session.sessionId + " [" + errorMessage + "]"); + throw new PackageManagerException( + SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMessage); } - // APEX single-package staged session, nothing to do. } void commitSession(@NonNull PackageInstallerSession session) { @@ -836,8 +853,8 @@ public class StagingManager { Slog.d(TAG, "Running a pre-reboot verification for APKs in session " + session.sessionId + " by performing a dry-run install"); - // installApksInSession will notify the handler when APK verification is complete - installApksInSession(session, /* preReboot */ true); + // verifyApksInSession will notify the handler when APK verification is complete + verifyApksInSession(session); // TODO(b/118865310): abort the session on apexd. } catch (PackageManagerException e) { session.setStagedSessionFailed(e.error, e.getMessage()); diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 3e655edf5db0..793cdd2f2f6d 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -190,6 +190,7 @@ public final class DefaultPermissionGrantPolicy { static { STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE); STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); + STORAGE_PERMISSIONS.add(Manifest.permission.ACCESS_MEDIA_LOCATION); } private static final int MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS = 1; diff --git a/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java b/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java index 3076284a80ed..d49b9589cb15 100644 --- a/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java +++ b/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java @@ -34,6 +34,12 @@ final class ProcfsMemoryUtil { private static final Pattern RSS_HIGH_WATER_MARK_IN_KILOBYTES = Pattern.compile("VmHWM:\\s*(\\d+)\\s*kB"); + private static final Pattern RSS_IN_KILOBYTES = + Pattern.compile("VmRSS:\\s*(\\d+)\\s*kB"); + private static final Pattern ANON_RSS_IN_KILOBYTES = + Pattern.compile("RssAnon:\\s*(\\d+)\\s*kB"); + private static final Pattern SWAP_IN_KILOBYTES = + Pattern.compile("VmSwap:\\s*(\\d+)\\s*kB"); private ProcfsMemoryUtil() {} @@ -52,10 +58,41 @@ final class ProcfsMemoryUtil { */ @VisibleForTesting static int parseVmHWMFromStatus(String contents) { + return tryParseInt(contents, RSS_HIGH_WATER_MARK_IN_KILOBYTES); + } + + /** + * Reads memory stat of a process from procfs. Returns values of the VmRss, AnonRSS, VmSwap + * fields in /proc/pid/status in kilobytes or 0 if not available. + */ + static MemorySnapshot readMemorySnapshotFromProcfs(int pid) { + final String statusPath = String.format(Locale.US, STATUS_FILE_FMT, pid); + return parseMemorySnapshotFromStatus(readFile(statusPath)); + } + + @VisibleForTesting + static MemorySnapshot parseMemorySnapshotFromStatus(String contents) { + final MemorySnapshot snapshot = new MemorySnapshot(); + snapshot.rssInKilobytes = tryParseInt(contents, RSS_IN_KILOBYTES); + snapshot.anonRssInKilobytes = tryParseInt(contents, ANON_RSS_IN_KILOBYTES); + snapshot.swapInKilobytes = tryParseInt(contents, SWAP_IN_KILOBYTES); + return snapshot; + } + + private static String readFile(String path) { + try { + final File file = new File(path); + return FileUtils.readTextFile(file, 0 /* max */, null /* ellipsis */); + } catch (IOException e) { + return ""; + } + } + + private static int tryParseInt(String contents, Pattern pattern) { if (contents.isEmpty()) { return 0; } - final Matcher matcher = RSS_HIGH_WATER_MARK_IN_KILOBYTES.matcher(contents); + final Matcher matcher = pattern.matcher(contents); try { return matcher.find() ? Integer.parseInt(matcher.group(1)) : 0; } catch (NumberFormatException e) { @@ -64,12 +101,13 @@ final class ProcfsMemoryUtil { } } - private static String readFile(String path) { - try { - final File file = new File(path); - return FileUtils.readTextFile(file, 0 /* max */, null /* ellipsis */); - } catch (IOException e) { - return ""; + static final class MemorySnapshot { + public int rssInKilobytes; + public int anonRssInKilobytes; + public int swapInKilobytes; + + boolean isEmpty() { + return (anonRssInKilobytes + swapInKilobytes) == 0; } } } diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 19b80556a779..e1a48ed3b550 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -29,6 +29,7 @@ import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem; import static com.android.server.am.MemoryStatUtil.readMemoryStatFromProcfs; import static com.android.server.stats.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs; import static com.android.server.stats.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs; +import static com.android.server.stats.ProcfsMemoryUtil.readMemorySnapshotFromProcfs; import static com.android.server.stats.ProcfsMemoryUtil.readRssHighWaterMarkFromProcfs; import android.annotation.NonNull; @@ -141,6 +142,7 @@ import com.android.server.SystemServiceManager; import com.android.server.am.MemoryStatUtil.MemoryStat; import com.android.server.role.RoleManagerInternal; import com.android.server.stats.IonMemoryUtil.IonAllocations; +import com.android.server.stats.ProcfsMemoryUtil.MemorySnapshot; import com.android.server.storage.DiskStatsFileLogger; import com.android.server.storage.DiskStatsLoggingService; @@ -1270,6 +1272,47 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { SystemProperties.set("sys.rss_hwm_reset.on", "1"); } + private void pullProcessMemorySnapshot( + int tagId, long elapsedNanos, long wallClockNanos, + List<StatsLogEventWrapper> pulledData) { + List<ProcessMemoryState> managedProcessList = + LocalServices.getService( + ActivityManagerInternal.class).getMemoryStateForProcesses(); + for (ProcessMemoryState managedProcess : managedProcessList) { + StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); + e.writeInt(managedProcess.uid); + e.writeString(managedProcess.processName); + e.writeInt(managedProcess.pid); + e.writeInt(managedProcess.oomScore); + final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(managedProcess.pid); + if (snapshot.isEmpty()) { + continue; + } + e.writeInt(snapshot.rssInKilobytes); + e.writeInt(snapshot.anonRssInKilobytes); + e.writeInt(snapshot.swapInKilobytes); + e.writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes); + pulledData.add(e); + } + int[] pids = getPidsForCommands(MEMORY_INTERESTING_NATIVE_PROCESSES); + for (int pid : pids) { + StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); + e.writeInt(getUidForPid(pid)); + e.writeString(readCmdlineFromProcfs(pid)); + e.writeInt(pid); + e.writeInt(-1001); // Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1. + final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid); + if (snapshot.isEmpty()) { + continue; + } + e.writeInt(snapshot.rssInKilobytes); + e.writeInt(snapshot.anonRssInKilobytes); + e.writeInt(snapshot.swapInKilobytes); + e.writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes); + pulledData.add(e); + } + } + private void pullSystemIonHeapSize( int tagId, long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) { @@ -2352,6 +2395,10 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { pullProcessMemoryHighWaterMark(tagId, elapsedNanos, wallClockNanos, ret); break; } + case StatsLog.PROCESS_MEMORY_SNAPSHOT: { + pullProcessMemorySnapshot(tagId, elapsedNanos, wallClockNanos, ret); + break; + } case StatsLog.SYSTEM_ION_HEAP_SIZE: { pullSystemIonHeapSize(tagId, elapsedNanos, wallClockNanos, ret); break; diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java index fc7cfec9dc87..0a310d193675 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java @@ -118,17 +118,20 @@ public class PackageManagerServiceTest { String[] partitions = { "system", "vendor", "odm", "oem", "product", "system_ext" }; String[] appdir = { "app", "priv-app" }; for (int i = 0; i < partitions.length; i++) { + final PackageManagerService.SystemPartition systemPartition = + PackageManagerService.SYSTEM_PARTITIONS.get(i); for (int j = 0; j < appdir.length; j++) { String canonical = new File("/" + partitions[i]).getCanonicalPath(); String path = String.format("%s/%s/A.apk", canonical, appdir[j]); - Assert.assertEquals(j == 1 && i != 3, - PackageManagerService.locationIsPrivileged(path)); + Assert.assertEquals(j == 1 && i != 3, systemPartition.containsPrivPath(path)); - Assert.assertEquals(i == 1 || i == 2, PackageManagerService.locationIsVendor(path)); - Assert.assertEquals(i == 3, PackageManagerService.locationIsOem(path)); - Assert.assertEquals(i == 4, PackageManagerService.locationIsProduct(path)); - Assert.assertEquals(i == 5, PackageManagerService.locationIsSystemExt(path)); + final int scanFlag = systemPartition.scanFlag; + Assert.assertEquals(i == 1, scanFlag == PackageManagerService.SCAN_AS_VENDOR); + Assert.assertEquals(i == 2, scanFlag == PackageManagerService.SCAN_AS_ODM); + Assert.assertEquals(i == 3, scanFlag == PackageManagerService.SCAN_AS_OEM); + Assert.assertEquals(i == 4, scanFlag == PackageManagerService.SCAN_AS_PRODUCT); + Assert.assertEquals(i == 5, scanFlag == PackageManagerService.SCAN_AS_SYSTEM_EXT); } } } diff --git a/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java b/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java index 4fb533f726b2..ae5777403528 100644 --- a/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java +++ b/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java @@ -15,12 +15,15 @@ */ package com.android.server.stats; +import static com.android.server.stats.ProcfsMemoryUtil.parseMemorySnapshotFromStatus; import static com.android.server.stats.ProcfsMemoryUtil.parseVmHWMFromStatus; import static com.google.common.truth.Truth.assertThat; import androidx.test.filters.SmallTest; +import com.android.server.stats.ProcfsMemoryUtil.MemorySnapshot; + import org.junit.Test; /** @@ -90,4 +93,32 @@ public class ProcfsMemoryUtilTest { public void testParseVmHWMFromStatus_emptyContents() { assertThat(parseVmHWMFromStatus("")).isEqualTo(0); } + + @Test + public void testParseMemorySnapshotFromStatus_parsesCorrectValue() { + MemorySnapshot snapshot = parseMemorySnapshotFromStatus(STATUS_CONTENTS); + assertThat(snapshot.rssInKilobytes).isEqualTo(126776); + assertThat(snapshot.anonRssInKilobytes).isEqualTo(37860); + assertThat(snapshot.swapInKilobytes).isEqualTo(22); + assertThat(snapshot.isEmpty()).isFalse(); + } + + @Test + public void testParseMemorySnapshotFromStatus_invalidValue() { + MemorySnapshot snapshot = + parseMemorySnapshotFromStatus("test\nVmRSS:\tx0x0x\nVmSwap:\t1 kB\ntest"); + assertThat(snapshot.rssInKilobytes).isEqualTo(0); + assertThat(snapshot.anonRssInKilobytes).isEqualTo(0); + assertThat(snapshot.swapInKilobytes).isEqualTo(1); + assertThat(snapshot.isEmpty()).isFalse(); + } + + @Test + public void testParseMemorySnapshotFromStatus_emptyContents() { + MemorySnapshot snapshot = parseMemorySnapshotFromStatus(""); + assertThat(snapshot.rssInKilobytes).isEqualTo(0); + assertThat(snapshot.anonRssInKilobytes).isEqualTo(0); + assertThat(snapshot.swapInKilobytes).isEqualTo(0); + assertThat(snapshot.isEmpty()).isTrue(); + } } |