summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt1
-rw-r--r--cmds/incidentd/src/Reporter.cpp9
-rw-r--r--cmds/incidentd/src/Section.cpp17
-rw-r--r--cmds/incidentd/src/Section.h9
-rw-r--r--cmds/statsd/benchmark/metric_util.cpp2
-rw-r--r--cmds/statsd/src/StatsLogProcessor.cpp30
-rw-r--r--cmds/statsd/src/StatsLogProcessor.h10
-rw-r--r--cmds/statsd/src/StatsService.cpp17
-rw-r--r--cmds/statsd/src/StatsService.h5
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.cpp12
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.h3
-rw-r--r--cmds/statsd/src/storage/StorageManager.cpp28
-rw-r--r--cmds/statsd/src/storage/StorageManager.h5
-rw-r--r--cmds/statsd/tests/StatsLogProcessor_test.cpp12
-rw-r--r--cmds/statsd/tests/UidMap_test.cpp2
-rw-r--r--cmds/statsd/tests/statsd_test_util.cpp2
-rw-r--r--config/hiddenapi-light-greylist.txt8
-rw-r--r--core/java/android/bluetooth/BluetoothHeadset.java97
-rw-r--r--core/java/android/os/BatteryStats.java511
-rw-r--r--core/java/android/os/IStatsManager.aidl6
-rw-r--r--core/java/android/service/notification/ScheduleCalendar.java20
-rw-r--r--core/java/android/util/StatsLog.java82
-rw-r--r--core/java/android/util/TimeUtils.java8
-rw-r--r--core/java/android/view/autofill/AutofillManager.java16
-rw-r--r--core/java/com/android/internal/widget/MessagingGroup.java2
-rw-r--r--core/java/com/android/internal/widget/MessagingLayout.java21
-rw-r--r--core/java/com/android/internal/widget/MessagingMessage.java2
-rw-r--r--core/proto/android/os/incident.proto9
-rw-r--r--core/proto/android/service/batterystats.proto26
-rw-r--r--libs/androidfw/ResourceTypes.cpp406
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp22
-rw-r--r--libs/incident/proto/android/section.proto3
-rw-r--r--media/java/android/media/AudioFocusRequest.java4
-rw-r--r--media/java/android/media/AudioManager.java14
-rw-r--r--packages/SystemUI/AndroidManifest.xml6
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml1
-rw-r--r--packages/SystemUI/res-keyguard/values/strings.xml3
-rw-r--r--packages/SystemUI/res/drawable/car_ic_notification_2.xml31
-rw-r--r--packages/SystemUI/res/layout/volume_dialog.xml10
-rw-r--r--packages/SystemUI/res/layout/volume_dnd_icon.xml7
-rw-r--r--packages/SystemUI/res/values/attrs_car.xml28
-rw-r--r--packages/SystemUI/res/values/dimens.xml2
-rw-r--r--packages/SystemUI/res/xml/car_volume_items.xml68
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistManager.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridGroupManager.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java1038
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java67
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java2
-rw-r--r--proto/src/metrics_constants.proto43
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java27
-rw-r--r--services/autofill/java/com/android/server/autofill/Helper.java11
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java18
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java17
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/SaveUi.java13
-rw-r--r--services/core/java/com/android/server/am/ActivityDisplay.java41
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java10
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java8
-rw-r--r--services/core/java/com/android/server/am/RecentsAnimation.java27
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java425
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java115
-rw-r--r--services/core/java/com/android/server/pm/Settings.java16
-rw-r--r--services/core/java/com/android/server/pm/SharedUserSetting.java1
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java2
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java8
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java6
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java26
-rw-r--r--services/core/java/com/android/server/wm/DockedStackDividerController.java2
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java35
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java50
-rw-r--r--services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java115
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java63
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java108
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java150
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java15
-rw-r--r--tools/incident_section_gen/main.cpp6
90 files changed, 2700 insertions, 1511 deletions
diff --git a/api/current.txt b/api/current.txt
index 8703bf41438b..52b4741c7eec 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -41426,6 +41426,7 @@ package android.telecom {
method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
method public void cancelMissedCallsNotification();
method public android.content.Intent createManageBlockedNumbersIntent();
+ method public boolean endCall();
method public android.net.Uri getAdnUriForPhoneAccount(android.telecom.PhoneAccountHandle);
method public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts();
method public java.lang.String getDefaultDialerPackage();
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index 297a0711f686..b3bab0cfb4dc 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -22,6 +22,7 @@
#include "report_directory.h"
#include "section_list.h"
+#include <android-base/properties.h>
#include <android/os/DropBoxManager.h>
#include <private/android_filesystem_config.h>
#include <utils/SystemClock.h>
@@ -31,6 +32,7 @@
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <string>
/**
* The directory where the incident reports are stored.
@@ -129,6 +131,8 @@ Reporter::run_report_status_t Reporter::runReport(size_t* reportByteSize) {
int mainDest = -1;
HeaderSection headers;
MetadataSection metadataSection;
+ std::string buildType = android::base::GetProperty("ro.build.type", "");
+ const bool isUserdebugOrEng = buildType == "userdebug" || buildType == "eng";
// See if we need the main file
for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) {
@@ -175,6 +179,11 @@ Reporter::run_report_status_t Reporter::runReport(size_t* reportByteSize) {
// and report to those that care that we're doing it.
for (const Section** section = SECTION_LIST; *section; section++) {
const int id = (*section)->id;
+ if ((*section)->userdebugAndEngOnly && !isUserdebugOrEng) {
+ ALOGD("Skipping incident report section %d '%s' because it's limited to userdebug/eng",
+ id, (*section)->name.string());
+ continue;
+ }
if (this->batch.containsSection(id)) {
ALOGD("Taking incident report section %d '%s'", id, (*section)->name.string());
for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) {
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index e5bde0d83cf7..4bbe04204a54 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -151,8 +151,11 @@ DONE:
}
// ================================================================================
-Section::Section(int i, int64_t timeoutMs, bool deviceSpecific)
- : id(i), timeoutMs(timeoutMs), deviceSpecific(deviceSpecific) {}
+Section::Section(int i, int64_t timeoutMs, bool userdebugAndEngOnly, bool deviceSpecific)
+ : id(i),
+ timeoutMs(timeoutMs),
+ userdebugAndEngOnly(userdebugAndEngOnly),
+ deviceSpecific(deviceSpecific) {}
Section::~Section() {}
@@ -239,7 +242,7 @@ static inline bool isSysfs(const char* filename) { return strncmp(filename, "/sy
FileSection::FileSection(int id, const char* filename, const bool deviceSpecific,
const int64_t timeoutMs)
- : Section(id, timeoutMs, deviceSpecific), mFilename(filename) {
+ : Section(id, timeoutMs, false, deviceSpecific), mFilename(filename) {
name = filename;
mIsSysfs = isSysfs(filename);
}
@@ -417,8 +420,8 @@ WorkerThreadData::WorkerThreadData(const WorkerThreadSection* sec)
WorkerThreadData::~WorkerThreadData() {}
// ================================================================================
-WorkerThreadSection::WorkerThreadSection(int id, const int64_t timeoutMs)
- : Section(id, timeoutMs) {}
+WorkerThreadSection::WorkerThreadSection(int id, const int64_t timeoutMs, bool userdebugAndEngOnly)
+ : Section(id, timeoutMs, userdebugAndEngOnly) {}
WorkerThreadSection::~WorkerThreadSection() {}
@@ -615,8 +618,8 @@ status_t CommandSection::Execute(ReportRequestSet* requests) const {
}
// ================================================================================
-DumpsysSection::DumpsysSection(int id, const char* service, ...)
- : WorkerThreadSection(id), mService(service) {
+DumpsysSection::DumpsysSection(int id, bool userdebugAndEngOnly, const char* service, ...)
+ : WorkerThreadSection(id, REMOTE_CALL_TIMEOUT_MS, userdebugAndEngOnly), mService(service) {
name = "dumpsys ";
name += service;
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
index 577892ef3a38..a031a15fe7c9 100644
--- a/cmds/incidentd/src/Section.h
+++ b/cmds/incidentd/src/Section.h
@@ -40,10 +40,12 @@ class Section {
public:
const int id;
const int64_t timeoutMs; // each section must have a timeout
+ const bool userdebugAndEngOnly;
const bool deviceSpecific;
String8 name;
- Section(int id, int64_t timeoutMs = REMOTE_CALL_TIMEOUT_MS, bool deviceSpecific = false);
+ Section(int id, int64_t timeoutMs = REMOTE_CALL_TIMEOUT_MS, bool userdebugAndEngOnly = false,
+ bool deviceSpecific = false);
virtual ~Section();
virtual status_t Execute(ReportRequestSet* requests) const = 0;
@@ -107,7 +109,8 @@ private:
*/
class WorkerThreadSection : public Section {
public:
- WorkerThreadSection(int id, int64_t timeoutMs = REMOTE_CALL_TIMEOUT_MS);
+ WorkerThreadSection(int id, int64_t timeoutMs = REMOTE_CALL_TIMEOUT_MS,
+ bool userdebugAndEngOnly = false);
virtual ~WorkerThreadSection();
virtual status_t Execute(ReportRequestSet* requests) const;
@@ -137,7 +140,7 @@ private:
*/
class DumpsysSection : public WorkerThreadSection {
public:
- DumpsysSection(int id, const char* service, ...);
+ DumpsysSection(int id, bool userdebugAndEngOnly, const char* service, ...);
virtual ~DumpsysSection();
virtual status_t BlockingCall(int pipeWriteFd) const;
diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp
index e6272edf7dcc..50ed18d3e2b0 100644
--- a/cmds/statsd/benchmark/metric_util.cpp
+++ b/cmds/statsd/benchmark/metric_util.cpp
@@ -366,7 +366,7 @@ sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const Stat
sp<AlarmMonitor> periodicAlarmMonitor;
sp<StatsLogProcessor> processor = new StatsLogProcessor(
uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec * NS_PER_SEC,
- [](const ConfigKey&){});
+ [](const ConfigKey&){return true;});
processor->OnConfigUpdated(timeBaseSec * NS_PER_SEC, key, config);
return processor;
}
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index ed07acc6317b..4d279481dc76 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -75,7 +75,7 @@ StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
const sp<AlarmMonitor>& anomalyAlarmMonitor,
const sp<AlarmMonitor>& periodicAlarmMonitor,
const int64_t timeBaseNs,
- const std::function<void(const ConfigKey&)>& sendBroadcast)
+ const std::function<bool(const ConfigKey&)>& sendBroadcast)
: mUidMap(uidMap),
mAnomalyAlarmMonitor(anomalyAlarmMonitor),
mPeriodicAlarmMonitor(periodicAlarmMonitor),
@@ -465,12 +465,21 @@ void StatsLogProcessor::flushIfNecessaryLocked(
// We suspect that the byteSize() computation is expensive, so we set a rate limit.
size_t totalBytes = metricsManager.byteSize();
mLastByteSizeTimes[key] = timestampNs;
+ bool requestDump = false;
if (totalBytes >
StatsdStats::kMaxMetricsBytesPerConfig) { // Too late. We need to start clearing data.
metricsManager.dropData(timestampNs);
StatsdStats::getInstance().noteDataDropped(key);
VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str());
- } else if (totalBytes > StatsdStats::kBytesPerConfigTriggerGetData) {
+ } else if ((totalBytes > StatsdStats::kBytesPerConfigTriggerGetData) ||
+ (mOnDiskDataConfigs.find(key) != mOnDiskDataConfigs.end())) {
+ // Request to send a broadcast if:
+ // 1. in memory data > threshold OR
+ // 2. config has old data report on disk.
+ requestDump = true;
+ }
+
+ if (requestDump) {
// Send broadcast so that receivers can pull data.
auto lastBroadcastTime = mLastBroadcastTimes.find(key);
if (lastBroadcastTime != mLastBroadcastTimes.end()) {
@@ -479,10 +488,12 @@ void StatsLogProcessor::flushIfNecessaryLocked(
return;
}
}
- mLastBroadcastTimes[key] = timestampNs;
- VLOG("StatsD requesting broadcast for %s", key.ToString().c_str());
- mSendBroadcast(key);
- StatsdStats::getInstance().noteBroadcastSent(key);
+ if (mSendBroadcast(key)) {
+ mOnDiskDataConfigs.erase(key);
+ VLOG("StatsD triggered data fetch for %s", key.ToString().c_str());
+ mLastBroadcastTimes[key] = timestampNs;
+ StatsdStats::getInstance().noteBroadcastSent(key);
+ }
}
}
@@ -505,6 +516,8 @@ void StatsLogProcessor::WriteDataToDiskLocked(const ConfigKey& key,
return;
}
proto.flush(fd.get());
+ // We were able to write the ConfigMetricsReport to disk, so we should trigger collection ASAP.
+ mOnDiskDataConfigs.insert(key);
}
void StatsLogProcessor::WriteDataToDiskLocked(const DumpReportReason dumpReportReason) {
@@ -533,6 +546,11 @@ int64_t StatsLogProcessor::getLastReportTimeNs(const ConfigKey& key) {
}
}
+void StatsLogProcessor::noteOnDiskData(const ConfigKey& key) {
+ std::lock_guard<std::mutex> lock(mMetricsMutex);
+ mOnDiskDataConfigs.insert(key);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 8de0f4124841..d6fb8de801bf 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -48,7 +48,7 @@ public:
StatsLogProcessor(const sp<UidMap>& uidMap, const sp<AlarmMonitor>& anomalyAlarmMonitor,
const sp<AlarmMonitor>& subscriberTriggerAlarmMonitor,
const int64_t timeBaseNs,
- const std::function<void(const ConfigKey&)>& sendBroadcast);
+ const std::function<bool(const ConfigKey&)>& sendBroadcast);
virtual ~StatsLogProcessor();
void OnLogEvent(LogEvent* event, bool reconnectionStarts);
@@ -99,6 +99,9 @@ public:
#endif
}
+ // Add a specific config key to the possible configs to dump ASAP.
+ void noteOnDiskData(const ConfigKey& key);
+
private:
// For testing only.
inline sp<AlarmMonitor> getAnomalyAlarmMonitor() const {
@@ -118,6 +121,9 @@ private:
// Tracks when we last checked the bytes consumed for each config key.
std::unordered_map<ConfigKey, long> mLastByteSizeTimes;
+ // Tracks which config keys has metric reports on disk
+ std::set<ConfigKey> mOnDiskDataConfigs;
+
sp<UidMap> mUidMap; // Reference to the UidMap to lookup app name and version for each uid.
StatsPullerManager mStatsPullerManager;
@@ -159,7 +165,7 @@ private:
// Function used to send a broadcast so that receiver for the config key can call getData
// to retrieve the stored data.
- std::function<void(const ConfigKey& key)> mSendBroadcast;
+ std::function<bool(const ConfigKey& key)> mSendBroadcast;
const int64_t mTimeBaseNs;
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index e823f6884b29..acf3ad25ba1e 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -158,11 +158,14 @@ StatsService::StatsService(const sp<Looper>& handlerLooper)
auto receiver = mConfigManager->GetConfigReceiver(key);
if (sc == nullptr) {
VLOG("Could not find StatsCompanionService");
+ return false;
} else if (receiver == nullptr) {
VLOG("Statscompanion could not find a broadcast receiver for %s",
key.ToString().c_str());
+ return false;
} else {
sc->sendDataBroadcast(receiver, mProcessor->getLastReportTimeNs(key));
+ return true;
}
}
);
@@ -948,6 +951,11 @@ Status StatsService::setDataFetchOperation(int64_t key,
IPCThreadState* ipc = IPCThreadState::self();
ConfigKey configKey(ipc->getCallingUid(), key);
mConfigManager->SetConfigReceiver(configKey, intentSender);
+ if (StorageManager::hasConfigMetricsReport(configKey)) {
+ VLOG("StatsService::setDataFetchOperation marking configKey %s to dump reports on disk",
+ configKey.ToString().c_str());
+ mProcessor->noteOnDiskData(configKey);
+ }
return Status::ok();
}
@@ -988,6 +996,15 @@ Status StatsService::unsetBroadcastSubscriber(int64_t configId,
return Status::ok();
}
+Status StatsService::sendAppBreadcrumbAtom(int32_t label, int32_t state) {
+ // Permission check not necessary as it's meant for applications to write to
+ // statsd.
+ android::util::stats_write(util::APP_BREADCRUMB_REPORTED,
+ IPCThreadState::self()->getCallingUid(), label,
+ state);
+ return Status::ok();
+}
+
void StatsService::binderDied(const wp <IBinder>& who) {
ALOGW("statscompanion service died");
StatsdStats::getInstance().noteSystemServerRestart(getWallClockSec());
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 67fc7709103a..b3a477645b73 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -139,6 +139,11 @@ public:
/** Inform statsCompanion that statsd is ready. */
virtual void sayHiToStatsCompanion();
+ /**
+ * Binder call to get AppBreadcrumbReported atom.
+ */
+ virtual Status sendAppBreadcrumbAtom(int32_t label, int32_t state) override;
+
/** IBinder::DeathRecipient */
virtual void binderDied(const wp<IBinder>& who) override;
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index bf0f7206174b..2d14b05c8f5b 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -62,7 +62,7 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
: mConfigKey(key), mUidMap(uidMap),
mTtlNs(config.has_ttl_in_seconds() ? config.ttl_in_seconds() * NS_PER_SEC : -1),
mTtlEndNs(-1),
- mLastReportTimeNs(timeBaseNs),
+ mLastReportTimeNs(currentTimeNs),
mLastReportWallClockNs(getWallClockNs()) {
// Init the ttl end timestamp.
refreshTtl(timeBaseNs);
@@ -247,16 +247,6 @@ void MetricsManager::onLogEvent(const LogEvent& event) {
return;
}
- // Label is 2nd from last field and must be from [0, 15].
- long appHookLabel = event.GetLong(event.size()-1, &err);
- if (err != NO_ERROR ) {
- VLOG("APP_BREADCRUMB_REPORTED had error when parsing the label field");
- return;
- } else if (appHookLabel < 0 || appHookLabel > 15) {
- VLOG("APP_BREADCRUMB_REPORTED does not have valid label %ld", appHookLabel);
- return;
- }
-
// The state must be from 0,3. This part of code must be manually updated.
long appHookState = event.GetLong(event.size(), &err);
if (err != NO_ERROR ) {
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 170d6a7eacae..4f39df9e7ad2 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -79,7 +79,8 @@ public:
}
};
- // Returns the elapsed realtime when this metric manager last reported metrics.
+ // Returns the elapsed realtime when this metric manager last reported metrics. If this config
+ // has not yet dumped any reports, this is the time the metricsmanager was initialized.
inline int64_t getLastReportTimeNs() const {
return mLastReportTimeNs;
};
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index ea8da1408017..1f8181266b65 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -160,6 +160,34 @@ void StorageManager::sendBroadcast(const char* path,
}
}
+bool StorageManager::hasConfigMetricsReport(const ConfigKey& key) {
+ unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
+ if (dir == NULL) {
+ VLOG("Path %s does not exist", STATS_DATA_DIR);
+ return false;
+ }
+
+ string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
+
+ dirent* de;
+ while ((de = readdir(dir.get()))) {
+ char* name = de->d_name;
+ if (name[0] == '.') continue;
+
+ size_t nameLen = strlen(name);
+ size_t suffixLen = suffix.length();
+ if (suffixLen <= nameLen &&
+ strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
+ // Check again that the file name is parseable.
+ int64_t result[3];
+ parseFileName(name, result);
+ if (result[0] == -1) continue;
+ return true;
+ }
+ }
+ return false;
+}
+
void StorageManager::appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto) {
unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
if (dir == NULL) {
diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h
index 8953be97aeaf..4840f3c4fa34 100644
--- a/cmds/statsd/src/storage/StorageManager.h
+++ b/cmds/statsd/src/storage/StorageManager.h
@@ -63,6 +63,11 @@ public:
const std::function<void(const ConfigKey&)>& sendBroadcast);
/**
+ * Returns true if there's at least one report on disk.
+ */
+ static bool hasConfigMetricsReport(const ConfigKey& key);
+
+ /**
* Appends ConfigMetricsReport found on disk to the specific proto and
* delete it.
*/
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 9fdf7a33a7d0..de6e6e542c60 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -62,7 +62,7 @@ TEST(StatsLogProcessorTest, TestRateLimitByteSize) {
sp<AlarmMonitor> periodicAlarmMonitor;
// Construct the processor with a dummy sendBroadcast function that does nothing.
StatsLogProcessor p(m, anomalyAlarmMonitor, periodicAlarmMonitor, 0,
- [](const ConfigKey& key) {});
+ [](const ConfigKey& key) {return true;});
MockMetricsManager mockMetricsManager;
@@ -81,7 +81,7 @@ TEST(StatsLogProcessorTest, TestRateLimitBroadcast) {
sp<AlarmMonitor> subscriberAlarmMonitor;
int broadcastCount = 0;
StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
- [&broadcastCount](const ConfigKey& key) { broadcastCount++; });
+ [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true;});
MockMetricsManager mockMetricsManager;
@@ -107,7 +107,7 @@ TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) {
sp<AlarmMonitor> subscriberAlarmMonitor;
int broadcastCount = 0;
StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
- [&broadcastCount](const ConfigKey& key) { broadcastCount++; });
+ [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true;});
MockMetricsManager mockMetricsManager;
@@ -131,7 +131,7 @@ TEST(StatsLogProcessorTest, TestUidMapHasSnapshot) {
sp<AlarmMonitor> subscriberAlarmMonitor;
int broadcastCount = 0;
StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
- [&broadcastCount](const ConfigKey& key) { broadcastCount++; });
+ [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true;});
ConfigKey key(3, 4);
StatsdConfig config;
config.add_allowed_log_source("AID_ROOT");
@@ -156,7 +156,7 @@ TEST(StatsLogProcessorTest, TestReportIncludesSubConfig) {
sp<AlarmMonitor> subscriberAlarmMonitor;
int broadcastCount = 0;
StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
- [&broadcastCount](const ConfigKey& key) { broadcastCount++; });
+ [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true;});
ConfigKey key(3, 4);
StatsdConfig config;
auto annotation = config.add_annotation();
@@ -185,7 +185,7 @@ TEST(StatsLogProcessorTest, TestOutOfOrderLogs) {
sp<AlarmMonitor> subscriberAlarmMonitor;
int broadcastCount = 0;
StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
- [&broadcastCount](const ConfigKey& key) { broadcastCount++; });
+ [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true;});
LogEvent event1(0, 1 /*logd timestamp*/, 1001 /*elapsedRealtime*/);
event1.init();
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index dde50c2597da..e23131d7b45d 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -44,7 +44,7 @@ TEST(UidMapTest, TestIsolatedUID) {
sp<AlarmMonitor> subscriberAlarmMonitor;
// Construct the processor with a dummy sendBroadcast function that does nothing.
StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
- [](const ConfigKey& key) {});
+ [](const ConfigKey& key) {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/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 590399312b88..e0c98cb9735b 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -459,7 +459,7 @@ sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const in
new AlarmMonitor(1, [](const sp<IStatsCompanionService>&, int64_t){},
[](const sp<IStatsCompanionService>&){});
sp<StatsLogProcessor> processor = new StatsLogProcessor(
- uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseNs, [](const ConfigKey&){});
+ uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseNs, [](const ConfigKey&){return true;});
processor->OnConfigUpdated(currentTimeNs, key, config);
return processor;
}
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 29d7a21b8b77..9b695f9020be 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -366,8 +366,12 @@ Landroid/app/job/IJobService$Stub;-><init>()V
Landroid/app/job/IJobService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/job/IJobService;
Landroid/app/job/IJobService;->startJob(Landroid/app/job/JobParameters;)V
Landroid/app/job/IJobService;->stopJob(Landroid/app/job/JobParameters;)V
+Landroid/app/job/JobInfo$Builder;->setFlags(I)Landroid/app/job/JobInfo$Builder;
+Landroid/app/job/JobInfo$Builder;->setPriority(I)Landroid/app/job/JobInfo$Builder;
Landroid/app/job/JobInfo;->flags:I
+Landroid/app/job/JobInfo;->FLAG_WILL_BE_FOREGROUND:I
Landroid/app/job/JobInfo;->jobId:I
+Landroid/app/job/JobInfo;->PRIORITY_FOREGROUND_APP:I
Landroid/app/job/JobInfo;->service:Landroid/content/ComponentName;
Landroid/app/job/JobParameters;->callback:Landroid/os/IBinder;
Landroid/app/job/JobParameters;->jobId:I
@@ -571,8 +575,8 @@ Landroid/bluetooth/BluetoothHeadset;->connectAudio()Z
Landroid/bluetooth/BluetoothHeadset;->disconnectAudio()Z
Landroid/bluetooth/BluetoothHeadset;->getActiveDevice()Landroid/bluetooth/BluetoothDevice;
Landroid/bluetooth/BluetoothHeadset;->setActiveDevice(Landroid/bluetooth/BluetoothDevice;)Z
-Landroid/bluetooth/BluetoothHeadset;->startScoUsingVirtualVoiceCall(Landroid/bluetooth/BluetoothDevice;)Z
-Landroid/bluetooth/BluetoothHeadset;->stopScoUsingVirtualVoiceCall(Landroid/bluetooth/BluetoothDevice;)Z
+Landroid/bluetooth/BluetoothHeadset;->startScoUsingVirtualVoiceCall()Z
+Landroid/bluetooth/BluetoothHeadset;->stopScoUsingVirtualVoiceCall()Z
Landroid/bluetooth/BluetoothHearingAid;->ACTION_ACTIVE_DEVICE_CHANGED:Ljava/lang/String;
Landroid/bluetooth/BluetoothHearingAid;->getActiveDevices()Ljava/util/List;
Landroid/bluetooth/BluetoothHearingAid;->setActiveDevice(Landroid/bluetooth/BluetoothDevice;)Z
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index a68f485f4374..0c91a2054b8b 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -16,6 +16,7 @@
package android.bluetooth;
+import android.Manifest;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
@@ -633,8 +634,9 @@ public final class BluetoothHeadset implements BluetoothProfile {
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param device Bluetooth headset
- * @return false if there is no headset connected of if the connected headset doesn't support
- * voice recognition or on error, true otherwise
+ * @return false if there is no headset connected, or the connected headset doesn't support
+ * voice recognition, or voice recognition is already started, or audio channel is occupied,
+ * or on error, true otherwise
*/
public boolean startVoiceRecognition(BluetoothDevice device) {
if (DBG) log("startVoiceRecognition()");
@@ -654,10 +656,15 @@ public final class BluetoothHeadset implements BluetoothProfile {
* Stop Bluetooth Voice Recognition mode, and shut down the
* Bluetooth audio path.
*
+ * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
+ * If this function returns true, this intent will be broadcasted with
+ * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
+ *
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param device Bluetooth headset
- * @return false if there is no headset connected or on error, true otherwise
+ * @return false if there is no headset connected, or voice recognition has not started,
+ * or voice recognition has ended on this headset, or on error, true otherwise
*/
public boolean stopVoiceRecognition(BluetoothDevice device) {
if (DBG) log("stopVoiceRecognition()");
@@ -798,11 +805,12 @@ public final class BluetoothHeadset implements BluetoothProfile {
}
/**
- * Check if Bluetooth SCO audio is connected.
+ * Check if at least one headset's SCO audio is connected or connecting
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
- * @return true if SCO is connected, false otherwise or on error
+ * @return true if at least one device's SCO audio is connected or connecting, false otherwise
+ * or on error
* @hide
*/
public boolean isAudioOn() {
@@ -821,11 +829,21 @@ public final class BluetoothHeadset implements BluetoothProfile {
}
/**
- * Initiates a connection of headset audio.
- * It setup SCO channel with remote connected headset device.
+ * Initiates a connection of headset audio to the current active device
+ *
+ * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
+ * If this function returns true, this intent will be broadcasted with
+ * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}.
*
- * @return true if successful false if there was some error such as there is no connected
- * headset
+ * <p> {@link #EXTRA_STATE} will transition from
+ * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when
+ * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
+ * in case of failure to establish the audio connection.
+ *
+ * Note that this intent will not be sent if {@link BluetoothHeadset#isAudioOn()} is true
+ * before calling this method
+ *
+ * @return false if there was some error such as there is no active headset
* @hide
*/
public boolean connectAudio() {
@@ -844,11 +862,14 @@ public final class BluetoothHeadset implements BluetoothProfile {
}
/**
- * Initiates a disconnection of headset audio.
- * It tears down the SCO channel from remote headset device.
+ * Initiates a disconnection of HFP SCO audio.
+ * Tear down voice recognition or virtual voice call if any.
+ *
+ * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
+ * If this function returns true, this intent will be broadcasted with
+ * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
*
- * @return true if successful false if there was some error such as there is no connected SCO
- * channel
+ * @return false if audio is not connected, or on error, true otherwise
* @hide
*/
public boolean disconnectAudio() {
@@ -867,22 +888,33 @@ public final class BluetoothHeadset implements BluetoothProfile {
}
/**
- * Initiates a SCO channel connection with the headset (if connected).
- * Also initiates a virtual voice call for Handsfree devices as many devices
- * do not accept SCO audio without a call.
- * This API allows the handsfree device to be used for routing non-cellular
- * call audio.
+ * Initiates a SCO channel connection as a virtual voice call to the current active device
+ * Active handsfree device will be notified of incoming call and connected call.
*
- * @param device Remote Bluetooth Device
- * @return true if successful, false if there was some error.
+ * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
+ * If this function returns true, this intent will be broadcasted with
+ * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}.
+ *
+ * <p> {@link #EXTRA_STATE} will transition from
+ * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when
+ * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
+ * in case of failure to establish the audio connection.
+ *
+ * @return true if successful, false if one of the following case applies
+ * - SCO audio is not idle (connecting or connected)
+ * - virtual call has already started
+ * - there is no active device
+ * - a Telecom managed call is going on
+ * - binder is dead or Bluetooth is disabled or other error
* @hide
*/
- public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
+ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ public boolean startScoUsingVirtualVoiceCall() {
if (DBG) log("startScoUsingVirtualVoiceCall()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ if (service != null && isEnabled()) {
try {
- return service.startScoUsingVirtualVoiceCall(device);
+ return service.startScoUsingVirtualVoiceCall();
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -894,19 +926,24 @@ public final class BluetoothHeadset implements BluetoothProfile {
}
/**
- * Terminates an ongoing SCO connection and the associated virtual
- * call.
+ * Terminates an ongoing SCO connection and the associated virtual call.
*
- * @param device Remote Bluetooth Device
- * @return true if successful, false if there was some error.
+ * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
+ * If this function returns true, this intent will be broadcasted with
+ * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
+ *
+ * @return true if successful, false if one of the following case applies
+ * - virtual voice call is not started or has ended
+ * - binder is dead or Bluetooth is disabled or other error
* @hide
*/
- public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) {
+ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ public boolean stopScoUsingVirtualVoiceCall() {
if (DBG) log("stopScoUsingVirtualVoiceCall()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ if (service != null && isEnabled()) {
try {
- return service.stopScoUsingVirtualVoiceCall(device);
+ return service.stopScoUsingVirtualVoiceCall();
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 0b4b92107e42..619ec235913e 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -21,6 +21,7 @@ import android.app.job.JobParameters;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.server.ServerProtoEnums;
+import android.service.batterystats.BatteryStatsServiceDumpHistoryProto;
import android.service.batterystats.BatteryStatsServiceDumpProto;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
@@ -6030,51 +6031,51 @@ public abstract class BatteryStats implements Parcelable {
}
}
- static void printBitDescriptions(PrintWriter pw, int oldval, int newval, HistoryTag wakelockTag,
- BitDescription[] descriptions, boolean longNames) {
+ static void printBitDescriptions(StringBuilder sb, int oldval, int newval,
+ HistoryTag wakelockTag, BitDescription[] descriptions, boolean longNames) {
int diff = oldval ^ newval;
if (diff == 0) return;
boolean didWake = false;
for (int i=0; i<descriptions.length; i++) {
BitDescription bd = descriptions[i];
if ((diff&bd.mask) != 0) {
- pw.print(longNames ? " " : ",");
+ sb.append(longNames ? " " : ",");
if (bd.shift < 0) {
- pw.print((newval&bd.mask) != 0 ? "+" : "-");
- pw.print(longNames ? bd.name : bd.shortName);
+ sb.append((newval & bd.mask) != 0 ? "+" : "-");
+ sb.append(longNames ? bd.name : bd.shortName);
if (bd.mask == HistoryItem.STATE_WAKE_LOCK_FLAG && wakelockTag != null) {
didWake = true;
- pw.print("=");
+ sb.append("=");
if (longNames) {
- UserHandle.formatUid(pw, wakelockTag.uid);
- pw.print(":\"");
- pw.print(wakelockTag.string);
- pw.print("\"");
+ UserHandle.formatUid(sb, wakelockTag.uid);
+ sb.append(":\"");
+ sb.append(wakelockTag.string);
+ sb.append("\"");
} else {
- pw.print(wakelockTag.poolIdx);
+ sb.append(wakelockTag.poolIdx);
}
}
} else {
- pw.print(longNames ? bd.name : bd.shortName);
- pw.print("=");
+ sb.append(longNames ? bd.name : bd.shortName);
+ sb.append("=");
int val = (newval&bd.mask)>>bd.shift;
if (bd.values != null && val >= 0 && val < bd.values.length) {
- pw.print(longNames? bd.values[val] : bd.shortValues[val]);
+ sb.append(longNames ? bd.values[val] : bd.shortValues[val]);
} else {
- pw.print(val);
+ sb.append(val);
}
}
}
}
if (!didWake && wakelockTag != null) {
- pw.print(longNames ? " wake_lock=" : ",w=");
+ sb.append(longNames ? " wake_lock=" : ",w=");
if (longNames) {
- UserHandle.formatUid(pw, wakelockTag.uid);
- pw.print(":\"");
- pw.print(wakelockTag.string);
- pw.print("\"");
+ UserHandle.formatUid(sb, wakelockTag.uid);
+ sb.append(":\"");
+ sb.append(wakelockTag.string);
+ sb.append("\"");
} else {
- pw.print(wakelockTag.poolIdx);
+ sb.append(wakelockTag.poolIdx);
}
}
}
@@ -6108,339 +6109,360 @@ public abstract class BatteryStats implements Parcelable {
public void printNextItem(PrintWriter pw, HistoryItem rec, long baseTime, boolean checkin,
boolean verbose) {
+ pw.print(printNextItem(rec, baseTime, checkin, verbose));
+ }
+
+ /** Print the next history item to proto. */
+ public void printNextItem(ProtoOutputStream proto, HistoryItem rec, long baseTime,
+ boolean verbose) {
+ String item = printNextItem(rec, baseTime, true, verbose);
+ for (String line : item.split("\n")) {
+ proto.write(BatteryStatsServiceDumpHistoryProto.CSV_LINES, line);
+ }
+ }
+
+ private String printNextItem(HistoryItem rec, long baseTime, boolean checkin,
+ boolean verbose) {
+ StringBuilder item = new StringBuilder();
if (!checkin) {
- pw.print(" ");
- TimeUtils.formatDuration(rec.time - baseTime, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
- pw.print(" (");
- pw.print(rec.numReadInts);
- pw.print(") ");
+ item.append(" ");
+ TimeUtils.formatDuration(
+ rec.time - baseTime, item, TimeUtils.HUNDRED_DAY_FIELD_LEN);
+ item.append(" (");
+ item.append(rec.numReadInts);
+ item.append(") ");
} else {
- pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
- pw.print(HISTORY_DATA); pw.print(',');
+ item.append(BATTERY_STATS_CHECKIN_VERSION); item.append(',');
+ item.append(HISTORY_DATA); item.append(',');
if (lastTime < 0) {
- pw.print(rec.time - baseTime);
+ item.append(rec.time - baseTime);
} else {
- pw.print(rec.time - lastTime);
+ item.append(rec.time - lastTime);
}
lastTime = rec.time;
}
if (rec.cmd == HistoryItem.CMD_START) {
if (checkin) {
- pw.print(":");
+ item.append(":");
}
- pw.println("START");
+ item.append("START\n");
reset();
} else if (rec.cmd == HistoryItem.CMD_CURRENT_TIME
|| rec.cmd == HistoryItem.CMD_RESET) {
if (checkin) {
- pw.print(":");
+ item.append(":");
}
if (rec.cmd == HistoryItem.CMD_RESET) {
- pw.print("RESET:");
+ item.append("RESET:");
reset();
}
- pw.print("TIME:");
+ item.append("TIME:");
if (checkin) {
- pw.println(rec.currentTime);
+ item.append(rec.currentTime);
+ item.append("\n");
} else {
- pw.print(" ");
- pw.println(DateFormat.format("yyyy-MM-dd-HH-mm-ss",
+ item.append(" ");
+ item.append(DateFormat.format("yyyy-MM-dd-HH-mm-ss",
rec.currentTime).toString());
+ item.append("\n");
}
} else if (rec.cmd == HistoryItem.CMD_SHUTDOWN) {
if (checkin) {
- pw.print(":");
+ item.append(":");
}
- pw.println("SHUTDOWN");
+ item.append("SHUTDOWN\n");
} else if (rec.cmd == HistoryItem.CMD_OVERFLOW) {
if (checkin) {
- pw.print(":");
+ item.append(":");
}
- pw.println("*OVERFLOW*");
+ item.append("*OVERFLOW*\n");
} else {
if (!checkin) {
- if (rec.batteryLevel < 10) pw.print("00");
- else if (rec.batteryLevel < 100) pw.print("0");
- pw.print(rec.batteryLevel);
+ if (rec.batteryLevel < 10) item.append("00");
+ else if (rec.batteryLevel < 100) item.append("0");
+ item.append(rec.batteryLevel);
if (verbose) {
- pw.print(" ");
+ item.append(" ");
if (rec.states < 0) ;
- else if (rec.states < 0x10) pw.print("0000000");
- else if (rec.states < 0x100) pw.print("000000");
- else if (rec.states < 0x1000) pw.print("00000");
- else if (rec.states < 0x10000) pw.print("0000");
- else if (rec.states < 0x100000) pw.print("000");
- else if (rec.states < 0x1000000) pw.print("00");
- else if (rec.states < 0x10000000) pw.print("0");
- pw.print(Integer.toHexString(rec.states));
+ else if (rec.states < 0x10) item.append("0000000");
+ else if (rec.states < 0x100) item.append("000000");
+ else if (rec.states < 0x1000) item.append("00000");
+ else if (rec.states < 0x10000) item.append("0000");
+ else if (rec.states < 0x100000) item.append("000");
+ else if (rec.states < 0x1000000) item.append("00");
+ else if (rec.states < 0x10000000) item.append("0");
+ item.append(Integer.toHexString(rec.states));
}
} else {
if (oldLevel != rec.batteryLevel) {
oldLevel = rec.batteryLevel;
- pw.print(",Bl="); pw.print(rec.batteryLevel);
+ item.append(",Bl="); item.append(rec.batteryLevel);
}
}
if (oldStatus != rec.batteryStatus) {
oldStatus = rec.batteryStatus;
- pw.print(checkin ? ",Bs=" : " status=");
+ item.append(checkin ? ",Bs=" : " status=");
switch (oldStatus) {
case BatteryManager.BATTERY_STATUS_UNKNOWN:
- pw.print(checkin ? "?" : "unknown");
+ item.append(checkin ? "?" : "unknown");
break;
case BatteryManager.BATTERY_STATUS_CHARGING:
- pw.print(checkin ? "c" : "charging");
+ item.append(checkin ? "c" : "charging");
break;
case BatteryManager.BATTERY_STATUS_DISCHARGING:
- pw.print(checkin ? "d" : "discharging");
+ item.append(checkin ? "d" : "discharging");
break;
case BatteryManager.BATTERY_STATUS_NOT_CHARGING:
- pw.print(checkin ? "n" : "not-charging");
+ item.append(checkin ? "n" : "not-charging");
break;
case BatteryManager.BATTERY_STATUS_FULL:
- pw.print(checkin ? "f" : "full");
+ item.append(checkin ? "f" : "full");
break;
default:
- pw.print(oldStatus);
+ item.append(oldStatus);
break;
}
}
if (oldHealth != rec.batteryHealth) {
oldHealth = rec.batteryHealth;
- pw.print(checkin ? ",Bh=" : " health=");
+ item.append(checkin ? ",Bh=" : " health=");
switch (oldHealth) {
case BatteryManager.BATTERY_HEALTH_UNKNOWN:
- pw.print(checkin ? "?" : "unknown");
+ item.append(checkin ? "?" : "unknown");
break;
case BatteryManager.BATTERY_HEALTH_GOOD:
- pw.print(checkin ? "g" : "good");
+ item.append(checkin ? "g" : "good");
break;
case BatteryManager.BATTERY_HEALTH_OVERHEAT:
- pw.print(checkin ? "h" : "overheat");
+ item.append(checkin ? "h" : "overheat");
break;
case BatteryManager.BATTERY_HEALTH_DEAD:
- pw.print(checkin ? "d" : "dead");
+ item.append(checkin ? "d" : "dead");
break;
case BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE:
- pw.print(checkin ? "v" : "over-voltage");
+ item.append(checkin ? "v" : "over-voltage");
break;
case BatteryManager.BATTERY_HEALTH_UNSPECIFIED_FAILURE:
- pw.print(checkin ? "f" : "failure");
+ item.append(checkin ? "f" : "failure");
break;
case BatteryManager.BATTERY_HEALTH_COLD:
- pw.print(checkin ? "c" : "cold");
+ item.append(checkin ? "c" : "cold");
break;
default:
- pw.print(oldHealth);
+ item.append(oldHealth);
break;
}
}
if (oldPlug != rec.batteryPlugType) {
oldPlug = rec.batteryPlugType;
- pw.print(checkin ? ",Bp=" : " plug=");
+ item.append(checkin ? ",Bp=" : " plug=");
switch (oldPlug) {
case 0:
- pw.print(checkin ? "n" : "none");
+ item.append(checkin ? "n" : "none");
break;
case BatteryManager.BATTERY_PLUGGED_AC:
- pw.print(checkin ? "a" : "ac");
+ item.append(checkin ? "a" : "ac");
break;
case BatteryManager.BATTERY_PLUGGED_USB:
- pw.print(checkin ? "u" : "usb");
+ item.append(checkin ? "u" : "usb");
break;
case BatteryManager.BATTERY_PLUGGED_WIRELESS:
- pw.print(checkin ? "w" : "wireless");
+ item.append(checkin ? "w" : "wireless");
break;
default:
- pw.print(oldPlug);
+ item.append(oldPlug);
break;
}
}
if (oldTemp != rec.batteryTemperature) {
oldTemp = rec.batteryTemperature;
- pw.print(checkin ? ",Bt=" : " temp=");
- pw.print(oldTemp);
+ item.append(checkin ? ",Bt=" : " temp=");
+ item.append(oldTemp);
}
if (oldVolt != rec.batteryVoltage) {
oldVolt = rec.batteryVoltage;
- pw.print(checkin ? ",Bv=" : " volt=");
- pw.print(oldVolt);
+ item.append(checkin ? ",Bv=" : " volt=");
+ item.append(oldVolt);
}
final int chargeMAh = rec.batteryChargeUAh / 1000;
if (oldChargeMAh != chargeMAh) {
oldChargeMAh = chargeMAh;
- pw.print(checkin ? ",Bcc=" : " charge=");
- pw.print(oldChargeMAh);
+ item.append(checkin ? ",Bcc=" : " charge=");
+ item.append(oldChargeMAh);
}
- printBitDescriptions(pw, oldState, rec.states, rec.wakelockTag,
+ printBitDescriptions(item, oldState, rec.states, rec.wakelockTag,
HISTORY_STATE_DESCRIPTIONS, !checkin);
- printBitDescriptions(pw, oldState2, rec.states2, null,
+ printBitDescriptions(item, oldState2, rec.states2, null,
HISTORY_STATE2_DESCRIPTIONS, !checkin);
if (rec.wakeReasonTag != null) {
if (checkin) {
- pw.print(",wr=");
- pw.print(rec.wakeReasonTag.poolIdx);
+ item.append(",wr=");
+ item.append(rec.wakeReasonTag.poolIdx);
} else {
- pw.print(" wake_reason=");
- pw.print(rec.wakeReasonTag.uid);
- pw.print(":\"");
- pw.print(rec.wakeReasonTag.string);
- pw.print("\"");
+ item.append(" wake_reason=");
+ item.append(rec.wakeReasonTag.uid);
+ item.append(":\"");
+ item.append(rec.wakeReasonTag.string);
+ item.append("\"");
}
}
if (rec.eventCode != HistoryItem.EVENT_NONE) {
- pw.print(checkin ? "," : " ");
+ item.append(checkin ? "," : " ");
if ((rec.eventCode&HistoryItem.EVENT_FLAG_START) != 0) {
- pw.print("+");
+ item.append("+");
} else if ((rec.eventCode&HistoryItem.EVENT_FLAG_FINISH) != 0) {
- pw.print("-");
+ item.append("-");
}
String[] eventNames = checkin ? HISTORY_EVENT_CHECKIN_NAMES
: HISTORY_EVENT_NAMES;
int idx = rec.eventCode & ~(HistoryItem.EVENT_FLAG_START
| HistoryItem.EVENT_FLAG_FINISH);
if (idx >= 0 && idx < eventNames.length) {
- pw.print(eventNames[idx]);
+ item.append(eventNames[idx]);
} else {
- pw.print(checkin ? "Ev" : "event");
- pw.print(idx);
+ item.append(checkin ? "Ev" : "event");
+ item.append(idx);
}
- pw.print("=");
+ item.append("=");
if (checkin) {
- pw.print(rec.eventTag.poolIdx);
+ item.append(rec.eventTag.poolIdx);
} else {
- pw.append(HISTORY_EVENT_INT_FORMATTERS[idx]
+ item.append(HISTORY_EVENT_INT_FORMATTERS[idx]
.applyAsString(rec.eventTag.uid));
- pw.print(":\"");
- pw.print(rec.eventTag.string);
- pw.print("\"");
+ item.append(":\"");
+ item.append(rec.eventTag.string);
+ item.append("\"");
}
}
- pw.println();
+ item.append("\n");
if (rec.stepDetails != null) {
if (!checkin) {
- pw.print(" Details: cpu=");
- pw.print(rec.stepDetails.userTime);
- pw.print("u+");
- pw.print(rec.stepDetails.systemTime);
- pw.print("s");
+ item.append(" Details: cpu=");
+ item.append(rec.stepDetails.userTime);
+ item.append("u+");
+ item.append(rec.stepDetails.systemTime);
+ item.append("s");
if (rec.stepDetails.appCpuUid1 >= 0) {
- pw.print(" (");
- printStepCpuUidDetails(pw, rec.stepDetails.appCpuUid1,
+ item.append(" (");
+ printStepCpuUidDetails(item, rec.stepDetails.appCpuUid1,
rec.stepDetails.appCpuUTime1, rec.stepDetails.appCpuSTime1);
if (rec.stepDetails.appCpuUid2 >= 0) {
- pw.print(", ");
- printStepCpuUidDetails(pw, rec.stepDetails.appCpuUid2,
+ item.append(", ");
+ printStepCpuUidDetails(item, rec.stepDetails.appCpuUid2,
rec.stepDetails.appCpuUTime2, rec.stepDetails.appCpuSTime2);
}
if (rec.stepDetails.appCpuUid3 >= 0) {
- pw.print(", ");
- printStepCpuUidDetails(pw, rec.stepDetails.appCpuUid3,
+ item.append(", ");
+ printStepCpuUidDetails(item, rec.stepDetails.appCpuUid3,
rec.stepDetails.appCpuUTime3, rec.stepDetails.appCpuSTime3);
}
- pw.print(')');
+ item.append(')');
}
- pw.println();
- pw.print(" /proc/stat=");
- pw.print(rec.stepDetails.statUserTime);
- pw.print(" usr, ");
- pw.print(rec.stepDetails.statSystemTime);
- pw.print(" sys, ");
- pw.print(rec.stepDetails.statIOWaitTime);
- pw.print(" io, ");
- pw.print(rec.stepDetails.statIrqTime);
- pw.print(" irq, ");
- pw.print(rec.stepDetails.statSoftIrqTime);
- pw.print(" sirq, ");
- pw.print(rec.stepDetails.statIdlTime);
- pw.print(" idle");
+ item.append("\n");
+ item.append(" /proc/stat=");
+ item.append(rec.stepDetails.statUserTime);
+ item.append(" usr, ");
+ item.append(rec.stepDetails.statSystemTime);
+ item.append(" sys, ");
+ item.append(rec.stepDetails.statIOWaitTime);
+ item.append(" io, ");
+ item.append(rec.stepDetails.statIrqTime);
+ item.append(" irq, ");
+ item.append(rec.stepDetails.statSoftIrqTime);
+ item.append(" sirq, ");
+ item.append(rec.stepDetails.statIdlTime);
+ item.append(" idle");
int totalRun = rec.stepDetails.statUserTime + rec.stepDetails.statSystemTime
+ rec.stepDetails.statIOWaitTime + rec.stepDetails.statIrqTime
+ rec.stepDetails.statSoftIrqTime;
int total = totalRun + rec.stepDetails.statIdlTime;
if (total > 0) {
- pw.print(" (");
+ item.append(" (");
float perc = ((float)totalRun) / ((float)total) * 100;
- pw.print(String.format("%.1f%%", perc));
- pw.print(" of ");
+ item.append(String.format("%.1f%%", perc));
+ item.append(" of ");
StringBuilder sb = new StringBuilder(64);
formatTimeMsNoSpace(sb, total*10);
- pw.print(sb);
- pw.print(")");
+ item.append(sb);
+ item.append(")");
}
- pw.print(", PlatformIdleStat ");
- pw.print(rec.stepDetails.statPlatformIdleState);
- pw.println();
+ item.append(", PlatformIdleStat ");
+ item.append(rec.stepDetails.statPlatformIdleState);
+ item.append("\n");
- pw.print(", SubsystemPowerState ");
- pw.print(rec.stepDetails.statSubsystemPowerState);
- pw.println();
+ item.append(", SubsystemPowerState ");
+ item.append(rec.stepDetails.statSubsystemPowerState);
+ item.append("\n");
} else {
- pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
- pw.print(HISTORY_DATA); pw.print(",0,Dcpu=");
- pw.print(rec.stepDetails.userTime);
- pw.print(":");
- pw.print(rec.stepDetails.systemTime);
+ item.append(BATTERY_STATS_CHECKIN_VERSION); item.append(',');
+ item.append(HISTORY_DATA); item.append(",0,Dcpu=");
+ item.append(rec.stepDetails.userTime);
+ item.append(":");
+ item.append(rec.stepDetails.systemTime);
if (rec.stepDetails.appCpuUid1 >= 0) {
- printStepCpuUidCheckinDetails(pw, rec.stepDetails.appCpuUid1,
+ printStepCpuUidCheckinDetails(item, rec.stepDetails.appCpuUid1,
rec.stepDetails.appCpuUTime1, rec.stepDetails.appCpuSTime1);
if (rec.stepDetails.appCpuUid2 >= 0) {
- printStepCpuUidCheckinDetails(pw, rec.stepDetails.appCpuUid2,
+ printStepCpuUidCheckinDetails(item, rec.stepDetails.appCpuUid2,
rec.stepDetails.appCpuUTime2, rec.stepDetails.appCpuSTime2);
}
if (rec.stepDetails.appCpuUid3 >= 0) {
- printStepCpuUidCheckinDetails(pw, rec.stepDetails.appCpuUid3,
+ printStepCpuUidCheckinDetails(item, rec.stepDetails.appCpuUid3,
rec.stepDetails.appCpuUTime3, rec.stepDetails.appCpuSTime3);
}
}
- pw.println();
- pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
- pw.print(HISTORY_DATA); pw.print(",0,Dpst=");
- pw.print(rec.stepDetails.statUserTime);
- pw.print(',');
- pw.print(rec.stepDetails.statSystemTime);
- pw.print(',');
- pw.print(rec.stepDetails.statIOWaitTime);
- pw.print(',');
- pw.print(rec.stepDetails.statIrqTime);
- pw.print(',');
- pw.print(rec.stepDetails.statSoftIrqTime);
- pw.print(',');
- pw.print(rec.stepDetails.statIdlTime);
- pw.print(',');
+ item.append("\n");
+ item.append(BATTERY_STATS_CHECKIN_VERSION); item.append(',');
+ item.append(HISTORY_DATA); item.append(",0,Dpst=");
+ item.append(rec.stepDetails.statUserTime);
+ item.append(',');
+ item.append(rec.stepDetails.statSystemTime);
+ item.append(',');
+ item.append(rec.stepDetails.statIOWaitTime);
+ item.append(',');
+ item.append(rec.stepDetails.statIrqTime);
+ item.append(',');
+ item.append(rec.stepDetails.statSoftIrqTime);
+ item.append(',');
+ item.append(rec.stepDetails.statIdlTime);
+ item.append(',');
if (rec.stepDetails.statPlatformIdleState != null) {
- pw.print(rec.stepDetails.statPlatformIdleState);
+ item.append(rec.stepDetails.statPlatformIdleState);
if (rec.stepDetails.statSubsystemPowerState != null) {
- pw.print(',');
+ item.append(',');
}
}
if (rec.stepDetails.statSubsystemPowerState != null) {
- pw.print(rec.stepDetails.statSubsystemPowerState);
+ item.append(rec.stepDetails.statSubsystemPowerState);
}
- pw.println();
+ item.append("\n");
}
}
oldState = rec.states;
oldState2 = rec.states2;
}
+
+ return item.toString();
}
- private void printStepCpuUidDetails(PrintWriter pw, int uid, int utime, int stime) {
- UserHandle.formatUid(pw, uid);
- pw.print("=");
- pw.print(utime);
- pw.print("u+");
- pw.print(stime);
- pw.print("s");
+ private void printStepCpuUidDetails(StringBuilder sb, int uid, int utime, int stime) {
+ UserHandle.formatUid(sb, uid);
+ sb.append("=");
+ sb.append(utime);
+ sb.append("u+");
+ sb.append(stime);
+ sb.append("s");
}
- private void printStepCpuUidCheckinDetails(PrintWriter pw, int uid, int utime, int stime) {
- pw.print('/');
- pw.print(uid);
- pw.print(":");
- pw.print(utime);
- pw.print(":");
- pw.print(stime);
+ private void printStepCpuUidCheckinDetails(StringBuilder sb, int uid, int utime,
+ int stime) {
+ sb.append('/');
+ sb.append(uid);
+ sb.append(":");
+ sb.append(utime);
+ sb.append(":");
+ sb.append(stime);
}
}
@@ -7046,21 +7068,30 @@ public abstract class BatteryStats implements Parcelable {
}
}
- /** Dump #STATS_SINCE_CHARGED batterystats data to a proto. @hide */
+ /**
+ * Dump #STATS_SINCE_CHARGED batterystats data to a proto. If the flags include
+ * DUMP_INCLUDE_HISTORY or DUMP_HISTORY_ONLY, only the history will be dumped.
+ * @hide
+ */
public void dumpProtoLocked(Context context, FileDescriptor fd, List<ApplicationInfo> apps,
- int flags) {
+ int flags, long histStart) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
- final long bToken = proto.start(BatteryStatsServiceDumpProto.BATTERYSTATS);
prepareForDumpLocked();
+ if ((flags & (DUMP_INCLUDE_HISTORY | DUMP_HISTORY_ONLY)) != 0) {
+ dumpProtoHistoryLocked(proto, flags, histStart);
+ proto.flush();
+ return;
+ }
+
+ final long bToken = proto.start(BatteryStatsServiceDumpProto.BATTERYSTATS);
+
proto.write(BatteryStatsProto.REPORT_VERSION, CHECKIN_VERSION);
proto.write(BatteryStatsProto.PARCEL_VERSION, getParcelVersion());
proto.write(BatteryStatsProto.START_PLATFORM_VERSION, getStartPlatformVersion());
proto.write(BatteryStatsProto.END_PLATFORM_VERSION, getEndPlatformVersion());
- // History intentionally not included in proto dump.
-
- if ((flags & (DUMP_HISTORY_ONLY | DUMP_DAILY_ONLY)) == 0) {
+ if ((flags & DUMP_DAILY_ONLY) == 0) {
final BatteryStatsHelper helper = new BatteryStatsHelper(context, false,
(flags & DUMP_DEVICE_WIFI_ONLY) != 0);
helper.create(this);
@@ -7530,6 +7561,108 @@ public abstract class BatteryStats implements Parcelable {
}
}
+ private void dumpProtoHistoryLocked(ProtoOutputStream proto, int flags, long histStart) {
+ if (!startIteratingHistoryLocked()) {
+ return;
+ }
+
+ proto.write(BatteryStatsServiceDumpHistoryProto.REPORT_VERSION, CHECKIN_VERSION);
+ proto.write(BatteryStatsServiceDumpHistoryProto.PARCEL_VERSION, getParcelVersion());
+ proto.write(BatteryStatsServiceDumpHistoryProto.START_PLATFORM_VERSION,
+ getStartPlatformVersion());
+ proto.write(BatteryStatsServiceDumpHistoryProto.END_PLATFORM_VERSION,
+ getEndPlatformVersion());
+ try {
+ long token;
+ // History string pool (HISTORY_STRING_POOL)
+ for (int i = 0; i < getHistoryStringPoolSize(); ++i) {
+ token = proto.start(BatteryStatsServiceDumpHistoryProto.KEYS);
+ proto.write(BatteryStatsServiceDumpHistoryProto.Key.INDEX, i);
+ proto.write(BatteryStatsServiceDumpHistoryProto.Key.UID, getHistoryTagPoolUid(i));
+ proto.write(BatteryStatsServiceDumpHistoryProto.Key.TAG,
+ getHistoryTagPoolString(i));
+ proto.end(token);
+ }
+
+ // History data (HISTORY_DATA)
+ final HistoryPrinter hprinter = new HistoryPrinter();
+ final HistoryItem rec = new HistoryItem();
+ long lastTime = -1;
+ long baseTime = -1;
+ boolean printed = false;
+ HistoryEventTracker tracker = null;
+ while (getNextHistoryLocked(rec)) {
+ lastTime = rec.time;
+ if (baseTime < 0) {
+ baseTime = lastTime;
+ }
+ if (rec.time >= histStart) {
+ if (histStart >= 0 && !printed) {
+ if (rec.cmd == HistoryItem.CMD_CURRENT_TIME
+ || rec.cmd == HistoryItem.CMD_RESET
+ || rec.cmd == HistoryItem.CMD_START
+ || rec.cmd == HistoryItem.CMD_SHUTDOWN) {
+ printed = true;
+ hprinter.printNextItem(proto, rec, baseTime,
+ (flags & DUMP_VERBOSE) != 0);
+ rec.cmd = HistoryItem.CMD_UPDATE;
+ } else if (rec.currentTime != 0) {
+ printed = true;
+ byte cmd = rec.cmd;
+ rec.cmd = HistoryItem.CMD_CURRENT_TIME;
+ hprinter.printNextItem(proto, rec, baseTime,
+ (flags & DUMP_VERBOSE) != 0);
+ rec.cmd = cmd;
+ }
+ if (tracker != null) {
+ if (rec.cmd != HistoryItem.CMD_UPDATE) {
+ hprinter.printNextItem(proto, rec, baseTime,
+ (flags & DUMP_VERBOSE) != 0);
+ rec.cmd = HistoryItem.CMD_UPDATE;
+ }
+ int oldEventCode = rec.eventCode;
+ HistoryTag oldEventTag = rec.eventTag;
+ rec.eventTag = new HistoryTag();
+ for (int i = 0; i < HistoryItem.EVENT_COUNT; i++) {
+ HashMap<String, SparseIntArray> active =
+ tracker.getStateForEvent(i);
+ if (active == null) {
+ continue;
+ }
+ for (HashMap.Entry<String, SparseIntArray> ent
+ : active.entrySet()) {
+ SparseIntArray uids = ent.getValue();
+ for (int j = 0; j < uids.size(); j++) {
+ rec.eventCode = i;
+ rec.eventTag.string = ent.getKey();
+ rec.eventTag.uid = uids.keyAt(j);
+ rec.eventTag.poolIdx = uids.valueAt(j);
+ hprinter.printNextItem(proto, rec, baseTime,
+ (flags & DUMP_VERBOSE) != 0);
+ rec.wakeReasonTag = null;
+ rec.wakelockTag = null;
+ }
+ }
+ }
+ rec.eventCode = oldEventCode;
+ rec.eventTag = oldEventTag;
+ tracker = null;
+ }
+ }
+ hprinter.printNextItem(proto, rec, baseTime,
+ (flags & DUMP_VERBOSE) != 0);
+ }
+ }
+ if (histStart >= 0) {
+ commitCurrentHistoryBatchLocked();
+ proto.write(BatteryStatsServiceDumpHistoryProto.CSV_LINES,
+ "NEXT: " + (lastTime + 1));
+ }
+ } finally {
+ finishIteratingHistoryLocked();
+ }
+ }
+
private void dumpProtoSystemLocked(ProtoOutputStream proto, BatteryStatsHelper helper) {
final long sToken = proto.start(BatteryStatsProto.SYSTEM);
final long rawUptimeUs = SystemClock.uptimeMillis() * 1000;
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index 8c256be20479..124f2072933e 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -152,4 +152,10 @@ interface IStatsManager {
* Requires Manifest.permission.DUMP.
*/
void unsetBroadcastSubscriber(long configKey, long subscriberId, in String packageName);
+
+ /**
+ * Apps can send an atom via this application breadcrumb with the specified label and state for
+ * this label. This allows building custom metrics and predicates.
+ */
+ void sendAppBreadcrumbAtom(int label, int state);
}
diff --git a/core/java/android/service/notification/ScheduleCalendar.java b/core/java/android/service/notification/ScheduleCalendar.java
index 01287104f9b2..8b7946ce7651 100644
--- a/core/java/android/service/notification/ScheduleCalendar.java
+++ b/core/java/android/service/notification/ScheduleCalendar.java
@@ -135,6 +135,24 @@ public class ScheduleCalendar {
}
/**
+ * @param alarm milliseconds since Epoch
+ * @param now milliseconds since Epoch
+ * @return true if alarm and now is within the schedule, else false
+ */
+ public boolean isAlarmInSchedule(long alarm, long now) {
+ if (mSchedule == null || mDays.size() == 0) return false;
+ final long start = getTime(alarm, mSchedule.startHour, mSchedule.startMinute);
+ long end = getTime(alarm, mSchedule.endHour, mSchedule.endMinute);
+ if (end <= start) {
+ end = addDays(end, 1);
+ }
+ return (isInSchedule(-1, alarm, start, end)
+ && isInSchedule(-1, now, start, end))
+ || (isInSchedule(0, alarm, start, end)
+ && isInSchedule(0, now, start, end));
+ }
+
+ /**
* @param time milliseconds since Epoch
* @return true if should exit at time for next alarm, else false
*/
@@ -145,7 +163,7 @@ public class ScheduleCalendar {
return mSchedule.exitAtAlarm
&& mSchedule.nextAlarm != 0
&& time >= mSchedule.nextAlarm
- && isInSchedule(mSchedule.nextAlarm);
+ && isAlarmInSchedule(mSchedule.nextAlarm, time);
}
private boolean isInSchedule(int daysOffset, long time, long start, long end) {
diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java
index e8b4197259ce..e3de30713652 100644
--- a/core/java/android/util/StatsLog.java
+++ b/core/java/android/util/StatsLog.java
@@ -16,59 +16,101 @@
package android.util;
-import android.os.Process;
+import android.os.IStatsManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
/**
* StatsLog provides an API for developers to send events to statsd. The events can be used to
* define custom metrics inside statsd.
*/
public final class StatsLog extends StatsLogInternal {
- private static final String TAG = "StatsManager";
+ private static final String TAG = "StatsLog";
+ private static final boolean DEBUG = false;
+
+ private static IStatsManager sService;
private StatsLog() {}
/**
* Logs a start event.
*
- * @param label developer-chosen label that is from [0, 16).
+ * @param label developer-chosen label.
* @return True if the log request was sent to statsd.
*/
public static boolean logStart(int label) {
- if (label >= 0 && label < 16) {
- StatsLog.write(APP_BREADCRUMB_REPORTED, Process.myUid(),
- label, APP_BREADCRUMB_REPORTED__STATE__START);
- return true;
+ synchronized (StatsLog.class) {
+ try {
+ IStatsManager service = getIStatsManagerLocked();
+ if (service == null) {
+ if (DEBUG) Slog.d(TAG, "Failed to find statsd when logging start");
+ return false;
+ }
+ service.sendAppBreadcrumbAtom(label,
+ StatsLog.APP_BREADCRUMB_REPORTED__STATE__START);
+ return true;
+ } catch (RemoteException e) {
+ sService = null;
+ if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when logging start");
+ return false;
+ }
}
- return false;
}
/**
* Logs a stop event.
*
- * @param label developer-chosen label that is from [0, 16).
+ * @param label developer-chosen label.
* @return True if the log request was sent to statsd.
*/
public static boolean logStop(int label) {
- if (label >= 0 && label < 16) {
- StatsLog.write(APP_BREADCRUMB_REPORTED, Process.myUid(),
- label, APP_BREADCRUMB_REPORTED__STATE__STOP);
- return true;
+ synchronized (StatsLog.class) {
+ try {
+ IStatsManager service = getIStatsManagerLocked();
+ if (service == null) {
+ if (DEBUG) Slog.d(TAG, "Failed to find statsd when logging stop");
+ return false;
+ }
+ service.sendAppBreadcrumbAtom(label, StatsLog.APP_BREADCRUMB_REPORTED__STATE__STOP);
+ return true;
+ } catch (RemoteException e) {
+ sService = null;
+ if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when logging stop");
+ return false;
+ }
}
- return false;
}
/**
* Logs an event that does not represent a start or stop boundary.
*
- * @param label developer-chosen label that is from [0, 16).
+ * @param label developer-chosen label.
* @return True if the log request was sent to statsd.
*/
public static boolean logEvent(int label) {
- if (label >= 0 && label < 16) {
- StatsLog.write(APP_BREADCRUMB_REPORTED, Process.myUid(), label,
- APP_BREADCRUMB_REPORTED__STATE__UNSPECIFIED);
- return true;
+ synchronized (StatsLog.class) {
+ try {
+ IStatsManager service = getIStatsManagerLocked();
+ if (service == null) {
+ if (DEBUG) Slog.d(TAG, "Failed to find statsd when logging event");
+ return false;
+ }
+ service.sendAppBreadcrumbAtom(
+ label, StatsLog.APP_BREADCRUMB_REPORTED__STATE__UNSPECIFIED);
+ return true;
+ } catch (RemoteException e) {
+ sService = null;
+ if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when logging event");
+ return false;
+ }
+ }
+ }
+
+ private static IStatsManager getIStatsManagerLocked() throws RemoteException {
+ if (sService != null) {
+ return sService;
}
- return false;
+ sService = IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
+ return sService;
}
}
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 84ae20b92f3c..05b613c3c7c7 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -240,6 +240,14 @@ public class TimeUtils {
}
/** @hide Just for debugging; not internationalized. */
+ public static void formatDuration(long duration, StringBuilder builder, int fieldLen) {
+ synchronized (sFormatSync) {
+ int len = formatDurationLocked(duration, fieldLen);
+ builder.append(sFormatStr, 0, len);
+ }
+ }
+
+ /** @hide Just for debugging; not internationalized. */
public static void formatDuration(long duration, PrintWriter pw, int fieldLen) {
synchronized (sFormatSync) {
int len = formatDurationLocked(duration, fieldLen);
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 88300dbda1c4..4c7dc116d167 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1132,8 +1132,7 @@ public final class AutofillManager {
if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
commitLocked();
- mMetricsLogger.action(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED,
- mContext.getPackageName());
+ mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED));
}
}
}
@@ -1893,14 +1892,19 @@ public final class AutofillManager {
}
}
- final LogMaker log = new LogMaker(MetricsEvent.AUTOFILL_DATASET_APPLIED)
- .setPackageName(mContext.getPackageName())
+ mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_DATASET_APPLIED)
.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount)
- .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied);
- mMetricsLogger.write(log);
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied));
}
}
+ private LogMaker newLog(int category) {
+ return new LogMaker(category)
+ .setPackageName(mContext.getPackageName())
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE,
+ isCompatibilityModeEnabledLocked() ? 1 : 0);
+ }
+
/**
* Set the tracked views.
*
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index 15b2718bea42..b9a8864600c7 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -147,7 +147,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
setAvatar(sender.getIcon());
}
mAvatarView.setVisibility(VISIBLE);
- mSenderName.setVisibility(VISIBLE);
+ mSenderName.setVisibility(TextUtils.isEmpty(nameOverride) ? GONE : VISIBLE);
mTextColor = getNormalTextColor();
mSendingTextColor = calculateSendingTextColor();
}
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index af9aae3c318d..538ea11b6aa9 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -436,10 +436,29 @@ public class MessagingLayout extends FrameLayout {
}
private void updateHistoricMessageVisibility() {
- for (int i = 0; i < mHistoricMessages.size(); i++) {
+ int numHistoric = mHistoricMessages.size();
+ for (int i = 0; i < numHistoric; i++) {
MessagingMessage existing = mHistoricMessages.get(i);
existing.setVisibility(mShowHistoricMessages ? VISIBLE : GONE);
}
+ int numGroups = mGroups.size();
+ for (int i = 0; i < numGroups; i++) {
+ MessagingGroup group = mGroups.get(i);
+ int visibleChildren = 0;
+ List<MessagingMessage> messages = group.getMessages();
+ int numGroupMessages = messages.size();
+ for (int j = 0; j < numGroupMessages; j++) {
+ MessagingMessage message = messages.get(j);
+ if (message.getVisibility() != GONE) {
+ visibleChildren++;
+ }
+ }
+ if (visibleChildren > 0 && group.getVisibility() == GONE) {
+ group.setVisibility(VISIBLE);
+ } else if (visibleChildren == 0 && group.getVisibility() != GONE) {
+ group.setVisibility(GONE);
+ }
+ }
}
@Override
diff --git a/core/java/com/android/internal/widget/MessagingMessage.java b/core/java/com/android/internal/widget/MessagingMessage.java
index d2b670fa2d66..ffcb50309feb 100644
--- a/core/java/com/android/internal/widget/MessagingMessage.java
+++ b/core/java/com/android/internal/widget/MessagingMessage.java
@@ -145,4 +145,6 @@ public interface MessagingMessage extends MessagingLinearLayout.MessagingChild {
MessagingMessageState getState();
void setVisibility(int visibility);
+
+ int getVisibility();
}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 64e1239f36f6..3aea3a767f46 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -18,6 +18,7 @@ syntax = "proto2";
option java_multiple_files = true;
import "frameworks/base/core/proto/android/os/backtrace.proto";
+import "frameworks/base/core/proto/android/os/batterystats.proto";
import "frameworks/base/core/proto/android/os/batterytype.proto";
import "frameworks/base/core/proto/android/os/cpufreq.proto";
import "frameworks/base/core/proto/android/os/cpuinfo.proto";
@@ -289,6 +290,14 @@ message IncidentProto {
(section).args = "usb --proto"
];
+ // The history can be large and may cause issues in consumers, so put the
+ // history in a separate section to compensate.
+ optional android.service.batterystats.BatteryStatsServiceDumpHistoryProto battery_history = 3022 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "batterystats --proto --history",
+ (section).userdebug_and_eng_only = true
+ ];
+
// Reserved for OEMs.
extensions 50000 to 100000;
}
diff --git a/core/proto/android/service/batterystats.proto b/core/proto/android/service/batterystats.proto
index 5586263d0fac..25b47d3f88f2 100644
--- a/core/proto/android/service/batterystats.proto
+++ b/core/proto/android/service/batterystats.proto
@@ -23,8 +23,34 @@ option java_outer_classname = "BatteryStatsServiceProto";
import "frameworks/base/core/proto/android/os/batterystats.proto";
import "frameworks/base/libs/incident/proto/android/privacy.proto";
+// Dump of batterystats aggregate data (dumpsys batterystats --proto).
message BatteryStatsServiceDumpProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
optional android.os.BatteryStatsProto batterystats = 1;
}
+
+// Dump of batterystats history data (dumpsys batterystats --proto --history).
+message BatteryStatsServiceDumpHistoryProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 report_version = 1;
+ optional int64 parcel_version = 2;
+ optional string start_platform_version = 3;
+ optional string end_platform_version = 4;
+
+ // HistoryStringPool data
+ message Key {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 index = 1;
+ // Not valid for all keys.
+ optional int32 uid = 2;
+ optional string tag = 3;
+ }
+ repeated Key keys = 5;
+
+ // Dump of battery history in csv format (equivalent of
+ // 'batterystats -c --history', with the hsp lines extracted).
+ repeated string csv_lines = 6;
+}
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index b3a3f455bac0..298b699055a5 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -7367,222 +7367,238 @@ void ResTable::print(bool inclValues) const
printf("\n");
}
- int packageId = pg->id;
- size_t pkgCount = pg->packages.size();
- for (size_t pkgIndex=0; pkgIndex<pkgCount; pkgIndex++) {
- const Package* pkg = pg->packages[pkgIndex];
- // Use a package's real ID, since the ID may have been assigned
- // if this package is a shared library.
- packageId = pkg->package->id;
- char16_t tmpName[sizeof(pkg->package->name)/sizeof(pkg->package->name[0])];
- strcpy16_dtoh(tmpName, pkg->package->name, sizeof(pkg->package->name)/sizeof(pkg->package->name[0]));
- printf(" Package %d id=0x%02x name=%s\n", (int)pkgIndex,
- pkg->package->id, String8(tmpName).string());
- }
-
- for (size_t typeIndex=0; typeIndex < pg->types.size(); typeIndex++) {
- const TypeList& typeList = pg->types[typeIndex];
- if (typeList.isEmpty()) {
- continue;
- }
- const Type* typeConfigs = typeList[0];
- const size_t NTC = typeConfigs->configs.size();
- printf(" type %d configCount=%d entryCount=%d\n",
- (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount);
- if (typeConfigs->typeSpecFlags != NULL) {
- for (size_t entryIndex=0; entryIndex<typeConfigs->entryCount; entryIndex++) {
- uint32_t resID = (0xff000000 & ((packageId)<<24))
- | (0x00ff0000 & ((typeIndex+1)<<16))
- | (0x0000ffff & (entryIndex));
- // Since we are creating resID without actually
- // iterating over them, we have no idea which is a
- // dynamic reference. We must check.
- if (packageId == 0) {
- pg->dynamicRefTable.lookupResourceId(&resID);
- }
-
- resource_name resName;
- if (this->getResourceName(resID, true, &resName)) {
- String8 type8;
- String8 name8;
- if (resName.type8 != NULL) {
- type8 = String8(resName.type8, resName.typeLen);
- } else {
- type8 = String8(resName.type, resName.typeLen);
- }
- if (resName.name8 != NULL) {
- name8 = String8(resName.name8, resName.nameLen);
- } else {
- name8 = String8(resName.name, resName.nameLen);
- }
- printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n",
- resID,
- CHAR16_TO_CSTR(resName.package, resName.packageLen),
- type8.string(), name8.string(),
- dtohl(typeConfigs->typeSpecFlags[entryIndex]));
- } else {
- printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID);
- }
- }
- }
- for (size_t configIndex=0; configIndex<NTC; configIndex++) {
- const ResTable_type* type = typeConfigs->configs[configIndex];
- if ((((uint64_t)type)&0x3) != 0) {
- printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type);
- continue;
- }
-
- // Always copy the config, as fields get added and we need to
- // set the defaults.
- ResTable_config thisConfig;
- thisConfig.copyFromDtoH(type->config);
-
- String8 configStr = thisConfig.toString();
- printf(" config %s", configStr.size() > 0
- ? configStr.string() : "(default)");
- if (type->flags != 0u) {
- printf(" flags=0x%02x", type->flags);
- if (type->flags & ResTable_type::FLAG_SPARSE) {
- printf(" [sparse]");
- }
- }
-
- printf(":\n");
+ // Determine the number of resource splits for the resource types in this package.
+ // It needs to be done outside of the loop below so all of the information for a
+ // is displayed in a single block. Otherwise, a resource split's resource types
+ // would be interleaved with other splits.
+ size_t splitCount = 0;
+ for (size_t typeIndex = 0; typeIndex < pg->types.size(); typeIndex++) {
+ splitCount = max(splitCount, pg->types[typeIndex].size());
+ }
- size_t entryCount = dtohl(type->entryCount);
- uint32_t entriesStart = dtohl(type->entriesStart);
- if ((entriesStart&0x3) != 0) {
- printf(" NON-INTEGER ResTable_type entriesStart OFFSET: 0x%x\n", entriesStart);
- continue;
- }
- uint32_t typeSize = dtohl(type->header.size);
- if ((typeSize&0x3) != 0) {
- printf(" NON-INTEGER ResTable_type header.size: 0x%x\n", typeSize);
+ int packageId = pg->id;
+ for (size_t splitIndex = 0; splitIndex < splitCount; splitIndex++) {
+ size_t pkgCount = pg->packages.size();
+ for (size_t pkgIndex=0; pkgIndex<pkgCount; pkgIndex++) {
+ const Package* pkg = pg->packages[pkgIndex];
+ // Use a package's real ID, since the ID may have been assigned
+ // if this package is a shared library.
+ packageId = pkg->package->id;
+ char16_t tmpName[sizeof(pkg->package->name)/sizeof(pkg->package->name[0])];
+ strcpy16_dtoh(tmpName, pkg->package->name,
+ sizeof(pkg->package->name)/sizeof(pkg->package->name[0]));
+ printf(" Package %d id=0x%02x name=%s\n", (int)pkgIndex,
+ pkg->package->id, String8(tmpName).string());
+ }
+
+ for (size_t typeIndex = 0; typeIndex < pg->types.size(); typeIndex++) {
+ const TypeList& typeList = pg->types[typeIndex];
+ if (splitIndex >= typeList.size() || typeList.isEmpty()) {
+ // Only dump if the split exists and contains entries for this type
continue;
}
-
- const uint32_t* const eindex = (const uint32_t*)
- (((const uint8_t*)type) + dtohs(type->header.headerSize));
- for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) {
- size_t entryId;
- uint32_t thisOffset;
- if (type->flags & ResTable_type::FLAG_SPARSE) {
- const ResTable_sparseTypeEntry* entry =
- reinterpret_cast<const ResTable_sparseTypeEntry*>(
- eindex + entryIndex);
- entryId = dtohs(entry->idx);
- // Offsets are encoded as divided by 4.
- thisOffset = static_cast<uint32_t>(dtohs(entry->offset)) * 4u;
- } else {
- entryId = entryIndex;
- thisOffset = dtohl(eindex[entryIndex]);
- if (thisOffset == ResTable_type::NO_ENTRY) {
- continue;
+ const Type* typeConfigs = typeList[splitIndex];
+ const size_t NTC = typeConfigs->configs.size();
+ printf(" type %d configCount=%d entryCount=%d\n",
+ (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount);
+ if (typeConfigs->typeSpecFlags != NULL) {
+ for (size_t entryIndex=0; entryIndex<typeConfigs->entryCount; entryIndex++) {
+ uint32_t resID = (0xff000000 & ((packageId)<<24))
+ | (0x00ff0000 & ((typeIndex+1)<<16))
+ | (0x0000ffff & (entryIndex));
+ // Since we are creating resID without actually
+ // iterating over them, we have no idea which is a
+ // dynamic reference. We must check.
+ if (packageId == 0) {
+ pg->dynamicRefTable.lookupResourceId(&resID);
}
- }
- uint32_t resID = (0xff000000 & ((packageId)<<24))
- | (0x00ff0000 & ((typeIndex+1)<<16))
- | (0x0000ffff & (entryId));
- if (packageId == 0) {
- pg->dynamicRefTable.lookupResourceId(&resID);
- }
- resource_name resName;
- if (this->getResourceName(resID, true, &resName)) {
- String8 type8;
- String8 name8;
- if (resName.type8 != NULL) {
- type8 = String8(resName.type8, resName.typeLen);
- } else {
- type8 = String8(resName.type, resName.typeLen);
- }
- if (resName.name8 != NULL) {
- name8 = String8(resName.name8, resName.nameLen);
+ resource_name resName;
+ if (this->getResourceName(resID, true, &resName)) {
+ String8 type8;
+ String8 name8;
+ if (resName.type8 != NULL) {
+ type8 = String8(resName.type8, resName.typeLen);
+ } else {
+ type8 = String8(resName.type, resName.typeLen);
+ }
+ if (resName.name8 != NULL) {
+ name8 = String8(resName.name8, resName.nameLen);
+ } else {
+ name8 = String8(resName.name, resName.nameLen);
+ }
+ printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n",
+ resID,
+ CHAR16_TO_CSTR(resName.package, resName.packageLen),
+ type8.string(), name8.string(),
+ dtohl(typeConfigs->typeSpecFlags[entryIndex]));
} else {
- name8 = String8(resName.name, resName.nameLen);
+ printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID);
}
- printf(" resource 0x%08x %s:%s/%s: ", resID,
- CHAR16_TO_CSTR(resName.package, resName.packageLen),
- type8.string(), name8.string());
- } else {
- printf(" INVALID RESOURCE 0x%08x: ", resID);
- }
- if ((thisOffset&0x3) != 0) {
- printf("NON-INTEGER OFFSET: 0x%x\n", thisOffset);
- continue;
}
- if ((thisOffset+sizeof(ResTable_entry)) > typeSize) {
- printf("OFFSET OUT OF BOUNDS: 0x%x+0x%x (size is 0x%x)\n",
- entriesStart, thisOffset, typeSize);
+ }
+ for (size_t configIndex=0; configIndex<NTC; configIndex++) {
+ const ResTable_type* type = typeConfigs->configs[configIndex];
+ if ((((uint64_t)type)&0x3) != 0) {
+ printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type);
continue;
}
- const ResTable_entry* ent = (const ResTable_entry*)
- (((const uint8_t*)type) + entriesStart + thisOffset);
- if (((entriesStart + thisOffset)&0x3) != 0) {
- printf("NON-INTEGER ResTable_entry OFFSET: 0x%x\n",
- (entriesStart + thisOffset));
- continue;
+ // Always copy the config, as fields get added and we need to
+ // set the defaults.
+ ResTable_config thisConfig;
+ thisConfig.copyFromDtoH(type->config);
+
+ String8 configStr = thisConfig.toString();
+ printf(" config %s", configStr.size() > 0
+ ? configStr.string() : "(default)");
+ if (type->flags != 0u) {
+ printf(" flags=0x%02x", type->flags);
+ if (type->flags & ResTable_type::FLAG_SPARSE) {
+ printf(" [sparse]");
+ }
}
- uintptr_t esize = dtohs(ent->size);
- if ((esize&0x3) != 0) {
- printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void *)esize);
+ printf(":\n");
+
+ size_t entryCount = dtohl(type->entryCount);
+ uint32_t entriesStart = dtohl(type->entriesStart);
+ if ((entriesStart&0x3) != 0) {
+ printf(" NON-INTEGER ResTable_type entriesStart OFFSET: 0x%x\n",
+ entriesStart);
continue;
}
- if ((thisOffset+esize) > typeSize) {
- printf("ResTable_entry OUT OF BOUNDS: 0x%x+0x%x+%p (size is 0x%x)\n",
- entriesStart, thisOffset, (void *)esize, typeSize);
+ uint32_t typeSize = dtohl(type->header.size);
+ if ((typeSize&0x3) != 0) {
+ printf(" NON-INTEGER ResTable_type header.size: 0x%x\n", typeSize);
continue;
}
- const Res_value* valuePtr = NULL;
- const ResTable_map_entry* bagPtr = NULL;
- Res_value value;
- if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) {
- printf("<bag>");
- bagPtr = (const ResTable_map_entry*)ent;
- } else {
- valuePtr = (const Res_value*)
- (((const uint8_t*)ent) + esize);
- value.copyFrom_dtoh(*valuePtr);
- printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)",
- (int)value.dataType, (int)value.data,
- (int)value.size, (int)value.res0);
- }
+ const uint32_t* const eindex = (const uint32_t*)
+ (((const uint8_t*)type) + dtohs(type->header.headerSize));
+ for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) {
+ size_t entryId;
+ uint32_t thisOffset;
+ if (type->flags & ResTable_type::FLAG_SPARSE) {
+ const ResTable_sparseTypeEntry* entry =
+ reinterpret_cast<const ResTable_sparseTypeEntry*>(
+ eindex + entryIndex);
+ entryId = dtohs(entry->idx);
+ // Offsets are encoded as divided by 4.
+ thisOffset = static_cast<uint32_t>(dtohs(entry->offset)) * 4u;
+ } else {
+ entryId = entryIndex;
+ thisOffset = dtohl(eindex[entryIndex]);
+ if (thisOffset == ResTable_type::NO_ENTRY) {
+ continue;
+ }
+ }
- if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) {
- printf(" (PUBLIC)");
- }
- printf("\n");
-
- if (inclValues) {
- if (valuePtr != NULL) {
- printf(" ");
- print_value(typeConfigs->package, value);
- } else if (bagPtr != NULL) {
- const int N = dtohl(bagPtr->count);
- const uint8_t* baseMapPtr = (const uint8_t*)ent;
- size_t mapOffset = esize;
- const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
- const uint32_t parent = dtohl(bagPtr->parent.ident);
- uint32_t resolvedParent = parent;
- if (Res_GETPACKAGE(resolvedParent) + 1 == 0) {
- status_t err = pg->dynamicRefTable.lookupResourceId(&resolvedParent);
- if (err != NO_ERROR) {
- resolvedParent = 0;
- }
+ uint32_t resID = (0xff000000 & ((packageId)<<24))
+ | (0x00ff0000 & ((typeIndex+1)<<16))
+ | (0x0000ffff & (entryId));
+ if (packageId == 0) {
+ pg->dynamicRefTable.lookupResourceId(&resID);
+ }
+ resource_name resName;
+ if (this->getResourceName(resID, true, &resName)) {
+ String8 type8;
+ String8 name8;
+ if (resName.type8 != NULL) {
+ type8 = String8(resName.type8, resName.typeLen);
+ } else {
+ type8 = String8(resName.type, resName.typeLen);
}
- printf(" Parent=0x%08x(Resolved=0x%08x), Count=%d\n",
- parent, resolvedParent, N);
- for (int i=0; i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) {
- printf(" #%i (Key=0x%08x): ",
- i, dtohl(mapPtr->name.ident));
- value.copyFrom_dtoh(mapPtr->value);
+ if (resName.name8 != NULL) {
+ name8 = String8(resName.name8, resName.nameLen);
+ } else {
+ name8 = String8(resName.name, resName.nameLen);
+ }
+ printf(" resource 0x%08x %s:%s/%s: ", resID,
+ CHAR16_TO_CSTR(resName.package, resName.packageLen),
+ type8.string(), name8.string());
+ } else {
+ printf(" INVALID RESOURCE 0x%08x: ", resID);
+ }
+ if ((thisOffset&0x3) != 0) {
+ printf("NON-INTEGER OFFSET: 0x%x\n", thisOffset);
+ continue;
+ }
+ if ((thisOffset+sizeof(ResTable_entry)) > typeSize) {
+ printf("OFFSET OUT OF BOUNDS: 0x%x+0x%x (size is 0x%x)\n",
+ entriesStart, thisOffset, typeSize);
+ continue;
+ }
+
+ const ResTable_entry* ent = (const ResTable_entry*)
+ (((const uint8_t*)type) + entriesStart + thisOffset);
+ if (((entriesStart + thisOffset)&0x3) != 0) {
+ printf("NON-INTEGER ResTable_entry OFFSET: 0x%x\n",
+ (entriesStart + thisOffset));
+ continue;
+ }
+
+ uintptr_t esize = dtohs(ent->size);
+ if ((esize&0x3) != 0) {
+ printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void *)esize);
+ continue;
+ }
+ if ((thisOffset+esize) > typeSize) {
+ printf("ResTable_entry OUT OF BOUNDS: 0x%x+0x%x+%p (size is 0x%x)\n",
+ entriesStart, thisOffset, (void *)esize, typeSize);
+ continue;
+ }
+
+ const Res_value* valuePtr = NULL;
+ const ResTable_map_entry* bagPtr = NULL;
+ Res_value value;
+ if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) {
+ printf("<bag>");
+ bagPtr = (const ResTable_map_entry*)ent;
+ } else {
+ valuePtr = (const Res_value*)
+ (((const uint8_t*)ent) + esize);
+ value.copyFrom_dtoh(*valuePtr);
+ printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)",
+ (int)value.dataType, (int)value.data,
+ (int)value.size, (int)value.res0);
+ }
+
+ if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) {
+ printf(" (PUBLIC)");
+ }
+ printf("\n");
+
+ if (inclValues) {
+ if (valuePtr != NULL) {
+ printf(" ");
print_value(typeConfigs->package, value);
- const size_t size = dtohs(mapPtr->value.size);
- mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value);
- mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
+ } else if (bagPtr != NULL) {
+ const int N = dtohl(bagPtr->count);
+ const uint8_t* baseMapPtr = (const uint8_t*)ent;
+ size_t mapOffset = esize;
+ const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
+ const uint32_t parent = dtohl(bagPtr->parent.ident);
+ uint32_t resolvedParent = parent;
+ if (Res_GETPACKAGE(resolvedParent) + 1 == 0) {
+ status_t err =
+ pg->dynamicRefTable.lookupResourceId(&resolvedParent);
+ if (err != NO_ERROR) {
+ resolvedParent = 0;
+ }
+ }
+ printf(" Parent=0x%08x(Resolved=0x%08x), Count=%d\n",
+ parent, resolvedParent, N);
+ for (int i=0;
+ i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) {
+ printf(" #%i (Key=0x%08x): ",
+ i, dtohl(mapPtr->name.ident));
+ value.copyFrom_dtoh(mapPtr->value);
+ print_value(typeConfigs->package, value);
+ const size_t size = dtohs(mapPtr->value.size);
+ mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value);
+ mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
+ }
}
}
}
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 79bb534a99be..270527d551a9 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -297,6 +297,26 @@ private:
GLuint mTexture = 0;
};
+static bool isFP16Supported(const sk_sp<GrContext>& grContext) {
+ static std::once_flag sOnceFlag;
+ static bool supported = false;
+
+ std::call_once(sOnceFlag, [](const sk_sp<GrContext>& grContext) {
+ if (!grContext->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig)) {
+ supported = false;
+ return;
+ }
+
+ sp<GraphicBuffer> buffer = new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_FP16,
+ GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
+ GraphicBuffer::USAGE_SW_READ_NEVER, "tempFp16Buffer");
+ status_t error = buffer->initCheck();
+ supported = !error;
+ }, grContext);
+
+ return supported;
+}
+
sk_sp<Bitmap> SkiaOpenGLPipeline::allocateHardwareBitmap(renderthread::RenderThread& renderThread,
SkBitmap& skBitmap) {
renderThread.eglManager().initialize();
@@ -318,7 +338,7 @@ sk_sp<Bitmap> SkiaOpenGLPipeline::allocateHardwareBitmap(renderthread::RenderThr
type = GL_UNSIGNED_BYTE;
break;
case kRGBA_F16_SkColorType:
- isSupported = grContext->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig);
+ isSupported = isFP16Supported(grContext);
if (isSupported) {
type = GL_HALF_FLOAT;
pixelFormat = PIXEL_FORMAT_RGBA_FP16;
diff --git a/libs/incident/proto/android/section.proto b/libs/incident/proto/android/section.proto
index e8280ed690f6..45f3c91850e7 100644
--- a/libs/incident/proto/android/section.proto
+++ b/libs/incident/proto/android/section.proto
@@ -52,6 +52,9 @@ message SectionFlags {
optional SectionType type = 1 [default = SECTION_NONE];
optional string args = 2;
optional bool device_specific = 3 [default = false];
+ // If true, then the section will only be generated for userdebug and eng
+ // builds.
+ optional bool userdebug_and_eng_only = 4 [default = false];
}
extend google.protobuf.FieldOptions {
diff --git a/media/java/android/media/AudioFocusRequest.java b/media/java/android/media/AudioFocusRequest.java
index fe89b89dcfec..b9731d111aa3 100644
--- a/media/java/android/media/AudioFocusRequest.java
+++ b/media/java/android/media/AudioFocusRequest.java
@@ -414,7 +414,9 @@ public final class AudioFocusRequest {
* with {@link AudioManager#abandonAudioFocusRequest(AudioFocusRequest)}.
* Note that only focus changes (gains and losses) affecting the focus owner are reported,
* not gains and losses of other focus requesters in the system.<br>
- * Notifications are delivered on the main {@link Looper}.
+ * Notifications are delivered on the {@link Looper} associated with the one of
+ * the creation of the {@link AudioManager} used to request focus
+ * (see {@link AudioManager#requestAudioFocus(AudioFocusRequest)}).
* @param listener the listener receiving the focus change notifications.
* @return this {@code Builder} instance.
* @throws NullPointerException thrown when a null focus listener is used.
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 68463e11bbfe..566db94690cb 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4558,8 +4558,7 @@ public class AudioManager {
/**
* The list of {@link AudioDeviceCallback} objects to receive add/remove notifications.
*/
- private ArrayMap<AudioDeviceCallback, NativeEventHandlerDelegate>
- mDeviceCallbacks =
+ private final ArrayMap<AudioDeviceCallback, NativeEventHandlerDelegate> mDeviceCallbacks =
new ArrayMap<AudioDeviceCallback, NativeEventHandlerDelegate>();
/**
@@ -4859,22 +4858,21 @@ public class AudioManager {
calcListDeltas(mPreviousPorts, current_ports, GET_DEVICES_ALL);
AudioDeviceInfo[] removed_devices =
calcListDeltas(current_ports, mPreviousPorts, GET_DEVICES_ALL);
-
if (added_devices.length != 0 || removed_devices.length != 0) {
synchronized (mDeviceCallbacks) {
for (int i = 0; i < mDeviceCallbacks.size(); i++) {
handler = mDeviceCallbacks.valueAt(i).getHandler();
if (handler != null) {
- if (added_devices.length != 0) {
- handler.sendMessage(Message.obtain(handler,
- MSG_DEVICES_DEVICES_ADDED,
- added_devices));
- }
if (removed_devices.length != 0) {
handler.sendMessage(Message.obtain(handler,
MSG_DEVICES_DEVICES_REMOVED,
removed_devices));
}
+ if (added_devices.length != 0) {
+ handler.sendMessage(Message.obtain(handler,
+ MSG_DEVICES_DEVICES_ADDED,
+ added_devices));
+ }
}
}
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index d53f1a02b514..88edd12ab2f4 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -208,6 +208,9 @@
<!-- to read and change hvac values in a car -->
<uses-permission android:name="android.car.permission.CONTROL_CAR_CLIMATE" />
+ <!-- Permission necessary to change car audio volume through CarAudioManager -->
+ <uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" />
+
<application
android:name=".SystemUIApplication"
android:persistent="true"
@@ -419,7 +422,8 @@
android:theme="@style/Theme.AlertDialogHost"
android:finishOnCloseSystemDialogs="true"
android:launchMode="singleTop"
- android:excludeFromRecents="true" />
+ android:excludeFromRecents="true"
+ android:visibleToInstantApps="true"/>
<!-- started from PipUI -->
<activity
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
index 9fdb00eebe10..11bd98f549ca 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
@@ -49,6 +49,7 @@
<EditText android:id="@+id/passwordEntry"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:contentDescription="@string/keyguard_accessibility_password"
android:gravity="center_horizontal"
android:singleLine="true"
android:textStyle="normal"
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index ed63089f22dd..513d8488a0d4 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -120,6 +120,9 @@
<!-- Accessibility description of the PIN password view. [CHAR_LIMIT=none] -->
<string name="keyguard_accessibility_pin_area">PIN area</string>
+ <!-- Accessibility description of the normal password view. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_password">Device password</string>
+
<!-- Accessibility description of the SIM PIN password view. [CHAR_LIMIT=none] -->
<string name="keyguard_accessibility_sim_pin_area">SIM PIN area</string>
<!-- Accessibility description of the SIM PUK password view. [CHAR_LIMIT=none] -->
diff --git a/packages/SystemUI/res/drawable/car_ic_notification_2.xml b/packages/SystemUI/res/drawable/car_ic_notification_2.xml
deleted file mode 100644
index c74ae153cf09..000000000000
--- a/packages/SystemUI/res/drawable/car_ic_notification_2.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2018 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="32dp"
- android:height="38dp"
- android:viewportWidth="32"
- android:viewportHeight="38" >
- <group
- android:translateX="-6"
- android:translateY="-3">
- <path
- android:pathData="M26.6195649,6.98115478 C31.5083629,8.85235985 34.9817444,13.6069337 34.9817444,19.1767606 L34.9817444,27.9542254 L38,27.9542254 L38,34.2161972 L6,34.2161972 L6,27.9542254 L9.01825558,27.9542254 L9.01825558,19.1767606 C9.01825558,13.6069337 12.4916371,8.85235985 17.3804351,6.98115478 C17.723241,4.726863 19.6609451,3 22,3 C24.3390549,3 26.276759,4.726863 26.6195649,6.98115478 Z M17.326572,36.3035211 L26.673428,36.3035211 C26.673428,38.8973148 24.581063,41 22,41 C19.418937,41 17.326572,38.8973148 17.326572,36.3035211 Z"
- android:strokeColor="#00000000"
- android:fillType="evenOdd"
- android:fillColor="@color/car_grey_50" />
- </group>
-</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index f6c2eebbab66..c70e8296340e 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -53,7 +53,11 @@
android:layout_gravity="center"
android:soundEffectsEnabled="false" />
- <include layout="@layout/volume_dnd_icon"/>
+ <include layout="@layout/volume_dnd_icon"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="@dimen/volume_dialog_stream_padding"
+ android:layout_marginTop="6dp"/>
</FrameLayout>
<LinearLayout
@@ -71,7 +75,9 @@
android:layout_height="wrap_content"
android:minWidth="@dimen/volume_dialog_panel_width"
android:gravity="center"
- android:orientation="horizontal" >
+ android:orientation="horizontal"
+ android:paddingRight="@dimen/volume_dialog_stream_padding"
+ android:paddingLeft="@dimen/volume_dialog_stream_padding">
<!-- volume rows added and removed here! :-) -->
</LinearLayout>
<FrameLayout
diff --git a/packages/SystemUI/res/layout/volume_dnd_icon.xml b/packages/SystemUI/res/layout/volume_dnd_icon.xml
index ac235b7090c5..037d143fa69e 100644
--- a/packages/SystemUI/res/layout/volume_dnd_icon.xml
+++ b/packages/SystemUI/res/layout/volume_dnd_icon.xml
@@ -17,14 +17,13 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dnd_icon"
android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ android:layout_height="wrap_content"
+ android:layout_marginTop="6dp">
<ImageView
android:layout_width="14dp"
android:layout_height="14dp"
- android:layout_marginTop="6dp"
- android:layout_marginRight="6dp"
android:layout_gravity="right|top"
android:src="@drawable/ic_dnd"
android:tint="?android:attr/textColorTertiary"/>
-</FrameLayout>
+</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/values/attrs_car.xml b/packages/SystemUI/res/values/attrs_car.xml
index 99d242591e92..41e078699647 100644
--- a/packages/SystemUI/res/values/attrs_car.xml
+++ b/packages/SystemUI/res/values/attrs_car.xml
@@ -63,4 +63,32 @@
<attr name="hvacPropertyId" format="integer"/>
<attr name="hvacTempFormat" format="string"/>
</declare-styleable>
+
+ <declare-styleable name="carVolumeItems"/>
+ <declare-styleable name="carVolumeItems_item">
+ <!-- Align with AudioAttributes.USAGE_* -->
+ <attr name="usage">
+ <enum name="unknown" value="0"/>
+ <enum name="media" value="1"/>
+ <enum name="voice_communication" value="2"/>
+ <enum name="voice_communication_signalling" value="3"/>
+ <enum name="alarm" value="4"/>
+ <enum name="notification" value="5"/>
+ <enum name="notification_ringtone" value="6"/>
+ <enum name="notification_communication_request" value="7"/>
+ <enum name="notification_communication_instant" value="8"/>
+ <enum name="notification_communication_delayed" value="9"/>
+ <enum name="notification_event" value="10"/>
+ <enum name="assistance_accessibility" value="11"/>
+ <enum name="assistance_navigation_guidance" value="12"/>
+ <enum name="assistance_sonification" value="13"/>
+ <enum name="game" value="14"/>
+ <!-- hidden, do not use -->
+ <!-- enum name="virtual_source" value="15"/ -->
+ <enum name="assistant" value="16"/>
+ </attr>
+
+ <!-- Icon resource ids to render on UI -->
+ <attr name="icon" format="reference"/>
+ </declare-styleable>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ad74725aa19d..f3fe29794bac 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -296,6 +296,8 @@
<dimen name="volume_dialog_panel_transparent_padding">20dp</dimen>
+ <dimen name="volume_dialog_stream_padding">8dp</dimen>
+
<!-- the amount the volume panel should be offset at the end from the view next to it (or
the screen edge, in portrait-->
<dimen name="volume_dialog_base_margin">8dp</dimen>
diff --git a/packages/SystemUI/res/xml/car_volume_items.xml b/packages/SystemUI/res/xml/car_volume_items.xml
new file mode 100644
index 000000000000..742dfdda73c8
--- /dev/null
+++ b/packages/SystemUI/res/xml/car_volume_items.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ *
+ * Copyright 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+-->
+
+<!--
+ Defines all possible items on car volume settings UI, keyed by usage.
+
+ This enables the CarSettings UI to associate VolumeGroups surfaced by
+ CarAudioManager.getVolumeGroupCount with renderable assets (ie: title, icon)
+ for presentation.
+
+ Order matters in this configuration. If one volume group contains multiple
+ audio usages, the first one appears in this file would be picked to be
+ presented on UI.
+
+ When overriding this configuration, please consult also the
+ car_volume_groups.xml, which is read by car audio service.
+-->
+<carVolumeItems xmlns:car="http://schemas.android.com/apk/res-auto">
+ <item car:usage="unknown"
+ car:icon="@drawable/car_ic_music"/>
+ <item car:usage="media"
+ car:icon="@drawable/car_ic_music"/>
+ <item car:usage="voice_communication"
+ car:icon="@*android:drawable/ic_audio_ring_notif"/>
+ <item car:usage="voice_communication_signalling"
+ car:icon="@*android:drawable/ic_audio_ring_notif"/>
+ <item car:usage="alarm"
+ car:icon="@*android:drawable/ic_audio_alarm"/>
+ <item car:usage="notification"
+ car:icon="@drawable/car_ic_notification"/>
+ <item car:usage="notification_ringtone"
+ car:icon="@*android:drawable/ic_audio_ring_notif"/>
+ <item car:usage="notification_communication_request"
+ car:icon="@drawable/car_ic_notification"/>
+ <item car:usage="notification_communication_instant"
+ car:icon="@drawable/car_ic_notification"/>
+ <item car:usage="notification_communication_delayed"
+ car:icon="@drawable/car_ic_notification"/>
+ <item car:usage="notification_event"
+ car:icon="@drawable/car_ic_notification"/>
+ <item car:usage="assistance_accessibility"
+ car:icon="@drawable/car_ic_notification"/>
+ <item car:usage="assistance_navigation_guidance"
+ car:icon="@drawable/car_ic_navigation"/>
+ <item car:usage="assistance_sonification"
+ car:icon="@drawable/car_ic_notification"/>
+ <item car:usage="game"
+ car:icon="@drawable/car_ic_music"/>
+ <item car:usage="assistant"
+ car:icon="@drawable/car_ic_music"/>
+</carVolumeItems>
+
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index 30a17a129e41..f066e3401752 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -57,6 +57,7 @@ import java.util.function.Consumer;
import androidx.slice.Slice;
import androidx.slice.SliceItem;
+import androidx.slice.SliceManager;
import androidx.slice.core.SliceQuery;
import androidx.slice.widget.ListContent;
import androidx.slice.widget.RowContent;
@@ -390,6 +391,11 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe
}
}
+ public void refresh() {
+ Slice slice = SliceManager.getInstance(getContext()).bindSlice(mKeyguardSliceUri);
+ onChanged(slice);
+ }
+
public static class Row extends LinearLayout {
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 454528e4f5f6..f6b5d69b99d3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -287,7 +287,12 @@ public class KeyguardStatusView extends GridLayout implements
}
}
- public void refreshTime() {
+ public void dozeTimeTick() {
+ refreshTime();
+ mKeyguardSlice.refresh();
+ }
+
+ private void refreshTime() {
mClockView.refresh();
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 8a8bafaf9853..38a90cfd96a5 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -207,8 +207,12 @@ public class AssistManager implements ConfigurationChangedReceiver {
boolean structureEnabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1, UserHandle.USER_CURRENT) != 0;
- final Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
- .getAssistIntent(structureEnabled);
+ final SearchManager searchManager =
+ (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
+ if (searchManager == null) {
+ return;
+ }
+ final Intent intent = searchManager.getAssistIntent(structureEnabled);
if (intent == null) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index f867b342c8de..c5e66f9f4f99 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -79,7 +79,6 @@ public class KeyguardSliceProvider extends SliceProvider implements
private DateFormat mDateFormat;
private String mLastText;
private boolean mRegistered;
- private boolean mRegisteredEveryMinute;
private String mNextAlarm;
private NextAlarmController mNextAlarmController;
protected AlarmManager mAlarmManager;
@@ -175,7 +174,7 @@ public class KeyguardSliceProvider extends SliceProvider implements
mZenModeController = new ZenModeControllerImpl(getContext(), mHandler);
mZenModeController.addCallback(this);
mDatePattern = getContext().getString(R.string.system_ui_aod_date_pattern);
- registerClockUpdate(false /* everyMinute */);
+ registerClockUpdate();
updateClock();
return true;
}
@@ -214,22 +213,13 @@ public class KeyguardSliceProvider extends SliceProvider implements
/**
* Registers a broadcast receiver for clock updates, include date, time zone and manually
* changing the date/time via the settings app.
- *
- * @param everyMinute {@code true} if you also want updates every minute.
*/
- protected void registerClockUpdate(boolean everyMinute) {
+ private void registerClockUpdate() {
if (mRegistered) {
- if (mRegisteredEveryMinute == everyMinute) {
- return;
- } else {
- unregisterClockUpdate();
- }
+ return;
}
IntentFilter filter = new IntentFilter();
- if (everyMinute) {
- filter.addAction(Intent.ACTION_TIME_TICK);
- }
filter.addAction(Intent.ACTION_DATE_CHANGED);
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
@@ -237,15 +227,6 @@ public class KeyguardSliceProvider extends SliceProvider implements
getContext().registerReceiver(mIntentReceiver, filter, null /* permission*/,
null /* scheduler */);
mRegistered = true;
- mRegisteredEveryMinute = everyMinute;
- }
-
- protected void unregisterClockUpdate() {
- if (!mRegistered) {
- return;
- }
- getContext().unregisterReceiver(mIntentReceiver);
- mRegistered = false;
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index c6bb17c2d133..065e9cc2d1d3 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -198,7 +198,13 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
showWarningNotification();
mShowing = SHOWING_WARNING;
} else if (mShowAutoSaverSuggestion) {
- showAutoSaverSuggestionNotification();
+ // Once we showed the notification, don't show it again until it goes SHOWING_NOTHING.
+ // This shouldn't be needed, because we have a delete intent on this notification
+ // so when it's dismissed we should notice it and clear mShowAutoSaverSuggestion,
+ // However we double check here just in case the dismiss intent broadcast is delayed.
+ if (mShowing != SHOWING_AUTO_SAVER_SUGGESTION) {
+ showAutoSaverSuggestionNotification();
+ }
mShowing = SHOWING_AUTO_SAVER_SUGGESTION;
} else {
mNoMan.cancelAsUser(TAG_BATTERY, SystemMessage.NOTE_BAD_CHARGER, UserHandle.ALL);
@@ -303,7 +309,9 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
private PendingIntent pendingBroadcast(String action) {
return PendingIntent.getBroadcastAsUser(mContext, 0,
- new Intent(action).setPackage(mContext.getPackageName()), 0, UserHandle.CURRENT);
+ new Intent(action).setPackage(mContext.getPackageName())
+ .setFlags(Intent.FLAG_RECEIVER_FOREGROUND),
+ 0, UserHandle.CURRENT);
}
private static Intent settings(String action) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 227f2d2be0a9..8a0d7e38f875 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -129,13 +129,6 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
private final int mImageWidth;
private final int mImageHeight;
- // WORKAROUND: We want the same notification across screenshots that we update so that we don't
- // spam a user's notification drawer. However, we only show the ticker for the saving state
- // and if the ticker text is the same as the previous notification, then it will not show. So
- // for now, we just add and remove a space from the ticker text to trigger the animation when
- // necessary.
- private static boolean mTickerAddSpace;
-
SaveImageInBackgroundTask(Context context, SaveImageInBackgroundData data,
NotificationManager nManager) {
Resources r = context.getResources();
@@ -176,8 +169,6 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
Bitmap icon = generateAdjustedHwBitmap(data.image, iconSize, iconSize, matrix, paint,
overlayColor);
- // Show the intermediate notification
- mTickerAddSpace = !mTickerAddSpace;
mNotificationManager = nManager;
final long now = System.currentTimeMillis();
@@ -199,8 +190,6 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
mNotificationBuilder = new Notification.Builder(context,
NotificationChannels.SCREENSHOTS_HEADSUP)
- .setTicker(r.getString(R.string.screenshot_saving_ticker)
- + (mTickerAddSpace ? " " : ""))
.setContentTitle(r.getString(R.string.screenshot_saving_title))
.setSmallIcon(R.drawable.stat_notify_image)
.setWhen(now)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 27fa48aee216..8adf4bc3d5c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -2599,6 +2599,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
protected boolean disallowSingleClick(MotionEvent event) {
+ if (areGutsExposed()) {
+ return false;
+ }
float x = event.getX();
float y = event.getY();
NotificationHeaderView header = getVisibleNotificationHeader();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridGroupManager.java
index 3ed8cce21ac5..3975ec2d2776 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridGroupManager.java
@@ -37,10 +37,10 @@ public class HybridGroupManager {
private final NotificationDozeHelper mDozer;
private final ViewGroup mParent;
- private final float mOverflowNumberSizeDark;
- private final int mOverflowNumberPaddingDark;
- private final float mOverflowNumberSize;
- private final int mOverflowNumberPadding;
+ private float mOverflowNumberSizeDark;
+ private int mOverflowNumberPaddingDark;
+ private float mOverflowNumberSize;
+ private int mOverflowNumberPadding;
private int mOverflowNumberColor;
private int mOverflowNumberColorDark;
@@ -50,7 +50,10 @@ public class HybridGroupManager {
mContext = ctx;
mParent = parent;
mDozer = new NotificationDozeHelper();
+ initDimens();
+ }
+ public void initDimens() {
Resources res = mContext.getResources();
mOverflowNumberSize = res.getDimensionPixelSize(
R.dimen.group_overflow_number_size);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 9fcb0905263c..ee8325003870 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -212,7 +212,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
}
public void hideClock(boolean animate) {
- animateHide(mClockView, animate);
+ animateHiddenState(mClockView, View.GONE, animate);
}
public void showClock(boolean animate) {
@@ -240,21 +240,29 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
}
/**
- * Hides a view.
+ * Animate a view to INVISIBLE or GONE
*/
- private void animateHide(final View v, boolean animate) {
+ private void animateHiddenState(final View v, int state, boolean animate) {
v.animate().cancel();
if (!animate) {
v.setAlpha(0f);
- v.setVisibility(View.INVISIBLE);
+ v.setVisibility(state);
return;
}
+
v.animate()
.alpha(0f)
.setDuration(160)
.setStartDelay(0)
.setInterpolator(Interpolators.ALPHA_OUT)
- .withEndAction(() -> v.setVisibility(View.INVISIBLE));
+ .withEndAction(() -> v.setVisibility(state));
+ }
+
+ /**
+ * Hides a view.
+ */
+ private void animateHide(final View v, boolean animate) {
+ animateHiddenState(v, View.INVISIBLE, animate);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index b650944403cb..b475b64eb201 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -2267,7 +2267,7 @@ public class NotificationPanelView extends PanelView implements
}
public void onScreenTurningOn() {
- mKeyguardStatusView.refreshTime();
+ mKeyguardStatusView.dozeTimeTick();
}
@Override
@@ -2690,7 +2690,7 @@ public class NotificationPanelView extends PanelView implements
}
public void dozeTimeTick() {
- mKeyguardStatusView.refreshTime();
+ mKeyguardStatusView.dozeTimeTick();
mKeyguardBottomArea.dozeTimeTick();
if (mDarkAmount > 0) {
positionClockAndNotifications();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index e5ab712e9bdd..81152971645c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -121,8 +121,8 @@ public class NotificationChildrenContainer extends ViewGroup {
public NotificationChildrenContainer(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- initDimens();
mHybridGroupManager = new HybridGroupManager(getContext(), this);
+ initDimens();
setClipChildren(false);
}
@@ -148,6 +148,7 @@ public class NotificationChildrenContainer extends ViewGroup {
mTranslationForHeader = res.getDimensionPixelSize(
com.android.internal.R.dimen.notification_content_margin)
- mNotificationHeaderMargin;
+ mHybridGroupManager.initDimens();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 7370c4c1c0d1..5deebffc4778 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -3331,8 +3331,16 @@ public class NotificationStackScrollLayout extends ViewGroup
private void generateTopPaddingEvent() {
if (mTopPaddingNeedsAnimation) {
- mAnimationEvents.add(
- new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED));
+ AnimationEvent event;
+ if (mAmbientState.isDark()) {
+ event = new AnimationEvent(null /* view */,
+ AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED,
+ KeyguardSliceView.DEFAULT_ANIM_DURATION);
+ } else {
+ event = new AnimationEvent(null /* view */,
+ AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED);
+ }
+ mAnimationEvents.add(event);
}
mTopPaddingNeedsAnimation = false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
index f1a7183e1602..8034345c37a7 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -19,23 +19,34 @@ package com.android.systemui.volume;
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorSet;
+import android.annotation.DrawableRes;
import android.annotation.Nullable;
import android.app.Dialog;
import android.app.KeyguardManager;
+import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.media.CarAudioManager;
+import android.car.media.ICarVolumeCallback;
+import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.ServiceConnection;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
-import android.media.AudioManager;
-import android.media.AudioSystem;
+import android.media.AudioAttributes;
import android.os.Debug;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.util.AttributeSet;
import android.util.Log;
-import android.util.SparseBooleanArray;
+import android.util.SparseArray;
+import android.util.Xml;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.MotionEvent;
@@ -53,626 +64,533 @@ import androidx.car.widget.ListItemProvider.ListProvider;
import androidx.car.widget.PagedListView;
import androidx.car.widget.SeekbarListItem;
+import java.util.Iterator;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.VolumeDialog;
-import com.android.systemui.plugins.VolumeDialogController;
-import com.android.systemui.plugins.VolumeDialogController.State;
-import com.android.systemui.plugins.VolumeDialogController.StreamState;
/**
* Car version of the volume dialog.
*
- * A client of VolumeDialogControllerImpl and its state model.
- *
* Methods ending in "H" must be called on the (ui) handler.
*/
public class CarVolumeDialogImpl implements VolumeDialog {
- private static final String TAG = Util.logTag(CarVolumeDialogImpl.class);
-
- private static final long USER_ATTEMPT_GRACE_PERIOD = 1000;
-
- private final Context mContext;
- private final H mHandler = new H();
- private final VolumeDialogController mController;
- private final AudioManager mAudioManager;
-
- private Window mWindow;
- private CustomDialog mDialog;
- private PagedListView mListView;
- private ListItemAdapter mPagedListAdapter;
- private final List<ListItem> mVolumeLineItems = new ArrayList<>();
- private final List<VolumeRow> mRows = new ArrayList<>();
- private ConfigurableTexts mConfigurableTexts;
- private final SparseBooleanArray mDynamic = new SparseBooleanArray();
- private final KeyguardManager mKeyguard;
- private final Object mSafetyWarningLock = new Object();
-
- private boolean mShowing;
-
- private boolean mAutomute = VolumePrefs.DEFAULT_ENABLE_AUTOMUTE;
- private boolean mSilentMode = VolumePrefs.DEFAULT_ENABLE_SILENT_MODE;
- private State mState;
- private SafetyWarningDialog mSafetyWarning;
- private boolean mHovering = false;
- private boolean mExpanded;
-
- public CarVolumeDialogImpl(Context context) {
- mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
- mController = Dependency.get(VolumeDialogController.class);
- mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ private static final String TAG = Util.logTag(CarVolumeDialogImpl.class);
+
+ private static final String XML_TAG_VOLUME_ITEMS = "carVolumeItems";
+ private static final String XML_TAG_VOLUME_ITEM = "item";
+ private static final int HOVERING_TIMEOUT = 16000;
+ private static final int NORMAL_TIMEOUT = 3000;
+ private static final int LISTVIEW_ANIMATION_DURATION_IN_MILLIS = 250;
+ private static final int DISMISS_DELAY_IN_MILLIS = 50;
+ private static final int ARROW_FADE_IN_START_DELAY_IN_MILLIS = 100;
+
+ private final Context mContext;
+ private final H mHandler = new H();
+
+ private Window mWindow;
+ private CustomDialog mDialog;
+ private PagedListView mListView;
+ private ListItemAdapter mPagedListAdapter;
+ // All the volume items.
+ private final SparseArray<VolumeItem> mVolumeItems = new SparseArray<>();
+ // Available volume items in car audio manager.
+ private final List<VolumeItem> mAvailableVolumeItems = new ArrayList<>();
+ // Volume items in the PagedListView.
+ private final List<ListItem> mVolumeLineItems = new ArrayList<>();
+ private final KeyguardManager mKeyguard;
+
+ private Car mCar;
+ private CarAudioManager mCarAudioManager;
+
+ private boolean mHovering;
+ private boolean mShowing;
+ private boolean mExpanded;
+
+ public CarVolumeDialogImpl(Context context) {
+ mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
+ mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+ mCar = Car.createCar(mContext, mServiceConnection);
+ }
+
+ public void init(int windowType, Callback callback) {
+ initDialog();
+
+ mCar.connect();
+ }
+
+ @Override
+ public void destroy() {
+ mHandler.removeCallbacksAndMessages(null);
+
+ cleanupAudioManager();
+ // unregisterVolumeCallback is not being called when disconnect car, so we manually cleanup
+ // audio manager beforehand.
+ mCar.disconnect();
+ }
+
+ private void initDialog() {
+ loadAudioUsageItems();
+ mVolumeLineItems.clear();
+ mDialog = new CustomDialog(mContext);
+
+ mHovering = false;
+ mShowing = false;
+ mExpanded = false;
+ mWindow = mDialog.getWindow();
+ mWindow.requestFeature(Window.FEATURE_NO_TITLE);
+ mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+ mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
+ | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
+ mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
+ mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
+ mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast);
+ final WindowManager.LayoutParams lp = mWindow.getAttributes();
+ lp.format = PixelFormat.TRANSLUCENT;
+ lp.setTitle(VolumeDialogImpl.class.getSimpleName());
+ lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
+ lp.windowAnimations = -1;
+ mWindow.setAttributes(lp);
+ mWindow.setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+
+ mDialog.setCanceledOnTouchOutside(true);
+ mDialog.setContentView(R.layout.car_volume_dialog);
+ mDialog.setOnShowListener(dialog -> {
+ mListView.setTranslationY(-mListView.getHeight());
+ mListView.setAlpha(0);
+ mListView.animate()
+ .alpha(1)
+ .translationY(0)
+ .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS)
+ .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
+ .start();
+ });
+ mListView = (PagedListView) mWindow.findViewById(R.id.volume_list);
+ mListView.setOnHoverListener((v, event) -> {
+ int action = event.getActionMasked();
+ mHovering = (action == MotionEvent.ACTION_HOVER_ENTER)
+ || (action == MotionEvent.ACTION_HOVER_MOVE);
+ rescheduleTimeoutH();
+ return true;
+ });
+
+ mPagedListAdapter = new ListItemAdapter(mContext, new ListProvider(mVolumeLineItems),
+ BackgroundStyle.PANEL);
+ mListView.setAdapter(mPagedListAdapter);
+ mListView.setMaxPages(PagedListView.UNLIMITED_PAGES);
+ }
+
+ public void show(int reason) {
+ mHandler.obtainMessage(H.SHOW, reason, 0).sendToTarget();
+ }
+
+ public void dismiss(int reason) {
+ mHandler.obtainMessage(H.DISMISS, reason, 0).sendToTarget();
+ }
+
+ private void showH(int reason) {
+ if (D.BUG) {
+ Log.d(TAG, "showH r=" + Events.DISMISS_REASONS[reason]);
}
- public void init(int windowType, Callback callback) {
- initDialog();
-
- mController.addCallback(mControllerCallbackH, mHandler);
- mController.getState();
+ mHandler.removeMessages(H.SHOW);
+ mHandler.removeMessages(H.DISMISS);
+ rescheduleTimeoutH();
+ // Refresh the data set before showing.
+ mPagedListAdapter.notifyDataSetChanged();
+ if (mShowing) {
+ return;
}
+ mShowing = true;
- @Override
- public void destroy() {
- mController.removeCallback(mControllerCallbackH);
- mHandler.removeCallbacksAndMessages(null);
- }
+ mDialog.show();
+ Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
+ }
- private void initDialog() {
- mRows.clear();
- mVolumeLineItems.clear();
- mDialog = new CustomDialog(mContext);
+ protected void rescheduleTimeoutH() {
+ mHandler.removeMessages(H.DISMISS);
+ final int timeout = computeTimeoutH();
+ mHandler.sendMessageDelayed(mHandler
+ .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT, 0), timeout);
- mConfigurableTexts = new ConfigurableTexts(mContext);
- mHovering = false;
- mShowing = false;
- mExpanded = false;
- mWindow = mDialog.getWindow();
- mWindow.requestFeature(Window.FEATURE_NO_TITLE);
- mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
- mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
- | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
- mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
- mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
- mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast);
- final WindowManager.LayoutParams lp = mWindow.getAttributes();
- lp.format = PixelFormat.TRANSLUCENT;
- lp.setTitle(VolumeDialogImpl.class.getSimpleName());
- lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
- lp.windowAnimations = -1;
- mWindow.setAttributes(lp);
- mWindow.setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-
- mDialog.setCanceledOnTouchOutside(true);
- mDialog.setContentView(R.layout.car_volume_dialog);
- mDialog.setOnShowListener(dialog -> {
- mListView.setTranslationY(-mListView.getHeight());
- mListView.setAlpha(0);
- mListView.animate()
- .alpha(1)
- .translationY(0)
- .setDuration(300)
- .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
- .start();
- });
- mListView = (PagedListView) mWindow.findViewById(R.id.volume_list);
- mListView.setOnHoverListener((v, event) -> {
- int action = event.getActionMasked();
- mHovering = (action == MotionEvent.ACTION_HOVER_ENTER)
- || (action == MotionEvent.ACTION_HOVER_MOVE);
- rescheduleTimeoutH();
- return true;
- });
-
- addSeekbarListItem(addVolumeRow(AudioManager.STREAM_MUSIC, R.drawable.car_ic_music,
- R.drawable.car_ic_keyboard_arrow_down, true, true),
- new ExpandIconListener());
- // We map AudioManager.STREAM_RING to a navigation icon for demo.
- addVolumeRow(AudioManager.STREAM_RING, R.drawable.car_ic_navigation, 0,
- true, false);
- addVolumeRow(AudioManager.STREAM_NOTIFICATION, R.drawable.car_ic_notification_2, 0,
- true, false);
-
- mPagedListAdapter = new ListItemAdapter(mContext, new ListProvider(mVolumeLineItems),
- BackgroundStyle.PANEL);
- mListView.setAdapter(mPagedListAdapter);
- mListView.setMaxPages(PagedListView.UNLIMITED_PAGES);
+ if (D.BUG) {
+ Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller());
}
+ }
- public void setStreamImportant(int stream, boolean important) {
- mHandler.obtainMessage(H.SET_STREAM_IMPORTANT, stream, important ? 1 : 0).sendToTarget();
- }
+ private int computeTimeoutH() {
+ return mHovering ? HOVERING_TIMEOUT : NORMAL_TIMEOUT;
+ }
- public void setAutomute(boolean automute) {
- if (mAutomute == automute) {
- return;
- }
- mAutomute = automute;
- mHandler.sendEmptyMessage(H.RECHECK_ALL);
+ protected void dismissH(int reason) {
+ if (D.BUG) {
+ Log.d(TAG, "dismissH r=" + Events.DISMISS_REASONS[reason]);
}
- public void setSilentMode(boolean silentMode) {
- if (mSilentMode == silentMode) {
- return;
- }
- mSilentMode = silentMode;
- mHandler.sendEmptyMessage(H.RECHECK_ALL);
+ mHandler.removeMessages(H.DISMISS);
+ mHandler.removeMessages(H.SHOW);
+ if (!mShowing) {
+ return;
}
- private VolumeRow addVolumeRow(int stream, int primaryActionIcon, int supplementalIcon,
- boolean important, boolean defaultStream) {
- VolumeRow volumeRow = new VolumeRow();
- volumeRow.stream = stream;
- volumeRow.primaryActionIcon = primaryActionIcon;
- volumeRow.supplementalIcon = supplementalIcon;
- volumeRow.important = important;
- volumeRow.defaultStream = defaultStream;
- volumeRow.listItem = null;
- mRows.add(volumeRow);
- return volumeRow;
+ mListView.animate().cancel();
+ mShowing = false;
+
+ mListView.setTranslationY(0);
+ mListView.setAlpha(1);
+ mListView.animate()
+ .alpha(0)
+ .translationY(-mListView.getHeight())
+ .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS)
+ .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
+ .withEndAction(() -> mHandler.postDelayed(() -> {
+ if (D.BUG) {
+ Log.d(TAG, "mDialog.dismiss()");
+ }
+ mDialog.dismiss();
+ }, DISMISS_DELAY_IN_MILLIS))
+ .start();
+
+ Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason);
+ }
+
+ public void dump(PrintWriter writer) {
+ writer.println(VolumeDialogImpl.class.getSimpleName() + " state:");
+ writer.print(" mShowing: "); writer.println(mShowing);
+ }
+
+ private void loadAudioUsageItems() {
+ try (XmlResourceParser parser = mContext.getResources().getXml(R.xml.car_volume_items)) {
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+ int type;
+ // Traverse to the first start tag
+ while ((type=parser.next()) != XmlResourceParser.END_DOCUMENT
+ && type != XmlResourceParser.START_TAG) {
+ }
+
+ if (!XML_TAG_VOLUME_ITEMS.equals(parser.getName())) {
+ throw new RuntimeException("Meta-data does not start with carVolumeItems tag");
+ }
+ int outerDepth = parser.getDepth();
+ int rank = 0;
+ while ((type=parser.next()) != XmlResourceParser.END_DOCUMENT
+ && (type != XmlResourceParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlResourceParser.END_TAG) {
+ continue;
+ }
+ if (XML_TAG_VOLUME_ITEM.equals(parser.getName())) {
+ TypedArray item = mContext.getResources().obtainAttributes(
+ attrs, R.styleable.carVolumeItems_item);
+ int usage = item.getInt(R.styleable.carVolumeItems_item_usage, -1);
+ if (usage >= 0) {
+ VolumeItem volumeItem = new VolumeItem();
+ volumeItem.usage = usage;
+ volumeItem.rank = rank;
+ volumeItem.icon = item.getResourceId(R.styleable.carVolumeItems_item_icon, 0);
+ mVolumeItems.put(usage, volumeItem);
+ rank++;
+ }
+ item.recycle();
+ }
+ }
+ } catch (XmlPullParserException | IOException e) {
+ Log.e(TAG, "Error parsing volume groups configuration", e);
}
-
- private SeekbarListItem addSeekbarListItem(
- VolumeRow volumeRow, @Nullable View.OnClickListener supplementalIconOnClickListener) {
- int volumeMax = mAudioManager.getStreamMaxVolume(volumeRow.stream);
- int currentVolume = mAudioManager.getStreamVolume(volumeRow.stream);
- SeekbarListItem listItem =
- new SeekbarListItem(mContext, volumeMax, currentVolume,
- new VolumeSeekBarChangeListener(volumeRow), null);
- Drawable primaryIcon = mContext.getResources().getDrawable(volumeRow.primaryActionIcon);
- listItem.setPrimaryActionIcon(primaryIcon);
- if (volumeRow.supplementalIcon != 0) {
- Drawable supplementalIcon = mContext.getResources()
- .getDrawable(volumeRow.supplementalIcon);
- listItem.setSupplementalIcon(supplementalIcon, true,
- supplementalIconOnClickListener);
- } else {
- listItem.setSupplementalEmptyIcon(true);
- }
-
- mVolumeLineItems.add(listItem);
- volumeRow.listItem = listItem;
-
- return listItem;
+ }
+
+ private VolumeItem getVolumeItemForUsages(int[] usages) {
+ int rank = Integer.MAX_VALUE;
+ VolumeItem result = null;
+ for (int usage : usages) {
+ VolumeItem volumeItem = mVolumeItems.get(usage);
+ if (volumeItem.rank < rank) {
+ rank = volumeItem.rank;
+ result = volumeItem;
+ }
}
-
- private static int getImpliedLevel(SeekBar seekBar, int progress) {
- final int m = seekBar.getMax();
- final int n = m / 100 - 1;
- final int level = progress == 0 ? 0
- : progress == m ? (m / 100) : (1 + (int)((progress / (float) m) * n));
- return level;
+ return result;
+ }
+
+ private static int getSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
+ try {
+ return carAudioManager.getGroupVolume(volumeGroupId);
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
}
-
- public void show(int reason) {
- mHandler.obtainMessage(H.SHOW, reason, 0).sendToTarget();
+ return 0;
+ }
+
+ private static int getMaxSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
+ try {
+ return carAudioManager.getGroupMaxVolume(volumeGroupId);
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
}
-
- public void dismiss(int reason) {
- mHandler.obtainMessage(H.DISMISS, reason, 0).sendToTarget();
+ return 0;
+ }
+
+ private SeekbarListItem addSeekbarListItem(VolumeItem volumeItem, int volumeGroupId,
+ int supplementalIconId, @Nullable View.OnClickListener supplementalIconOnClickListener) {
+ SeekbarListItem listItem = new SeekbarListItem(mContext);
+ listItem.setMax(getMaxSeekbarValue(mCarAudioManager, volumeGroupId));
+ int progress = getSeekbarValue(mCarAudioManager, volumeGroupId);
+ listItem.setProgress(progress);
+ listItem.setOnSeekBarChangeListener(
+ new CarVolumeDialogImpl.VolumeSeekBarChangeListener(volumeGroupId, mCarAudioManager));
+ listItem.setPrimaryActionIcon(mContext.getResources().getDrawable(volumeItem.icon));
+ if (supplementalIconId != 0) {
+ Drawable supplementalIcon = mContext.getResources().getDrawable(supplementalIconId);
+ listItem.setSupplementalIcon(supplementalIcon, true,
+ supplementalIconOnClickListener);
+ } else {
+ listItem.setSupplementalEmptyIcon(true);
}
- private void showH(int reason) {
- if (D.BUG) Log.d(TAG, "showH r=" + Events.DISMISS_REASONS[reason]);
- mHandler.removeMessages(H.SHOW);
- mHandler.removeMessages(H.DISMISS);
- rescheduleTimeoutH();
- if (mShowing) return;
- mShowing = true;
-
- mDialog.show();
- Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
- mController.notifyVisible(true);
+ mVolumeLineItems.add(listItem);
+ volumeItem.listItem = listItem;
+ volumeItem.progress = progress;
+ return listItem;
+ }
+
+ private VolumeItem findVolumeItem(SeekbarListItem targetItem) {
+ for (int i = 0; i < mVolumeItems.size(); ++i) {
+ VolumeItem volumeItem = mVolumeItems.valueAt(i);
+ if (volumeItem.listItem == targetItem) {
+ return volumeItem;
+ }
}
-
- protected void rescheduleTimeoutH() {
- mHandler.removeMessages(H.DISMISS);
- final int timeout = computeTimeoutH();
- mHandler.sendMessageDelayed(mHandler
- .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT, 0), timeout);
- if (D.BUG) Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller());
- mController.userActivity();
+ return null;
+ }
+
+ private void cleanupAudioManager() {
+ try {
+ mCarAudioManager.unregisterVolumeCallback(mVolumeChangeCallback.asBinder());
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
}
+ mVolumeLineItems.clear();
+ mCarAudioManager = null;
+ }
- private int computeTimeoutH() {
- if (mHovering) return 16000;
- if (mSafetyWarning != null) return 5000;
- return 3000;
- }
+ private final class H extends Handler {
+ private static final int SHOW = 1;
+ private static final int DISMISS = 2;
- protected void dismissH(int reason) {
- mHandler.removeMessages(H.DISMISS);
- mHandler.removeMessages(H.SHOW);
- if (!mShowing) return;
- mListView.animate().cancel();
- mShowing = false;
-
- mListView.setTranslationY(0);
- mListView.setAlpha(1);
- mListView.animate()
- .alpha(0)
- .translationY(-mListView.getHeight())
- .setDuration(250)
- .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
- .withEndAction(() -> mHandler.postDelayed(() -> {
- if (D.BUG) Log.d(TAG, "mDialog.dismiss()");
- mDialog.dismiss();
- }, 50))
- .start();
-
- Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason);
- mController.notifyVisible(false);
- synchronized (mSafetyWarningLock) {
- if (mSafetyWarning != null) {
- if (D.BUG) Log.d(TAG, "SafetyWarning dismissed");
- mSafetyWarning.dismiss();
- }
- }
+ public H() {
+ super(Looper.getMainLooper());
}
- private void trimObsoleteH() {
- int initialVolumeItemSize = mVolumeLineItems.size();
- for (int i = mRows.size() - 1; i >= 0; i--) {
- final VolumeRow row = mRows.get(i);
- if (row.ss == null || !row.ss.dynamic) continue;
- if (!mDynamic.get(row.stream)) {
- mRows.remove(i);
- mVolumeLineItems.remove(row.listItem);
- }
- }
-
- if (mVolumeLineItems.size() != initialVolumeItemSize) {
- mPagedListAdapter.notifyDataSetChanged();
- }
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case SHOW:
+ showH(msg.arg1);
+ break;
+ case DISMISS:
+ dismissH(msg.arg1);
+ break;
+ default:
+ }
}
+ }
- private void onStateChangedH(State state) {
- mState = state;
- mDynamic.clear();
- // add any new dynamic rows
- for (int i = 0; i < state.states.size(); i++) {
- final int stream = state.states.keyAt(i);
- final StreamState ss = state.states.valueAt(i);
- if (!ss.dynamic) {
- continue;
- }
- mDynamic.put(stream, true);
- if (findRow(stream) == null) {
- VolumeRow row = addVolumeRow(stream, R.drawable.ic_volume_remote,
- 0, true,false);
- if (mExpanded) {
- addSeekbarListItem(row, null);
- }
- }
- }
-
- for (VolumeRow row : mRows) {
- updateVolumeRowH(row);
- }
+ private final class CustomDialog extends Dialog implements DialogInterface {
+ public CustomDialog(Context context) {
+ super(context, com.android.systemui.R.style.qs_theme);
}
- private void updateVolumeRowH(VolumeRow row) {
- if (D.BUG) Log.d(TAG, "updateVolumeRowH s=" + row.stream);
- if (mState == null) {
- return;
- }
- final StreamState ss = mState.states.get(row.stream);
- if (ss == null) {
- return;
- }
- row.ss = ss;
- if (ss.level == row.requestedLevel) {
- row.requestedLevel = -1;
- }
- // TODO: update Seekbar progress and change the mute icon if necessary.
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ rescheduleTimeoutH();
+ return super.dispatchTouchEvent(ev);
}
- private VolumeRow findRow(int stream) {
- for (VolumeRow row : mRows) {
- if (row.stream == stream) {
- return row;
- }
- }
- return null;
+ @Override
+ protected void onStart() {
+ super.setCanceledOnTouchOutside(true);
+ super.onStart();
}
- private VolumeRow findRow(SeekbarListItem targetItem) {
- for (VolumeRow row : mRows) {
- if (row.listItem == targetItem) {
- return row;
- }
- }
- return null;
+ @Override
+ protected void onStop() {
+ super.onStop();
}
- public void dump(PrintWriter writer) {
- writer.println(VolumeDialogImpl.class.getSimpleName() + " state:");
- writer.print(" mShowing: "); writer.println(mShowing);
- writer.print(" mDynamic: "); writer.println(mDynamic);
- writer.print(" mAutomute: "); writer.println(mAutomute);
- writer.print(" mSilentMode: "); writer.println(mSilentMode);
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (isShowing()) {
+ if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ dismissH(Events.DISMISS_REASON_TOUCH_OUTSIDE);
+ return true;
+ }
+ }
+ return false;
}
+ }
- private void recheckH(VolumeRow row) {
- if (row == null) {
- if (D.BUG) Log.d(TAG, "recheckH ALL");
- trimObsoleteH();
- for (VolumeRow r : mRows) {
- updateVolumeRowH(r);
- }
- } else {
- if (D.BUG) Log.d(TAG, "recheckH " + row.stream);
- updateVolumeRowH(row);
- }
+ private final class ExpandIconListener implements View.OnClickListener {
+ @Override
+ public void onClick(final View v) {
+ mExpanded = !mExpanded;
+ Animator inAnimator;
+ if (mExpanded) {
+ for (int groupId = 0; groupId < mAvailableVolumeItems.size(); ++groupId) {
+ // Adding the items which are not coming from the default item.
+ VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
+ if (volumeItem.defaultItem) {
+ // Set progress here due to the progress of seekbar may not be updated.
+ volumeItem.listItem.setProgress(volumeItem.progress);
+ } else {
+ addSeekbarListItem(volumeItem, groupId, 0, null);
+ }
+ }
+ inAnimator = AnimatorInflater.loadAnimator(
+ mContext, R.anim.car_arrow_fade_in_rotate_up);
+ } else {
+ // Only keeping the default stream if it is not expended.
+ Iterator itr = mVolumeLineItems.iterator();
+ while (itr.hasNext()) {
+ SeekbarListItem seekbarListItem = (SeekbarListItem) itr.next();
+ VolumeItem volumeItem = findVolumeItem(seekbarListItem);
+ if (!volumeItem.defaultItem) {
+ itr.remove();
+ } else {
+ // Set progress here due to the progress of seekbar may not be updated.
+ seekbarListItem.setProgress(volumeItem.progress);
+ }
+ }
+ inAnimator = AnimatorInflater.loadAnimator(
+ mContext, R.anim.car_arrow_fade_in_rotate_down);
+ }
+
+ Animator outAnimator = AnimatorInflater.loadAnimator(
+ mContext, R.anim.car_arrow_fade_out);
+ inAnimator.setStartDelay(ARROW_FADE_IN_START_DELAY_IN_MILLIS);
+ AnimatorSet animators = new AnimatorSet();
+ animators.playTogether(outAnimator, inAnimator);
+ animators.setTarget(v);
+ animators.start();
+ mPagedListAdapter.notifyDataSetChanged();
}
+ }
- private void setStreamImportantH(int stream, boolean important) {
- for (VolumeRow row : mRows) {
- if (row.stream == stream) {
- row.important = important;
- return;
- }
- }
- }
+ private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener {
+ private final int mVolumeGroupId;
+ private final CarAudioManager mCarAudioManager;
- private void showSafetyWarningH(int flags) {
- if ((flags & (AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_SHOW_UI_WARNINGS)) != 0
- || mShowing) {
- synchronized (mSafetyWarningLock) {
- if (mSafetyWarning != null) {
- return;
- }
- mSafetyWarning = new SafetyWarningDialog(mContext, mController.getAudioManager()) {
- @Override
- protected void cleanUp() {
- synchronized (mSafetyWarningLock) {
- mSafetyWarning = null;
- }
- recheckH(null);
- }
- };
- mSafetyWarning.show();
- }
- recheckH(null);
- }
- rescheduleTimeoutH();
+ private VolumeSeekBarChangeListener(int volumeGroupId, CarAudioManager carAudioManager) {
+ mVolumeGroupId = volumeGroupId;
+ mCarAudioManager = carAudioManager;
}
- private final VolumeDialogController.Callbacks mControllerCallbackH
- = new VolumeDialogController.Callbacks() {
- @Override
- public void onShowRequested(int reason) {
- showH(reason);
- }
-
- @Override
- public void onDismissRequested(int reason) {
- dismissH(reason);
- }
-
- @Override
- public void onScreenOff() {
- dismissH(Events.DISMISS_REASON_SCREEN_OFF);
- }
-
- @Override
- public void onStateChanged(State state) {
- onStateChangedH(state);
- }
-
- @Override
- public void onLayoutDirectionChanged(int layoutDirection) {
- mListView.setLayoutDirection(layoutDirection);
- }
-
- @Override
- public void onConfigurationChanged() {
- mDialog.dismiss();
- initDialog();
- mConfigurableTexts.update();
- }
-
- @Override
- public void onShowVibrateHint() {
- if (mSilentMode) {
- mController.setRingerMode(AudioManager.RINGER_MODE_SILENT, false);
- }
- }
-
- @Override
- public void onShowSilentHint() {
- if (mSilentMode) {
- mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false);
- }
- }
-
- @Override
- public void onShowSafetyWarning(int flags) {
- showSafetyWarningH(flags);
- }
-
- @Override
- public void onAccessibilityModeChanged(Boolean showA11yStream) {
- }
- };
-
- private final class H extends Handler {
- private static final int SHOW = 1;
- private static final int DISMISS = 2;
- private static final int RECHECK = 3;
- private static final int RECHECK_ALL = 4;
- private static final int SET_STREAM_IMPORTANT = 5;
- private static final int RESCHEDULE_TIMEOUT = 6;
- private static final int STATE_CHANGED = 7;
-
- public H() {
- super(Looper.getMainLooper());
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case SHOW: showH(msg.arg1); break;
- case DISMISS: dismissH(msg.arg1); break;
- case RECHECK: recheckH((VolumeRow) msg.obj); break;
- case RECHECK_ALL: recheckH(null); break;
- case SET_STREAM_IMPORTANT: setStreamImportantH(msg.arg1, msg.arg2 != 0); break;
- case RESCHEDULE_TIMEOUT: rescheduleTimeoutH(); break;
- case STATE_CHANGED: onStateChangedH(mState); break;
- }
- }
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (!fromUser) {
+ // For instance, if this event is originated from AudioService,
+ // we can ignore it as it has already been handled and doesn't need to be
+ // sent back down again.
+ return;
+ }
+ try {
+ if (mCarAudioManager == null) {
+ Log.w(TAG, "Ignoring volume change event because the car isn't connected");
+ return;
+ }
+ mAvailableVolumeItems.get(mVolumeGroupId).progress = progress;
+ mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0);
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
+ }
}
- private final class CustomDialog extends Dialog implements DialogInterface {
- public CustomDialog(Context context) {
- super(context, com.android.systemui.R.style.qs_theme);
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- rescheduleTimeoutH();
- return super.dispatchTouchEvent(ev);
- }
-
- @Override
- protected void onStart() {
- super.setCanceledOnTouchOutside(true);
- super.onStart();
- }
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {}
- @Override
- protected void onStop() {
- super.onStop();
- mHandler.sendEmptyMessage(H.RECHECK_ALL);
- }
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {}
+ }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (isShowing()) {
- if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
- dismissH(Events.DISMISS_REASON_TOUCH_OUTSIDE);
- return true;
- }
- }
- return false;
- }
+ private final ICarVolumeCallback mVolumeChangeCallback = new ICarVolumeCallback.Stub() {
+ @Override
+ public void onGroupVolumeChanged(int groupId) {
+ VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
+ int value = getSeekbarValue(mCarAudioManager, groupId);
+ // Do not update the progress if it is the same as before. When car audio manager sets its
+ // group volume caused by the seekbar progress changed, it also triggers this callback.
+ // Updating the seekbar at the same time could block the continuous seeking.
+ if (value != volumeItem.progress) {
+ volumeItem.listItem.setProgress(value);
+ volumeItem.progress = value;
+ show(Events.SHOW_REASON_VOLUME_CHANGED);
+ }
}
- private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener {
- private final VolumeRow mRow;
-
- private VolumeSeekBarChangeListener(VolumeRow volumeRow) {
- mRow = volumeRow;
- }
-
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- if (mRow.ss == null) {
- return;
- }
- if (D.BUG) {
- Log.d(TAG, AudioSystem.streamToString(mRow.stream)
- + " onProgressChanged " + progress + " fromUser=" + fromUser);
- }
- if (!fromUser) {
- return;
- }
- if (mRow.ss.levelMin > 0) {
- final int minProgress = mRow.ss.levelMin;
- if (progress < minProgress) {
- seekBar.setProgress(minProgress);
- progress = minProgress;
- }
- }
- final int userLevel = getImpliedLevel(seekBar, progress);
- if (mRow.ss.level != userLevel || mRow.ss.muted && userLevel > 0) {
- if (mRow.requestedLevel != userLevel) {
- mController.setStreamVolume(mRow.stream, userLevel);
- mRow.requestedLevel = userLevel;
- Events.writeEvent(mContext, Events.EVENT_TOUCH_LEVEL_CHANGED, mRow.stream,
- userLevel);
- }
- }
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- if (D.BUG) {
- Log.d(TAG, "onStartTrackingTouch"+ " " + mRow.stream);
- }
- mController.setActiveStream(mRow.stream);
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- if (D.BUG) {
- Log.d(TAG, "onStopTrackingTouch"+ " " + mRow.stream);
- }
- final int userLevel = getImpliedLevel(seekBar, seekBar.getProgress());
- Events.writeEvent(mContext, Events.EVENT_TOUCH_LEVEL_DONE, mRow.stream, userLevel);
- if (mRow.ss.level != userLevel) {
- mHandler.sendMessageDelayed(mHandler.obtainMessage(H.RECHECK, mRow),
- USER_ATTEMPT_GRACE_PERIOD);
- }
- }
+ @Override
+ public void onMasterMuteChanged() {
+ // ignored
}
+ };
- private final class ExpandIconListener implements View.OnClickListener {
- @Override
- public void onClick(final View v) {
- mExpanded = !mExpanded;
- Animator inAnimator;
- if (mExpanded) {
- for (VolumeRow row : mRows) {
- // Adding the items which are not coming from default stream.
- if (!row.defaultStream) {
- addSeekbarListItem(row, null);
- }
- }
- inAnimator = AnimatorInflater.loadAnimator(
- mContext, R.anim.car_arrow_fade_in_rotate_up);
- } else {
- // Only keeping the default stream if it is not expended.
- Iterator itr = mVolumeLineItems.iterator();
- while (itr.hasNext()) {
- SeekbarListItem item = (SeekbarListItem) itr.next();
- VolumeRow row = findRow(item);
- if (!row.defaultStream) {
- itr.remove();
- }
- }
- inAnimator = AnimatorInflater.loadAnimator(
- mContext, R.anim.car_arrow_fade_in_rotate_down);
- }
-
- Animator outAnimator = AnimatorInflater.loadAnimator(
- mContext, R.anim.car_arrow_fade_out);
- inAnimator.setStartDelay(100);
- AnimatorSet animators = new AnimatorSet();
- animators.playTogether(outAnimator, inAnimator);
- animators.setTarget(v);
- animators.start();
- mPagedListAdapter.notifyDataSetChanged();
- }
+ private final ServiceConnection mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ try {
+ mExpanded = false;
+ mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
+ int volumeGroupCount = mCarAudioManager.getVolumeGroupCount();
+ // Populates volume slider items from volume groups to UI.
+ for (int groupId = 0; groupId < volumeGroupCount; groupId++) {
+ VolumeItem volumeItem = getVolumeItemForUsages(
+ mCarAudioManager.getUsagesForVolumeGroupId(groupId));
+ mAvailableVolumeItems.add(volumeItem);
+ // The first one is the default item.
+ if (groupId == 0) {
+ volumeItem.defaultItem = true;
+ addSeekbarListItem(volumeItem, groupId, R.drawable.car_ic_keyboard_arrow_down,
+ new ExpandIconListener());
+ }
+ }
+
+ // If list is already initiated, update its content.
+ if (mPagedListAdapter != null) {
+ mPagedListAdapter.notifyDataSetChanged();
+ }
+ mCarAudioManager.registerVolumeCallback(mVolumeChangeCallback.asBinder());
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
+ }
}
- private static class VolumeRow {
- private int stream;
- private StreamState ss;
- private boolean important;
- private boolean defaultStream;
- private int primaryActionIcon;
- private int supplementalIcon;
- private SeekbarListItem listItem;
- private int requestedLevel = -1; // pending user-requested level via progress changed
+ /**
+ * This does not get called when service is properly disconnected.
+ * So we need to also handle cleanups in destroy().
+ */
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ cleanupAudioManager();
}
-} \ No newline at end of file
+ };
+
+ /**
+ * Wrapper class which contains information of each volume group.
+ */
+ private static class VolumeItem {
+ private @AudioAttributes.AttributeUsage int usage;
+ private int rank;
+ private boolean defaultItem = false;
+ private @DrawableRes int icon;
+ private SeekbarListItem listItem;
+ private int progress;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index 6e5b5484cabe..dd552646955a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -103,11 +103,7 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna
}
private VolumeDialog createCarDefault() {
- CarVolumeDialogImpl impl = new CarVolumeDialogImpl(mContext);
- impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
- impl.setAutomute(true);
- impl.setSilentMode(false);
- return impl;
+ return new CarVolumeDialogImpl(mContext);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index b0e40fc88054..6b322c72936c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -695,26 +695,6 @@ public class VolumeDialogImpl implements VolumeDialog {
private void enableVolumeRowViewsH(VolumeRow row, boolean enable) {
boolean showDndIcon = !enable;
row.dndIcon.setVisibility(showDndIcon ? VISIBLE : GONE);
-
- if (showDndIcon && getNumVisibleRows() == 1) {
- row.dndIcon.setLayoutParams(new FrameLayout.LayoutParams(
- mContext.getResources().getDimensionPixelSize(
- R.dimen.volume_dialog_panel_width),
- FrameLayout.LayoutParams.WRAP_CONTENT));
- } else if (row.view.getVisibility() == VISIBLE) {
- row.dndIcon.setLayoutParams(new FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT));
- }
- }
-
- private int getNumVisibleRows() {
- int count = 0;
- for (int i = 0; i < mRows.size(); i++) {
- if (mRows.get(i).view.getVisibility() == VISIBLE) {
- count++;
- }
- }
- return count;
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
index 210764adb4a6..17a4fbc4c1f6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
@@ -90,6 +90,17 @@ public class KeyguardSliceViewTest extends SysuiTestCase {
}
@Test
+ public void refresh_replacesSliceContentAndNotifiesListener() {
+ AtomicBoolean notified = new AtomicBoolean();
+ mKeyguardSliceView.setContentChangeListener((hasHeader)-> {
+ notified.set(true);
+ });
+ mKeyguardSliceView.refresh();
+ Assert.assertTrue("Listener should be notified about slice changes.",
+ notified.get());
+ }
+
+ @Test
public void getTextColor_whiteTextWhenAOD() {
// Set text color to red since the default is white and test would always pass
mKeyguardSliceView.setTextColor(Color.RED);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
new file mode 100644
index 000000000000..1d8de2fbbcca
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.keyguard;
+
+import static org.mockito.Mockito.verify;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.LayoutInflater;
+import android.widget.TextClock;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+
+@SmallTest
+@RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidTestingRunner.class)
+public class KeyguardStatusViewTest extends SysuiTestCase {
+
+ @Mock
+ KeyguardSliceView mKeyguardSlice;
+ @Mock
+ TextClock mClockView;
+ @InjectMocks
+ KeyguardStatusView mKeyguardStatusView;
+
+ @Before
+ public void setUp() {
+ LayoutInflater layoutInflater = LayoutInflater.from(getContext());
+ mKeyguardStatusView =
+ (KeyguardStatusView) layoutInflater.inflate(R.layout.keyguard_status_view, null);
+ org.mockito.MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void dozeTimeTick_updatesSlice() {
+ mKeyguardStatusView.dozeTimeTick();
+ verify(mKeyguardSlice).refresh();
+ }
+
+ @Test
+ public void dozeTimeTick_updatesClock() {
+ mKeyguardStatusView.dozeTimeTick();
+ verify(mClockView).refresh();
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index a45e6900ece4..46e2bfbb1431 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -85,13 +85,6 @@ public class KeyguardSliceProviderTest extends SysuiTestCase {
}
@Test
- public void unregisterClockUpdate() {
- mProvider.unregisterClockUpdate();
- Assert.assertFalse("Clock updates should have been unregistered.",
- mProvider.isRegistered());
- }
-
- @Test
public void returnsValidSlice() {
Slice slice = mProvider.onBindSlice(mProvider.getUri());
SliceItem text = SliceQuery.find(slice, android.app.slice.SliceItem.FORMAT_TEXT,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index 9e8fa2243ccc..231cdf5e8e7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -121,7 +121,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
fragment.initNotificationIconArea(mMockNotificiationAreaController);
fragment.disable(StatusBarManager.DISABLE_CLOCK, 0, false);
- assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.clock).getVisibility());
+ assertEquals(View.GONE, mFragment.getView().findViewById(R.id.clock).getVisibility());
fragment.disable(0, 0, false);
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index d61f228adb69..1541231befc6 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -3967,6 +3967,7 @@ message MetricsEvent {
// Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
// NOTE: starting on OS P, it also added the following field:
// Tag FIELD_FLAGS - Flags used to start the session
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_SESSION_STARTED = 906;
// An autofill request was processed by a service
@@ -3980,6 +3981,7 @@ message MetricsEvent {
// Type TYPE_CLOSE: Service returned a null response.
// Tag FIELD_AUTOFILL_NUM_FIELD_CLASSIFICATION_IDS: if service requested field classification,
// number of entries field ids in the request.
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_REQUEST = 907;
// Tag of a field for a package of an autofill service
@@ -3998,6 +4000,7 @@ message MetricsEvent {
// Tag FIELD_AUTOFILL_NUM_DATASETS: The number of datasets shown
// NOTE: starting on OS P, it also added the following field:
// Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_FILL_UI = 910;
// Tag of a field for the length of the filter text
@@ -4005,12 +4008,17 @@ message MetricsEvent {
// An autofill authentication succeeded
// Package: Package of app that was autofilled
+ // Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
+ // NOTE: starting on OS P, it also added the following field:
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_AUTHENTICATED = 912;
// An activity was autofilled and all values could be applied
// Package: Package of app that is autofilled
// Tag FIELD_AUTOFILL_NUM_VALUES: Number of values that were suggested to be autofilled
// Tag FIELD_AUTOFILL_NUM_VIEWS_FILLED: Number of views that could be filled
+ // NOTE: starting on OS P, it also added the following field:
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_DATASET_APPLIED = 913;
// Tag of a field for the number values to be filled in
@@ -4027,6 +4035,7 @@ message MetricsEvent {
// Tag FIELD_AUTOFILL_NUM_IDS: The number of ids that are saved
// NOTE: starting on OS P, it also added the following field:
// Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_SAVE_UI = 916;
// Tag of a field for the number of saveable ids
@@ -4038,10 +4047,14 @@ message MetricsEvent {
// Type TYPE_FAILURE: The request failed
// Package: Package of app that was autofilled
// Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
+ // NOTE: starting on OS P, it also added the following field:
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_DATA_SAVE_REQUEST = 918;
// An auto-fill session was finished
// Package: Package of app that was autofilled
+ // NOTE: starting on OS P, it also added the following field:
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_SESSION_FINISHED = 919;
// meta-event: a reader has checkpointed the log here.
@@ -4167,6 +4180,8 @@ message MetricsEvent {
// Package: Real package of the app being autofilled
// Tag FIELD_AUTOFILL_SERVICE: Package of the autofill service that processed the request
// TAG FIELD_AUTOFILL_FORGED_COMPONENT_NAME: Component name being forged
+ // NOTE: starting on OS P, it also added the following field:
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_FORGED_COMPONENT_ATTEMPT = 948;
// FIELD - The component that an app tried tro forged.
@@ -4624,6 +4639,8 @@ message MetricsEvent {
// OS: O MR
// Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
// Tag FIELD_AUTOFILL_PREVIOUS_LENGTH: the previous length of the value
+ // NOTE: starting on OS P, it also added the following field:
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_VALUE_RESET = 1124;
// Tag of AUTOFILL_VALUE_RESET
@@ -4634,18 +4651,24 @@ message MetricsEvent {
// Package: Package of app that was autofilled
// OS: O MR
// Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
+ // NOTE: starting on OS P, it also added the following field:
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_DATASET_AUTHENTICATED = 1126;
// An autofill service provided an invalid dataset authentication
// Package: Package of app that was autofilled
// OS: O MR
// Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
+ // NOTE: starting on OS P, it also added the following field:
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_INVALID_DATASET_AUTHENTICATION = 1127;
// An autofill service provided an invalid authentication extra
// Package: Package of app that was autofilled
// OS: O MR
// Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
+ // NOTE: starting on OS P, it also added the following field:
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_INVALID_AUTHENTICATION = 1128;
// An autofill service used a custom description (using RemoteViews) in the autofill save UI
@@ -4653,6 +4676,8 @@ message MetricsEvent {
// OS: O MR
// Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
// Tag FIELD_AUTOFILL_SAVE_TYPE: Type of save object passed by the service
+ // NOTE: starting on OS P, it also added the following field:
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_SAVE_CUSTOM_DESCRIPTION = 1129;
// FIELD - Type of save object passed by the service when the Save UI is shown
@@ -4664,6 +4689,8 @@ message MetricsEvent {
// OS: O MR
// Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
// Tag FIELD_AUTOFILL_SAVE_TYPE: Type of save object passed by the service
+ // NOTE: starting on OS P, it also added the following field:
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_SAVE_CUSTOM_SUBTITLE = 1131;
// User tapped a link in the custom description of the autofill save UI provided by an autofill service
@@ -4674,6 +4701,8 @@ message MetricsEvent {
// Type TYPE_FAILURE: The link could not launc an activity
// Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
// Tag FIELD_AUTOFILL_SAVE_TYPE: Type of save object passed by the service
+ // NOTE: starting on OS P, it also added the following field:
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_SAVE_LINK_TAPPED = 1132;
// Result of the validation on save when an autofill service provided a validator
@@ -4684,6 +4713,8 @@ message MetricsEvent {
// Type TYPE_DISMISS: The validation failed
// Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
// Tag FIELD_AUTOFILL_SAVE_TYPE: Type of save object passed by the service
+ // NOTE: starting on OS P, it also added the following field:
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_SAVE_VALIDATION = 1133;
// Result of an operation in the autofill save UI after the user tapped a link in the custom description
@@ -4693,6 +4724,8 @@ message MetricsEvent {
// Type TYPE_OPEN: The autofill save UI was restored
// Type TYPE_DISMISS: The autofill save UI was destroyed
// Type TYPE_FAILURE: An invalid opperation was reported by the app's AutofillManager
+ // NOTE: starting on OS P, it also added the following field:
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_PENDING_SAVE_UI_OPERATION = 1134;
// Autofill service called API that disables itself
@@ -4705,6 +4738,8 @@ message MetricsEvent {
// Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
// Package: Package of the autofill service
// OS: O MR
+ // NOTE: starting on OS P, it also added the following field:
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_UI_LATENCY = 1136;
// Action: the snooze leave-behind was shown after the user clicked the snooze icon
@@ -4872,12 +4907,14 @@ message MetricsEvent {
// Package: Package of app that is autofilled
// OS: P
// Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_EXPLICIT_SAVE_TRIGGER_DEFINITION = 1228;
// The autofill context was commited when the user clicked a view explicitly marked by the
// service as committing it
// Package: Package of app that is autofilled
// OS: P
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_SAVE_EXPLICITLY_TRIGGERED = 1229;
// OPEN: Settings > Network & Internet > Mobile network > Wi-Fi calling
@@ -4890,6 +4927,7 @@ message MetricsEvent {
// OS: P
// Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
// Tag FIELD_AUTOFILL_DURATION: duration (in ms) that autofill will be disabled
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_SERVICE_DISABLED_APP = 1231;
// An autofill service asked to disable autofill for a given activity.
@@ -4898,6 +4936,7 @@ message MetricsEvent {
// Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
// Tag FIELD_CLASS_NAME: Class name of the activity that is being disabled for autofill
// Tag FIELD_AUTOFILL_DURATION: duration (in ms) that autofill will be disabled
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_SERVICE_DISABLED_ACTIVITY = 1232;
// ACTION: Stop an app and turn on background check
@@ -5109,6 +5148,7 @@ message MetricsEvent {
// OS: P
// Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
// Tag FIELD_AUTOFILL_MATCH_SCORE: Average score of the matches, in the range of 0 to 100
+ // Type FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
AUTOFILL_FIELD_CLASSIFICATION_MATCHES = 1273;
// Tag used to report autofill field classification scores
@@ -5775,6 +5815,9 @@ message MetricsEvent {
// OS: P
ACTION_STORAGE_MIGRATE_LATER = 1413;
+ // Tag used to report whether an activity is being autofilled on compatibility mode.
+ FIELD_AUTOFILL_COMPAT_MODE = 1414;
+
// ---- End P Constants, all P constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index e582daa29335..6ff9539ff5e6 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -504,7 +504,7 @@ final class AutofillManagerServiceImpl {
sessionId = sRandom.nextInt();
} while (sessionId == NO_SESSION || mSessions.indexOfKey(sessionId) >= 0);
- assertCallerLocked(componentName);
+ assertCallerLocked(componentName, compatMode);
final Session newSession = new Session(this, mUi, mContext, mHandler, mUserId, mLock,
sessionId, uid, activityToken, appCallbackToken, hasCallback, mUiLatencyHistory,
@@ -518,7 +518,7 @@ final class AutofillManagerServiceImpl {
/**
* Asserts the component is owned by the caller.
*/
- private void assertCallerLocked(@NonNull ComponentName componentName) {
+ private void assertCallerLocked(@NonNull ComponentName componentName, boolean compatMode) {
final String packageName = componentName.getPackageName();
final PackageManager pm = mContext.getPackageManager();
final int callingUid = Binder.getCallingUid();
@@ -536,7 +536,7 @@ final class AutofillManagerServiceImpl {
+ ") passed component (" + componentName + ") owned by UID " + packageUid);
mMetricsLogger.write(
Helper.newLogMaker(MetricsEvent.AUTOFILL_FORGED_COMPONENT_ATTEMPT,
- callingPackage, getServicePackageName())
+ callingPackage, getServicePackageName(), compatMode)
.addTaggedData(MetricsEvent.FIELD_AUTOFILL_FORGED_COMPONENT_NAME,
componentName == null ? "null" : componentName.flattenToShortString()));
@@ -774,10 +774,10 @@ final class AutofillManagerServiceImpl {
@Nullable ArrayList<String> changedDatasetIds,
@Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
@Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
- @NonNull String appPackageName) {
+ @NonNull String appPackageName, boolean compatMode) {
logContextCommittedLocked(sessionId, clientState, selectedDatasets, ignoredDatasets,
changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
- manuallyFilledDatasetIds, null, null, appPackageName);
+ manuallyFilledDatasetIds, null, null, appPackageName, compatMode);
}
@GuardedBy("mLock")
@@ -790,7 +790,7 @@ final class AutofillManagerServiceImpl {
@Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
@Nullable ArrayList<AutofillId> detectedFieldIdsList,
@Nullable ArrayList<FieldClassification> detectedFieldClassificationsList,
- @NonNull String appPackageName) {
+ @NonNull String appPackageName, boolean compatMode) {
if (isValidEventLocked("logDatasetNotSelected()", sessionId)) {
if (sVerbose) {
Slog.v(TAG, "logContextCommitted() with FieldClassification: id=" + sessionId
@@ -800,7 +800,8 @@ final class AutofillManagerServiceImpl {
+ ", changedDatasetIds=" + changedDatasetIds
+ ", manuallyFilledFieldIds=" + manuallyFilledFieldIds
+ ", detectedFieldIds=" + detectedFieldIdsList
- + ", detectedFieldClassifications=" + detectedFieldClassificationsList);
+ + ", detectedFieldClassifications=" + detectedFieldClassificationsList
+ + ", compatMode=" + compatMode);
}
AutofillId[] detectedFieldsIds = null;
FieldClassification[] detectedFieldClassifications = null;
@@ -827,7 +828,7 @@ final class AutofillManagerServiceImpl {
final int averageScore = (int) ((totalScore * 100) / totalSize);
mMetricsLogger.write(Helper
.newLogMaker(MetricsEvent.AUTOFILL_FIELD_CLASSIFICATION_MATCHES,
- appPackageName, getServicePackageName())
+ appPackageName, getServicePackageName(), compatMode)
.setCounterValue(numberFields)
.addTaggedData(MetricsEvent.FIELD_AUTOFILL_MATCH_SCORE,
averageScore));
@@ -1122,7 +1123,7 @@ final class AutofillManagerServiceImpl {
/**
* Called by {@link Session} when service asked to disable autofill for an app.
*/
- void disableAutofillForApp(@NonNull String packageName, long duration) {
+ void disableAutofillForApp(@NonNull String packageName, long duration, boolean compatMode) {
synchronized (mLock) {
if (mDisabledApps == null) {
mDisabledApps = new ArrayMap<>(1);
@@ -1135,7 +1136,7 @@ final class AutofillManagerServiceImpl {
mDisabledApps.put(packageName, expiration);
int intDuration = duration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) duration;
mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_APP,
- packageName, getServicePackageName())
+ packageName, getServicePackageName(), compatMode)
.addTaggedData(MetricsEvent.FIELD_AUTOFILL_DURATION, intDuration));
}
}
@@ -1143,7 +1144,8 @@ final class AutofillManagerServiceImpl {
/**
* Called by {@link Session} when service asked to disable autofill an app.
*/
- void disableAutofillForActivity(@NonNull ComponentName componentName, long duration) {
+ void disableAutofillForActivity(@NonNull ComponentName componentName, long duration,
+ boolean compatMode) {
synchronized (mLock) {
if (mDisabledActivities == null) {
mDisabledActivities = new ArrayMap<>(1);
@@ -1160,7 +1162,8 @@ final class AutofillManagerServiceImpl {
mMetricsLogger.write(new LogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_ACTIVITY)
.setComponentName(componentName)
.addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, getServicePackageName())
- .addTaggedData(MetricsEvent.FIELD_AUTOFILL_DURATION, intDuration));
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_DURATION, intDuration)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE, compatMode ? 1 : 0));
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 78526f53cdde..5372d0c649cf 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -21,7 +21,6 @@ import android.annotation.Nullable;
import android.app.assist.AssistStructure;
import android.app.assist.AssistStructure.ViewNode;
import android.metrics.LogMaker;
-import android.os.Bundle;
import android.service.autofill.Dataset;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -35,10 +34,7 @@ import com.android.internal.util.ArrayUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.LinkedList;
-import java.util.Objects;
-import java.util.Set;
public final class Helper {
@@ -119,6 +115,13 @@ public final class Helper {
return log;
}
+ @NonNull
+ public static LogMaker newLogMaker(int category, String packageName,
+ String servicePackageName, boolean compatMode) {
+ return newLogMaker(category, packageName, servicePackageName)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE, compatMode ? 1 : 0);
+ }
+
public static void printlnRedactedText(@NonNull PrintWriter pw, @Nullable CharSequence text) {
if (text == null) {
pw.println("null");
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index c05506066e6a..88a679b43cc6 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -644,9 +644,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Slog.d(TAG, message.toString());
}
if ((flags & FillResponse.FLAG_DISABLE_ACTIVITY_ONLY) != 0) {
- mService.disableAutofillForActivity(mComponentName, disableDuration);
+ mService.disableAutofillForActivity(mComponentName, disableDuration, mCompatMode);
} else {
- mService.disableAutofillForApp(mComponentName.getPackageName(), disableDuration);
+ mService.disableAutofillForApp(mComponentName.getPackageName(), disableDuration,
+ mCompatMode);
}
sessionFinishedState = AutofillManager.STATE_DISABLED_BY_SERVICE;
}
@@ -1251,7 +1252,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds,
ignoredDatasets, changedFieldIds, changedDatasetIds,
manuallyFilledFieldIds, manuallyFilledDatasetIds,
- mComponentName.getPackageName());
+ mComponentName.getPackageName(), mCompatMode);
}
}
@@ -1306,7 +1307,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds,
ignoredDatasets, changedFieldIds, changedDatasetIds,
manuallyFilledFieldIds, manuallyFilledDatasetIds,
- mComponentName.getPackageName());
+ mComponentName.getPackageName(), mCompatMode);
return;
}
final Scores scores = result.getParcelable(EXTRA_SCORES);
@@ -1371,7 +1372,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds,
ignoredDatasets, changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications,
- mComponentName.getPackageName());
+ mComponentName.getPackageName(), mCompatMode);
});
fcStrategy.getScores(callback, algorithm, algorithmArgs, currentValues, userValues);
@@ -1602,7 +1603,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
getUiForShowing().showSaveUi(mService.getServiceLabel(), mService.getServiceIcon(),
mService.getServicePackageName(), saveInfo, this,
mComponentName.getPackageName(), this,
- mPendingSaveUi);
+ mPendingSaveUi, mCompatMode);
if (client != null) {
try {
client.setSaveUiState(id, true);
@@ -2080,7 +2081,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
getUiForShowing().showFillUi(filledId, response, filterText,
mService.getServicePackageName(), mComponentName.getPackageName(),
- mService.getServiceLabel(), mService.getServiceIcon(), this);
+ mService.getServiceLabel(), mService.getServiceIcon(), this, mCompatMode);
synchronized (mLock) {
if (mUiShownTime == 0) {
@@ -2717,7 +2718,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
private LogMaker newLogMaker(int category, String servicePackageName) {
- return Helper.newLogMaker(category, mComponentName.getPackageName(), servicePackageName);
+ return Helper.newLogMaker(category, mComponentName.getPackageName(), servicePackageName,
+ mCompatMode);
}
private void writeLog(int category) {
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index ee18dc2e5824..811d87beaaa5 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -170,13 +170,14 @@ public final class AutoFillUI {
public void showFillUi(@NonNull AutofillId focusedId, @NonNull FillResponse response,
@Nullable String filterText, @Nullable String servicePackageName,
@NonNull String packageName, @NonNull CharSequence serviceLabel,
- @NonNull Drawable serviceIcon, @NonNull AutoFillUiCallback callback) {
+ @NonNull Drawable serviceIcon, @NonNull AutoFillUiCallback callback, boolean compatMode) {
if (sDebug) {
final int size = filterText == null ? 0 : filterText.length();
Slog.d(TAG, "showFillUi(): id=" + focusedId + ", filter=" + size + " chars");
}
- final LogMaker log =
- Helper.newLogMaker(MetricsEvent.AUTOFILL_FILL_UI, packageName, servicePackageName)
+ final LogMaker log = Helper
+ .newLogMaker(MetricsEvent.AUTOFILL_FILL_UI, packageName, servicePackageName,
+ compatMode)
.addTaggedData(MetricsEvent.FIELD_AUTOFILL_FILTERTEXT_LEN,
filterText == null ? 0 : filterText.length())
.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS,
@@ -262,14 +263,16 @@ public final class AutoFillUI {
public void showSaveUi(@NonNull CharSequence serviceLabel, @NonNull Drawable serviceIcon,
@Nullable String servicePackageName, @NonNull SaveInfo info,
@NonNull ValueFinder valueFinder, @NonNull String packageName,
- @NonNull AutoFillUiCallback callback, @NonNull PendingUi pendingSaveUi) {
+ @NonNull AutoFillUiCallback callback, @NonNull PendingUi pendingSaveUi,
+ boolean compatMode) {
if (sVerbose) Slog.v(TAG, "showSaveUi() for " + packageName + ": " + info);
int numIds = 0;
numIds += info.getRequiredIds() == null ? 0 : info.getRequiredIds().length;
numIds += info.getOptionalIds() == null ? 0 : info.getOptionalIds().length;
- final LogMaker log =
- Helper.newLogMaker(MetricsEvent.AUTOFILL_SAVE_UI, packageName, servicePackageName)
+ final LogMaker log = Helper
+ .newLogMaker(MetricsEvent.AUTOFILL_SAVE_UI, packageName, servicePackageName,
+ compatMode)
.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_IDS, numIds);
mHandler.post(() -> {
@@ -319,7 +322,7 @@ public final class AutoFillUI {
}
mMetricsLogger.write(log);
}
- });
+ }, compatMode);
});
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 80903c1a1fe4..fa2a0d9ad837 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -60,6 +60,7 @@ import com.android.internal.R;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.UiThread;
+import com.android.server.autofill.Helper;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -134,6 +135,7 @@ final class SaveUi {
private final PendingUi mPendingUi;
private final String mServicePackageName;
private final String mPackageName;
+ private final boolean mCompatMode;
private boolean mDestroyed;
@@ -141,12 +143,14 @@ final class SaveUi {
@NonNull CharSequence serviceLabel, @NonNull Drawable serviceIcon,
@Nullable String servicePackageName, @NonNull String packageName,
@NonNull SaveInfo info, @NonNull ValueFinder valueFinder,
- @NonNull OverlayControl overlayControl, @NonNull OnSaveListener listener) {
+ @NonNull OverlayControl overlayControl, @NonNull OnSaveListener listener,
+ boolean compatMode) {
mPendingUi= pendingUi;
mListener = new OneTimeListener(listener);
mOverlayControl = overlayControl;
mServicePackageName = servicePackageName;
mPackageName = packageName;
+ mCompatMode = compatMode;
context = new ContextThemeWrapper(context, THEME_ID);
final LayoutInflater inflater = LayoutInflater.from(context);
@@ -409,14 +413,12 @@ final class SaveUi {
}
private LogMaker newLogMaker(int category, int saveType) {
- return newLogMaker(category)
+ return Helper.newLogMaker(category, mPackageName, mServicePackageName, mCompatMode)
.addTaggedData(MetricsEvent.FIELD_AUTOFILL_SAVE_TYPE, saveType);
}
private LogMaker newLogMaker(int category) {
- return new LogMaker(category)
- .setPackageName(mPackageName)
- .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, mServicePackageName);
+ return Helper.newLogMaker(category, mPackageName, mServicePackageName, mCompatMode);
}
private void writeLog(int category, int saveType) {
@@ -505,6 +507,7 @@ final class SaveUi {
pw.print(prefix); pw.print("pendingUi: "); pw.println(mPendingUi);
pw.print(prefix); pw.print("service: "); pw.println(mServicePackageName);
pw.print(prefix); pw.print("app: "); pw.println(mPackageName);
+ pw.print(prefix); pw.print("compat mode: "); pw.println(mCompatMode);
final View view = mDialog.getWindow().getDecorView();
final int[] loc = view.getLocationOnScreen();
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 0f42103e4178..27eae57302b0 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -78,9 +78,13 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
int mDisplayId;
Display mDisplay;
- /** All of the stacks on this display. Order matters, topmost stack is in front of all other
- * stacks, bottommost behind. Accessed directly by ActivityManager package classes */
+ /**
+ * All of the stacks on this display. Order matters, topmost stack is in front of all other
+ * stacks, bottommost behind. Accessed directly by ActivityManager package classes. Any calls
+ * changing the list should also call {@link #onStackOrderChanged()}.
+ */
private final ArrayList<ActivityStack> mStacks = new ArrayList<>();
+ private ArrayList<OnStackOrderChangedListener> mStackOrderChangedCallbacks = new ArrayList<>();
/** Array of all UIDs that are present on the display. */
private IntArray mDisplayAccessUIDs = new IntArray();
@@ -145,6 +149,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
mStacks.remove(stack);
removeStackReferenceIfNeeded(stack);
mSupervisor.mService.updateSleepIfNeededLocked();
+ onStackOrderChanged();
}
void positionChildAtTop(ActivityStack stack) {
@@ -163,6 +168,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
mStacks.add(insertPosition, stack);
mWindowContainerController.positionChildAt(stack.getWindowContainerController(),
insertPosition);
+ onStackOrderChanged();
}
private int getTopInsertPosition(ActivityStack stack, int candidatePosition) {
@@ -770,6 +776,30 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
mSleeping = asleep;
}
+ /**
+ * Adds a listener to be notified whenever the stack order in the display changes. Currently
+ * only used by the {@link RecentsAnimation} to determine whether to interrupt and cancel the
+ * current animation when the system state changes.
+ */
+ void registerStackOrderChangedListener(OnStackOrderChangedListener listener) {
+ if (!mStackOrderChangedCallbacks.contains(listener)) {
+ mStackOrderChangedCallbacks.add(listener);
+ }
+ }
+
+ /**
+ * Removes a previously registered stack order change listener.
+ */
+ void unregisterStackOrderChangedListener(OnStackOrderChangedListener listener) {
+ mStackOrderChangedCallbacks.remove(listener);
+ }
+
+ private void onStackOrderChanged() {
+ for (int i = mStackOrderChangedCallbacks.size() - 1; i >= 0; i--) {
+ mStackOrderChangedCallbacks.get(i).onStackOrderChanged();
+ }
+ }
+
public void dump(PrintWriter pw, String prefix) {
pw.println(prefix + "displayId=" + mDisplayId + " stacks=" + mStacks.size());
final String myPrefix = prefix + " ";
@@ -806,4 +836,11 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
}
proto.end(token);
}
+
+ /**
+ * Callback for when the order of the stacks in the display changes.
+ */
+ interface OnStackOrderChangedListener {
+ void onStackOrderChanged();
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a8e63f6695d4..9de876ec4408 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5291,14 +5291,9 @@ public class ActivityManagerService extends IActivityManager.Stub
final int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
try {
- final int recentsUid;
- final String recentsPackage;
- final List<IBinder> topVisibleActivities;
synchronized (this) {
final ComponentName recentsComponent = mRecentTasks.getRecentsComponent();
- recentsPackage = recentsComponent.getPackageName();
- recentsUid = mRecentTasks.getRecentsComponentUid();
- topVisibleActivities = mStackSupervisor.getTopVisibleActivities();
+ final int recentsUid = mRecentTasks.getRecentsComponentUid();
// Start a new recents animation
final RecentsAnimation anim = new RecentsAnimation(this, mStackSupervisor,
@@ -5314,13 +5309,14 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "cancelRecentsAnimation()");
+ final long callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
synchronized (this) {
// Cancel the recents animation synchronously (do not hold the WM lock)
mWindowManager.cancelRecentsAnimationSynchronously(restoreHomeStackPosition
? REORDER_MOVE_TO_ORIGINAL_POSITION
- : REORDER_KEEP_IN_PLACE, "cancelRecentsAnimation");
+ : REORDER_KEEP_IN_PLACE, "cancelRecentsAnimation/uid=" + callingUid);
}
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 0c328a89ef84..ef23a83cd881 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1214,11 +1214,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub
private void dumpHelp(PrintWriter pw) {
pw.println("Battery stats (batterystats) dump options:");
- pw.println(" [--checkin] [--history] [--history-start] [--charged] [-c]");
+ pw.println(" [--checkin] [--proto] [--history] [--history-start] [--charged] [-c]");
pw.println(" [--daily] [--reset] [--write] [--new-daily] [--read-daily] [-h] [<package.name>]");
pw.println(" --checkin: generate output for a checkin report; will write (and clear) the");
pw.println(" last old completed stats when they had been reset.");
pw.println(" -c: write the current stats in checkin format.");
+ pw.println(" --proto: write the current aggregate stats (without history) in proto format.");
pw.println(" --history: show only history data.");
pw.println(" --history-start <num>: show only history data starting at given time offset.");
pw.println(" --charged: only output data since last charged.");
@@ -1431,7 +1432,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub
null, mStats.mHandler, null, mUserManagerUserInfoProvider);
checkinStats.readSummaryFromParcel(in);
in.recycle();
- checkinStats.dumpProtoLocked(mContext, fd, apps, flags);
+ checkinStats.dumpProtoLocked(
+ mContext, fd, apps, flags, historyStart);
mStats.mCheckinFile.delete();
return;
}
@@ -1444,7 +1446,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
if (DBG) Slog.d(TAG, "begin dumpProtoLocked from UID " + Binder.getCallingUid());
synchronized (mStats) {
- mStats.dumpProtoLocked(mContext, fd, apps, flags);
+ mStats.dumpProtoLocked(mContext, fd, apps, flags, historyStart);
if (writeData) {
mStats.writeAsyncLocked();
}
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index a88f4080a784..a3d2173b294c 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -49,7 +49,8 @@ import com.android.server.wm.WindowManagerService;
* Manages the recents animation, including the reordering of the stacks for the transition and
* cleanup. See {@link com.android.server.wm.RecentsAnimationController}.
*/
-class RecentsAnimation implements RecentsAnimationCallbacks {
+class RecentsAnimation implements RecentsAnimationCallbacks,
+ ActivityDisplay.OnStackOrderChangedListener {
private static final String TAG = RecentsAnimation.class.getSimpleName();
// TODO (b/73188263): Reset debugging flags
private static final boolean DEBUG = true;
@@ -140,13 +141,11 @@ class RecentsAnimation implements RecentsAnimationCallbacks {
recentsUid, recentsComponent.getPackageName());
}
- final ActivityDisplay display;
if (hasExistingActivity) {
// Move the recents activity into place for the animation if it is not top most
- display = targetActivity.getDisplay();
- display.moveStackBehindBottomMostVisibleStack(targetStack);
+ mDefaultDisplay.moveStackBehindBottomMostVisibleStack(targetStack);
if (DEBUG) Slog.d(TAG, "Moved stack=" + targetStack + " behind stack="
- + display.getStackAbove(targetStack));
+ + mDefaultDisplay.getStackAbove(targetStack));
// If there are multiple tasks in the target stack (ie. the home stack, with 3p
// and default launchers coexisting), then move the task to the top as a part of
@@ -173,7 +172,6 @@ class RecentsAnimation implements RecentsAnimationCallbacks {
targetActivity = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
mTargetActivityType).getTopActivity();
- display = targetActivity.getDisplay();
// TODO: Maybe wait for app to draw in this particular case?
@@ -190,7 +188,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks {
mWindowManager.cancelRecentsAnimationSynchronously(REORDER_MOVE_TO_ORIGINAL_POSITION,
"startRecentsActivity");
mWindowManager.initializeRecentsAnimation(mTargetActivityType, recentsAnimationRunner,
- this, display.mDisplayId, mStackSupervisor.mRecentTasks.getRecentTaskIds());
+ this, mDefaultDisplay.mDisplayId,
+ mStackSupervisor.mRecentTasks.getRecentTaskIds());
// If we updated the launch-behind state, update the visibility of the activities after
// we fetch the visible tasks to be controlled by the animation
@@ -198,6 +197,9 @@ class RecentsAnimation implements RecentsAnimationCallbacks {
mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunched(START_TASK_TO_FRONT,
targetActivity);
+
+ // Register for stack order changes
+ mDefaultDisplay.registerStackOrderChangedListener(this);
} catch (Exception e) {
Slog.e(TAG, "Failed to start recents activity", e);
throw e;
@@ -219,6 +221,9 @@ class RecentsAnimation implements RecentsAnimationCallbacks {
mAssistDataRequester = null;
}
+ // Unregister for stack order changes
+ mDefaultDisplay.unregisterStackOrderChangedListener(this);
+
if (mWindowManager.getRecentsAnimationController() == null) return;
// Just to be sure end the launch hint in case the target activity was never launched.
@@ -316,6 +321,14 @@ class RecentsAnimation implements RecentsAnimationCallbacks {
}
}
+ @Override
+ public void onStackOrderChanged() {
+ // If the activity display stack order changes, cancel any running recents animation in
+ // place
+ mWindowManager.cancelRecentsAnimationSynchronously(REORDER_KEEP_IN_PLACE,
+ "stackOrderChanged");
+ }
+
/**
* Called only when the animation should be canceled prior to starting.
*/
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index b2cf1b7d60f3..36e5340f6be7 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -525,6 +525,8 @@ public class AudioService extends IAudioService.Stub
private static final int SCO_STATE_ACTIVE_INTERNAL = 3;
// SCO audio deactivation request waiting for headset service to connect
private static final int SCO_STATE_DEACTIVATE_REQ = 5;
+ // SCO audio deactivation in progress, waiting for Bluetooth audio intent
+ private static final int SCO_STATE_DEACTIVATING = 6;
// SCO audio state is active due to an action in BT handsfree (either voice recognition or
// in call audio)
@@ -2705,9 +2707,13 @@ public class AudioService extends IAudioService.Stub
}
public void binderDied() {
+ int oldModeOwnerPid = 0;
int newModeOwnerPid = 0;
synchronized(mSetModeDeathHandlers) {
Log.w(TAG, "setMode() client died");
+ if (!mSetModeDeathHandlers.isEmpty()) {
+ oldModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
+ }
int index = mSetModeDeathHandlers.indexOf(this);
if (index < 0) {
Log.w(TAG, "unregistered setMode() client died");
@@ -2716,8 +2722,8 @@ public class AudioService extends IAudioService.Stub
}
}
// when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
- // SCO connections not started by the application changing the mode
- if (newModeOwnerPid != 0) {
+ // SCO connections not started by the application changing the mode when pid changes
+ if ((newModeOwnerPid != oldModeOwnerPid) && (newModeOwnerPid != 0)) {
final long ident = Binder.clearCallingIdentity();
disconnectBluetoothSco(newModeOwnerPid);
Binder.restoreCallingIdentity(ident);
@@ -2761,17 +2767,21 @@ public class AudioService extends IAudioService.Stub
return;
}
+ int oldModeOwnerPid = 0;
int newModeOwnerPid = 0;
synchronized(mSetModeDeathHandlers) {
+ if (!mSetModeDeathHandlers.isEmpty()) {
+ oldModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
+ }
if (mode == AudioSystem.MODE_CURRENT) {
mode = mMode;
}
newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid(), callingPackage);
}
// when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
- // SCO connections not started by the application changing the mode
- if (newModeOwnerPid != 0) {
- disconnectBluetoothSco(newModeOwnerPid);
+ // SCO connections not started by the application changing the mode when pid changes
+ if ((newModeOwnerPid != oldModeOwnerPid) && (newModeOwnerPid != 0)) {
+ disconnectBluetoothSco(newModeOwnerPid);
}
}
@@ -3187,28 +3197,17 @@ public class AudioService extends IAudioService.Stub
}
public void setBluetoothScoOnInt(boolean on, String eventSource) {
- if (DEBUG_DEVICES) {
- Log.d(TAG, "setBluetoothScoOnInt: " + on + " " + eventSource);
- }
+ Log.i(TAG, "setBluetoothScoOnInt: " + on + " " + eventSource);
if (on) {
// do not accept SCO ON if SCO audio is not connected
synchronized (mScoClients) {
- if (mBluetoothHeadset != null) {
- if (mBluetoothHeadsetDevice == null) {
- BluetoothDevice activeDevice = mBluetoothHeadset.getActiveDevice();
- if (activeDevice != null) {
- // setBtScoActiveDevice() might trigger resetBluetoothSco() which
- // will call setBluetoothScoOnInt(false, "resetBluetoothSco")
- setBtScoActiveDevice(activeDevice);
- }
- }
- if (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
- != BluetoothHeadset.STATE_AUDIO_CONNECTED) {
- mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
- Log.w(TAG, "setBluetoothScoOnInt(true) failed because "
- + mBluetoothHeadsetDevice + " is not in audio connected mode");
- return;
- }
+ if ((mBluetoothHeadset != null)
+ && (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
+ != BluetoothHeadset.STATE_AUDIO_CONNECTED)) {
+ mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
+ Log.w(TAG, "setBluetoothScoOnInt(true) failed because "
+ + mBluetoothHeadsetDevice + " is not in audio connected mode");
+ return;
}
}
mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
@@ -3388,9 +3387,8 @@ public class AudioService extends IAudioService.Stub
public int totalCount() {
synchronized(mScoClients) {
int count = 0;
- int size = mScoClients.size();
- for (int i = 0; i < size; i++) {
- count += mScoClients.get(i).getCount();
+ for (ScoClient mScoClient : mScoClients) {
+ count += mScoClient.getCount();
}
return count;
}
@@ -3398,128 +3396,161 @@ public class AudioService extends IAudioService.Stub
private void requestScoState(int state, int scoAudioMode) {
checkScoAudioState();
- if (totalCount() == 0) {
- if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
- // Make sure that the state transitions to CONNECTING even if we cannot initiate
- // the connection.
- broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
- // Accept SCO audio activation only in NORMAL audio mode or if the mode is
- // currently controlled by the same client process.
- synchronized(mSetModeDeathHandlers) {
- if ((mSetModeDeathHandlers.isEmpty() ||
- mSetModeDeathHandlers.get(0).getPid() == mCreatorPid) &&
- (mScoAudioState == SCO_STATE_INACTIVE ||
- mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
- if (mScoAudioState == SCO_STATE_INACTIVE) {
- mScoAudioMode = scoAudioMode;
- if (scoAudioMode == SCO_MODE_UNDEFINED) {
- if (mBluetoothHeadsetDevice != null) {
- mScoAudioMode = new Integer(Settings.Global.getInt(
- mContentResolver,
- "bluetooth_sco_channel_"+
- mBluetoothHeadsetDevice.getAddress(),
- SCO_MODE_VIRTUAL_CALL));
- if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
- mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
- }
- } else {
- mScoAudioMode = SCO_MODE_RAW;
+ int clientCount = totalCount();
+ if (clientCount != 0) {
+ Log.i(TAG, "requestScoState: state=" + state + ", scoAudioMode=" + scoAudioMode
+ + ", clientCount=" + clientCount);
+ return;
+ }
+ if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
+ // Make sure that the state transitions to CONNECTING even if we cannot initiate
+ // the connection.
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
+ // Accept SCO audio activation only in NORMAL audio mode or if the mode is
+ // currently controlled by the same client process.
+ synchronized(mSetModeDeathHandlers) {
+ int modeOwnerPid = mSetModeDeathHandlers.isEmpty()
+ ? 0 : mSetModeDeathHandlers.get(0).getPid();
+ if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) {
+ Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid "
+ + modeOwnerPid + " != creatorPid " + mCreatorPid);
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ return;
+ }
+ switch (mScoAudioState) {
+ case SCO_STATE_INACTIVE:
+ mScoAudioMode = scoAudioMode;
+ if (scoAudioMode == SCO_MODE_UNDEFINED) {
+ mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
+ if (mBluetoothHeadsetDevice != null) {
+ mScoAudioMode = Settings.Global.getInt(mContentResolver,
+ "bluetooth_sco_channel_"
+ + mBluetoothHeadsetDevice.getAddress(),
+ SCO_MODE_VIRTUAL_CALL);
+ if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
+ mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
}
}
- if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
- boolean status = false;
- if (mScoAudioMode == SCO_MODE_RAW) {
- status = mBluetoothHeadset.connectAudio();
- } else if (mScoAudioMode == SCO_MODE_VIRTUAL_CALL) {
- status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice);
- } else if (mScoAudioMode == SCO_MODE_VR) {
- status = mBluetoothHeadset.startVoiceRecognition(
- mBluetoothHeadsetDevice);
- }
-
- if (status) {
- mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
- } else {
- broadcastScoConnectionState(
- AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- }
- } else if (getBluetoothHeadset()) {
+ }
+ if (mBluetoothHeadset == null) {
+ if (getBluetoothHeadset()) {
mScoAudioState = SCO_STATE_ACTIVATE_REQ;
+ } else {
+ Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
+ + " connection, mScoAudioMode=" + mScoAudioMode);
+ broadcastScoConnectionState(
+ AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
}
- } else {
+ break;
+ }
+ if (mBluetoothHeadsetDevice == null) {
+ Log.w(TAG, "requestScoState: no active device while connecting,"
+ + " mScoAudioMode=" + mScoAudioMode);
+ broadcastScoConnectionState(
+ AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ break;
+ }
+ if (connectBluetoothScoAudioHelper(mBluetoothHeadset,
+ mBluetoothHeadsetDevice, mScoAudioMode)) {
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
- broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
+ } else {
+ Log.w(TAG, "requestScoState: connect to " + mBluetoothHeadsetDevice
+ + " failed, mScoAudioMode=" + mScoAudioMode);
+ broadcastScoConnectionState(
+ AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
}
- } else {
+ break;
+ case SCO_STATE_DEACTIVATING:
+ mScoAudioState = SCO_STATE_ACTIVATE_REQ;
+ break;
+ case SCO_STATE_DEACTIVATE_REQ:
+ mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
+ break;
+ default:
+ Log.w(TAG, "requestScoState: failed to connect in state "
+ + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- }
- }
- } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED &&
- (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
- mScoAudioState == SCO_STATE_ACTIVATE_REQ)) {
- if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
- if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
- boolean status = false;
- if (mScoAudioMode == SCO_MODE_RAW) {
- status = mBluetoothHeadset.disconnectAudio();
- } else if (mScoAudioMode == SCO_MODE_VIRTUAL_CALL) {
- status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice);
- } else if (mScoAudioMode == SCO_MODE_VR) {
- status = mBluetoothHeadset.stopVoiceRecognition(
- mBluetoothHeadsetDevice);
- }
+ break;
- if (!status) {
+ }
+ }
+ } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
+ switch (mScoAudioState) {
+ case SCO_STATE_ACTIVE_INTERNAL:
+ if (mBluetoothHeadset == null) {
+ if (getBluetoothHeadset()) {
+ mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
+ } else {
+ Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
+ + " disconnection, mScoAudioMode=" + mScoAudioMode);
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
}
- } else if (getBluetoothHeadset()) {
- mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
+ break;
}
- } else {
+ if (mBluetoothHeadsetDevice == null) {
+ mScoAudioState = SCO_STATE_INACTIVE;
+ broadcastScoConnectionState(
+ AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ break;
+ }
+ if (disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
+ mBluetoothHeadsetDevice, mScoAudioMode)) {
+ mScoAudioState = SCO_STATE_DEACTIVATING;
+ } else {
+ mScoAudioState = SCO_STATE_INACTIVE;
+ broadcastScoConnectionState(
+ AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ }
+ break;
+ case SCO_STATE_ACTIVATE_REQ:
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- }
+ break;
+ default:
+ Log.w(TAG, "requestScoState: failed to disconnect in state "
+ + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ break;
}
}
}
}
private void checkScoAudioState() {
- if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null &&
- mScoAudioState == SCO_STATE_INACTIVE &&
- mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
- != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
- mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
+ synchronized (mScoClients) {
+ if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null &&
+ mScoAudioState == SCO_STATE_INACTIVE &&
+ mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
+ != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
+ mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
+ }
}
}
+
private ScoClient getScoClient(IBinder cb, boolean create) {
synchronized(mScoClients) {
- ScoClient client = null;
- int size = mScoClients.size();
- for (int i = 0; i < size; i++) {
- client = mScoClients.get(i);
- if (client.getBinder() == cb)
- return client;
+ for (ScoClient existingClient : mScoClients) {
+ if (existingClient.getBinder() == cb) {
+ return existingClient;
+ }
}
if (create) {
- client = new ScoClient(cb);
- mScoClients.add(client);
+ ScoClient newClient = new ScoClient(cb);
+ mScoClients.add(newClient);
+ return newClient;
}
- return client;
+ return null;
}
}
public void clearAllScoClients(int exceptPid, boolean stopSco) {
synchronized(mScoClients) {
ScoClient savedClient = null;
- int size = mScoClients.size();
- for (int i = 0; i < size; i++) {
- ScoClient cl = mScoClients.get(i);
+ for (ScoClient cl : mScoClients) {
if (cl.getPid() != exceptPid) {
cl.clearCount(stopSco);
} else {
@@ -3556,10 +3587,17 @@ public class AudioService extends IAudioService.Stub
mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
if (mBluetoothHeadsetDevice != null) {
if (mBluetoothHeadset != null) {
- if (!mBluetoothHeadset.stopVoiceRecognition(
- mBluetoothHeadsetDevice)) {
- sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
- SENDMSG_REPLACE, 0, 0, null, 0);
+ boolean status = disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
+ mBluetoothHeadsetDevice, SCO_MODE_RAW)
+ || disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
+ mBluetoothHeadsetDevice, SCO_MODE_VIRTUAL_CALL)
+ || disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
+ mBluetoothHeadsetDevice, SCO_MODE_VR);
+ if (status) {
+ mScoAudioState = SCO_STATE_DEACTIVATING;
+ } else {
+ clearAllScoClients(exceptPid, false);
+ mScoAudioState = SCO_STATE_INACTIVE;
}
} else if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL &&
getBluetoothHeadset()) {
@@ -3572,6 +3610,34 @@ public class AudioService extends IAudioService.Stub
}
}
+ private static boolean disconnectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset,
+ BluetoothDevice device, int scoAudioMode) {
+ switch (scoAudioMode) {
+ case SCO_MODE_RAW:
+ return bluetoothHeadset.disconnectAudio();
+ case SCO_MODE_VIRTUAL_CALL:
+ return bluetoothHeadset.stopScoUsingVirtualVoiceCall();
+ case SCO_MODE_VR:
+ return bluetoothHeadset.stopVoiceRecognition(device);
+ default:
+ return false;
+ }
+ }
+
+ private static boolean connectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset,
+ BluetoothDevice device, int scoAudioMode) {
+ switch (scoAudioMode) {
+ case SCO_MODE_RAW:
+ return bluetoothHeadset.connectAudio();
+ case SCO_MODE_VIRTUAL_CALL:
+ return bluetoothHeadset.startScoUsingVirtualVoiceCall();
+ case SCO_MODE_VR:
+ return bluetoothHeadset.startVoiceRecognition(device);
+ default:
+ return false;
+ }
+ }
+
private void resetBluetoothSco() {
synchronized(mScoClients) {
clearAllScoClients(0, false);
@@ -3629,11 +3695,9 @@ public class AudioService extends IAudioService.Stub
return result;
}
- void setBtScoActiveDevice(BluetoothDevice btDevice) {
- if (DEBUG_DEVICES) {
- Log.d(TAG, "setBtScoActiveDevice(" + btDevice + ")");
- }
+ private void setBtScoActiveDevice(BluetoothDevice btDevice) {
synchronized (mScoClients) {
+ Log.i(TAG, "setBtScoActiveDevice: " + mBluetoothHeadsetDevice + " -> " + btDevice);
final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice;
if (!Objects.equals(btDevice, previousActiveDevice)) {
if (!handleBtScoActiveDeviceChange(previousActiveDevice, false)) {
@@ -3713,37 +3777,36 @@ public class AudioService extends IAudioService.Stub
boolean status = false;
if (mBluetoothHeadsetDevice != null) {
switch (mScoAudioState) {
- case SCO_STATE_ACTIVATE_REQ:
- mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
- if (mScoAudioMode == SCO_MODE_RAW) {
- status = mBluetoothHeadset.connectAudio();
- } else if (mScoAudioMode == SCO_MODE_VIRTUAL_CALL) {
- status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice);
- } else if (mScoAudioMode == SCO_MODE_VR) {
- status = mBluetoothHeadset.startVoiceRecognition(
- mBluetoothHeadsetDevice);
- }
- break;
- case SCO_STATE_DEACTIVATE_REQ:
- if (mScoAudioMode == SCO_MODE_RAW) {
- status = mBluetoothHeadset.disconnectAudio();
- } else if (mScoAudioMode == SCO_MODE_VIRTUAL_CALL) {
- status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice);
- } else if (mScoAudioMode == SCO_MODE_VR) {
- status = mBluetoothHeadset.stopVoiceRecognition(
- mBluetoothHeadsetDevice);
- }
- break;
- case SCO_STATE_DEACTIVATE_EXT_REQ:
- status = mBluetoothHeadset.stopVoiceRecognition(
- mBluetoothHeadsetDevice);
+ case SCO_STATE_ACTIVATE_REQ:
+ status = connectBluetoothScoAudioHelper(mBluetoothHeadset,
+ mBluetoothHeadsetDevice, mScoAudioMode);
+ if (status) {
+ mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+ }
+ break;
+ case SCO_STATE_DEACTIVATE_REQ:
+ status = disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
+ mBluetoothHeadsetDevice, mScoAudioMode);
+ if (status) {
+ mScoAudioState = SCO_STATE_DEACTIVATING;
+ }
+ break;
+ case SCO_STATE_DEACTIVATE_EXT_REQ:
+ status = disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
+ mBluetoothHeadsetDevice, SCO_MODE_RAW) ||
+ disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
+ mBluetoothHeadsetDevice, SCO_MODE_VIRTUAL_CALL) ||
+ disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
+ mBluetoothHeadsetDevice, SCO_MODE_VR);
+ if (status) {
+ mScoAudioState = SCO_STATE_DEACTIVATING;
+ }
+ break;
}
}
if (!status) {
- sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
- SENDMSG_REPLACE, 0, 0, null, 0);
+ mScoAudioState = SCO_STATE_INACTIVE;
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
}
}
}
@@ -6313,33 +6376,47 @@ public class AudioService extends IAudioService.Stub
if (!mScoClients.isEmpty() &&
(mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
- mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
+ mScoAudioState == SCO_STATE_DEACTIVATE_REQ ||
+ mScoAudioState == SCO_STATE_DEACTIVATING)) {
broadcast = true;
}
switch (btState) {
- case BluetoothHeadset.STATE_AUDIO_CONNECTED:
- scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
- if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
- mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
- mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
- mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
- }
- break;
- case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
- scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
- mScoAudioState = SCO_STATE_INACTIVE;
- clearAllScoClients(0, false);
- break;
- case BluetoothHeadset.STATE_AUDIO_CONNECTING:
- if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
- mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
- mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
- mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
- }
- default:
- // do not broadcast CONNECTING or invalid state
- broadcast = false;
- break;
+ case BluetoothHeadset.STATE_AUDIO_CONNECTED:
+ scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
+ if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
+ mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
+ mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
+ mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
+ }
+ setBluetoothScoOn(true);
+ break;
+ case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
+ setBluetoothScoOn(false);
+ scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
+ // startBluetoothSco called after stopBluetoothSco
+ if (mScoAudioState == SCO_STATE_ACTIVATE_REQ) {
+ if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null
+ && connectBluetoothScoAudioHelper(mBluetoothHeadset,
+ mBluetoothHeadsetDevice, mScoAudioMode)) {
+ mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+ broadcast = false;
+ break;
+ }
+ }
+ // Tear down SCO if disconnected from external
+ clearAllScoClients(0, mScoAudioState == SCO_STATE_ACTIVE_INTERNAL);
+ mScoAudioState = SCO_STATE_INACTIVE;
+ break;
+ case BluetoothHeadset.STATE_AUDIO_CONNECTING:
+ if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
+ mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
+ mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
+ mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
+ }
+ default:
+ // do not broadcast CONNECTING or invalid state
+ broadcast = false;
+ break;
}
}
if (broadcast) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index af5521d036af..c2769380dc58 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -88,11 +88,11 @@ import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDWR;
+
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
-import static com.android.internal.util.ArrayUtils.appendElement;
import static com.android.internal.util.ArrayUtils.appendInt;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
@@ -275,7 +275,6 @@ import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IMediaContainerService;
import com.android.internal.app.ResolverActivity;
-import com.android.internal.app.SuspendedAppActivity;
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
import com.android.internal.logging.MetricsLogger;
@@ -411,7 +410,7 @@ public class PackageManagerService extends IPackageManager.Stub
static final boolean DEBUG_DOMAIN_VERIFICATION = false;
private static final boolean DEBUG_BACKUP = false;
public static final boolean DEBUG_INSTALL = false;
- public static final boolean DEBUG_REMOVE = true;
+ public static final boolean DEBUG_REMOVE = false;
private static final boolean DEBUG_BROADCASTS = false;
private static final boolean DEBUG_SHOW_INFO = false;
private static final boolean DEBUG_PACKAGE_INFO = false;
@@ -8482,6 +8481,29 @@ public class PackageManagerService extends IPackageManager.Stub
}
/**
+ * Clear the package profile if this was an upgrade and the package
+ * version was updated.
+ */
+ private void maybeClearProfilesForUpgradesLI(
+ @Nullable PackageSetting originalPkgSetting,
+ @NonNull PackageParser.Package currentPkg) {
+ if (originalPkgSetting == null || !isUpgrade()) {
+ return;
+ }
+ if (originalPkgSetting.versionCode == currentPkg.mVersionCode) {
+ return;
+ }
+
+ clearAppProfilesLIF(currentPkg, UserHandle.USER_ALL);
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG, originalPkgSetting.name
+ + " clear profile due to version change "
+ + originalPkgSetting.versionCode + " != "
+ + currentPkg.mVersionCode);
+ }
+ }
+
+ /**
* Traces a package scan.
* @see #scanPackageLI(File, int, int, long, UserHandle)
*/
@@ -8763,6 +8785,13 @@ public class PackageManagerService extends IPackageManager.Stub
(forceCollect && canSkipFullPackageVerification(pkg));
collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify);
+ // Reset profile if the application version is changed
+ maybeClearProfilesForUpgradesLI(pkgSetting, pkg);
+
+ /*
+ * A new system app appeared, but we already had a non-system one of the
+ * same name installed earlier.
+ */
boolean shouldHideSystemApp = false;
// A new application appeared on /system, but, we already have a copy of
// the application installed on /data.
@@ -9415,6 +9444,17 @@ public class PackageManagerService extends IPackageManager.Stub
mPackageUsage.writeNow(mPackages);
mCompilerStats.writeNow();
mDexManager.writePackageDexUsageNow();
+
+ // This is the last chance to write out pending restriction settings
+ synchronized (mPackages) {
+ if (mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) {
+ mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS);
+ for (int userId : mDirtyUsers) {
+ mSettings.writePackageRestrictionsLPr(userId);
+ }
+ mDirtyUsers.clear();
+ }
+ }
}
@Override
@@ -10186,10 +10226,14 @@ public class PackageManagerService extends IPackageManager.Stub
// if this is is a sharedUser, check to see if the new package is signed by a newer
// signing certificate than the existing one, and if so, copy over the new details
- if (signatureCheckPs.sharedUser != null
- && pkg.mSigningDetails.hasAncestor(
+ if (signatureCheckPs.sharedUser != null) {
+ if (pkg.mSigningDetails.hasAncestor(
signatureCheckPs.sharedUser.signatures.mSigningDetails)) {
- signatureCheckPs.sharedUser.signatures.mSigningDetails = pkg.mSigningDetails;
+ signatureCheckPs.sharedUser.signatures.mSigningDetails = pkg.mSigningDetails;
+ }
+ if (signatureCheckPs.sharedUser.signaturesChanged == null) {
+ signatureCheckPs.sharedUser.signaturesChanged = Boolean.FALSE;
+ }
}
} catch (PackageManagerException e) {
if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
@@ -10198,10 +10242,24 @@ public class PackageManagerService extends IPackageManager.Stub
// The signature has changed, but this package is in the system
// image... let's recover!
pkgSetting.signatures.mSigningDetails = pkg.mSigningDetails;
+
// If the system app is part of a shared user we allow that shared user to change
- // signatures as well in part as part of an OTA.
+ // signatures as well as part of an OTA. We still need to verify that the signatures
+ // are consistent within the shared user for a given boot, so only allow updating
+ // the signatures on the first package scanned for the shared user (i.e. if the
+ // signaturesChanged state hasn't been initialized yet in SharedUserSetting).
if (signatureCheckPs.sharedUser != null) {
+ if (signatureCheckPs.sharedUser.signaturesChanged != null &&
+ compareSignatures(
+ signatureCheckPs.sharedUser.signatures.mSigningDetails.signatures,
+ pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH) {
+ throw new PackageManagerException(
+ INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+ "Signature mismatch for shared user: " + pkgSetting.sharedUser);
+ }
+
signatureCheckPs.sharedUser.signatures.mSigningDetails = pkg.mSigningDetails;
+ signatureCheckPs.sharedUser.signaturesChanged = Boolean.TRUE;
}
// File a report about this.
String msg = "System package " + pkg.packageName
@@ -14031,13 +14089,10 @@ public class PackageManagerService extends IPackageManager.Stub
+ Manifest.permission.MANAGE_USERS);
}
final int callingUid = Binder.getCallingUid();
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- true /* requireFullPermission */, true /* checkShell */,
- "setPackagesSuspended for user " + userId);
- if (callingUid != Process.ROOT_UID &&
- !UserHandle.isSameApp(getPackageUid(callingPackage, 0, userId), callingUid)) {
- throw new IllegalArgumentException("CallingPackage " + callingPackage + " does not"
- + " belong to calling app id " + UserHandle.getAppId(callingUid));
+ if (callingUid != Process.ROOT_UID && callingUid != Process.SYSTEM_UID
+ && getPackageUid(callingPackage, 0, userId) != callingUid) {
+ throw new SecurityException("Calling package " + callingPackage + " in user "
+ + userId + " does not belong to calling uid " + callingUid);
}
if (!PLATFORM_PACKAGE_NAME.equals(callingPackage)
&& mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(userId) != null) {
@@ -14164,9 +14219,19 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- void onSuspendingPackageRemoved(String packageName, int removedForUser) {
- final int[] userIds = (removedForUser == UserHandle.USER_ALL) ? sUserManager.getUserIds()
- : new int[] {removedForUser};
+ /**
+ * Immediately unsuspends any packages suspended by the given package. To be called
+ * when such a package's data is cleared or it is removed from the device.
+ *
+ * <p><b>Should not be used on a frequent code path</b> as it flushes state to disk
+ * synchronously
+ *
+ * @param packageName The package holding {@link Manifest.permission#SUSPEND_APPS} permission
+ * @param affectedUser The user for which the changes are taking place.
+ */
+ void unsuspendForSuspendingPackage(String packageName, int affectedUser) {
+ final int[] userIds = (affectedUser == UserHandle.USER_ALL) ? sUserManager.getUserIds()
+ : new int[] {affectedUser};
for (int userId : userIds) {
List<String> affectedPackages = new ArrayList<>();
synchronized (mPackages) {
@@ -14183,6 +14248,8 @@ public class PackageManagerService extends IPackageManager.Stub
new String[affectedPackages.size()]);
sendMyPackageSuspendedOrUnsuspended(packageArray, false, null, userId);
sendPackagesSuspendedForUser(packageArray, userId, false, null);
+ // Write package restrictions immediately to avoid an inconsistent state.
+ mSettings.writePackageRestrictionsLPr(userId);
}
}
}
@@ -18758,7 +18825,7 @@ public class PackageManagerService extends IPackageManager.Stub
final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier();
if (ps.getPermissionsState().hasPermission(Manifest.permission.SUSPEND_APPS, userId)) {
- onSuspendingPackageRemoved(packageName, userId);
+ unsuspendForSuspendingPackage(packageName, userId);
}
@@ -18899,10 +18966,10 @@ public class PackageManagerService extends IPackageManager.Stub
true /*notLaunched*/,
false /*hidden*/,
false /*suspended*/,
- null, /*suspendingPackage*/
- null, /*dialogMessage*/
- null, /*suspendedAppExtras*/
- null, /*suspendedLauncherExtras*/
+ null /*suspendingPackage*/,
+ null /*dialogMessage*/,
+ null /*suspendedAppExtras*/,
+ null /*suspendedLauncherExtras*/,
false /*instantApp*/,
false /*virtualPreload*/,
null /*lastDisableAppCaller*/,
@@ -19089,6 +19156,10 @@ public class PackageManagerService extends IPackageManager.Stub
if (dsm != null) {
dsm.checkMemory();
}
+ if (checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId)
+ == PERMISSION_GRANTED) {
+ unsuspendForSuspendingPackage(packageName, userId);
+ }
}
} else {
succeeded = false;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index f8bf9c45e8b1..5c6338dd0a16 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -734,10 +734,10 @@ public final class Settings {
true /*notLaunched*/,
false /*hidden*/,
false /*suspended*/,
- null, /*suspendingPackage*/
- null, /*dialogMessage*/
- null, /*suspendedAppExtras*/
- null, /*suspendedLauncherExtras*/
+ null /*suspendingPackage*/,
+ null /*dialogMessage*/,
+ null /*suspendedAppExtras*/,
+ null /*suspendedLauncherExtras*/,
instantApp,
virtualPreload,
null /*lastDisableAppCaller*/,
@@ -1634,10 +1634,10 @@ public final class Settings {
false /*notLaunched*/,
false /*hidden*/,
false /*suspended*/,
- null, /*suspendingPackage*/
- null, /*dialogMessage*/
- null, /*suspendedAppExtras*/
- null, /*suspendedLauncherExtras*/
+ null /*suspendingPackage*/,
+ null /*dialogMessage*/,
+ null /*suspendedAppExtras*/,
+ null /*suspendedLauncherExtras*/,
false /*instantApp*/,
false /*virtualPreload*/,
null /*lastDisableAppCaller*/,
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index b6b94f5e3cab..ca08415f7a77 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -46,6 +46,7 @@ public final class SharedUserSetting extends SettingBase {
final ArraySet<PackageSetting> packages = new ArraySet<PackageSetting>();
final PackageSignatures signatures = new PackageSignatures();
+ Boolean signaturesChanged;
SharedUserSetting(String _name, int _pkgFlags, int _pkgPrivateFlags) {
super(_pkgFlags, _pkgPrivateFlags);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index f6592257b636..d3526f761301 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1953,7 +1953,7 @@ public class PermissionManagerService {
UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
}
if (userId == UserHandle.getUserId(callingUid)) return;
- if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
+ if (callingUid != Process.SYSTEM_UID && callingUid != Process.ROOT_UID) {
if (requireFullPermission) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 18f3434ece49..e6d851b83aa8 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -6092,14 +6092,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
&& (!isNavBarVirtKey || mNavBarVirtualKeyHapticFeedbackEnabled)
&& event.getRepeatCount() == 0;
- // Cancel any pending remote recents animations before handling the button itself. In the
- // case where we are going home and the recents animation has already started, just cancel
- // the recents animation, leaving the home stack in place for the pending start activity
- if (isNavBarVirtKey && !down && !canceled) {
- boolean isHomeKey = keyCode == KeyEvent.KEYCODE_HOME;
- mActivityManagerInternal.cancelRecentsAnimation(!isHomeKey);
- }
-
// Handle special keys.
switch (keyCode) {
case KeyEvent.KEYCODE_BACK: {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 0ccbb251d2a2..a7c203dca03b 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1303,8 +1303,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
synchronized (mLock) {
if (mCurrentUserId == userId) {
if (mWaitingForUnlock) {
- // If we're switching users, now is when we transition the wallpaper
- switchUser(userId, null);
+ // the desired wallpaper is not direct-boot aware, load it now
+ final WallpaperData systemWallpaper =
+ getWallpaperSafeLocked(userId, FLAG_SYSTEM);
+ switchWallpaper(systemWallpaper, null);
}
// Make sure that the SELinux labeling of all the relevant files is correct.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 9621eddc348a..c0dc750949bc 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1510,6 +1510,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return (stack != null && stack.isVisible()) ? stack : null;
}
+ boolean hasSplitScreenPrimaryStack() {
+ return getSplitScreenPrimaryStack() != null;
+ }
+
/**
* Like {@link #getSplitScreenPrimaryStack}, but also returns the stack if it's currently
* not visible.
@@ -1613,6 +1617,18 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mService.mWindowsChanged = true;
}
+ /**
+ * In split-screen mode we process the IME containers above the docked divider
+ * rather than directly above their target.
+ */
+ private boolean skipTraverseChild(WindowContainer child) {
+ if (child == mImeWindowsContainers && mService.mInputMethodTarget != null
+ && !hasSplitScreenPrimaryStack()) {
+ return true;
+ }
+ return false;
+ }
+
@Override
boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
// Special handling so we can process IME windows with #forAllImeWindows above their IME
@@ -1620,11 +1636,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
if (traverseTopToBottom) {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final DisplayChildWindowContainer child = mChildren.get(i);
- if (child == mImeWindowsContainers && mService.mInputMethodTarget != null) {
- // In this case the Ime windows will be processed above their target so we skip
- // here.
+ if (skipTraverseChild(child)) {
continue;
}
+
if (child.forAllWindows(callback, traverseTopToBottom)) {
return true;
}
@@ -1633,11 +1648,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
final int count = mChildren.size();
for (int i = 0; i < count; i++) {
final DisplayChildWindowContainer child = mChildren.get(i);
- if (child == mImeWindowsContainers && mService.mInputMethodTarget != null) {
- // In this case the Ime windows will be processed above their target so we skip
- // here.
+ if (skipTraverseChild(child)) {
continue;
}
+
if (child.forAllWindows(callback, traverseTopToBottom)) {
return true;
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index c8baced4cd5d..39a362906b9d 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -408,7 +408,7 @@ public class DockedStackDividerController {
}
void positionDockedStackedDivider(Rect frame) {
- TaskStack stack = mDisplayContent.getSplitScreenPrimaryStack();
+ TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
if (stack == null) {
// Unfortunately we might end up with still having a divider, even though the underlying
// stack was already removed. This is because we are on AM thread and the removal of the
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 85e4ac7fe4ce..553b4fe53115 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -79,7 +79,7 @@ public class RecentsAnimationController implements DeathRecipient {
public @interface ReorderMode {}
private final WindowManagerService mService;
- private final IRecentsAnimationRunner mRunner;
+ private IRecentsAnimationRunner mRunner;
private final RecentsAnimationCallbacks mCallbacks;
private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
private final int mDisplayId;
@@ -426,7 +426,10 @@ public class RecentsAnimationController implements DeathRecipient {
removeAnimation(taskAdapter);
}
+ // Clear references to the runner
unlinkToDeathOfRunner();
+ mRunner = null;
+
// Clear associated input consumers
mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
mService.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 0de3c921735e..c710c9789004 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3997,32 +3997,33 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return false;
}
+ private boolean applyImeWindowsIfNeeded(ToBooleanFunction<WindowState> callback,
+ boolean traverseTopToBottom) {
+ // If this window is the current IME target, so we need to process the IME windows
+ // directly above it. The exception is if we are in split screen
+ // in which case we process the IME at the DisplayContent level to
+ // ensure it is above the docked divider.
+ if (isInputMethodTarget() && !inSplitScreenWindowingMode()) {
+ if (getDisplayContent().forAllImeWindows(callback, traverseTopToBottom)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private boolean applyInOrderWithImeWindows(ToBooleanFunction<WindowState> callback,
boolean traverseTopToBottom) {
if (traverseTopToBottom) {
- if (isInputMethodTarget()) {
- // This window is the current IME target, so we need to process the IME windows
- // directly above it.
- if (getDisplayContent().forAllImeWindows(callback, traverseTopToBottom)) {
- return true;
- }
- }
- if (callback.apply(this)) {
+ if (applyImeWindowsIfNeeded(callback, traverseTopToBottom)
+ || callback.apply(this)) {
return true;
}
} else {
- if (callback.apply(this)) {
+ if (callback.apply(this)
+ || applyImeWindowsIfNeeded(callback, traverseTopToBottom)) {
return true;
}
- if (isInputMethodTarget()) {
- // This window is the current IME target, so we need to process the IME windows
- // directly above it.
- if (getDisplayContent().forAllImeWindows(callback, traverseTopToBottom)) {
- return true;
- }
- }
}
-
return false;
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 4b8dcc1e56c1..01425ed51b55 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -565,6 +565,47 @@ public class ActivityStackTests extends ActivityTestsBase {
false /* displaySleeping */, false /* expected*/);
}
+ @Test
+ public void testStackOrderChangedOnRemoveStack() throws Exception {
+ StackOrderChangedListener listener = new StackOrderChangedListener();
+ mDefaultDisplay.registerStackOrderChangedListener(listener);
+ try {
+ mDefaultDisplay.removeChild(mStack);
+ } finally {
+ mDefaultDisplay.unregisterStackOrderChangedListener(listener);
+ }
+ assertTrue(listener.changed);
+ }
+
+ @Test
+ public void testStackOrderChangedOnAddPositionStack() throws Exception {
+ mDefaultDisplay.removeChild(mStack);
+
+ StackOrderChangedListener listener = new StackOrderChangedListener();
+ mDefaultDisplay.registerStackOrderChangedListener(listener);
+ try {
+ mDefaultDisplay.addChild(mStack, 0);
+ } finally {
+ mDefaultDisplay.unregisterStackOrderChangedListener(listener);
+ }
+ assertTrue(listener.changed);
+ }
+
+ @Test
+ public void testStackOrderChangedOnPositionStack() throws Exception {
+ StackOrderChangedListener listener = new StackOrderChangedListener();
+ try {
+ final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+ mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+ true /* onTop */);
+ mDefaultDisplay.registerStackOrderChangedListener(listener);
+ mDefaultDisplay.positionChildAtBottom(fullscreenStack1);
+ } finally {
+ mDefaultDisplay.unregisterStackOrderChangedListener(listener);
+ }
+ assertTrue(listener.changed);
+ }
+
private void verifyShouldSleepActivities(boolean focusedStack,
boolean keyguardGoingAway, boolean displaySleeping, boolean expected) {
mSupervisor.mFocusedStack = focusedStack ? mStack : null;
@@ -578,4 +619,13 @@ public class ActivityStackTests extends ActivityTestsBase {
assertEquals(expected, mStack.shouldSleepActivities());
}
+
+ private class StackOrderChangedListener implements ActivityDisplay.OnStackOrderChangedListener {
+ boolean changed = false;
+
+ @Override
+ public void onStackOrderChanged() {
+ changed = true;
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java b/services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java
new file mode 100644
index 000000000000..eefd973112f5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.IRecentsAnimationRunner;
+import com.android.server.AttributeCache;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * atest FrameworksServicesTests:RecentsAnimationTest
+ */
+@MediumTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class RecentsAnimationTest extends ActivityTestsBase {
+ private static final int TEST_CALLING_PID = 3;
+
+ private Context mContext = InstrumentationRegistry.getContext();
+ private ActivityManagerService mService;
+ private ComponentName mRecentsComponent;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity");
+ mService = setupActivityManagerService(new MyTestActivityManagerService(mContext));
+ AttributeCache.init(mContext);
+ }
+
+ @Test
+ public void testCancelAnimationOnStackOrderChange() throws Exception {
+ ActivityStack fullscreenStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ ActivityStack recentsStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */);
+ ActivityRecord recentsActivity = new ActivityBuilder(mService)
+ .setComponent(mRecentsComponent)
+ .setCreateTask(true)
+ .setStack(recentsStack)
+ .build();
+ ActivityStack fullscreenStack2 = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ ActivityRecord fsActivity = new ActivityBuilder(mService)
+ .setComponent(new ComponentName(mContext.getPackageName(), "App1"))
+ .setCreateTask(true)
+ .setStack(fullscreenStack2)
+ .build();
+ doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation();
+
+ // Start the recents animation
+ Intent recentsIntent = new Intent();
+ recentsIntent.setComponent(mRecentsComponent);
+ mService.startRecentsActivity(recentsIntent, null, mock(IRecentsAnimationRunner.class));
+
+ fullscreenStack.moveToFront("Activity start");
+
+ // Ensure that the recents animation was canceled
+ verify(mService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
+ eq(REORDER_KEEP_IN_PLACE), any());
+ }
+
+ private class MyTestActivityManagerService extends TestActivityManagerService {
+ MyTestActivityManagerService(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected RecentTasks createRecentTasks() {
+ RecentTasks recents = mock(RecentTasks.class);
+ doReturn(mRecentsComponent).when(recents).getRecentsComponent();
+ System.out.println(mRecentsComponent);
+ return recents;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java
new file mode 100644
index 000000000000..e07639933b7a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Matchers.eq;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.mockito.Mock;
+
+import java.util.function.Consumer;
+
+/**
+ * Tests for {@link WindowContainer#forAllWindows} and various implementations.
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class WindowContainerTraversalTests extends WindowTestsBase {
+
+ @Test
+ public void testDockedDividerPosition() throws Exception {
+ final WindowState splitScreenWindow = createWindowOnStack(null,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
+ mDisplayContent, "splitScreenWindow");
+ final WindowState splitScreenSecondaryWindow = createWindowOnStack(null,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
+ TYPE_BASE_APPLICATION, mDisplayContent, "splitScreenSecondaryWindow");
+
+ sWm.mInputMethodTarget = splitScreenWindow;
+
+ Consumer<WindowState> c = mock(Consumer.class);
+ mDisplayContent.forAllWindows(c, false);
+
+ verify(c).accept(eq(mDockedDividerWindow));
+ verify(c).accept(eq(mImeWindow));
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
index 1be164395d23..942a07acbf9f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
@@ -206,15 +206,14 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
assertTrue(mScheduleCalendar.shouldExitForAlarm(1000));
}
- @Ignore
@Test
public void testShouldExitForAlarm_oldAlarm() {
// Cal: today 2:15pm
- Calendar cal = new GregorianCalendar();
- cal.set(Calendar.HOUR_OF_DAY, 14);
- cal.set(Calendar.MINUTE, 15);
- cal.set(Calendar.SECOND, 0);
- cal.set(Calendar.MILLISECOND, 0);
+ Calendar now = new GregorianCalendar();
+ now.set(Calendar.HOUR_OF_DAY, 14);
+ now.set(Calendar.MINUTE, 15);
+ now.set(Calendar.SECOND, 0);
+ now.set(Calendar.MILLISECOND, 0);
// ScheduleInfo: today 12:16pm - today 3:15pm
mScheduleInfo.days = new int[] {getTodayDay()};
@@ -226,10 +225,45 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
mScheduleInfo.nextAlarm = 1000; // very old alarm
mScheduleCalendar.setSchedule(mScheduleInfo);
- assertTrue(mScheduleCalendar.isInSchedule(cal.getTimeInMillis()));
+ assertTrue(mScheduleCalendar.isInSchedule(now.getTimeInMillis()));
// don't exit for an alarm if it's an old alarm
- assertFalse(mScheduleCalendar.shouldExitForAlarm(1000));
+ assertFalse(mScheduleCalendar.shouldExitForAlarm(now.getTimeInMillis()));
+ }
+
+ @Test
+ public void testShouldExitForAlarm_oldAlarmInSchedule() {
+ // calNow: day 2 at 9pm
+ Calendar calNow = new GregorianCalendar();
+ calNow.set(Calendar.HOUR_OF_DAY, 21);
+ calNow.set(Calendar.MINUTE, 0);
+ calNow.set(Calendar.SECOND, 0);
+ calNow.set(Calendar.MILLISECOND, 0);
+ calNow.add(Calendar.DATE, 1); // add a day
+
+ // calAlarm: day 2 at 5am
+ Calendar calAlarm = new GregorianCalendar();
+ calAlarm.set(Calendar.HOUR_OF_DAY, 5);
+ calAlarm.set(Calendar.MINUTE, 0);
+ calAlarm.set(Calendar.SECOND, 0);
+ calAlarm.set(Calendar.MILLISECOND, 0);
+ calAlarm.add(Calendar.DATE, 1); // add a day
+
+ // ScheduleInfo: day 1, day 2: 9pm-7am
+ mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay() + 1};
+ mScheduleInfo.startHour = 21;
+ mScheduleInfo.endHour = 7;
+ mScheduleInfo.startMinute = 0;
+ mScheduleInfo.endMinute = 0;
+ mScheduleInfo.exitAtAlarm = true;
+ mScheduleInfo.nextAlarm = calAlarm.getTimeInMillis(); // old alarm (5am day 2)
+
+ mScheduleCalendar.setSchedule(mScheduleInfo);
+ assertTrue(mScheduleCalendar.isInSchedule(calNow.getTimeInMillis()));
+ assertTrue(mScheduleCalendar.isInSchedule(calAlarm.getTimeInMillis()));
+
+ // don't exit for an alarm if it's an old alarm
+ assertFalse(mScheduleCalendar.shouldExitForAlarm(calNow.getTimeInMillis()));
}
@Test
@@ -369,6 +403,64 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
assertFalse(mScheduleCalendar.isInSchedule(cal.getTimeInMillis()));
}
+ @Test
+ public void testIsAlarmInSchedule_alarmAndNowInSchedule_sameScheduleTrigger() {
+ Calendar alarm = new GregorianCalendar();
+ alarm.set(Calendar.HOUR_OF_DAY, 23);
+ alarm.set(Calendar.MINUTE, 15);
+ alarm.set(Calendar.SECOND, 0);
+ alarm.set(Calendar.MILLISECOND, 0);
+
+ Calendar now = new GregorianCalendar();
+ now.set(Calendar.HOUR_OF_DAY, 2);
+ now.set(Calendar.MINUTE, 15);
+ now.set(Calendar.SECOND, 0);
+ now.set(Calendar.MILLISECOND, 0);
+ now.add(Calendar.DATE, 1); // add a day
+
+ mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay() + 1};
+ mScheduleInfo.startHour = 22;
+ mScheduleInfo.startMinute = 15;
+ mScheduleInfo.endHour = 3;
+ mScheduleInfo.endMinute = 15;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
+
+ assertTrue(mScheduleCalendar.isInSchedule(alarm.getTimeInMillis()));
+ assertTrue(mScheduleCalendar.isInSchedule(now.getTimeInMillis()));
+ assertTrue(mScheduleCalendar.isAlarmInSchedule(alarm.getTimeInMillis(),
+ now.getTimeInMillis()));
+ }
+
+ @Test
+ public void testIsAlarmInSchedule_alarmAndNowInSchedule_differentScheduleTrigger() {
+ Calendar alarm = new GregorianCalendar();
+ alarm.set(Calendar.HOUR_OF_DAY, 23);
+ alarm.set(Calendar.MINUTE, 15);
+ alarm.set(Calendar.SECOND, 0);
+ alarm.set(Calendar.MILLISECOND, 0);
+
+ Calendar now = new GregorianCalendar();
+ now.set(Calendar.HOUR_OF_DAY, 23);
+ now.set(Calendar.MINUTE, 15);
+ now.set(Calendar.SECOND, 0);
+ now.set(Calendar.MILLISECOND, 0);
+ now.add(Calendar.DATE, 1); // add a day
+
+ mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay() + 1};
+ mScheduleInfo.startHour = 22;
+ mScheduleInfo.startMinute = 15;
+ mScheduleInfo.endHour = 3;
+ mScheduleInfo.endMinute = 15;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
+
+ // even though both alarm and now are in schedule, they are not in the same part of
+ // the schedule (alarm is in schedule for the previous day's schedule compared to now)
+ assertTrue(mScheduleCalendar.isInSchedule(alarm.getTimeInMillis()));
+ assertTrue(mScheduleCalendar.isInSchedule(now.getTimeInMillis()));
+ assertFalse(mScheduleCalendar.isAlarmInSchedule(alarm.getTimeInMillis(),
+ now.getTimeInMillis()));
+ }
+
private int getTodayDay() {
return new GregorianCalendar().get(Calendar.DAY_OF_WEEK);
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index cd524a517376..13e3069b302b 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -46,6 +46,10 @@ import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
import android.hardware.soundtrigger.SoundTrigger.SoundModel;
+import android.media.AudioAttributes;
+import android.media.AudioFormat;
+import android.media.AudioRecord;
+import android.media.MediaRecorder;
import android.media.soundtrigger.ISoundTriggerDetectionService;
import android.media.soundtrigger.ISoundTriggerDetectionServiceClient;
import android.media.soundtrigger.SoundTriggerDetectionService;
@@ -654,8 +658,45 @@ public class SoundTriggerService extends SystemService {
}
}
- private interface Operation {
- void run(int opId, ISoundTriggerDetectionService service) throws RemoteException;
+ /**
+ * A single operation run in a {@link RemoteSoundTriggerDetectionService}.
+ *
+ * <p>Once the remote service is connected either setup + execute or setup + stop is executed.
+ */
+ private static class Operation {
+ private interface ExecuteOp {
+ void run(int opId, ISoundTriggerDetectionService service) throws RemoteException;
+ }
+
+ private final @Nullable Runnable mSetupOp;
+ private final @NonNull ExecuteOp mExecuteOp;
+ private final @Nullable Runnable mDropOp;
+
+ private Operation(@Nullable Runnable setupOp, @NonNull ExecuteOp executeOp,
+ @Nullable Runnable cancelOp) {
+ mSetupOp = setupOp;
+ mExecuteOp = executeOp;
+ mDropOp = cancelOp;
+ }
+
+ private void setup() {
+ if (mSetupOp != null) {
+ mSetupOp.run();
+ }
+ }
+
+ void run(int opId, @NonNull ISoundTriggerDetectionService service) throws RemoteException {
+ setup();
+ mExecuteOp.run(opId, service);
+ }
+
+ void drop() {
+ setup();
+
+ if (mDropOp != null) {
+ mDropOp.run();
+ }
+ }
}
/**
@@ -902,6 +943,10 @@ public class SoundTriggerService extends SystemService {
private void runOrAddOperation(Operation op) {
synchronized (mRemoteServiceLock) {
if (mIsDestroyed || mDestroyOnceRunningOpsDone) {
+ Slog.w(TAG, mPuuid + ": Dropped operation as already destroyed or marked for "
+ + "destruction");
+
+ op.drop();
return;
}
@@ -922,9 +967,15 @@ public class SoundTriggerService extends SystemService {
int opsAdded = mNumOps.getOpsAdded();
if (mNumOps.getOpsAdded() >= opsAllowed) {
- if (DEBUG || opsAllowed + 10 > opsAdded) {
- Slog.w(TAG, mPuuid + ": Dropped operation as too many operations were "
- + "run in last 24 hours");
+ try {
+ if (DEBUG || opsAllowed + 10 > opsAdded) {
+ Slog.w(TAG, mPuuid + ": Dropped operation as too many operations "
+ + "were run in last 24 hours");
+ }
+
+ op.drop();
+ } catch (Exception e) {
+ Slog.e(TAG, mPuuid + ": Could not drop operation", e);
}
} else {
mNumOps.addOp(currentTime);
@@ -972,34 +1023,87 @@ public class SoundTriggerService extends SystemService {
+ ")");
}
+ /**
+ * Create an AudioRecord enough for starting and releasing the data buffered for the event.
+ *
+ * @param event The event that was received
+ * @return The initialized AudioRecord
+ */
+ private @NonNull AudioRecord createAudioRecordForEvent(
+ @NonNull SoundTrigger.GenericRecognitionEvent event) {
+ AudioAttributes.Builder attributesBuilder = new AudioAttributes.Builder();
+ attributesBuilder.setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD);
+ AudioAttributes attributes = attributesBuilder.build();
+
+ // Use same AudioFormat processing as in RecognitionEvent.fromParcel
+ AudioFormat originalFormat = event.getCaptureFormat();
+ AudioFormat captureFormat = (new AudioFormat.Builder())
+ .setChannelMask(originalFormat.getChannelMask())
+ .setEncoding(originalFormat.getEncoding())
+ .setSampleRate(originalFormat.getSampleRate())
+ .build();
+
+ int bufferSize = AudioRecord.getMinBufferSize(
+ captureFormat.getSampleRate() == AudioFormat.SAMPLE_RATE_UNSPECIFIED
+ ? AudioFormat.SAMPLE_RATE_HZ_MAX
+ : captureFormat.getSampleRate(),
+ captureFormat.getChannelCount() == 2
+ ? AudioFormat.CHANNEL_IN_STEREO
+ : AudioFormat.CHANNEL_IN_MONO,
+ captureFormat.getEncoding());
+
+ return new AudioRecord(attributes, captureFormat, bufferSize,
+ event.getCaptureSession());
+ }
+
@Override
public void onGenericSoundTriggerDetected(SoundTrigger.GenericRecognitionEvent event) {
if (DEBUG) Slog.v(TAG, mPuuid + ": Generic sound trigger event: " + event);
- runOrAddOperation((opId, service) -> {
- if (!mRecognitionConfig.allowMultipleTriggers) {
- synchronized (mCallbacksLock) {
- mCallbacks.remove(mPuuid.getUuid());
- }
- mDestroyOnceRunningOpsDone = true;
- }
-
- service.onGenericRecognitionEvent(mPuuid, opId, event);
- });
+ runOrAddOperation(new Operation(
+ // always execute:
+ () -> {
+ if (!mRecognitionConfig.allowMultipleTriggers) {
+ // Unregister this remoteService once op is done
+ synchronized (mCallbacksLock) {
+ mCallbacks.remove(mPuuid.getUuid());
+ }
+ mDestroyOnceRunningOpsDone = true;
+ }
+ },
+ // execute if not throttled:
+ (opId, service) -> service.onGenericRecognitionEvent(mPuuid, opId, event),
+ // execute if throttled:
+ () -> {
+ if (event.isCaptureAvailable()) {
+ AudioRecord capturedData = createAudioRecordForEvent(event);
+
+ // Currently we need to start and release the audio record to reset
+ // the DSP even if we don't want to process the event
+ capturedData.startRecording();
+ capturedData.release();
+ }
+ }));
}
@Override
public void onError(int status) {
if (DEBUG) Slog.v(TAG, mPuuid + ": onError: " + status);
- runOrAddOperation((opId, service) -> {
- synchronized (mCallbacksLock) {
- mCallbacks.remove(mPuuid.getUuid());
- }
- mDestroyOnceRunningOpsDone = true;
-
- service.onError(mPuuid, opId, status);
- });
+ runOrAddOperation(
+ new Operation(
+ // always execute:
+ () -> {
+ // Unregister this remoteService once op is done
+ synchronized (mCallbacksLock) {
+ mCallbacks.remove(mPuuid.getUuid());
+ }
+ mDestroyOnceRunningOpsDone = true;
+ },
+ // execute if not throttled:
+ (opId, service) -> service.onError(mPuuid, opId, status),
+ // nothing to do if throttled
+ null));
}
@Override
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 72c67d378158..29898ffbd343 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1299,13 +1299,18 @@ public class TelecomManager {
}
/**
- * Ends an ongoing call.
- * TODO: L-release - need to convert all invocations of ITelecomService#endCall to use this
- * method (clockwork & gearhead).
- * @hide
+ * Ends the foreground call on the device.
+ * <p>
+ * If there is a ringing call, calling this method rejects the ringing call. Otherwise the
+ * foreground call is ended.
+ * <p>
+ * Requires permission {@link android.Manifest.permission#ANSWER_PHONE_CALLS}.
+ *
+ * @return {@code true} if there is a call which will be rejected or terminated, {@code false}
+ * otherwise.
*/
+ @RequiresPermission(Manifest.permission.ANSWER_PHONE_CALLS)
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public boolean endCall() {
try {
if (isServiceConnected()) {
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index 3f9588acf827..4e202df3134e 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -14,13 +14,12 @@
* limitations under the License.
*/
-
#include <frameworks/base/core/proto/android/os/incident.pb.h>
#include <map>
#include <set>
-#include <string>
#include <sstream>
+#include <string>
using namespace android;
using namespace android::os;
@@ -422,7 +421,8 @@ static bool generateSectionListCpp(Descriptor const* descriptor) {
printf(" NULL),\n");
break;
case SECTION_DUMPSYS:
- printf(" new DumpsysSection(%d,", field->number());
+ printf(" new DumpsysSection(%d, \"%s\",", field->number(),
+ s.userdebug_and_eng_only() ? "true" : "false");
splitAndPrint(s.args());
printf(" NULL),\n");
break;