diff options
56 files changed, 953 insertions, 567 deletions
diff --git a/Android.bp b/Android.bp index 9602941adc1c..8947cfae00bb 100644 --- a/Android.bp +++ b/Android.bp @@ -744,7 +744,6 @@ java_defaults { "apex_aidl_interface-java", "framework-protos", "game-driver-protos", - "mediaplayer2-protos", "android.hidl.base-V1.0-java", "android.hardware.cas-V1.1-java", "android.hardware.cas-V1.0-java", diff --git a/api/current.txt b/api/current.txt index 921cfb5b14aa..16bc8c8cf804 100644 --- a/api/current.txt +++ b/api/current.txt @@ -44381,6 +44381,7 @@ package android.telephony { method public int getChannelNumber(); method public String getMccString(); method public String getMncString(); + method public long getNci(); method public int getPci(); method public int getTac(); method public void writeToParcel(android.os.Parcel, int); diff --git a/cmds/bootanimation/bootanim.rc b/cmds/bootanimation/bootanim.rc index 1b3c32b20503..469c9646a4aa 100644 --- a/cmds/bootanimation/bootanim.rc +++ b/cmds/bootanimation/bootanim.rc @@ -2,7 +2,6 @@ service bootanim /system/bin/bootanimation class core animation user graphics group graphics audio - updatable disabled oneshot writepid /dev/stune/top-app/tasks diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp index 067b6eddf254..cca6d525ea16 100644 --- a/cmds/statsd/benchmark/metric_util.cpp +++ b/cmds/statsd/benchmark/metric_util.cpp @@ -367,7 +367,8 @@ sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const Stat sp<AlarmMonitor> periodicAlarmMonitor; sp<StatsLogProcessor> processor = new StatsLogProcessor(uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec * NS_PER_SEC, [](const ConfigKey&) { return true; }); + timeBaseSec * NS_PER_SEC, [](const ConfigKey&) { return true; }, + [](const int&, const vector<int64_t>&) { return true; }); processor->OnConfigUpdated(timeBaseSec * NS_PER_SEC, key, config); return processor; } @@ -393,4 +394,4 @@ int64_t StringToId(const string& str) { } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index dd18bd4cc8ad..653ef2ec9869 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -88,12 +88,15 @@ StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap, const sp<AlarmMonitor>& anomalyAlarmMonitor, const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, - const std::function<bool(const ConfigKey&)>& sendBroadcast) + const std::function<bool(const ConfigKey&)>& sendBroadcast, + const std::function<bool( + const int&, const vector<int64_t>&)>& activateBroadcast) : mUidMap(uidMap), mPullerManager(pullerManager), mAnomalyAlarmMonitor(anomalyAlarmMonitor), mPeriodicAlarmMonitor(periodicAlarmMonitor), mSendBroadcast(sendBroadcast), + mSendActivationBroadcast(activateBroadcast), mTimeBaseNs(timeBaseNs), mLargestTimestampSeen(0), mLastTimestampSeen(0) { @@ -223,11 +226,73 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { mapIsolatedUidToHostUidIfNecessaryLocked(event); } + std::unordered_set<int> uidsWithActiveConfigsChanged; + std::unordered_map<int, std::vector<int64_t>> activeConfigsPerUid; // pass the event to metrics managers. for (auto& pair : mMetricsManagers) { + int uid = pair.first.GetUid(); + int64_t configId = pair.first.GetId(); + bool isPrevActive = pair.second->isActive(); pair.second->onLogEvent(*event); + bool isCurActive = pair.second->isActive(); + // Map all active configs by uid. + if (isCurActive) { + auto activeConfigs = activeConfigsPerUid.find(uid); + if (activeConfigs != activeConfigsPerUid.end()) { + activeConfigs->second.push_back(configId); + } else { + vector<int64_t> newActiveConfigs; + newActiveConfigs.push_back(configId); + activeConfigsPerUid[uid] = newActiveConfigs; + } + } + // The activation state of this config changed. + if (isPrevActive != isCurActive) { + VLOG("Active status changed for uid %d", uid); + uidsWithActiveConfigsChanged.insert(uid); + StatsdStats::getInstance().noteActiveStatusChanged(pair.first, isCurActive); + } flushIfNecessaryLocked(event->GetElapsedTimestampNs(), pair.first, *(pair.second)); } + + for (int uid : uidsWithActiveConfigsChanged) { + // Send broadcast so that receivers can pull data. + auto lastBroadcastTime = mLastActivationBroadcastTimes.find(uid); + if (lastBroadcastTime != mLastActivationBroadcastTimes.end()) { + if (currentTimestampNs - lastBroadcastTime->second < + StatsdStats::kMinActivationBroadcastPeriodNs) { + VLOG("StatsD would've sent an activation broadcast but the rate limit stopped us."); + return; + } + } + auto activeConfigs = activeConfigsPerUid.find(uid); + if (activeConfigs != activeConfigsPerUid.end()) { + if (mSendActivationBroadcast(uid, activeConfigs->second)) { + VLOG("StatsD sent activation notice for uid %d", uid); + mLastActivationBroadcastTimes[uid] = currentTimestampNs; + } + } else { + std::vector<int64_t> emptyActiveConfigs; + if (mSendActivationBroadcast(uid, emptyActiveConfigs)) { + VLOG("StatsD sent EMPTY activation notice for uid %d", uid); + mLastActivationBroadcastTimes[uid] = currentTimestampNs; + } + } + } +} + +void StatsLogProcessor::GetActiveConfigs(const int uid, vector<int64_t>& outActiveConfigs) { + std::lock_guard<std::mutex> lock(mMetricsMutex); + GetActiveConfigsLocked(uid, outActiveConfigs); +} + +void StatsLogProcessor::GetActiveConfigsLocked(const int uid, vector<int64_t>& outActiveConfigs) { + outActiveConfigs.clear(); + for (auto& pair : mMetricsManagers) { + if (pair.first.GetUid() == uid && pair.second->isActive()) { + outActiveConfigs.push_back(pair.first.GetId()); + } + } } void StatsLogProcessor::OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key, @@ -444,6 +509,18 @@ void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) { mLastBroadcastTimes.erase(key); + int uid = key.GetUid(); + bool lastConfigForUid = true; + for (auto it : mMetricsManagers) { + if (it.first.GetUid() == uid) { + lastConfigForUid = false; + break; + } + } + if (lastConfigForUid) { + mLastActivationBroadcastTimes.erase(uid); + } + if (mMetricsManagers.empty()) { mPullerManager->ForceClearPullerCache(); } diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index caf1a713986d..ea9c6e704017 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -49,7 +49,9 @@ public: const sp<AlarmMonitor>& anomalyAlarmMonitor, const sp<AlarmMonitor>& subscriberTriggerAlarmMonitor, const int64_t timeBaseNs, - const std::function<bool(const ConfigKey&)>& sendBroadcast); + const std::function<bool(const ConfigKey&)>& sendBroadcast, + const std::function<bool(const int&, + const vector<int64_t>&)>& sendActivationBroadcast); virtual ~StatsLogProcessor(); void OnLogEvent(LogEvent* event); @@ -60,6 +62,8 @@ public: size_t GetMetricsSize(const ConfigKey& key) const; + void GetActiveConfigs(const int uid, vector<int64_t>& outActiveConfigs); + void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs, const bool include_current_partial_bucket, const bool erase_data, const DumpReportReason dumpReportReason, vector<uint8_t>* outData); @@ -125,6 +129,9 @@ private: std::unordered_map<ConfigKey, long> mLastBroadcastTimes; + // Last time we sent a broadcast to this uid that the active configs had changed. + std::unordered_map<int, long> mLastActivationBroadcastTimes; + // Tracks when we last checked the bytes consumed for each config key. std::unordered_map<ConfigKey, long> mLastByteSizeTimes; @@ -144,6 +151,8 @@ private: void OnConfigUpdatedLocked( const int64_t currentTimestampNs, const ConfigKey& key, const StatsdConfig& config); + void GetActiveConfigsLocked(const int uid, vector<int64_t>& outActiveConfigs); + void WriteDataToDiskLocked(const DumpReportReason dumpReportReason); void WriteDataToDiskLocked(const ConfigKey& key, const int64_t timestampNs, const DumpReportReason dumpReportReason); @@ -174,6 +183,10 @@ private: // to retrieve the stored data. std::function<bool(const ConfigKey& key)> mSendBroadcast; + // Function used to send a broadcast so that receiver can be notified of which configs + // are currently active. + std::function<bool(const int& uid, const vector<int64_t>& configIds)> mSendActivationBroadcast; + const int64_t mTimeBaseNs; // Largest timestamp of the events that we have processed. diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index bd21a955729d..b478fed49a54 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -176,6 +176,21 @@ StatsService::StatsService(const sp<Looper>& handlerLooper) sc->sendDataBroadcast(receiver, mProcessor->getLastReportTimeNs(key)); return true; } + }, + [this](const int& uid, const vector<int64_t>& activeConfigs) { + auto receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid); + sp<IStatsCompanionService> sc = getStatsCompanionService(); + if (sc == nullptr) { + VLOG("Could not access statsCompanion"); + return false; + } else if (receiver == nullptr) { + VLOG("Could not find receiver for uid %d", uid); + return false; + } else { + sc->sendActiveConfigsChangedBroadcast(receiver, activeConfigs); + VLOG("StatsService::active configs broadcast succeeded for uid %d" , uid); + return true; + } }); mConfigManager->AddListener(mProcessor); @@ -357,6 +372,9 @@ status_t StatsService::command(int in, int out, int err, Vector<String8>& args, if (!args[0].compare(String8("print-logs"))) { return cmd_print_logs(out, args); } + if (!args[0].compare(String8("send-active-configs"))) { + return cmd_trigger_active_config_broadcast(out, args); + } if (!args[0].compare(String8("data-subscribe"))) { if (mShellSubscriber == nullptr) { mShellSubscriber = new ShellSubscriber(mUidMap, mPullerManager); @@ -449,6 +467,19 @@ void StatsService::print_cmd_help(int out) { dprintf(out, " NAME The name of the configuration\n"); dprintf(out, "\n"); dprintf(out, "\n"); + dprintf(out, + "usage: adb shell cmd stats send-active-configs [--uid=UID] [--configs] " + "[NAME1] [NAME2] [NAME3..]\n"); + dprintf(out, " Send a broadcast that informs the subscriber of the current active configs.\n"); + dprintf(out, " --uid=UID The uid of the configurations. It is only possible to pass\n"); + dprintf(out, " the UID parameter on eng builds. If UID is omitted the\n"); + dprintf(out, " calling uid is used.\n"); + dprintf(out, " --configs Send the list of configs in the name list instead of\n"); + dprintf(out, " the currently active configs\n"); + dprintf(out, " NAME LIST List of configuration names to be included in the broadcast.\n"); + + dprintf(out, "\n"); + dprintf(out, "\n"); dprintf(out, "usage: adb shell cmd stats print-stats\n"); dprintf(out, " Prints some basic stats.\n"); dprintf(out, " --proto Print proto binary instead of string format.\n"); @@ -499,6 +530,59 @@ status_t StatsService::cmd_trigger_broadcast(int out, Vector<String8>& args) { return NO_ERROR; } +status_t StatsService::cmd_trigger_active_config_broadcast(int out, Vector<String8>& args) { + const int argCount = args.size(); + int uid; + vector<int64_t> configIds; + if (argCount == 1) { + // Automatically pick the uid and send a broadcast that has no active configs. + uid = IPCThreadState::self()->getCallingUid(); + mProcessor->GetActiveConfigs(uid, configIds); + } else { + int curArg = 1; + if(args[curArg].find("--uid=") == 0) { + string uidArgStr(args[curArg].c_str()); + string uidStr = uidArgStr.substr(6); + if (!getUidFromString(uidStr.c_str(), uid)) { + dprintf(out, "Invalid UID. Note that the config can only be set for " + "other UIDs on eng or userdebug builds.\n"); + return UNKNOWN_ERROR; + } + curArg++; + } else { + uid = IPCThreadState::self()->getCallingUid(); + } + if (curArg == argCount || args[curArg] != "--configs") { + VLOG("Reached end of args, or specify configs not set. Sending actual active configs,"); + mProcessor->GetActiveConfigs(uid, configIds); + } else { + // Flag specified, use the given list of configs. + curArg++; + for (int i = curArg; i < argCount; i++) { + char* endp; + int64_t configID = strtoll(args[i].c_str(), &endp, 10); + if (endp == args[i].c_str() || *endp != '\0') { + dprintf(out, "Error parsing config ID.\n"); + return UNKNOWN_ERROR; + } + VLOG("Adding config id %ld", static_cast<long>(configID)); + configIds.push_back(configID); + } + } + } + auto receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid); + sp<IStatsCompanionService> sc = getStatsCompanionService(); + if (sc == nullptr) { + VLOG("Could not access statsCompanion"); + } else if (receiver == nullptr) { + VLOG("Could not find receiver for uid %d", uid); + } else { + sc->sendActiveConfigsChangedBroadcast(receiver, configIds); + VLOG("StatsService::trigger active configs changed broadcast succeeded for uid %d" , uid); + } + return NO_ERROR; +} + status_t StatsService::cmd_config(int in, int out, int err, Vector<String8>& args) { const int argCount = args.size(); if (argCount >= 2) { @@ -762,7 +846,10 @@ status_t StatsService::cmd_print_logs(int out, const Vector<String8>& args) { } bool StatsService::getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid) { - const char* s = args[uidArgIndex].c_str(); + return getUidFromString(args[uidArgIndex].c_str(), uid); +} + +bool StatsService::getUidFromString(const char* s, int32_t& uid) { if (*s == '\0') { return false; } @@ -998,8 +1085,13 @@ Status StatsService::setActiveConfigsChangedOperation(const sp<android::IBinder> ENFORCE_DUMP_AND_USAGE_STATS(packageName); IPCThreadState* ipc = IPCThreadState::self(); - mConfigManager->SetActiveConfigsChangedReceiver(ipc->getCallingUid(), intentSender); - //TODO: Return the list of configs that are already active + int uid = ipc->getCallingUid(); + mConfigManager->SetActiveConfigsChangedReceiver(uid, intentSender); + if (output != nullptr) { + mProcessor->GetActiveConfigs(uid, *output); + } else { + ALOGW("StatsService::setActiveConfigsChanged output was nullptr"); + } return Status::ok(); } diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 941ed462b303..7f10d74ec7d6 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -275,6 +275,12 @@ private: */ status_t cmd_trigger_broadcast(int outFd, Vector<String8>& args); + + /** + * Trigger an active configs changed broadcast. + */ + status_t cmd_trigger_active_config_broadcast(int outFd, Vector<String8>& args); + /** * Handle the config sub-command. */ @@ -341,6 +347,15 @@ private: bool getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid); /** + * Writes the value of uidSting into uid. + * Returns whether the uid is reasonable (type uid_t) and whether + * 1. it is equal to the calling uid, or + * 2. the device is mEngBuild, or + * 3. the caller is AID_ROOT and the uid is AID_SHELL (i.e. ROOT can impersonate SHELL). + */ + bool getUidFromString(const char* uidString, int32_t& uid); + + /** * Adds a configuration after checking permissions and obtaining UID from binder call. */ bool addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config); diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp index aa22333ab26c..fc949b494194 100644 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ b/cmds/statsd/src/config/ConfigManager.cpp @@ -144,10 +144,20 @@ void ConfigManager::RemoveConfig(const ConfigKey& key) { { lock_guard <mutex> lock(mMutex); - auto uidIt = mConfigs.find(key.GetUid()); + auto uid = key.GetUid(); + auto uidIt = mConfigs.find(uid); if (uidIt != mConfigs.end() && uidIt->second.find(key) != uidIt->second.end()) { // Remove from map uidIt->second.erase(key); + + // No more configs for this uid, lets remove the active configs callback. + if (uidIt->second.empty()) { + auto itActiveConfigsChangedReceiver = mActiveConfigsChangedReceivers.find(uid); + if (itActiveConfigsChangedReceiver != mActiveConfigsChangedReceivers.end()) { + mActiveConfigsChangedReceivers.erase(itActiveConfigsChangedReceiver); + } + } + for (const sp<ConfigListener>& listener : mListeners) { broadcastList.push_back(listener); } diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index 40329b7c86ab..b433c41518cc 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -82,6 +82,8 @@ const int FIELD_ID_CONFIG_STATS_METRIC_STATS = 15; const int FIELD_ID_CONFIG_STATS_ALERT_STATS = 16; const int FIELD_ID_CONFIG_STATS_METRIC_DIMENSION_IN_CONDITION_STATS = 17; const int FIELD_ID_CONFIG_STATS_ANNOTATION = 18; +const int FIELD_ID_CONFIG_STATS_ACTIVATION = 22; +const int FIELD_ID_CONFIG_STATS_DEACTIVATION = 23; const int FIELD_ID_CONFIG_STATS_ANNOTATION_INT64 = 1; const int FIELD_ID_CONFIG_STATS_ANNOTATION_INT32 = 2; @@ -206,6 +208,25 @@ void StatsdStats::noteBroadcastSent(const ConfigKey& key, int32_t timeSec) { it->second->broadcast_sent_time_sec.push_back(timeSec); } +void StatsdStats::noteActiveStatusChanged(const ConfigKey& key, bool activated) { + noteActiveStatusChanged(key, activated, getWallClockSec()); +} + +void StatsdStats::noteActiveStatusChanged(const ConfigKey& key, bool activated, int32_t timeSec) { + lock_guard<std::mutex> lock(mLock); + auto it = mConfigStats.find(key); + if (it == mConfigStats.end()) { + ALOGE("Config key %s not found!", key.ToString().c_str()); + return; + } + auto& vec = activated ? it->second->activation_time_sec + : it->second->deactivation_time_sec; + if (vec.size() == kMaxTimestampCount) { + vec.pop_front(); + } + vec.push_back(timeSec); +} + void StatsdStats::noteDataDropped(const ConfigKey& key, const size_t totalBytes) { noteDataDropped(key, totalBytes, getWallClockSec()); } @@ -501,6 +522,8 @@ void StatsdStats::resetInternalLocked() { mLogLossStats.clear(); for (auto& config : mConfigStats) { config.second->broadcast_sent_time_sec.clear(); + config.second->activation_time_sec.clear(); + config.second->deactivation_time_sec.clear(); config.second->data_drop_time_sec.clear(); config.second->data_drop_bytes.clear(); config.second->dump_report_stats.clear(); @@ -558,6 +581,14 @@ void StatsdStats::dumpStats(int out) const { dprintf(out, "\tbroadcast time: %d\n", broadcastTime); } + for (const int& activationTime : configStats->activation_time_sec) { + dprintf(out, "\tactivation time: %d\n", activationTime); + } + + for (const int& deactivationTime : configStats->deactivation_time_sec) { + dprintf(out, "\tdeactivation time: %d\n", deactivationTime); + } + auto dropTimePtr = configStats->data_drop_time_sec.begin(); auto dropBytesPtr = configStats->data_drop_bytes.begin(); for (int i = 0; i < (int)configStats->data_drop_time_sec.size(); @@ -586,6 +617,14 @@ void StatsdStats::dumpStats(int out) const { (long long)broadcastTime); } + for (const int& activationTime : configStats->activation_time_sec) { + dprintf(out, "\tactivation time: %d\n", activationTime); + } + + for (const int& deactivationTime : configStats->deactivation_time_sec) { + dprintf(out, "\tdeactivation time: %d\n", deactivationTime); + } + auto dropTimePtr = configStats->data_drop_time_sec.begin(); auto dropBytesPtr = configStats->data_drop_bytes.begin(); for (int i = 0; i < (int)configStats->data_drop_time_sec.size(); @@ -696,6 +735,16 @@ void addConfigStatsToProto(const ConfigStats& configStats, ProtoOutputStream* pr broadcast); } + for (const auto& activation : configStats.activation_time_sec) { + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_ACTIVATION | FIELD_COUNT_REPEATED, + activation); + } + + for (const auto& deactivation : configStats.deactivation_time_sec) { + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DEACTIVATION | FIELD_COUNT_REPEATED, + deactivation); + } + for (const auto& drop_time : configStats.data_drop_time_sec) { proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DATA_DROP_TIME | FIELD_COUNT_REPEATED, drop_time); diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index 65e8a324d7d3..5275c8f34fd0 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -42,6 +42,13 @@ struct ConfigStats { bool is_valid; std::list<int32_t> broadcast_sent_time_sec; + + // Times at which this config is activated. + std::list<int32_t> activation_time_sec; + + // Times at which this config is deactivated. + std::list<int32_t> deactivation_time_sec; + std::list<int32_t> data_drop_time_sec; // Number of bytes dropped at corresponding time. std::list<int64_t> data_drop_bytes; @@ -132,6 +139,9 @@ public: /* Min period between two checks of byte size per config key in nanoseconds. */ static const int64_t kMinByteSizeCheckPeriodNs = 60 * NS_PER_SEC; + /* Minimum period between two activation broadcasts in nanoseconds. */ + static const int64_t kMinActivationBroadcastPeriodNs = 10 * NS_PER_SEC; + // Maximum age (30 days) that files on disk can exist in seconds. static const int kMaxAgeSecond = 60 * 60 * 24 * 30; @@ -175,6 +185,13 @@ public: void noteBroadcastSent(const ConfigKey& key); /** + * Report that a config has become activated or deactivated. + * This can be different from whether or not a broadcast is sent if the + * guardrail prevented the broadcast from being sent. + */ + void noteActiveStatusChanged(const ConfigKey& key, bool activate); + + /** * Report a config's metrics data has been dropped. */ void noteDataDropped(const ConfigKey& key, const size_t totalBytes); @@ -507,6 +524,8 @@ private: void noteBroadcastSent(const ConfigKey& key, int32_t timeSec); + void noteActiveStatusChanged(const ConfigKey& key, bool activate, int32_t timeSec); + void addToIceBoxLocked(std::shared_ptr<ConfigStats>& stats); /** diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index 495138ee9b77..11075685b7fa 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -94,7 +94,7 @@ void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) { void MetricProducer::addActivation(int activationTrackerIndex, int64_t ttl_seconds) { std::lock_guard<std::mutex> lock(mMutex); // When a metric producer does not depend on any activation, its mIsActive is true. - // Therefor, if this is the 1st activation, mIsActive will turn to false. Otherwise it does not + // Therefore, if this is the 1st activation, mIsActive will turn to false. Otherwise it does not // change. if (mEventActivationMap.empty()) { mIsActive = false; diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 6ed6ab500597..4851a8d40baa 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -118,6 +118,16 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, ALOGE("This config has too many alerts! Reject!"); mConfigValid = false; } + + mIsAlwaysActive = (mMetricIndexesWithActivation.size() != mAllMetricProducers.size()) || + (mAllMetricProducers.size() == 0); + bool isActive = mIsAlwaysActive; + for (int metric : mMetricIndexesWithActivation) { + isActive |= mAllMetricProducers[metric]->isActive(); + } + mIsActive = isActive; + VLOG("mIsActive is initialized to %d", mIsActive) + // no matter whether this config is valid, log it in the stats. StatsdStats::getInstance().noteConfigReceived( key, mAllMetricProducers.size(), mAllConditionTrackers.size(), mAllAtomMatchers.size(), @@ -332,12 +342,14 @@ void MetricsManager::onLogEvent(const LogEvent& event) { int tagId = event.GetTagId(); int64_t eventTimeNs = event.GetElapsedTimestampNs(); - bool isActive = false; + bool isActive = mIsAlwaysActive; for (int metric : mMetricIndexesWithActivation) { mAllMetricProducers[metric]->flushIfExpire(eventTimeNs); isActive |= mAllMetricProducers[metric]->isActive(); } + mIsActive = isActive; + if (mTagIds.find(tagId) == mTagIds.end()) { // not interesting... return; diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index cb1cefbf2063..eab1f762b390 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -128,6 +128,8 @@ public: // Does not change the state. virtual size_t byteSize(); + // Returns whether or not this config is active. + // The config is active if any metric in the config is active. inline bool isActive() const { return mIsActive; } @@ -241,9 +243,12 @@ private: // The metrics that don't need to be uploaded or even reported. std::set<int64_t> mNoReportMetricIds; - // Any metric active means the config is active. + // The config is active if any metric in the config is active. bool mIsActive; + // The config is always active if any metric in the config does not have an activation signal. + bool mIsAlwaysActive; + FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions); FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks); FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid); diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index 863261ae1626..f7428a50b3da 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -367,6 +367,8 @@ message StatsdStatsReport { optional int32 field_int32 = 2; } repeated Annotation annotation = 18; + repeated int32 activation_time_sec = 22; + repeated int32 deactivation_time_sec = 23; } repeated ConfigStats config_stats = 3; diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index 60df165f102c..5f3aae3ab93a 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -65,7 +65,8 @@ TEST(StatsLogProcessorTest, TestRateLimitByteSize) { sp<AlarmMonitor> periodicAlarmMonitor; // Construct the processor with a dummy sendBroadcast function that does nothing. StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, 0, - [](const ConfigKey& key) { return true; }); + [](const ConfigKey& key) { return true; }, + [](const int&, const vector<int64_t>&) {return true;}); MockMetricsManager mockMetricsManager; @@ -88,7 +89,8 @@ TEST(StatsLogProcessorTest, TestRateLimitBroadcast) { [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true; - }); + }, + [](const int&, const vector<int64_t>&) {return true;}); MockMetricsManager mockMetricsManager; @@ -118,7 +120,8 @@ TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) { [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true; - }); + }, + [](const int&, const vector<int64_t>&) {return true;}); MockMetricsManager mockMetricsManager; @@ -162,7 +165,8 @@ TEST(StatsLogProcessorTest, TestUidMapHasSnapshot) { [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true; - }); + }, + [](const int&, const vector<int64_t>&) {return true;}); ConfigKey key(3, 4); StatsdConfig config = MakeConfig(true); p.OnConfigUpdated(0, key, config); @@ -192,7 +196,8 @@ TEST(StatsLogProcessorTest, TestEmptyConfigHasNoUidMap) { [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true; - }); + }, + [](const int&, const vector<int64_t>&) {return true;}); ConfigKey key(3, 4); StatsdConfig config = MakeConfig(false); p.OnConfigUpdated(0, key, config); @@ -218,7 +223,8 @@ TEST(StatsLogProcessorTest, TestReportIncludesSubConfig) { [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true; - }); + }, + [](const int&, const vector<int64_t>&) {return true;}); ConfigKey key(3, 4); StatsdConfig config; auto annotation = config.add_annotation(); diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp index f0d9cf188661..c04a40cfebd9 100644 --- a/cmds/statsd/tests/UidMap_test.cpp +++ b/cmds/statsd/tests/UidMap_test.cpp @@ -45,7 +45,8 @@ TEST(UidMapTest, TestIsolatedUID) { sp<AlarmMonitor> subscriberAlarmMonitor; // Construct the processor with a dummy sendBroadcast function that does nothing. StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, - [](const ConfigKey& key) { return true; }); + [](const ConfigKey& key) { return true; }, + [](const int&, const vector<int64_t>&) {return true;}); LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1); addEvent.write(100); // parent UID addEvent.write(101); // isolated UID diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp index 44a88f049443..1ff7982e1232 100644 --- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp +++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp @@ -133,6 +133,13 @@ TEST(StatsdStatsTest, TestSubStats) { stats.noteMetricsReportSent(key, 0); stats.noteMetricsReportSent(key, 0); + // activation_time_sec -> 2 + stats.noteActiveStatusChanged(key, true); + stats.noteActiveStatusChanged(key, true); + + // deactivation_time_sec -> 1 + stats.noteActiveStatusChanged(key, false); + vector<uint8_t> output; stats.dumpStats(&output, true); // Dump and reset stats StatsdStatsReport report; @@ -146,6 +153,8 @@ TEST(StatsdStatsTest, TestSubStats) { EXPECT_EQ(123, configReport.data_drop_bytes(0)); EXPECT_EQ(3, configReport.dump_report_time_sec_size()); EXPECT_EQ(3, configReport.dump_report_data_size_size()); + EXPECT_EQ(2, configReport.activation_time_sec_size()); + EXPECT_EQ(1, configReport.deactivation_time_sec_size()); EXPECT_EQ(1, configReport.annotation_size()); EXPECT_EQ(123, configReport.annotation(0).field_int64()); EXPECT_EQ(456, configReport.annotation(0).field_int32()); @@ -344,6 +353,8 @@ TEST(StatsdStatsTest, TestTimestampThreshold) { stats.noteDataDropped(key, timestamps[i]); stats.noteBroadcastSent(key, timestamps[i]); stats.noteMetricsReportSent(key, 0, timestamps[i]); + stats.noteActiveStatusChanged(key, true, timestamps[i]); + stats.noteActiveStatusChanged(key, false, timestamps[i]); } int32_t newTimestamp = 10000; @@ -352,6 +363,8 @@ TEST(StatsdStatsTest, TestTimestampThreshold) { stats.noteDataDropped(key, 123, 10000); stats.noteBroadcastSent(key, 10000); stats.noteMetricsReportSent(key, 0, 10000); + stats.noteActiveStatusChanged(key, true, 10000); + stats.noteActiveStatusChanged(key, false, 10000); EXPECT_TRUE(stats.mConfigStats.find(key) != stats.mConfigStats.end()); const auto& configStats = stats.mConfigStats[key]; @@ -360,17 +373,23 @@ TEST(StatsdStatsTest, TestTimestampThreshold) { EXPECT_EQ(maxCount, configStats->broadcast_sent_time_sec.size()); EXPECT_EQ(maxCount, configStats->data_drop_time_sec.size()); EXPECT_EQ(maxCount, configStats->dump_report_stats.size()); + EXPECT_EQ(maxCount, configStats->activation_time_sec.size()); + EXPECT_EQ(maxCount, configStats->deactivation_time_sec.size()); // the oldest timestamp is the second timestamp in history EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front()); - EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front()); - EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front()); + EXPECT_EQ(1, configStats->data_drop_bytes.front()); + EXPECT_EQ(1, configStats->dump_report_stats.front().first); + EXPECT_EQ(1, configStats->activation_time_sec.front()); + EXPECT_EQ(1, configStats->deactivation_time_sec.front()); // the last timestamp is the newest timestamp. EXPECT_EQ(newTimestamp, configStats->broadcast_sent_time_sec.back()); EXPECT_EQ(newTimestamp, configStats->data_drop_time_sec.back()); EXPECT_EQ(123, configStats->data_drop_bytes.back()); EXPECT_EQ(newTimestamp, configStats->dump_report_stats.back().first); + EXPECT_EQ(newTimestamp, configStats->activation_time_sec.back()); + EXPECT_EQ(newTimestamp, configStats->deactivation_time_sec.back()); } TEST(StatsdStatsTest, TestSystemServerCrash) { diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index b8b1a1db2c12..2c4f3c7692c9 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -461,7 +461,8 @@ sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const in [](const sp<IStatsCompanionService>&){}); sp<StatsLogProcessor> processor = new StatsLogProcessor(uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseNs, [](const ConfigKey&) { return true; }); + timeBaseNs, [](const ConfigKey&) { return true; }, + [](const int&, const vector<int64_t>&) {return true;}); processor->OnConfigUpdated(currentTimeNs, key, config); return processor; } @@ -826,4 +827,4 @@ void backfillStartEndTimestamp(ConfigMetricsReportList *config_report_list) { } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index d46dbedfe8c2..5c4c0052cfbb 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -870,8 +870,9 @@ public final class LoadedApk { } } - // /vendor/lib, /odm/lib and /product/lib are added to the native lib search - // paths of the classloader. Note that this is done AFTER the classloader is + // /aepx/com.android.runtime/lib, /vendor/lib, /odm/lib and /product/lib + // are added to the native lib search paths of the classloader. + // Note that this is done AFTER the classloader is // created by ApplicationLoaders.getDefault().getClassLoader(...). The // reason is because if we have added the paths when creating the classloader // above, the paths are also added to the search path of the linker namespace @@ -888,8 +889,11 @@ public final class LoadedApk { // System.loadLibrary(). In order to prevent the problem, we explicitly // add the paths only to the classloader, and not to the native loader // (linker namespace). - List<String> extraLibPaths = new ArrayList<>(3); + List<String> extraLibPaths = new ArrayList<>(4); String abiSuffix = VMRuntime.getRuntime().is64Bit() ? "64" : ""; + if (!defaultSearchPaths.contains("/apex/com.android.runtime/lib")) { + extraLibPaths.add("/apex/com.android.runtime/lib" + abiSuffix); + } if (!defaultSearchPaths.contains("/vendor/lib")) { extraLibPaths.add("/vendor/lib" + abiSuffix); } diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index f3ebd7f36fd6..ac44fe93ac31 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -16,6 +16,8 @@ package android.hardware.display; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.ParceledListSlice; @@ -229,7 +231,17 @@ public final class DisplayManagerGlobal { return getCompatibleDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS); } - public void registerDisplayListener(DisplayListener listener, Handler handler) { + /** + * Register a listener for display-related changes. + * + * @param listener The listener that will be called when display changes occur. + * @param handler Handler for the thread that will be receiving the callbacks. May be null. + * If null, listener will use the handler for the current thread, and if still null, + * the handler for the main thread. + * If that is still null, a runtime exception will be thrown. + */ + public void registerDisplayListener(@NonNull DisplayListener listener, + @Nullable Handler handler) { if (listener == null) { throw new IllegalArgumentException("listener must not be null"); } @@ -237,7 +249,8 @@ public final class DisplayManagerGlobal { synchronized (mLock) { int index = findDisplayListenerLocked(listener); if (index < 0) { - mDisplayListeners.add(new DisplayListenerDelegate(listener, handler)); + Looper looper = getLooperForHandler(handler); + mDisplayListeners.add(new DisplayListenerDelegate(listener, looper)); registerCallbackIfNeededLocked(); } } @@ -258,6 +271,17 @@ public final class DisplayManagerGlobal { } } + private static Looper getLooperForHandler(@Nullable Handler handler) { + Looper looper = handler != null ? handler.getLooper() : Looper.myLooper(); + if (looper == null) { + looper = Looper.getMainLooper(); + } + if (looper == null) { + throw new RuntimeException("Could not get Looper for the UI thread."); + } + return looper; + } + private int findDisplayListenerLocked(DisplayListener listener) { final int numListeners = mDisplayListeners.size(); for (int i = 0; i < numListeners; i++) { @@ -636,8 +660,8 @@ public final class DisplayManagerGlobal { private static final class DisplayListenerDelegate extends Handler { public final DisplayListener mListener; - public DisplayListenerDelegate(DisplayListener listener, Handler handler) { - super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/); + DisplayListenerDelegate(DisplayListener listener, @NonNull Looper looper) { + super(looper, null, true /*async*/); mListener = listener; } diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java index 7a4c9bc16ac7..ca49438390e9 100644 --- a/core/java/android/net/NetworkStack.java +++ b/core/java/android/net/NetworkStack.java @@ -15,6 +15,7 @@ */ package android.net; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; @@ -27,6 +28,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.PackageManager; import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.IDhcpServerCallbacks; import android.net.ip.IIpClientCallbacks; @@ -201,7 +203,33 @@ public class NetworkStack { final ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0); intent.setComponent(comp); - if (comp == null || !context.bindServiceAsUser(intent, new NetworkStackConnection(), + if (comp == null) { + Slog.wtf(TAG, "Could not resolve the network stack with " + intent); + // TODO: crash/reboot system server ? + return; + } + + final PackageManager pm = context.getPackageManager(); + int uid = -1; + try { + uid = pm.getPackageUid(comp.getPackageName(), UserHandle.USER_SYSTEM); + } catch (PackageManager.NameNotFoundException e) { + Slog.wtf("Network stack package not found", e); + // Fall through + } + + if (uid != Process.NETWORK_STACK_UID) { + throw new SecurityException("Invalid network stack UID: " + uid); + } + + final int hasPermission = + pm.checkPermission(PERMISSION_MAINLINE_NETWORK_STACK, comp.getPackageName()); + if (hasPermission != PERMISSION_GRANTED) { + throw new SecurityException( + "Network stack does not have permission " + PERMISSION_MAINLINE_NETWORK_STACK); + } + + if (!context.bindServiceAsUser(intent, new NetworkStackConnection(), Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) { Slog.wtf(TAG, "Could not bind to network stack in-process, or in app with " + intent); diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java index 1f3369376b10..a851e04e78ec 100644 --- a/core/java/android/os/AsyncTask.java +++ b/core/java/android/os/AsyncTask.java @@ -21,13 +21,14 @@ import android.annotation.Nullable; import android.annotation.WorkerThread; import java.util.ArrayDeque; -import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.FutureTask; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -190,13 +191,19 @@ import java.util.concurrent.atomic.AtomicInteger; public abstract class AsyncTask<Params, Progress, Result> { private static final String LOG_TAG = "AsyncTask"; - private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); - // We want at least 2 threads and at most 4 threads in the core pool, - // preferring to have 1 less than the CPU count to avoid saturating - // the CPU with background work - private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)); - private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; - private static final int KEEP_ALIVE_SECONDS = 30; + // We keep only a single pool thread around all the time. + // We let the pool grow to a fairly large number of threads if necessary, + // but let them time out quickly. In the unlikely case that we run out of threads, + // we fall back to a simple unbounded-queue executor. + // This combination ensures that: + // 1. We normally keep few threads (1) around. + // 2. We queue only after launching a significantly larger, but still bounded, set of threads. + // 3. We keep the total number of threads bounded, but still allow an unbounded set + // of tasks to be queued. + private static final int CORE_POOL_SIZE = 1; + private static final int MAXIMUM_POOL_SIZE = 20; + private static final int BACKUP_POOL_SIZE = 5; + private static final int KEEP_ALIVE_SECONDS = 3; private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); @@ -206,8 +213,29 @@ public abstract class AsyncTask<Params, Progress, Result> { } }; - private static final BlockingQueue<Runnable> sPoolWorkQueue = - new LinkedBlockingQueue<Runnable>(128); + // Used only for rejected executions. + // Initialization protected by sRunOnSerialPolicy lock. + private static ThreadPoolExecutor sBackupExecutor; + private static LinkedBlockingQueue<Runnable> sBackupExecutorQueue; + + private static final RejectedExecutionHandler sRunOnSerialPolicy = + new RejectedExecutionHandler() { + public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { + android.util.Log.w(LOG_TAG, "Exceeded ThreadPoolExecutor pool size"); + // As a last ditch fallback, run it on an executor with an unbounded queue. + // Create this executor lazily, hopefully almost never. + synchronized (this) { + if (sBackupExecutor == null) { + sBackupExecutorQueue = new LinkedBlockingQueue<Runnable>(); + sBackupExecutor = new ThreadPoolExecutor( + BACKUP_POOL_SIZE, BACKUP_POOL_SIZE, KEEP_ALIVE_SECONDS, + TimeUnit.SECONDS, sBackupExecutorQueue, sThreadFactory); + sBackupExecutor.allowCoreThreadTimeOut(true); + } + } + sBackupExecutor.execute(r); + } + }; /** * An {@link Executor} that can be used to execute tasks in parallel. @@ -217,8 +245,8 @@ public abstract class AsyncTask<Params, Progress, Result> { static { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, - sPoolWorkQueue, sThreadFactory); - threadPoolExecutor.allowCoreThreadTimeOut(true); + new SynchronousQueue<Runnable>(), sThreadFactory); + threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy); THREAD_POOL_EXECUTOR = threadPoolExecutor; } diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl index dde46cd93d5f..0751b964f85e 100644 --- a/core/java/android/os/IStatsCompanionService.aidl +++ b/core/java/android/os/IStatsCompanionService.aidl @@ -69,6 +69,12 @@ interface IStatsCompanionService { oneway void sendDataBroadcast(in IBinder intentSender, long lastReportTimeNs); /** + * Send a broadcast to the specified PendingIntent's as IBinder notifying it that the list + * of active configs has changed. + */ + oneway void sendActiveConfigsChangedBroadcast(in IBinder intentSender, in long[] configIds); + + /** * Requests StatsCompanionService to send a broadcast using the given intentSender * (which should cast to an IIntentSender), along with the other information specified. */ diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 7f59d35586c1..2b638f6e283f 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6103,7 +6103,7 @@ public final class Settings { * Indicates which clock face to show on lock screen and AOD while docked. * @hide */ - public static final String DOCKED_CLOCK_FACE = "docked_clock_face"; + private static final String DOCKED_CLOCK_FACE = "docked_clock_face"; /** * Set by the system to track if the user needs to see the call to action for diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 345058bd4f6f..af0b7c307ef6 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -287,8 +287,8 @@ cc_library_shared { "libsoundtrigger", "libminikin", "libprocessgroup", - "libnativebridge", - "libnativeloader", + "libnativebridge_lazy", + "libnativeloader_lazy", "libmemunreachable", "libhidlbase", "libhidltransport", diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index c57b609023ca..4d2f005998c3 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -621,7 +621,6 @@ public class SettingsBackupTest { Settings.Secure.DISABLED_PRINT_SERVICES, Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS, Settings.Secure.DISPLAY_DENSITY_FORCED, - Settings.Secure.DOCKED_CLOCK_FACE, Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION, Settings.Secure.ENABLED_INPUT_METHODS, // Intentionally removed in P diff --git a/libs/hwui/FrameMetricsObserver.h b/libs/hwui/FrameMetricsObserver.h index 237fc622dd2e..b93f07853242 100644 --- a/libs/hwui/FrameMetricsObserver.h +++ b/libs/hwui/FrameMetricsObserver.h @@ -23,7 +23,7 @@ namespace uirenderer { class FrameMetricsObserver : public VirtualLightRefBase { public: - virtual void notify(const int64_t* buffer); + virtual void notify(const int64_t* buffer) = 0; }; } // namespace uirenderer diff --git a/media/Android.bp b/media/Android.bp index 141d415cbaf3..86dc509501a4 100644 --- a/media/Android.bp +++ b/media/Android.bp @@ -3,7 +3,6 @@ java_library { srcs: [ ":updatable-media-srcs", - ":framework-media-annotation-srcs", ], aidl: { @@ -28,7 +27,12 @@ java_library { installable: true, // Make sure that the implementaion only relies on SDK or system APIs. - sdk_version: "system_current", + no_framework_libs: true, + libs: [ + // The order matters. android_system_* library should come later. + "framework_media_annotation", + "android_system_stubs_current", + ], } filegroup { @@ -125,3 +129,8 @@ java_library { sdk_version: "28", } +java_library { + name: "framework_media_annotation", + srcs: [":framework-media-annotation-srcs"], + installable: false, +} diff --git a/media/apex/java/android/media/MediaPlayer2.java b/media/apex/java/android/media/MediaPlayer2.java index c38a83131a6c..89a954061958 100644 --- a/media/apex/java/android/media/MediaPlayer2.java +++ b/media/apex/java/android/media/MediaPlayer2.java @@ -45,8 +45,8 @@ import android.util.Size; import android.view.Surface; import android.view.SurfaceHolder; -import com.android.framework.protobuf.InvalidProtocolBufferException; import com.android.internal.annotations.GuardedBy; +import com.android.media.protobuf.InvalidProtocolBufferException; import java.io.ByteArrayOutputStream; import java.io.File; diff --git a/media/proto/jarjar-rules.txt b/media/proto/jarjar-rules.txt index 7be6e732d3fd..bfb0b2782486 100644 --- a/media/proto/jarjar-rules.txt +++ b/media/proto/jarjar-rules.txt @@ -1,2 +1,2 @@ -rule com.google.protobuf.** com.android.framework.protobuf.@1 +rule com.google.protobuf.** com.android.media.protobuf.@1 diff --git a/packages/SettingsLib/common.mk b/packages/SettingsLib/common.mk index 8c309ff97370..8d24eaba5cc4 100644 --- a/packages/SettingsLib/common.mk +++ b/packages/SettingsLib/common.mk @@ -31,3 +31,4 @@ LOCAL_STATIC_ANDROID_LIBRARIES += \ androidx.legacy_legacy-preference-v14 \ SettingsLib +LOCAL_RESOURCE_DIR += $(call my-dir)/res diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java index 305a1ff97e71..dd6d563b3197 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java @@ -40,6 +40,7 @@ import androidx.loader.content.AsyncTaskLoader; import com.android.settingslib.NetworkPolicyEditor; import java.time.ZonedDateTime; +import java.util.ArrayList; import java.util.Iterator; /** @@ -52,6 +53,7 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { protected final int mNetworkType; private final NetworkPolicy mPolicy; private final NetworkTemplate mNetworkTemplate; + private final ArrayList<Long> mCycles; @VisibleForTesting final INetworkStatsService mNetworkStatsService; @@ -60,6 +62,7 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { mSubId = builder.mSubId; mNetworkType = builder.mNetworkType; mNetworkTemplate = builder.mNetworkTemplate; + mCycles = builder.mCycles; mNetworkStatsManager = (NetworkStatsManager) builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE); mNetworkStatsService = INetworkStatsService.Stub.asInterface( @@ -77,7 +80,9 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { } public D loadInBackground() { - if (mPolicy == null) { + if (mCycles != null && mCycles.size() > 1) { + loadDataForSpecificCycles(); + } else if (mPolicy == null) { loadFourWeeksData(); } else { loadPolicyData(); @@ -132,6 +137,17 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { } @VisibleForTesting + void loadDataForSpecificCycles() { + long cycleEnd = mCycles.get(0); + final int lastCycleIndex = mCycles.size() - 1; + for (int i = 1; i <= lastCycleIndex; i++) { + final long cycleStart = mCycles.get(i); + recordUsage(cycleStart, cycleEnd); + cycleEnd = cycleStart; + } + } + + @VisibleForTesting abstract void recordUsage(long start, long end); abstract D getCycleUsage(); @@ -157,11 +173,17 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { return bytes; } + @VisibleForTesting(otherwise = VisibleForTesting.NONE) + public ArrayList<Long> getCycles() { + return mCycles; + } + public static abstract class Builder<T extends NetworkCycleDataLoader> { private final Context mContext; private String mSubId; private int mNetworkType; private NetworkTemplate mNetworkTemplate; + private ArrayList<Long> mCycles; public Builder (Context context) { mContext = context; @@ -178,6 +200,16 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { return this; } + /** + * Sets the network cycles to be used to query the usage data. + * @param cycles the time slots for the network cycle to be used to query the network usage. + * @return the builder + */ + public Builder<T> setCycles(ArrayList<Long> cycles) { + mCycles = cycles; + return this; + } + public abstract T build(); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java index b8a143a376fd..c5f54bb0f0d9 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java @@ -46,6 +46,7 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.util.ReflectionHelpers; import java.time.ZonedDateTime; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -99,6 +100,20 @@ public class NetworkCycleDataLoaderTest { } @Test + public void loadInBackground_hasCyclePeriod_shouldLoadDataForSpecificCycles() { + mLoader = spy(new NetworkCycleDataTestLoader(mContext)); + doNothing().when(mLoader).loadDataForSpecificCycles(); + final ArrayList<Long> cycles = new ArrayList<>(); + cycles.add(67890L); + cycles.add(12345L); + ReflectionHelpers.setField(mLoader, "mCycles", cycles); + + mLoader.loadInBackground(); + + verify(mLoader).loadDataForSpecificCycles(); + } + + @Test public void loadPolicyData_shouldRecordUsageFromPolicyCycle() { final int networkType = ConnectivityManager.TYPE_MOBILE; final String subId = "TestSubscriber"; @@ -139,6 +154,27 @@ public class NetworkCycleDataLoaderTest { verify(mLoader).recordUsage(fourWeeksAgo, now); } + @Test + public void loadDataForSpecificCycles_shouldRecordUsageForSpecifiedTime() { + mLoader = spy(new NetworkCycleDataTestLoader(mContext)); + final long now = System.currentTimeMillis(); + final long tenDaysAgo = now - (DateUtils.DAY_IN_MILLIS * 10); + final long twentyDaysAgo = now - (DateUtils.DAY_IN_MILLIS * 20); + final long thirtyDaysAgo = now - (DateUtils.DAY_IN_MILLIS * 30); + final ArrayList<Long> cycles = new ArrayList<>(); + cycles.add(now); + cycles.add(tenDaysAgo); + cycles.add(twentyDaysAgo); + cycles.add(thirtyDaysAgo); + ReflectionHelpers.setField(mLoader, "mCycles", cycles); + + mLoader.loadDataForSpecificCycles(); + + verify(mLoader).recordUsage(tenDaysAgo, now); + verify(mLoader).recordUsage(twentyDaysAgo, tenDaysAgo); + verify(mLoader).recordUsage(thirtyDaysAgo, twentyDaysAgo); + } + public class NetworkCycleDataTestLoader extends NetworkCycleDataLoader<List<NetworkCycleData>> { private NetworkCycleDataTestLoader(Context context) { diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java index 078108d658ee..95981427b642 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java @@ -15,7 +15,6 @@ */ package com.android.keyguard.clock; -import android.annotation.Nullable; import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; @@ -26,18 +25,16 @@ import android.os.Looper; import android.provider.Settings; import android.view.LayoutInflater; -import androidx.annotation.VisibleForTesting; - import com.android.keyguard.R; -import com.android.systemui.dock.DockManager; -import com.android.systemui.dock.DockManager.DockEventListener; import com.android.systemui.plugins.ClockPlugin; import com.android.systemui.statusbar.policy.ExtensionController; import com.android.systemui.statusbar.policy.ExtensionController.Extension; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.function.Consumer; +import java.util.function.Supplier; import javax.inject.Inject; import javax.inject.Singleton; @@ -48,6 +45,7 @@ import javax.inject.Singleton; @Singleton public final class ClockManager { + private final LayoutInflater mLayoutInflater; private final ContentResolver mContentResolver; private final List<ClockInfo> mClockInfos = new ArrayList<>(); @@ -64,6 +62,7 @@ public final class ClockManager { } } }; + private final ExtensionController mExtensionController; /** * Used to select between plugin or default implementations of ClockPlugin interface. @@ -73,35 +72,13 @@ public final class ClockManager { * Consumer that accepts the a new ClockPlugin implementation when the Extension reloads. */ private final Consumer<ClockPlugin> mClockPluginConsumer = this::setClockPlugin; - /** - * Supplier of default ClockPlugin implementation. - */ - private final DefaultClockSupplier mDefaultClockSupplier; - /** - * Observe changes to dock state to know when to switch the clock face. - */ - private final DockEventListener mDockEventListener = - new DockEventListener() { - @Override - public void onEvent(int event) { - final boolean isDocked = (event == DockManager.STATE_DOCKED - || event == DockManager.STATE_DOCKED_HIDE); - mDefaultClockSupplier.setDocked(isDocked); - if (mClockExtension != null) { - mClockExtension.reload(); - } - } - }; - @Nullable - private final DockManager mDockManager; private final List<ClockChangedListener> mListeners = new ArrayList<>(); @Inject - public ClockManager(Context context, ExtensionController extensionController, - @Nullable DockManager dockManager) { + public ClockManager(Context context, ExtensionController extensionController) { mExtensionController = extensionController; - mDockManager = dockManager; + mLayoutInflater = LayoutInflater.from(context); mContentResolver = context.getContentResolver(); Resources res = context.getResources(); @@ -133,9 +110,6 @@ public final class ClockManager { .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.type_thumbnail)) .setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.type_preview)) .build()); - - mDefaultClockSupplier = new DefaultClockSupplier(new SettingsWrapper(mContentResolver), - LayoutInflater.from(context)); } /** @@ -180,32 +154,41 @@ public final class ClockManager { mContentResolver.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE), false, mContentObserver); - mContentResolver.registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.DOCKED_CLOCK_FACE), - false, mContentObserver); - if (mDockManager != null) { - mDockManager.addListener(mDockEventListener); - } mClockExtension = mExtensionController.newExtension(ClockPlugin.class) .withPlugin(ClockPlugin.class) .withCallback(mClockPluginConsumer) - .withDefault(mDefaultClockSupplier) + // Using withDefault even though this isn't the default as a workaround. + // ExtensionBuilder doesn't provide the ability to supply a ClockPlugin + // instance based off of the value of a setting. Since multiple "default" + // can be provided, using a supplier that changes the settings value. + // A null return will cause Extension#reload to look at the next "default" + // supplier. + .withDefault( + new SettingsGattedSupplier( + mContentResolver, + Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, + BubbleClockController.class.getName(), + () -> BubbleClockController.build(mLayoutInflater))) + .withDefault( + new SettingsGattedSupplier( + mContentResolver, + Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, + StretchAnalogClockController.class.getName(), + () -> StretchAnalogClockController.build(mLayoutInflater))) + .withDefault( + new SettingsGattedSupplier( + mContentResolver, + Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, + TypeClockController.class.getName(), + () -> TypeClockController.build(mLayoutInflater))) .build(); } private void unregister() { mContentResolver.unregisterContentObserver(mContentObserver); - if (mDockManager != null) { - mDockManager.removeListener(mDockEventListener); - } mClockExtension.destroy(); } - @VisibleForTesting - boolean isDocked() { - return mDefaultClockSupplier.isDocked(); - } - /** * Listener for events that should cause the custom clock face to change. */ @@ -217,4 +200,44 @@ public final class ClockManager { */ void onClockChanged(ClockPlugin clock); } + + /** + * Supplier that only gets an instance when a settings value matches expected value. + */ + private static class SettingsGattedSupplier implements Supplier<ClockPlugin> { + + private final ContentResolver mContentResolver; + private final String mKey; + private final String mValue; + private final Supplier<ClockPlugin> mSupplier; + + /** + * Constructs a supplier that changes secure setting key against value. + * + * @param contentResolver Used to look up settings value. + * @param key Settings key. + * @param value If the setting matches this values that get supplies a ClockPlugin + * instance. + * @param supplier Supplier of ClockPlugin instance, only used if the setting + * matches value. + */ + SettingsGattedSupplier(ContentResolver contentResolver, String key, String value, + Supplier<ClockPlugin> supplier) { + mContentResolver = contentResolver; + mKey = key; + mValue = value; + mSupplier = supplier; + } + + /** + * Returns null if the settings value doesn't match the expected value. + * + * A null return causes Extension#reload to skip this supplier and move to the next. + */ + @Override + public ClockPlugin get() { + final String currentValue = Settings.Secure.getString(mContentResolver, mKey); + return Objects.equals(currentValue, mValue) ? mSupplier.get() : null; + } + } } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockSupplier.java b/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockSupplier.java deleted file mode 100644 index 7fdd2357bc8e..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockSupplier.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.util.ArrayMap; -import android.view.LayoutInflater; - -import com.android.systemui.plugins.ClockPlugin; - -import java.util.Map; -import java.util.function.Supplier; - -/** - * Supplier that only gets an instance when a settings value matches expected value. - */ -public class DefaultClockSupplier implements Supplier<ClockPlugin> { - - private final SettingsWrapper mSettingsWrapper; - /** - * Map from expected value stored in settings to supplier of custom clock face. - */ - private final Map<String, Supplier<ClockPlugin>> mClocks = new ArrayMap<>(); - /** - * When docked, the DOCKED_CLOCK_FACE setting will be checked for the custom clock face - * to show. - */ - private boolean mIsDocked; - - /** - * Constructs a supplier that changes secure setting key against value. - * - * @param settingsWrapper Wrapper around settings used to look up the custom clock face. - * @param layoutInflater Provided to clocks as dependency to inflate clock views. - */ - public DefaultClockSupplier(SettingsWrapper settingsWrapper, LayoutInflater layoutInflater) { - mSettingsWrapper = settingsWrapper; - - mClocks.put(BubbleClockController.class.getName(), - () -> BubbleClockController.build(layoutInflater)); - mClocks.put(StretchAnalogClockController.class.getName(), - () -> StretchAnalogClockController.build(layoutInflater)); - mClocks.put(TypeClockController.class.getName(), - () -> TypeClockController.build(layoutInflater)); - } - - /** - * Sets the dock state. - * - * @param isDocked True when docked, false otherwise. - */ - public void setDocked(boolean isDocked) { - mIsDocked = isDocked; - } - - boolean isDocked() { - return mIsDocked; - } - - /** - * Get the custom clock face based on values in settings. - * - * @return Custom clock face, null if the settings value doesn't match a custom clock. - */ - @Override - public ClockPlugin get() { - ClockPlugin plugin = null; - if (mIsDocked) { - final String name = mSettingsWrapper.getDockedClockFace(); - if (name != null) { - Supplier<ClockPlugin> supplier = mClocks.get(name); - if (supplier != null) { - plugin = supplier.get(); - if (plugin != null) { - return plugin; - } - } - } - } - final String name = mSettingsWrapper.getLockScreenCustomClockFace(); - if (name != null) { - Supplier<ClockPlugin> supplier = mClocks.get(name); - if (supplier != null) { - plugin = supplier.get(); - } - } - return plugin; - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java b/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java deleted file mode 100644 index 58e11553af9d..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.content.ContentResolver; -import android.provider.Settings; - -/** - * Wrapper around Settings used for testing. - */ -public class SettingsWrapper { - - private static final String CUSTOM_CLOCK_FACE = Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE; - private static final String DOCKED_CLOCK_FACE = Settings.Secure.DOCKED_CLOCK_FACE; - - private ContentResolver mContentResolver; - - public SettingsWrapper(ContentResolver contentResolver) { - mContentResolver = contentResolver; - } - - /** - * Gets the value stored in settings for the custom clock face. - */ - public String getLockScreenCustomClockFace() { - return Settings.Secure.getString(mContentResolver, CUSTOM_CLOCK_FACE); - } - - /** - * Gets the value stored in settings for the clock face to use when docked. - */ - public String getDockedClockFace() { - return Settings.Secure.getString(mContentResolver, DOCKED_CLOCK_FACE); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index d27a90332ac5..755d6fcffb43 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -31,7 +31,6 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.assist.AssistManager; import com.android.systemui.classifier.FalsingManager; -import com.android.systemui.dock.DockManager; import com.android.systemui.fragments.FragmentService; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -229,16 +228,6 @@ public class SystemUIFactory { return SysUiServiceProvider.getComponent(context, StatusBar.class); } - /** - * Provides DockManager. - */ - @Singleton - @Provides - @Nullable - public DockManager providesDockManager(Context context) { - return SysUiServiceProvider.getComponent(context, DockManager.class); - } - @Module protected static class ContextHolder { private Context mContext; diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java deleted file mode 100644 index f813ac693d42..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import static com.google.common.truth.Truth.assertThat; - -import android.test.suitebuilder.annotation.SmallTest; -import android.testing.AndroidTestingRunner; -import android.testing.LeakCheck; -import android.testing.TestableLooper.RunWithLooper; - -import com.android.systemui.SysuiTestCase; -import com.android.systemui.dock.DockManager; -import com.android.systemui.dock.DockManagerFake; -import com.android.systemui.utils.leaks.FakeExtensionController; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@RunWithLooper -public final class ClockManagerTest extends SysuiTestCase { - - private ClockManager mClockManager; - private LeakCheck mLeakCheck; - private FakeExtensionController mFakeExtensionController; - private DockManagerFake mFakeDockManager; - @Mock ClockManager.ClockChangedListener mMockListener; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mLeakCheck = new LeakCheck(); - mFakeExtensionController = new FakeExtensionController(mLeakCheck); - mFakeDockManager = new DockManagerFake(); - mClockManager = new ClockManager(getContext(), mFakeExtensionController, - mFakeDockManager); - mClockManager.addOnClockChangedListener(mMockListener); - } - - @After - public void tearDown() { - mClockManager.removeOnClockChangedListener(mMockListener); - } - - @Test - public void dockEvent() { - mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); - assertThat(mClockManager.isDocked()).isTrue(); - } - - @Test - public void undockEvent() { - mFakeDockManager.setDockEvent(DockManager.STATE_NONE); - assertThat(mClockManager.isDocked()).isFalse(); - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/DefaultClockSupplierTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/DefaultClockSupplierTest.java deleted file mode 100644 index 1a3b198ac0d6..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/DefaultClockSupplierTest.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.when; - -import android.test.suitebuilder.annotation.SmallTest; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper.RunWithLooper; -import android.view.LayoutInflater; - -import com.android.systemui.SysuiTestCase; -import com.android.systemui.plugins.ClockPlugin; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@RunWithLooper -public final class DefaultClockSupplierTest extends SysuiTestCase { - - private static final String BUBBLE_CLOCK = BubbleClockController.class.getName(); - private static final Class<?> BUBBLE_CLOCK_CLASS = BubbleClockController.class; - - private DefaultClockSupplier mDefaultClockSupplier; - @Mock SettingsWrapper mMockSettingsWrapper; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mDefaultClockSupplier = new DefaultClockSupplier(mMockSettingsWrapper, - LayoutInflater.from(getContext())); - } - - @Test - public void get_default() { - // GIVEN that settings doesn't contain any values - when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(null); - when(mMockSettingsWrapper.getDockedClockFace()).thenReturn(null); - // WHEN get is called - ClockPlugin plugin = mDefaultClockSupplier.get(); - // THEN the result is null, indicated the default clock face should be used. - assertThat(plugin).isNull(); - } - - @Test - public void get_customClock() { - // GIVEN that settings is set to the bubble clock face - when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK); - // WHEN get is called - ClockPlugin plugin = mDefaultClockSupplier.get(); - // THEN the plugin is the bubble clock face. - assertThat(plugin).isInstanceOf(BUBBLE_CLOCK_CLASS); - } - - @Test - public void get_badSettingsValue() { - // GIVEN that settings contains a value that doesn't correspond to a - // custom clock face. - when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn("bad value"); - // WHEN get is called - ClockPlugin plugin = mDefaultClockSupplier.get(); - // THEN the result is null. - assertThat(plugin).isNull(); - } - - @Test - public void get_dockedDefault() { - // GIVEN docked - mDefaultClockSupplier.setDocked(true); - // WHEN get is called - ClockPlugin plugin = mDefaultClockSupplier.get(); - // THEN the result is null, indicating the default clock face. - assertThat(plugin).isNull(); - } - - @Test - public void get_dockedCustomClock() { - // GIVEN docked and settings is set to the bubble clock face - mDefaultClockSupplier.setDocked(true); - when(mMockSettingsWrapper.getDockedClockFace()).thenReturn(BUBBLE_CLOCK); - // WHEN get is called - ClockPlugin plugin = mDefaultClockSupplier.get(); - // THEN the plugin is the bubble clock face. - assertThat(plugin).isInstanceOf(BUBBLE_CLOCK_CLASS); - } - - @Test - public void get_badDockedSettingsValue() { - // GIVEN docked and settings contains a value that doesn't correspond to - // an available clock face. - mDefaultClockSupplier.setDocked(true); - when(mMockSettingsWrapper.getDockedClockFace()).thenReturn("bad value"); - // WHEN get is called - ClockPlugin plugin = mDefaultClockSupplier.get(); - // THEN the result is null. - assertThat(plugin).isNull(); - } - - @Test - public void get_badDockedSettingsFallback() { - // GIVEN docked and settings contains a value that doesn't correspond to - // an available clock face, but locked screen settings is set to bubble - // clock. - mDefaultClockSupplier.setDocked(true); - when(mMockSettingsWrapper.getDockedClockFace()).thenReturn("bad value"); - when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK); - // WHEN get is called - ClockPlugin plugin = mDefaultClockSupplier.get(); - // THEN the plugin is the bubble clock face. - assertThat(plugin).isInstanceOf(BUBBLE_CLOCK_CLASS); - } -} diff --git a/services/core/java/com/android/server/ExtconUEventObserver.java b/services/core/java/com/android/server/ExtconUEventObserver.java index b3084f50071f..ebd4c5584fe1 100644 --- a/services/core/java/com/android/server/ExtconUEventObserver.java +++ b/services/core/java/com/android/server/ExtconUEventObserver.java @@ -22,8 +22,11 @@ import android.util.Slog; import java.io.File; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.regex.Pattern; /** * A specialized UEventObserver that receives UEvents from the kernel for devices in the {@code @@ -40,13 +43,14 @@ import java.util.Map; * time in that process. Once started the UEvent thread will not stop (although it can stop * notifying UEventObserver's via stopObserving()). * - * <p> - * * @hide */ public abstract class ExtconUEventObserver extends UEventObserver { private static final String TAG = "ExtconUEventObserver"; private static final boolean LOG = false; + private static final String SELINUX_POLICIES_NEED_TO_BE_CHANGED = + "This probably mean the selinux policies need to be changed."; + private final Map<String, ExtconInfo> mExtconInfos = new ArrayMap<>(); @Override @@ -70,15 +74,47 @@ public abstract class ExtconUEventObserver extends UEventObserver { /** Starts observing {@link ExtconInfo#getDevicePath()}. */ public void startObserving(ExtconInfo extconInfo) { - mExtconInfos.put(extconInfo.getDevicePath(), extconInfo); - if (LOG) Slog.v(TAG, "Observing " + extconInfo.getDevicePath()); - startObserving("DEVPATH=" + extconInfo.getDevicePath()); + String devicePath = extconInfo.getDevicePath(); + if (devicePath == null) { + Slog.wtf(TAG, "Unable to start observing " + extconInfo.getName() + + " because the device path is null. " + SELINUX_POLICIES_NEED_TO_BE_CHANGED); + } else { + mExtconInfos.put(devicePath, extconInfo); + if (LOG) Slog.v(TAG, "Observing " + devicePath); + startObserving("DEVPATH=" + devicePath); + } } /** An External Connection to watch. */ public static final class ExtconInfo { private static final String TAG = "ExtconInfo"; + /** Returns a new list of all external connections whose name matches {@code regex}. */ + public static List<ExtconInfo> getExtconInfos(@Nullable String regex) { + Pattern p = regex == null ? null : Pattern.compile(regex); + File file = new File("/sys/class/extcon"); + File[] files = file.listFiles(); + if (files == null) { + Slog.wtf(TAG, file + " exists " + file.exists() + " isDir " + file.isDirectory() + + " but listFiles returns null. " + + SELINUX_POLICIES_NEED_TO_BE_CHANGED); + return new ArrayList<>(0); // Always return a new list. + } else { + ArrayList list = new ArrayList(files.length); + for (File f : files) { + String name = f.getName(); + if (p == null || p.matcher(name).matches()) { + ExtconInfo uei = new ExtconInfo(name); + list.add(uei); + if (LOG) Slog.d(TAG, name + " matches " + regex); + } else { + if (LOG) Slog.d(TAG, name + " does not match " + regex); + } + } + return list; + } + } + private final String mName; public ExtconInfo(String name) { diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java index 3939bee52aa2..9bbc3158757c 100644 --- a/services/core/java/com/android/server/WiredAccessoryManager.java +++ b/services/core/java/com/android/server/WiredAccessoryManager.java @@ -23,6 +23,7 @@ import android.os.Message; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.UEventObserver; +import android.util.Pair; import android.util.Slog; import android.media.AudioManager; import android.util.Log; @@ -31,6 +32,7 @@ import android.view.InputDevice; import com.android.internal.R; import com.android.server.input.InputManagerService; import com.android.server.input.InputManagerService.WiredAccessoryCallbacks; + import static com.android.server.input.InputManagerService.SW_HEADPHONE_INSERT; import static com.android.server.input.InputManagerService.SW_MICROPHONE_INSERT; import static com.android.server.input.InputManagerService.SW_LINEOUT_INSERT; @@ -41,6 +43,7 @@ import static com.android.server.input.InputManagerService.SW_LINEOUT_INSERT_BIT import java.io.File; import java.io.FileReader; import java.io.FileNotFoundException; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -52,7 +55,7 @@ import java.util.Locale; */ final class WiredAccessoryManager implements WiredAccessoryCallbacks { private static final String TAG = WiredAccessoryManager.class.getSimpleName(); - private static final boolean LOG = true; + private static final boolean LOG = false; private static final int BIT_HEADSET = (1 << 0); private static final int BIT_HEADSET_NO_MIC = (1 << 1); @@ -60,9 +63,9 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { private static final int BIT_USB_HEADSET_DGTL = (1 << 3); private static final int BIT_HDMI_AUDIO = (1 << 4); private static final int BIT_LINEOUT = (1 << 5); - private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC| - BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL| - BIT_HDMI_AUDIO|BIT_LINEOUT); + private static final int SUPPORTED_HEADSETS = (BIT_HEADSET | BIT_HEADSET_NO_MIC | + BIT_USB_HEADSET_ANLG | BIT_USB_HEADSET_DGTL | + BIT_HDMI_AUDIO | BIT_LINEOUT); private static final String NAME_H2W = "h2w"; private static final String NAME_USB_AUDIO = "usb_audio"; @@ -82,30 +85,34 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { private int mSwitchValues; private final WiredAccessoryObserver mObserver; + private final WiredAccessoryExtconObserver mExtconObserver; private final InputManagerService mInputManager; private final boolean mUseDevInputEventForAudioJack; public WiredAccessoryManager(Context context, InputManagerService inputManager) { - PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); + PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WiredAccessoryManager"); mWakeLock.setReferenceCounted(false); - mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); + mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); mInputManager = inputManager; mUseDevInputEventForAudioJack = context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack); + mExtconObserver = new WiredAccessoryExtconObserver(); mObserver = new WiredAccessoryObserver(); } private void onSystemReady() { if (mUseDevInputEventForAudioJack) { int switchValues = 0; - if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_HEADPHONE_INSERT) == 1) { + if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_HEADPHONE_INSERT) + == 1) { switchValues |= SW_HEADPHONE_INSERT_BIT; } - if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_MICROPHONE_INSERT) == 1) { + if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_MICROPHONE_INSERT) + == 1) { switchValues |= SW_MICROPHONE_INSERT_BIT; } if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_LINEOUT_INSERT) == 1) { @@ -115,20 +122,31 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT); } - mObserver.init(); + + if (ExtconUEventObserver.extconExists()) { + if (mUseDevInputEventForAudioJack) { + Log.w(TAG, "Both input event and extcon are used for audio jack," + + " please just choose one."); + } + mExtconObserver.init(); + } else { + mObserver.init(); + } } @Override public void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask) { - if (LOG) Slog.v(TAG, "notifyWiredAccessoryChanged: when=" + whenNanos - + " bits=" + switchCodeToString(switchValues, switchMask) - + " mask=" + Integer.toHexString(switchMask)); + if (LOG) { + Slog.v(TAG, "notifyWiredAccessoryChanged: when=" + whenNanos + + " bits=" + switchCodeToString(switchValues, switchMask) + + " mask=" + Integer.toHexString(switchMask)); + } synchronized (mLock) { int headset; mSwitchValues = (mSwitchValues & ~switchMask) | switchValues; switch (mSwitchValues & - (SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT)) { + (SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT)) { case 0: headset = 0; break; @@ -155,7 +173,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { } updateLocked(NAME_H2W, - (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT)) | headset); + (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT)) | headset); } } @@ -175,7 +193,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { * results in support for the last one plugged in. Similarly, unplugging either is seen as * unplugging all. * - * @param newName One of the NAME_xxx variables defined above. + * @param newName One of the NAME_xxx variables defined above. * @param newState 0 or one of the BIT_xxx variables defined above. */ private void updateLocked(String newName, int newState) { @@ -186,10 +204,12 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { int h2w_headset = headsetState & (BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT); boolean h2wStateChange = true; boolean usbStateChange = true; - if (LOG) Slog.v(TAG, "newName=" + newName - + " newState=" + newState - + " headsetState=" + headsetState - + " prev headsetState=" + mHeadsetState); + if (LOG) { + Slog.v(TAG, "newName=" + newName + + " newState=" + newState + + " headsetState=" + headsetState + + " prev headsetState=" + mHeadsetState); + } if (mHeadsetState == headsetState) { Log.e(TAG, "No state change."); @@ -229,7 +249,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { public void handleMessage(Message msg) { switch (msg.what) { case MSG_NEW_DEVICE_STATE: - setDevicesState(msg.arg1, msg.arg2, (String)msg.obj); + setDevicesState(msg.arg1, msg.arg2, (String) msg.obj); mWakeLock.release(); break; case MSG_SYSTEM_READY: @@ -269,9 +289,9 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { if (headset == BIT_HEADSET) { outDevice = AudioManager.DEVICE_OUT_WIRED_HEADSET; inDevice = AudioManager.DEVICE_IN_WIRED_HEADSET; - } else if (headset == BIT_HEADSET_NO_MIC){ + } else if (headset == BIT_HEADSET_NO_MIC) { outDevice = AudioManager.DEVICE_OUT_WIRED_HEADPHONE; - } else if (headset == BIT_LINEOUT){ + } else if (headset == BIT_LINEOUT) { outDevice = AudioManager.DEVICE_OUT_LINE; } else if (headset == BIT_USB_HEADSET_ANLG) { outDevice = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET; @@ -280,7 +300,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { } else if (headset == BIT_HDMI_AUDIO) { outDevice = AudioManager.DEVICE_OUT_HDMI; } else { - Slog.e(TAG, "setDeviceState() invalid headset type: "+headset); + Slog.e(TAG, "setDeviceState() invalid headset type: " + headset); return; } @@ -290,10 +310,10 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { } if (outDevice != 0) { - mAudioManager.setWiredDeviceConnectionState(outDevice, state, "", headsetName); + mAudioManager.setWiredDeviceConnectionState(outDevice, state, "", headsetName); } if (inDevice != 0) { - mAudioManager.setWiredDeviceConnectionState(inDevice, state, "", headsetName); + mAudioManager.setWiredDeviceConnectionState(inDevice, state, "", headsetName); } } } @@ -340,7 +360,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { " not found while attempting to determine initial switch state"); } catch (Exception e) { Slog.e(TAG, "Error while attempting to determine initial switch state for " - + uei.getDevName() , e); + + uei.getDevName(), e); } } } @@ -350,7 +370,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { // observe three UEVENTs for (int i = 0; i < mUEventInfo.size(); ++i) { UEventInfo uei = mUEventInfo.get(i); - startObserving("DEVPATH="+uei.getDevPath()); + startObserving("DEVPATH=" + uei.getDevPath()); } } @@ -438,7 +458,9 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { mStateNbits = stateNbits; } - public String getDevName() { return mDevName; } + public String getDevName() { + return mDevName; + } public String getDevPath() { return String.format(Locale.US, "/devices/virtual/switch/%s", mDevName); @@ -456,11 +478,84 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { public int computeNewHeadsetState(int headsetState, int switchState) { int preserveMask = ~(mState1Bits | mState2Bits | mStateNbits); int setBits = ((switchState == 1) ? mState1Bits : - ((switchState == 2) ? mState2Bits : - ((switchState == mStateNbits) ? mStateNbits : 0))); + ((switchState == 2) ? mState2Bits : + ((switchState == mStateNbits) ? mStateNbits : 0))); return ((headsetState & preserveMask) | setBits); } } } + + private class WiredAccessoryExtconObserver extends ExtconStateObserver<Pair<Integer, Integer>> { + private final List<ExtconInfo> mExtconInfos; + + WiredAccessoryExtconObserver() { + mExtconInfos = ExtconInfo.getExtconInfos(".*audio.*"); + + } + + private void init() { + for (ExtconInfo extconInfo : mExtconInfos) { + Pair<Integer, Integer> state = null; + try { + state = parseStateFromFile(extconInfo); + } catch (FileNotFoundException e) { + Slog.w(TAG, extconInfo.getStatePath() + + " not found while attempting to determine initial state", e); + } catch (IOException e) { + Slog.e( + TAG, + "Error reading " + extconInfo.getStatePath() + + " while attempting to determine initial state", + e); + } + if (state != null) { + updateState(extconInfo, extconInfo.getName(), state); + } + if (LOG) Slog.d(TAG, "observing " + extconInfo.getName()); + startObserving(extconInfo); + } + + } + + @Override + public Pair<Integer, Integer> parseState(ExtconInfo extconInfo, String status) { + if (LOG) Slog.v(TAG, "status " + status); + int []maskAndState = {0,0}; + // extcon event state changes from kernel4.9 + // new state will be like STATE=MICROPHONE=1\nHEADPHONE=0 + updateBit(maskAndState, BIT_HEADSET_NO_MIC, status, "HEADPHONE") ; + updateBit(maskAndState, BIT_HEADSET, status,"MICROPHONE") ; + updateBit(maskAndState, BIT_HDMI_AUDIO, status,"HDMI") ; + updateBit(maskAndState, BIT_LINEOUT, status,"LINE-OUT") ; + if (LOG) Slog.v(TAG, "mask " + maskAndState[0] + " state " + maskAndState[1]); + return Pair.create(maskAndState[0],maskAndState[1]); + } + + @Override + public void updateState(ExtconInfo extconInfo, String name, + Pair<Integer, Integer> maskAndState) { + synchronized (mLock) { + int mask = maskAndState.first; + int state = maskAndState.second; + updateLocked(name, mHeadsetState | (mask & state) & ~(mask & ~state)); + return; + } + } + } + + /** + * Updates the mask bit at {@code position} to 1 and the state bit at {@code position} to true + * if {@code name=1} or false if {}@code name=0} is contained in {@code state}. + */ + private static void updateBit(int[] maskAndState, int position, String state, String name) { + maskAndState[0] |= position; + if (state.contains(name + "=1")) { + maskAndState[0] |= position; + maskAndState[1] |= position; + } else if (state.contains(name + "=0")) { + maskAndState[0] |= position; + maskAndState[1] &= ~position; + } + } } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 5e7ea05f799c..28393a209111 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -1805,9 +1805,10 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. - private int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) { + private int interceptMotionBeforeQueueingNonInteractive(int displayId, + long whenNanos, int policyFlags) { return mWindowManagerCallbacks.interceptMotionBeforeQueueingNonInteractive( - whenNanos, policyFlags); + displayId, whenNanos, policyFlags); } // Native callback. @@ -2021,7 +2022,13 @@ public class InputManagerService extends IInputManager.Stub public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags); - public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags); + /** + * Provides an opportunity for the window manager policy to intercept early motion event + * processing when the device is in a non-interactive state since these events are normally + * dropped. + */ + int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos, + int policyFlags); public long interceptKeyBeforeDispatching(IBinder token, KeyEvent event, int policyFlags); diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 2f0b388b9a7d..3abacc2c9c10 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -976,7 +976,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } if (DEBUG) Log.d(TAG, "setRequest " + mProviderRequest); - if (mProviderRequest.reportLocation && !mDisableGps && isEnabled()) { + if (mProviderRequest.reportLocation && isEnabled()) { // update client uids updateClientUids(mWorkSource); diff --git a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java index 5c7317892f3f..62d9b20b636d 100644 --- a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java @@ -284,13 +284,16 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { * Tells the system UI that volume has changed on an active remote session. */ public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) { - if (mRvc == null || !session.isActive()) { - return; - } - try { - mRvc.remoteVolumeChanged(session.getSessionToken(), flags); - } catch (Exception e) { - Log.wtf(TAG, "Error sending volume change to system UI.", e); + synchronized (mLock) { + if (mRvc == null || !session.isActive()) { + return; + } + try { + mRvc.remoteVolumeChanged(session.getSessionToken(), flags); + } catch (Exception e) { + Log.w(TAG, "Error sending volume change to system UI.", e); + mRvc = null; + } } } @@ -563,7 +566,7 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { String callerPackageName, SessionCallbackLink cb, String tag) { FullUserRecord user = getFullUserRecordLocked(userId); if (user == null) { - Log.wtf(TAG, "Request from invalid user: " + userId); + Log.w(TAG, "Request from invalid user: " + userId + ", pkg=" + callerPackageName); throw new RuntimeException("Session request from invalid user."); } @@ -643,7 +646,8 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId); mRvc.updateRemoteController(record == null ? null : record.getSessionToken()); } catch (RemoteException e) { - Log.wtf(TAG, "Error sending default remote volume to sys ui.", e); + Log.w(TAG, "Error sending default remote volume to sys ui.", e); + mRvc = null; } } } @@ -1661,7 +1665,9 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { final long token = Binder.clearCallingIdentity(); try { enforceSystemUiPermission("listen for volume changes", pid, uid); - mRvc = rvc; + synchronized (mLock) { + mRvc = rvc; + } } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 8c00380b82b1..400443a16fab 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -123,6 +123,7 @@ import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppDetailsActivity; import android.app.AppOpsManager; +import android.app.BroadcastOptions; import android.app.IActivityManager; import android.app.ResourcesManager; import android.app.admin.IDevicePolicyManager; @@ -1044,12 +1045,17 @@ public class PackageManagerService extends IPackageManager.Stub verificationIntent.setComponent(mIntentFilterVerifierComponent); verificationIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + final long whitelistTimeout = getVerificationTimeout(); + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setTemporaryAppWhitelistDuration(whitelistTimeout); + DeviceIdleController.LocalService idleController = getDeviceIdleController(); idleController.addPowerSaveTempWhitelistApp(Process.myUid(), - mIntentFilterVerifierComponent.getPackageName(), getVerificationTimeout(), + mIntentFilterVerifierComponent.getPackageName(), whitelistTimeout, UserHandle.USER_SYSTEM, true, "intent filter verifier"); - mContext.sendBroadcastAsUser(verificationIntent, UserHandle.SYSTEM); + mContext.sendBroadcastAsUser(verificationIntent, UserHandle.SYSTEM, + null, options.toBundle()); if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Sending IntentFilter verification broadcast"); } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index b00193f5453f..2e3e3e430839 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -22,7 +22,6 @@ import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; import static android.app.AppOpsManager.OP_TOAST_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.content.Context.CONTEXT_RESTRICTED; -import static android.content.Context.DISPLAY_SERVICE; import static android.content.Context.WINDOW_SERVICE; import static android.content.pm.PackageManager.FEATURE_HDMI_CEC; import static android.content.pm.PackageManager.FEATURE_LEANBACK; @@ -36,6 +35,7 @@ import static android.provider.Settings.Secure.VOLUME_HUSH_OFF; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.Display.STATE_OFF; +import static android.view.KeyEvent.KEYCODE_UNKNOWN; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW; @@ -84,10 +84,8 @@ import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED; -import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs - .CAMERA_LENS_COVER_ABSENT; -import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs - .CAMERA_LENS_UNCOVERED; +import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT; +import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN; import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_DELEGATE; @@ -371,6 +369,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { IStatusBarService mStatusBarService; StatusBarManagerInternal mStatusBarManagerInternal; AudioManagerInternal mAudioManagerInternal; + DisplayManager mDisplayManager; boolean mPreloadedRecentApps; final Object mServiceAquireLock = new Object(); Vibrator mVibrator; // Vibrator for giving feedback of orientation changes @@ -1717,7 +1716,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class); mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); - mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); + mAppOpsManager = mContext.getSystemService(AppOpsManager.class); + mDisplayManager = mContext.getSystemService(DisplayManager.class); mHasFeatureWatch = mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH); mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK); mHasFeatureHdmiCec = mContext.getPackageManager().hasSystemFeature(FEATURE_HDMI_CEC); @@ -2508,8 +2508,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { return context; } - final DisplayManager dm = (DisplayManager) context.getSystemService(DISPLAY_SERVICE); - final Display targetDisplay = dm.getDisplay(displayId); + final Display targetDisplay = mDisplayManager.getDisplay(displayId); if (targetDisplay == null) { // Failed to obtain the non-default display where splash screen should be shown, // lets not show at all. @@ -3655,7 +3654,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Reset the pending key mPendingWakeKey = PENDING_KEY_NULL; } - } else if (!interactive && shouldDispatchInputWhenNonInteractive(event)) { + } else if (!interactive && shouldDispatchInputWhenNonInteractive(displayId, keyCode)) { // If we're currently dozing with the screen on and the keyguard showing, pass the key // to the application but preserve its wake key status to make sure we still move // from dozing to fully interactive if we would normally go from off to fully @@ -4126,7 +4125,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // TODO(b/117479243): handle it in InputPolicy /** {@inheritDoc} */ @Override - public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) { + public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos, + int policyFlags) { if ((policyFlags & FLAG_WAKE) != 0) { if (wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotion, PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION")) { @@ -4134,7 +4134,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - if (shouldDispatchInputWhenNonInteractive(null)) { + if (shouldDispatchInputWhenNonInteractive(displayId, KEYCODE_UNKNOWN)) { return ACTION_PASS_TO_USER; } @@ -4149,9 +4149,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { return 0; } - private boolean shouldDispatchInputWhenNonInteractive(KeyEvent event) { - final boolean displayOff = (mDefaultDisplay == null - || mDefaultDisplay.getState() == STATE_OFF); + private boolean shouldDispatchInputWhenNonInteractive(int displayId, int keyCode) { + // Apply the default display policy to unknown displays as well. + final boolean isDefaultDisplay = displayId == DEFAULT_DISPLAY + || displayId == INVALID_DISPLAY; + final Display display = isDefaultDisplay + ? mDefaultDisplay + : mDisplayManager.getDisplay(displayId); + final boolean displayOff = (display == null + || display.getState() == STATE_OFF); if (displayOff && !mHasFeatureWatch) { return false; @@ -4163,25 +4169,25 @@ public class PhoneWindowManager implements WindowManagerPolicy { } // Watches handle BACK specially - if (mHasFeatureWatch - && event != null - && (event.getKeyCode() == KeyEvent.KEYCODE_BACK - || event.getKeyCode() == KeyEvent.KEYCODE_STEM_PRIMARY)) { + if (mHasFeatureWatch && (keyCode == KeyEvent.KEYCODE_BACK + || keyCode == KeyEvent.KEYCODE_STEM_PRIMARY)) { return false; } - // Send events to a dozing dream even if the screen is off since the dream - // is in control of the state of the screen. - IDreamManager dreamManager = getDreamManager(); + // TODO(b/123372519): Refine when dream can support multi display. + if (isDefaultDisplay) { + // Send events to a dozing dream even if the screen is off since the dream + // is in control of the state of the screen. + IDreamManager dreamManager = getDreamManager(); - try { - if (dreamManager != null && dreamManager.isDreaming()) { - return true; + try { + if (dreamManager != null && dreamManager.isDreaming()) { + return true; + } + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException when checking if dreaming", e); } - } catch (RemoteException e) { - Slog.e(TAG, "RemoteException when checking if dreaming", e); } - // Otherwise, consume events since the user can't see what is being // interacted with. return false; diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index d1bd102f11dc..870d61b2ab90 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -1003,11 +1003,13 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { * affect the power state of the device, for example, waking on motions. * Generally, it's best to keep as little as possible in the queue thread * because it's the most fragile. + * @param displayId The display ID of the motion event. * @param policyFlags The policy flags associated with the motion. * * @return Actions flags: may be {@link #ACTION_PASS_TO_USER}. */ - public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags); + int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos, + int policyFlags); /** * Called from the input dispatcher thread before a key is dispatched to a window. diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 0aa6051d67ff..f3393e2f29da 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -167,6 +167,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { public static final int CODE_DATA_BROADCAST = 1; public static final int CODE_SUBSCRIBER_BROADCAST = 1; + public static final int CODE_ACTIVE_CONFIGS_BROADCAST = 1; /** * The last report time is provided with each intent registered to * StatsManager#setFetchReportsOperation. This allows easy de-duping in the receiver if @@ -356,6 +357,22 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } @Override + public void sendActiveConfigsChangedBroadcast(IBinder intentSenderBinder, long[] configIds) { + enforceCallingPermission(); + IntentSender intentSender = new IntentSender(intentSenderBinder); + Intent intent = new Intent(); + intent.putExtra(StatsManager.EXTRA_STATS_ACTIVE_CONFIG_KEYS, configIds); + try { + intentSender.sendIntent(mContext, CODE_ACTIVE_CONFIGS_BROADCAST, intent, null, null); + if (DEBUG) { + Slog.d(TAG, "Sent broadcast with config ids " + Arrays.toString(configIds)); + } + } catch (IntentSender.SendIntentException e) { + Slog.w(TAG, "Unable to send active configs changed broadcast using IntentSender"); + } + } + + @Override public void sendSubscriberBroadcast(IBinder intentSenderBinder, long configUid, long configKey, long subscriptionId, long subscriptionRuleId, String[] cookies, StatsDimensionsValue dimensionsValue) { diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java index f9c9d33c561a..f46835eb51fc 100644 --- a/services/core/java/com/android/server/wm/InputManagerCallback.java +++ b/services/core/java/com/android/server/wm/InputManagerCallback.java @@ -163,15 +163,12 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags); } - /** - * Provides an opportunity for the window manager policy to intercept early motion event - * processing when the device is in a non-interactive state since these events are normally - * dropped. - */ + /** {@inheritDoc} */ @Override - public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) { + public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos, + int policyFlags) { return mService.mPolicy.interceptMotionBeforeQueueingNonInteractive( - whenNanos, policyFlags); + displayId, whenNanos, policyFlags); } /** diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java index fb5b1d85a4a2..5c91d9e6fff2 100644 --- a/services/core/java/com/android/server/wm/RootActivityContainer.java +++ b/services/core/java/com/android/server/wm/RootActivityContainer.java @@ -300,7 +300,7 @@ class RootActivityContainer extends ConfigurationContainer * corresponding record in display manager. */ // TODO: Look into consolidating with getActivityDisplay() - ActivityDisplay getActivityDisplayOrCreate(int displayId) { + @Nullable ActivityDisplay getActivityDisplayOrCreate(int displayId) { ActivityDisplay activityDisplay = getActivityDisplay(displayId); if (activityDisplay != null) { return activityDisplay; @@ -1317,6 +1317,9 @@ class RootActivityContainer extends ConfigurationContainer if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId); synchronized (mService.mGlobalLock) { final ActivityDisplay display = getActivityDisplayOrCreate(displayId); + if (display == null) { + return; + } // Do not start home before booting, or it may accidentally finish booting before it // starts. Instead, we expect home activities to be launched when the system is ready // (ActivityManagerService#systemReady). diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index c18e98bab9c4..57377c633c9a 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -249,7 +249,8 @@ public: virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags); virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig); virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags); - virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags); + virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when, + uint32_t& policyFlags); virtual nsecs_t interceptKeyBeforeDispatching( const sp<IBinder>& token, const KeyEvent* keyEvent, uint32_t policyFlags); @@ -1066,7 +1067,8 @@ void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent, } } -void NativeInputManager::interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) { +void NativeInputManager::interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when, + uint32_t& policyFlags) { ATRACE_CALL(); // Policy: // - Ignore untrusted events and pass them along. @@ -1084,7 +1086,7 @@ void NativeInputManager::interceptMotionBeforeQueueing(nsecs_t when, uint32_t& p JNIEnv* env = jniEnv(); jint wmActions = env->CallIntMethod(mServiceObj, gServiceClassInfo.interceptMotionBeforeQueueingNonInteractive, - when, policyFlags); + displayId, when, policyFlags); if (checkAndClearExceptionFromCallback(env, "interceptMotionBeforeQueueingNonInteractive")) { wmActions = 0; @@ -1794,7 +1796,7 @@ int register_android_server_InputManager(JNIEnv* env) { "interceptKeyBeforeQueueing", "(Landroid/view/KeyEvent;I)I"); GET_METHOD_ID(gServiceClassInfo.interceptMotionBeforeQueueingNonInteractive, clazz, - "interceptMotionBeforeQueueingNonInteractive", "(JI)I"); + "interceptMotionBeforeQueueingNonInteractive", "(IJI)I"); GET_METHOD_ID(gServiceClassInfo.interceptKeyBeforeDispatching, clazz, "interceptKeyBeforeDispatching", diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java index bfb9193551f2..849772a4a26d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -156,7 +156,8 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) { + public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos, + int policyFlags) { return 0; } diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java index 6b1b84cd3458..856f08107fd7 100644 --- a/telephony/java/android/telephony/CellIdentityNr.java +++ b/telephony/java/android/telephony/CellIdentityNr.java @@ -30,6 +30,7 @@ public final class CellIdentityNr extends CellIdentity { private final int mNrArfcn; private final int mPci; private final int mTac; + private final long mNci; /** * @@ -44,11 +45,12 @@ public final class CellIdentityNr extends CellIdentity { * @hide */ public CellIdentityNr(int pci, int tac, int nrArfcn, String mccStr, String mncStr, - String alphal, String alphas) { + long nci, String alphal, String alphas) { super(TAG, CellInfo.TYPE_NR, mccStr, mncStr, alphal, alphas); mPci = pci; mTac = tac; mNrArfcn = nrArfcn; + mNci = nci; } /** @@ -62,7 +64,7 @@ public final class CellIdentityNr extends CellIdentity { @Override public int hashCode() { - return Objects.hash(super.hashCode(), mPci, mTac, mNrArfcn); + return Objects.hash(super.hashCode(), mPci, mTac, mNrArfcn, mNci); } @Override @@ -72,7 +74,17 @@ public final class CellIdentityNr extends CellIdentity { } CellIdentityNr o = (CellIdentityNr) other; - return super.equals(o) && mPci == o.mPci && mTac == o.mTac && mNrArfcn == o.mNrArfcn; + return super.equals(o) && mPci == o.mPci && mTac == o.mTac && mNrArfcn == o.mNrArfcn + && mNci == o.mNci; + } + + /** + * Get the NR Cell Identity. + * + * @return The NR Cell Identity in range [0, 68719476735] or Long.MAX_VALUE if unknown. + */ + public long getNci() { + return mNci; } /** @@ -122,6 +134,7 @@ public final class CellIdentityNr extends CellIdentity { .append(" mNrArfcn = ").append(mNrArfcn) .append(" mMcc = ").append(mMccStr) .append(" mMnc = ").append(mMncStr) + .append(" mNci = ").append(mNci) .append(" mAlphaLong = ").append(mAlphaLong) .append(" mAlphaShort = ").append(mAlphaShort) .append(" }") @@ -134,6 +147,7 @@ public final class CellIdentityNr extends CellIdentity { dest.writeInt(mPci); dest.writeInt(mTac); dest.writeInt(mNrArfcn); + dest.writeLong(mNci); } /** Construct from Parcel, type has already been processed */ @@ -142,6 +156,7 @@ public final class CellIdentityNr extends CellIdentity { mPci = in.readInt(); mTac = in.readInt(); mNrArfcn = in.readInt(); + mNci = in.readLong(); } /** Implement the Parcelable interface */ diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java index ceb76b57ae0c..c37b492a9cf1 100644 --- a/telephony/java/android/telephony/NetworkRegistrationState.java +++ b/telephony/java/android/telephony/NetworkRegistrationState.java @@ -27,6 +27,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.Objects; +import java.util.stream.Collectors; /** * Description of a mobile network registration state @@ -360,7 +361,34 @@ public class NetworkRegistrationState implements Parcelable { return 0; } - private static String regStateToString(int regState) { + /** + * Convert service type to string + * + * @hide + * + * @param serviceType The service type + * @return The service type in string format + */ + public static String serviceTypeToString(@ServiceType int serviceType) { + switch (serviceType) { + case SERVICE_TYPE_VOICE: return "VOICE"; + case SERVICE_TYPE_DATA: return "DATA"; + case SERVICE_TYPE_SMS: return "SMS"; + case SERVICE_TYPE_VIDEO: return "VIDEO"; + case SERVICE_TYPE_EMERGENCY: return "EMERGENCY"; + } + return "Unknown service type " + serviceType; + } + + /** + * Convert registration state to string + * + * @hide + * + * @param regState The registration state + * @return The reg state in string + */ + public static String regStateToString(@RegState int regState) { switch (regState) { case REG_STATE_NOT_REG_NOT_SEARCHING: return "NOT_REG_NOT_SEARCHING"; case REG_STATE_HOME: return "HOME"; @@ -389,14 +417,17 @@ public class NetworkRegistrationState implements Parcelable { public String toString() { return new StringBuilder("NetworkRegistrationState{") .append(" domain=").append((mDomain == DOMAIN_CS) ? "CS" : "PS") - .append("transportType=").append(mTransportType) + .append(" transportType=").append(TransportType.toString(mTransportType)) .append(" regState=").append(regStateToString(mRegState)) - .append(" roamingType=").append(mRoamingType) + .append(" roamingType=").append(ServiceState.roamingTypeToString(mRoamingType)) .append(" accessNetworkTechnology=") .append(TelephonyManager.getNetworkTypeName(mAccessNetworkTechnology)) .append(" rejectCause=").append(mRejectCause) .append(" emergencyEnabled=").append(mEmergencyOnly) - .append(" supportedServices=").append(mAvailableServices) + .append(" availableServices=").append("[" + (mAvailableServices != null + ? Arrays.stream(mAvailableServices) + .mapToObj(type -> serviceTypeToString(type)) + .collect(Collectors.joining(",")) : null) + "]") .append(" cellIdentity=").append(mCellIdentity) .append(" voiceSpecificStates=").append(mVoiceSpecificStates) .append(" dataSpecificStates=").append(mDataSpecificStates) diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 33178766f3a3..402763e52cfa 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -887,6 +887,24 @@ public class ServiceState implements Parcelable { } /** + * Convert roaming type to string + * + * @param roamingType roaming type + * @return The roaming type in string format + * + * @hide + */ + public static String roamingTypeToString(@RoamingType int roamingType) { + switch (roamingType) { + case ROAMING_TYPE_NOT_ROAMING: return "NOT_ROAMING"; + case ROAMING_TYPE_UNKNOWN: return "UNKNOWN"; + case ROAMING_TYPE_DOMESTIC: return "DOMESTIC"; + case ROAMING_TYPE_INTERNATIONAL: return "INTERNATIONAL"; + } + return "Unknown roaming type " + roamingType; + } + + /** * Convert radio technology to String * * @param rt radioTechnology |