Merge "Statsd notifies listener with pendingintent."
diff --git a/Android.bp b/Android.bp
index 3d56254..c5d64fb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -578,7 +578,6 @@
"wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl",
"wifi/java/android/net/wifi/hotspot2/IProvisioningCallback.aidl",
"wifi/java/android/net/wifi/IWifiScanner.aidl",
- "wifi/java/android/net/wifi/IRttManager.aidl",
"packages/services/PacProcessor/com/android/net/IProxyService.aidl",
"packages/services/Proxy/com/android/net/IProxyCallback.aidl",
"packages/services/Proxy/com/android/net/IProxyPortListener.aidl",
diff --git a/Android.mk b/Android.mk
index a78a01a..7e2f472 100644
--- a/Android.mk
+++ b/Android.mk
@@ -827,35 +827,35 @@
# ==== hiddenapi lists =======================================
-# Copy blacklist and light greylist over into the build folder.
+# Copy light greylist and dark greylist over into the build folder.
# This is for ART buildbots which need to mock these lists and have alternative
# rules for building them. Other rules in the build system should depend on the
# files in the build folder.
-$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-blacklist.txt,\
- $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)))
$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-light-greylist.txt,\
$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)))
+$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-dark-greylist.txt,\
+ $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)))
-# Generate dark greylist as private API minus (blacklist plus light greylist).
+# Generate blacklist as private API minus (light greylist plus dark greylist).
-$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
-$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): BLACKLIST := $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): LIGHT_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
- $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST) \
- $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)
- if [ ! -z "`comm -12 <(sort $(BLACKLIST)) <(sort $(LIGHT_GREYLIST))`" ]; then \
- echo "There should be no overlap between $(BLACKLIST) and $(LIGHT_GREYLIST)" 1>&2; \
+$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST): PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
+$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST): LIGHT_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)
+$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST): DARK_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)
+$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST): $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
+ $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
+ $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)
+ if [ ! -z "`comm -12 <(sort $(LIGHT_GREYLIST)) <(sort $(DARK_GREYLIST))`" ]; then \
+ echo "There should be no overlap between $(LIGHT_GREYLIST) and $(DARK_GREYLIST)" 1>&2; \
exit 1; \
- elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST))`" ]; then \
- echo "$(BLACKLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
- exit 2; \
elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(LIGHT_GREYLIST))`" ]; then \
echo "$(LIGHT_GREYLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
+ exit 2; \
+ elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(DARK_GREYLIST))`" ]; then \
+ echo "$(DARK_GREYLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
exit 3; \
fi
- comm -23 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST) $(LIGHT_GREYLIST)) > $@
+ comm -23 <(sort $(PRIVATE_API)) <(sort $(LIGHT_GREYLIST) $(DARK_GREYLIST)) > $@
# Include subdirectory makefiles
# ============================================================
diff --git a/api/current.txt b/api/current.txt
index c03798f..9204289 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6739,6 +6739,7 @@
field public static final int TAG_APP_PROCESS_START = 210005; // 0x33455
field public static final int TAG_CERT_AUTHORITY_INSTALLED = 210029; // 0x3346d
field public static final int TAG_CERT_AUTHORITY_REMOVED = 210030; // 0x3346e
+ field public static final int TAG_CRYPTO_SELF_TEST_COMPLETED = 210031; // 0x3346f
field public static final int TAG_KEYGUARD_DISABLED_FEATURES_SET = 210021; // 0x33465
field public static final int TAG_KEYGUARD_DISMISSED = 210006; // 0x33456
field public static final int TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT = 210007; // 0x33457
@@ -40504,6 +40505,7 @@
public final class Call {
method public void answer(int);
method public void conference(android.telecom.Call);
+ method public void deflect(android.net.Uri);
method public void disconnect();
method public java.util.List<java.lang.String> getCannedTextResponses();
method public java.util.List<android.telecom.Call> getChildren();
@@ -40614,6 +40616,7 @@
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL = 3072; // 0xc00
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 1024; // 0x400
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800
+ field public static final int CAPABILITY_SUPPORT_DEFLECT = 16777216; // 0x1000000
field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200
@@ -40761,6 +40764,7 @@
method public void onAnswer();
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onCallEvent(java.lang.String, android.os.Bundle);
+ method public void onDeflect(android.net.Uri);
method public void onDisconnect();
method public void onExtrasChanged(android.os.Bundle);
method public void onHandoverComplete();
@@ -40829,6 +40833,7 @@
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL = 3072; // 0xc00
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 1024; // 0x400
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800
+ field public static final int CAPABILITY_SUPPORT_DEFLECT = 33554432; // 0x2000000
field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
field public static final java.lang.String EVENT_CALL_MERGE_FAILED = "android.telecom.event.CALL_MERGE_FAILED";
diff --git a/api/system-current.txt b/api/system-current.txt
index cc36b54..32d235d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5036,22 +5036,19 @@
}
public class UiccSlotInfo implements android.os.Parcelable {
- ctor public UiccSlotInfo(boolean, boolean, java.lang.String, int);
+ ctor public UiccSlotInfo(boolean, boolean, java.lang.String, int, int);
method public int describeContents();
method public java.lang.String getCardId();
method public int getCardStateInfo();
method public boolean getIsActive();
method public boolean getIsEuicc();
+ method public int getLogicalSlotIdx();
method public void writeToParcel(android.os.Parcel, int);
field public static final int CARD_STATE_INFO_ABSENT = 1; // 0x1
field public static final int CARD_STATE_INFO_ERROR = 3; // 0x3
field public static final int CARD_STATE_INFO_PRESENT = 2; // 0x2
field public static final int CARD_STATE_INFO_RESTRICTED = 4; // 0x4
field public static final android.os.Parcelable.Creator<android.telephony.UiccSlotInfo> CREATOR;
- field public final java.lang.String cardId;
- field public final int cardStateInfo;
- field public final boolean isActive;
- field public final boolean isEuicc;
}
public abstract class VisualVoicemailService extends android.app.Service {
@@ -5712,6 +5709,7 @@
ctor public ImsCallSessionImplBase();
method public void accept(int, android.telephony.ims.ImsStreamMediaProfile);
method public void close();
+ method public void deflect(java.lang.String);
method public void extendToConference(java.lang.String[]);
method public java.lang.String getCallId();
method public android.telephony.ims.ImsCallProfile getCallProfile();
diff --git a/api/test-current.txt b/api/test-current.txt
index 4cfe401..b02da04 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1038,6 +1038,7 @@
public class AccessibilityNodeInfo implements android.os.Parcelable {
method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
+ method public void writeToParcelNoRecycle(android.os.Parcel, int);
}
public final class AccessibilityWindowInfo implements android.os.Parcelable {
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index b46c5c1..90158a0 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -44,6 +44,7 @@
src/external/KernelUidCpuActiveTimeReader.cpp \
src/external/KernelUidCpuClusterTimeReader.cpp \
src/external/StatsPullerManagerImpl.cpp \
+ src/external/puller_util.cpp \
src/logd/LogEvent.cpp \
src/logd/LogListener.cpp \
src/logd/LogReader.cpp \
@@ -175,6 +176,7 @@
tests/AnomalyMonitor_test.cpp \
tests/anomaly/AnomalyTracker_test.cpp \
tests/ConfigManager_test.cpp \
+ tests/external/puller_util_test.cpp \
tests/indexed_priority_queue_test.cpp \
tests/LogEntryMatcher_test.cpp \
tests/LogReader_test.cpp \
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index f0eaeff..8483b02 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -181,7 +181,9 @@
return toString().compare(that.toString()) < 0;
};
-
+bool compareDimensionsValue(const DimensionsValue& s1, const DimensionsValue& s2) {
+ return EqualsTo(s1, s2);
+}
} // namespace statsd
} // namespace os
} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 0e90f2a..183a995 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -77,6 +77,7 @@
: mAnomalyMonitor(new AnomalyMonitor(MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS))
{
mUidMap = new UidMap();
+ StatsPuller::SetUidMap(mUidMap);
mConfigManager = new ConfigManager();
mProcessor = new StatsLogProcessor(mUidMap, mAnomalyMonitor, time(nullptr), [this](const ConfigKey& key) {
sp<IStatsCompanionService> sc = getStatsCompanionService();
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index 41e4705..fc0ad7c 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -19,6 +19,7 @@
#include "StatsPuller.h"
#include "guardrail/StatsdStats.h"
+#include "puller_util.h"
namespace android {
namespace os {
@@ -26,6 +27,9 @@
using std::lock_guard;
+sp<UidMap> StatsPuller::mUidMap = nullptr;
+void StatsPuller::SetUidMap(const sp<UidMap>& uidMap) { mUidMap = uidMap; }
+
// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
StatsPuller::StatsPuller(const int tagId)
: mTagId(tagId) {
@@ -54,7 +58,8 @@
mLastPullTimeSec = curTime;
bool ret = PullInternal(&mCachedData);
if (ret) {
- (*data) = mCachedData;
+ mergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId);
+ (*data) = mCachedData;
}
return ret;
}
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index 3446f9b..82a8611 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -18,11 +18,14 @@
#include <android/os/StatsLogEventWrapper.h>
#include <utils/String16.h>
+#include <utils/RefBase.h>
#include <mutex>
#include <vector>
+#include "packages/UidMap.h"
-#include "logd/LogEvent.h"
#include "guardrail/StatsdStats.h"
+#include "logd/LogEvent.h"
+#include "puller_util.h"
using android::os::StatsLogEventWrapper;
@@ -30,7 +33,7 @@
namespace os {
namespace statsd {
-class StatsPuller {
+class StatsPuller : public virtual RefBase {
public:
StatsPuller(const int tagId);
@@ -44,7 +47,9 @@
// Clear cache if elapsed time is more than cooldown time
int ClearCacheIfNecessary(long timestampSec);
-protected:
+ static void SetUidMap(const sp<UidMap>& uidMap);
+
+ protected:
// The atom tag id this puller pulls
const int mTagId;
@@ -67,6 +72,8 @@
long mLastPullTimeSec;
int clearCache();
+
+ static sp<UidMap> mUidMap;
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index 19b3a86..0b772b3 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true
+#define DEBUG false
#include "Log.h"
#include <android/os/IStatsCompanionService.h>
@@ -24,6 +24,9 @@
#include "CpuTimePerUidFreqPuller.h"
#include "CpuTimePerUidPuller.h"
#include "ResourceHealthManagerPuller.h"
+#include "KernelUidCpuActiveTimeReader.h"
+#include "KernelUidCpuClusterTimeReader.h"
+#include "SubsystemSleepStatePuller.h"
#include "StatsCompanionServicePuller.h"
#include "StatsPullerManagerImpl.h"
#include "StatsService.h"
@@ -44,60 +47,130 @@
namespace os {
namespace statsd {
+const std::map<int, PullAtomInfo> StatsPullerManagerImpl::kAllPullAtomInfo = {
+ // wifi_bytes_transfer
+ {android::util::WIFI_BYTES_TRANSFER,
+ {{2, 3, 4, 5},
+ {},
+ 1,
+ new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER)}},
+ // wifi_bytes_transfer_by_fg_bg
+ {android::util::WIFI_BYTES_TRANSFER_BY_FG_BG,
+ {{3, 4, 5, 6},
+ {2},
+ 1,
+ new StatsCompanionServicePuller(
+ android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}},
+ // mobile_bytes_transfer
+ {android::util::MOBILE_BYTES_TRANSFER,
+ {{2, 3, 4, 5},
+ {},
+ 1,
+ new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER)}},
+ // mobile_bytes_transfer_by_fg_bg
+ {android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
+ {{3, 4, 5, 6},
+ {2},
+ 1,
+ new StatsCompanionServicePuller(
+ android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}},
+ // bluetooth_bytes_transfer
+ {android::util::BLUETOOTH_BYTES_TRANSFER,
+ {{2, 3},
+ {},
+ 1,
+ new StatsCompanionServicePuller(
+ android::util::BLUETOOTH_BYTES_TRANSFER)}},
+ // kernel_wakelock
+ {android::util::KERNEL_WAKELOCK,
+ {{},
+ {},
+ 1,
+ new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
+ // subsystem_sleep_state
+ {android::util::SUBSYSTEM_SLEEP_STATE,
+ {{},
+ {},
+ 1,
+ new StatsCompanionServicePuller(android::util::SUBSYSTEM_SLEEP_STATE)}},
+ // cpu_time_per_freq
+ {android::util::CPU_TIME_PER_FREQ,
+ {{3},
+ {2},
+ 1,
+ new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
+ // cpu_time_per_uid
+ {android::util::CPU_TIME_PER_UID,
+ {{2, 3}, {}, 1, new CpuTimePerUidPuller()}},
+ // cpu_time_per_uid_freq
+ {android::util::CPU_TIME_PER_UID_FREQ,
+ {{3}, {2}, 1, new CpuTimePerUidFreqPuller()}},
+ // wifi_activity_energy_info
+ {android::util::WIFI_ACTIVITY_ENERGY_INFO,
+ {{},
+ {},
+ 1,
+ new StatsCompanionServicePuller(
+ android::util::WIFI_ACTIVITY_ENERGY_INFO)}},
+ // modem_activity_info
+ {android::util::MODEM_ACTIVITY_INFO,
+ {{},
+ {},
+ 1,
+ new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
+ // bluetooth_activity_info
+ {android::util::BLUETOOTH_ACTIVITY_INFO,
+ {{},
+ {},
+ 1,
+ new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
+ // system_elapsed_realtime
+ {android::util::SYSTEM_ELAPSED_REALTIME,
+ {{},
+ {},
+ 1,
+ new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
+ // system_uptime
+ {android::util::SYSTEM_UPTIME,
+ {{},
+ {},
+ 1,
+ new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
+ // cpu_active_time
+ {android::util::CPU_ACTIVE_TIME,
+ {{3}, {2}, 1, new KernelUidCpuActiveTimeReader()}},
+ // cpu_cluster_time
+ {android::util::CPU_CLUSTER_TIME,
+ {{3}, {2}, 1, new KernelUidCpuClusterTimeReader()}},
+ // disk_space
+ {android::util::DISK_SPACE,
+ {{}, {}, 1, new StatsCompanionServicePuller(android::util::DISK_SPACE)}},
+ // remaining_battery_capacity
+ {android::util::REMAINING_BATTERY_CAPACITY,
+ {{},
+ {},
+ 1,
+ new ResourceHealthManagerPuller(
+ android::util::REMAINING_BATTERY_CAPACITY)}},
+ // full_battery_capacity
+ {android::util::FULL_BATTERY_CAPACITY,
+ {{},
+ {},
+ 1,
+ new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}}};
+
StatsPullerManagerImpl::StatsPullerManagerImpl()
: mCurrentPullingInterval(LONG_MAX) {
- mPullers.insert({android::util::KERNEL_WAKELOCK,
- make_shared<StatsCompanionServicePuller>(android::util::KERNEL_WAKELOCK)});
- mPullers.insert({android::util::WIFI_BYTES_TRANSFER,
- make_shared<StatsCompanionServicePuller>(android::util::WIFI_BYTES_TRANSFER)});
- mPullers.insert(
- {android::util::MOBILE_BYTES_TRANSFER,
- make_shared<StatsCompanionServicePuller>(android::util::MOBILE_BYTES_TRANSFER)});
- mPullers.insert({android::util::WIFI_BYTES_TRANSFER_BY_FG_BG,
- make_shared<StatsCompanionServicePuller>(
- android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)});
- mPullers.insert({android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
- make_shared<StatsCompanionServicePuller>(
- android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)});
- mPullers.insert(
- {android::util::SUBSYSTEM_SLEEP_STATE,
- make_shared<SubsystemSleepStatePuller>()});
- mPullers.insert({android::util::CPU_TIME_PER_FREQ, make_shared<StatsCompanionServicePuller>(android::util::CPU_TIME_PER_FREQ)});
- mPullers.insert({android::util::CPU_TIME_PER_UID, make_shared<CpuTimePerUidPuller>()});
- mPullers.insert({android::util::CPU_TIME_PER_UID_FREQ, make_shared<CpuTimePerUidFreqPuller>()});
- mPullers.insert(
- {android::util::SYSTEM_ELAPSED_REALTIME,
- make_shared<StatsCompanionServicePuller>(android::util::SYSTEM_ELAPSED_REALTIME)});
- mPullers.insert({android::util::SYSTEM_UPTIME,
- make_shared<StatsCompanionServicePuller>(android::util::SYSTEM_UPTIME)});
- mPullers.insert({android::util::DISK_SPACE,
- make_shared<StatsCompanionServicePuller>(android::util::DISK_SPACE)});
- mPullers.insert(
- {android::util::BLUETOOTH_ACTIVITY_INFO,
- make_shared<StatsCompanionServicePuller>(android::util::BLUETOOTH_ACTIVITY_INFO)});
-
- mPullers.insert(
- {android::util::BLUETOOTH_BYTES_TRANSFER,
- make_shared<StatsCompanionServicePuller>(android::util::BLUETOOTH_BYTES_TRANSFER)});
- mPullers.insert(
- {android::util::WIFI_ACTIVITY_ENERGY_INFO,
- make_shared<StatsCompanionServicePuller>(android::util::WIFI_ACTIVITY_ENERGY_INFO)});
- mPullers.insert({android::util::MODEM_ACTIVITY_INFO,
- make_shared<StatsCompanionServicePuller>(android::util::MODEM_ACTIVITY_INFO)});
- mPullers.insert({android::util::REMAINING_BATTERY_CAPACITY,
- make_shared<ResourceHealthManagerPuller>(android::util::REMAINING_BATTERY_CAPACITY)});
- mPullers.insert({android::util::FULL_BATTERY_CAPACITY,
- make_shared<ResourceHealthManagerPuller>(android::util::FULL_BATTERY_CAPACITY)});
mStatsCompanionService = StatsService::getStatsCompanionService();
}
bool StatsPullerManagerImpl::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) {
if (DEBUG) ALOGD("Initiating pulling %d", tagId);
- if (mPullers.find(tagId) != mPullers.end()) {
- bool ret = mPullers.find(tagId)->second->Pull(data);
- ALOGD("pulled %d items", (int)data->size());
- return ret;
+ if (kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end()) {
+ bool ret = kAllPullAtomInfo.find(tagId)->second.puller->Pull(data);
+ ALOGD("pulled %d items", (int)data->size());
+ return ret;
} else {
ALOGD("Unknown tagId %d", tagId);
return false; // Return early since we don't know what to pull.
@@ -110,7 +183,7 @@
}
bool StatsPullerManagerImpl::PullerForMatcherExists(int tagId) const {
- return mPullers.find(tagId) != mPullers.end();
+ return kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end();
}
void StatsPullerManagerImpl::RegisterReceiver(int tagId, wp<PullDataReceiver> receiver,
@@ -201,16 +274,16 @@
int StatsPullerManagerImpl::ForceClearPullerCache() {
int totalCleared = 0;
- for (auto puller : mPullers) {
- totalCleared += puller.second->ForceClearCache();
+ for (const auto& pulledAtom : kAllPullAtomInfo) {
+ totalCleared += pulledAtom.second.puller->ForceClearCache();
}
return totalCleared;
}
int StatsPullerManagerImpl::ClearPullerCacheIfNecessary(long timestampSec) {
int totalCleared = 0;
- for (auto puller : mPullers) {
- totalCleared += puller.second->ClearCacheIfNecessary(timestampSec);
+ for (const auto& pulledAtom : kAllPullAtomInfo) {
+ totalCleared += pulledAtom.second.puller->ClearCacheIfNecessary(timestampSec);
}
return totalCleared;
}
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.h b/cmds/statsd/src/external/StatsPullerManagerImpl.h
index 3535fa3..76a4c14 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.h
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.h
@@ -32,6 +32,20 @@
namespace os {
namespace statsd {
+typedef struct {
+ // The field numbers of the fields that need to be summed when merging
+ // isolated uid with host uid.
+ std::vector<int> additiveFields;
+ // The field numbers of the fields that can't be merged when merging
+ // data belong to isolated uid and host uid.
+ std::vector<int> nonAdditiveFields;
+ // How long should the puller wait before doing an actual pull again. Default
+ // 1 sec. Set this to 0 if this is handled elsewhere.
+ long coolDownSec = 1;
+ // The actual puller
+ sp<StatsPuller> puller;
+} PullAtomInfo;
+
class StatsPullerManagerImpl : public virtual RefBase {
public:
static StatsPullerManagerImpl& GetInstance();
@@ -53,7 +67,9 @@
int ClearPullerCacheIfNecessary(long timestampSec);
-private:
+ const static std::map<int, PullAtomInfo> kAllPullAtomInfo;
+
+ private:
StatsPullerManagerImpl();
// use this to update alarm
@@ -61,9 +77,6 @@
sp<IStatsCompanionService> get_stats_companion_service();
- // mapping from simple matcher tagId to puller
- std::map<int, std::shared_ptr<StatsPuller>> mPullers;
-
typedef struct {
// pull_interval_sec : last_pull_time_sec
std::pair<uint64_t, uint64_t> timeInfo;
@@ -81,8 +94,6 @@
// bucket size. All pulled metrics start pulling based on this time, so that they can be
// correctly attributed to the correct buckets.
long mTimeBaseSec;
-
- LogEvent parse_pulled_data(String16 data);
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp
new file mode 100644
index 0000000..7cfc1d48
--- /dev/null
+++ b/cmds/statsd/src/external/puller_util.cpp
@@ -0,0 +1,156 @@
+/*
+ * 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.
+ */
+
+#define DEBUG false // STOPSHIP if true
+#include "Log.h"
+
+#include "StatsPullerManagerImpl.h"
+#include "field_util.h"
+#include "puller_util.h"
+#include "statslog.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::map;
+using std::shared_ptr;
+using std::vector;
+
+DimensionsValue* getFieldValue(shared_ptr<LogEvent> event, int tagId, int fieldNum) {
+ Field field;
+ buildSimpleAtomField(tagId, fieldNum, &field);
+ return event->findFieldValueOrNull(field);
+}
+
+bool shouldMerge(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs,
+ const vector<int>& nonAdditiveFields, int tagId) {
+ for (int f : nonAdditiveFields) {
+ DimensionsValue* lValue = getFieldValue(lhs, tagId, f);
+ DimensionsValue* rValue = getFieldValue(rhs, tagId, f);
+ if (!compareDimensionsValue(*lValue, *rValue)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// merge rhs to lhs
+void mergeEvent(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs,
+ const vector<int>& additiveFields, int tagId) {
+ for (int f : additiveFields) {
+ DimensionsValue* lValue = getFieldValue(lhs, tagId, f);
+ DimensionsValue* rValue = getFieldValue(rhs, tagId, f);
+ if (lValue->has_value_int()) {
+ lValue->set_value_int(lValue->value_int() + rValue->value_int());
+ } else if (lValue->has_value_long()) {
+ lValue->set_value_long(lValue->value_long() + rValue->value_long());
+ }
+ }
+}
+
+// process all data and merge isolated with host if necessary
+void mergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data,
+ const sp<UidMap>& uidMap, int tagId) {
+ if (StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId) ==
+ StatsPullerManagerImpl::kAllPullAtomInfo.end()) {
+ VLOG("Unknown pull atom id %d", tagId);
+ return;
+ }
+ if (android::util::kAtomsWithUidField.find(tagId) ==
+ android::util::kAtomsWithUidField.end()) {
+ VLOG("No uid to merge for atom %d", tagId);
+ return;
+ }
+ const vector<int>& additiveFields =
+ StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)
+ ->second.additiveFields;
+ const vector<int>& nonAdditiveFields =
+ StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)
+ ->second.nonAdditiveFields;
+
+ // map of host uid to isolated uid data index in the original vector.
+ // because of non additive fields, there could be multiple of them that can't
+ // be merged into one
+ map<int, vector<int>> hostToIsolated;
+ // map of host uid to their position in the original vector
+ map<int, vector<int>> hostPosition;
+ vector<int> isolatedUidPos;
+ // all uids in the original vector
+ vector<int> allUids;
+ for (size_t i = 0; i < data.size(); i++) {
+ // uid field is always first primitive filed, if present
+ DimensionsValue* uidField = getFieldValue(data[i], tagId, 1);
+ if (!uidField) {
+ VLOG("Bad data for %d, %s", tagId, data[i]->ToString().c_str());
+ return;
+ }
+ int uid = uidField->value_int();
+ allUids.push_back(uid);
+ const int hostUid = uidMap->getHostUidOrSelf(uid);
+ if (hostUid != uid) {
+ uidField->set_value_int(hostUid);
+ hostToIsolated[hostUid].push_back(i);
+ isolatedUidPos.push_back(i);
+ }
+ }
+ vector<shared_ptr<LogEvent>> mergedData;
+ for (size_t i = 0; i < allUids.size(); i++) {
+ if (hostToIsolated.find(allUids[i]) != hostToIsolated.end()) {
+ hostPosition[allUids[i]].push_back(i);
+ } else if (std::find(isolatedUidPos.begin(), isolatedUidPos.end(), i) != isolatedUidPos.end()) {
+ continue;
+ } else {
+ mergedData.push_back(data[i]);
+ }
+ }
+ for (auto iter = hostToIsolated.begin(); iter != hostToIsolated.end();
+ iter++) {
+ int uid = iter->first;
+ vector<int>& isolated = hostToIsolated[uid];
+ vector<int> toBeMerged;
+ toBeMerged.insert(toBeMerged.begin(), isolated.begin(), isolated.end());
+ if (hostPosition.find(uid) != hostPosition.end()) {
+ vector<int>& host = hostPosition[uid];
+ toBeMerged.insert(toBeMerged.end(), host.begin(), host.end());
+ }
+ vector<bool> used(toBeMerged.size());
+ for (size_t i = 0; i < toBeMerged.size(); i++) {
+ if (used[i] == true) {
+ continue;
+ }
+ for (size_t j = i + 1; j < toBeMerged.size(); j++) {
+ shared_ptr<LogEvent>& lhs = data[toBeMerged[i]];
+ shared_ptr<LogEvent>& rhs = data[toBeMerged[j]];
+ if (shouldMerge(lhs, rhs, nonAdditiveFields, tagId)) {
+ mergeEvent(lhs, rhs, additiveFields, tagId);
+ used[j] = true;
+ }
+ }
+ }
+ for (size_t i = 0; i < toBeMerged.size(); i++) {
+ if (used[i] == false) {
+ mergedData.push_back(data[i]);
+ }
+ }
+ }
+ data.clear();
+ data = mergedData;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/puller_util.h b/cmds/statsd/src/external/puller_util.h
new file mode 100644
index 0000000..70d5321
--- /dev/null
+++ b/cmds/statsd/src/external/puller_util.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <vector>
+#include "HashableDimensionKey.h"
+#include "StatsPuller.h"
+#include "logd/LogEvent.h"
+#include "packages/UidMap.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+void mergeIsolatedUidsToHostUid(std::vector<std::shared_ptr<LogEvent>>& data,
+ const sp<UidMap>& uidMap, int tagId);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 1dcd853..c8857aa 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -294,6 +294,28 @@
}
}
+int LogEvent::GetInt(size_t key, status_t* err) const {
+ DimensionsValue value;
+ if (!GetSimpleAtomDimensionsValueProto(key, &value)) {
+ *err = BAD_INDEX;
+ return 0;
+ }
+ const DimensionsValue* leafValue = getSingleLeafValue(&value);
+ switch (leafValue->value_case()) {
+ case DimensionsValue::ValueCase::kValueInt:
+ return leafValue->value_int();
+ case DimensionsValue::ValueCase::kValueLong:
+ case DimensionsValue::ValueCase::kValueBool:
+ case DimensionsValue::ValueCase::kValueFloat:
+ case DimensionsValue::ValueCase::kValueTuple:
+ case DimensionsValue::ValueCase::kValueStr:
+ case DimensionsValue::ValueCase::VALUE_NOT_SET: {
+ *err = BAD_TYPE;
+ return 0;
+ }
+ }
+}
+
const char* LogEvent::GetString(size_t key, status_t* err) const {
DimensionsValue value;
if (!GetSimpleAtomDimensionsValueProto(key, &value)) {
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index eb2c008..d521e09 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -76,6 +76,7 @@
* Returns BAD_TYPE if the index is available but the data is the wrong type.
*/
int64_t GetLong(size_t key, status_t* err) const;
+ int GetInt(size_t key, status_t* err) const;
const char* GetString(size_t key, status_t* err) const;
bool GetBool(size_t key, status_t* err) const;
float GetFloat(size_t key, status_t* err) const;
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index 3304f6c..f1da452 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -91,7 +91,7 @@
void removeIsolatedUid(int isolatedUid, int parentUid);
// Returns the host uid if it exists. Otherwise, returns the same uid that was passed-in.
- int getHostUidOrSelf(int uid) const;
+ virtual int getHostUidOrSelf(int uid) const;
// Gets the output. If every config key has received the output, then the output is cleared.
UidMapping getOutput(const ConfigKey& key);
diff --git a/cmds/statsd/tests/external/puller_util_test.cpp b/cmds/statsd/tests/external/puller_util_test.cpp
new file mode 100644
index 0000000..7d9c8a8
--- /dev/null
+++ b/cmds/statsd/tests/external/puller_util_test.cpp
@@ -0,0 +1,269 @@
+// 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.
+
+#include "external/puller_util.h"
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <vector>
+#include "../metrics/metrics_test_helper.h"
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using namespace testing;
+using std::make_shared;
+using std::shared_ptr;
+using std::vector;
+using testing::Contains;
+/*
+ * Test merge isolated and host uid
+ */
+
+int uidAtomTagId = android::util::CPU_TIME_PER_UID_FREQ;
+int nonUidAtomTagId = android::util::SYSTEM_UPTIME;
+int timestamp = 1234;
+int isolatedUid = 30;
+int isolatedAdditiveData = 31;
+int isolatedNonAdditiveData = 32;
+int hostUid = 20;
+int hostAdditiveData = 21;
+int hostNonAdditiveData = 22;
+
+void extractIntoVector(vector<shared_ptr<LogEvent>> events,
+ vector<vector<int>>& ret) {
+ ret.clear();
+ status_t err;
+ for (const auto& event : events) {
+ vector<int> vec;
+ vec.push_back(event->GetInt(1, &err));
+ vec.push_back(event->GetInt(2, &err));
+ vec.push_back(event->GetInt(3, &err));
+ ret.push_back(vec);
+ }
+}
+
+TEST(puller_util, MergeNoDimension) {
+ vector<shared_ptr<LogEvent>> inputData;
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ // 30->22->31
+ event->write(isolatedUid);
+ event->write(hostNonAdditiveData);
+ event->write(isolatedAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ // 20->22->21
+ event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ event->write(hostUid);
+ event->write(hostNonAdditiveData);
+ event->write(hostAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
+ .WillRepeatedly(Return(hostUid));
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
+ .WillRepeatedly(ReturnArg<0>());
+ mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+
+ vector<vector<int>> actual;
+ extractIntoVector(inputData, actual);
+ vector<int> expectedV1 = {20, 22, 52};
+ EXPECT_EQ(1, (int)actual.size());
+ EXPECT_THAT(actual, Contains(expectedV1));
+}
+
+TEST(puller_util, MergeWithDimension) {
+ vector<shared_ptr<LogEvent>> inputData;
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ // 30->32->31
+ event->write(isolatedUid);
+ event->write(isolatedNonAdditiveData);
+ event->write(isolatedAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ // 20->32->21
+ event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ event->write(hostUid);
+ event->write(isolatedNonAdditiveData);
+ event->write(hostAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ // 20->22->21
+ event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ event->write(hostUid);
+ event->write(hostNonAdditiveData);
+ event->write(hostAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
+ .WillRepeatedly(Return(hostUid));
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
+ .WillRepeatedly(ReturnArg<0>());
+ mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+
+ vector<vector<int>> actual;
+ extractIntoVector(inputData, actual);
+ vector<int> expectedV1 = {20, 22, 21};
+ vector<int> expectedV2 = {20, 32, 52};
+ EXPECT_EQ(2, (int)actual.size());
+ EXPECT_THAT(actual, Contains(expectedV1));
+ EXPECT_THAT(actual, Contains(expectedV2));
+}
+
+TEST(puller_util, NoMergeHostUidOnly) {
+ vector<shared_ptr<LogEvent>> inputData;
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ // 20->32->31
+ event->write(hostUid);
+ event->write(isolatedNonAdditiveData);
+ event->write(isolatedAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ // 20->22->21
+ event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ event->write(hostUid);
+ event->write(hostNonAdditiveData);
+ event->write(hostAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
+ .WillRepeatedly(Return(hostUid));
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
+ .WillRepeatedly(ReturnArg<0>());
+ mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+
+ // 20->32->31
+ // 20->22->21
+ vector<vector<int>> actual;
+ extractIntoVector(inputData, actual);
+ vector<int> expectedV1 = {20, 32, 31};
+ vector<int> expectedV2 = {20, 22, 21};
+ EXPECT_EQ(2, (int)actual.size());
+ EXPECT_THAT(actual, Contains(expectedV1));
+ EXPECT_THAT(actual, Contains(expectedV2));
+}
+
+TEST(puller_util, IsolatedUidOnly) {
+ vector<shared_ptr<LogEvent>> inputData;
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ // 30->32->31
+ event->write(hostUid);
+ event->write(isolatedNonAdditiveData);
+ event->write(isolatedAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ // 30->22->21
+ event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ event->write(hostUid);
+ event->write(hostNonAdditiveData);
+ event->write(hostAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
+ .WillRepeatedly(Return(hostUid));
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
+ .WillRepeatedly(ReturnArg<0>());
+ mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+
+ // 20->32->31
+ // 20->22->21
+ vector<vector<int>> actual;
+ extractIntoVector(inputData, actual);
+ vector<int> expectedV1 = {20, 32, 31};
+ vector<int> expectedV2 = {20, 22, 21};
+ EXPECT_EQ(2, (int)actual.size());
+ EXPECT_THAT(actual, Contains(expectedV1));
+ EXPECT_THAT(actual, Contains(expectedV2));
+}
+
+TEST(puller_util, MultipleIsolatedUidToOneHostUid) {
+ vector<shared_ptr<LogEvent>> inputData;
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ // 30->32->31
+ event->write(isolatedUid);
+ event->write(isolatedNonAdditiveData);
+ event->write(isolatedAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ // 31->32->21
+ event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ event->write(isolatedUid + 1);
+ event->write(isolatedNonAdditiveData);
+ event->write(hostAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ // 20->32->21
+ event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ event->write(hostUid);
+ event->write(isolatedNonAdditiveData);
+ event->write(hostAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(Return(hostUid));
+ mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+
+ vector<vector<int>> actual;
+ extractIntoVector(inputData, actual);
+ vector<int> expectedV1 = {20, 32, 73};
+ EXPECT_EQ(1, (int)actual.size());
+ EXPECT_THAT(actual, Contains(expectedV1));
+}
+
+TEST(puller_util, NoNeedToMerge) {
+ vector<shared_ptr<LogEvent>> inputData;
+ shared_ptr<LogEvent> event =
+ make_shared<LogEvent>(nonUidAtomTagId, timestamp);
+ // 32
+ event->write(isolatedNonAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ event = make_shared<LogEvent>(nonUidAtomTagId, timestamp);
+ // 22
+ event->write(hostNonAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+ mergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId);
+
+ EXPECT_EQ(2, (int)inputData.size());
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index 0a97456..b48de54 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -15,6 +15,7 @@
#include "src/condition/ConditionWizard.h"
#include "src/external/StatsPullerManager.h"
+#include "src/packages/UidMap.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -40,6 +41,11 @@
MOCK_METHOD2(Pull, bool(const int pullCode, vector<std::shared_ptr<LogEvent>>* data));
};
+class MockUidMap : public UidMap {
+ public:
+ MOCK_CONST_METHOD1(getHostUidOrSelf, int(int uid));
+};
+
HashableDimensionKey getMockedDimensionKey(int tagId, int key, std::string value);
MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, std::string value);
diff --git a/config/hiddenapi-blacklist.txt b/config/hiddenapi-blacklist.txt
deleted file mode 100644
index e69de29..0000000
--- a/config/hiddenapi-blacklist.txt
+++ /dev/null
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 2b648ea..eb26026 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -609,18 +609,19 @@
/**
* A key was pressed down.
+ * <p>
+ * If the focused view didn't want this event, this method is called.
+ * <p>
+ * Default implementation consumes {@link KeyEvent#KEYCODE_BACK KEYCODE_BACK}
+ * and, as of {@link android.os.Build.VERSION_CODES#P P}, {@link KeyEvent#KEYCODE_ESCAPE
+ * KEYCODE_ESCAPE} to later handle them in {@link #onKeyUp}.
*
- * <p>If the focused view didn't want this event, this method is called.
- *
- * <p>The default implementation consumed the KEYCODE_BACK to later
- * handle it in {@link #onKeyUp}.
- *
* @see #onKeyUp
* @see android.view.KeyEvent
*/
@Override
public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK) {
+ if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) {
event.startTracking();
return true;
}
@@ -640,16 +641,18 @@
/**
* A key was released.
- *
- * <p>The default implementation handles KEYCODE_BACK to close the
- * dialog.
+ * <p>
+ * Default implementation consumes {@link KeyEvent#KEYCODE_BACK KEYCODE_BACK}
+ * and, as of {@link android.os.Build.VERSION_CODES#P P}, {@link KeyEvent#KEYCODE_ESCAPE
+ * KEYCODE_ESCAPE} to close the dialog.
*
* @see #onKeyDown
- * @see KeyEvent
+ * @see android.view.KeyEvent
*/
@Override
public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
+ if ((keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE)
+ && event.isTracking()
&& !event.isCanceled()) {
onBackPressed();
return true;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f4836b7..c805658 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4616,10 +4616,15 @@
if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
for (int i=0; i<N; i++) {
Action action = mActions.get(i);
- validRemoteInput |= hasValidRemoteInput(action);
+ boolean actionHasValidInput = hasValidRemoteInput(action);
+ validRemoteInput |= actionHasValidInput;
final RemoteViews button = generateActionButton(action, emphazisedMode,
i % 2 != 0, p.ambient);
+ if (actionHasValidInput) {
+ // Clear the drawable
+ button.setInt(R.id.action0, "setBackgroundResource", 0);
+ }
big.addView(R.id.actions, button);
}
} else {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 87f32b2..aa52cde 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -38,7 +38,6 @@
import android.content.Context;
import android.content.IRestrictionsManager;
import android.content.RestrictionsManager;
-import android.content.pm.ApplicationInfo;
import android.content.pm.CrossProfileApps;
import android.content.pm.ICrossProfileApps;
import android.content.pm.IShortcutService;
@@ -90,7 +89,6 @@
import android.net.lowpan.LowpanManager;
import android.net.nsd.INsdManager;
import android.net.nsd.NsdManager;
-import android.net.wifi.IRttManager;
import android.net.wifi.IWifiManager;
import android.net.wifi.IWifiScanner;
import android.net.wifi.RttManager;
@@ -117,7 +115,6 @@
import android.os.IUserManager;
import android.os.IncidentManager;
import android.os.PowerManager;
-import android.os.Process;
import android.os.RecoverySystem;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
@@ -639,13 +636,13 @@
registerService(Context.WIFI_RTT_SERVICE, RttManager.class,
new CachedServiceFetcher<RttManager>() {
- @Override
- public RttManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_RTT_SERVICE);
- IRttManager service = IRttManager.Stub.asInterface(b);
- return new RttManager(ctx.getOuterContext(), service,
- ConnectivityThread.getInstanceLooper());
- }});
+ @Override
+ public RttManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_RTT_RANGING_SERVICE);
+ IWifiRttManager service = IWifiRttManager.Stub.asInterface(b);
+ return new RttManager(ctx.getOuterContext(),
+ new WifiRttManager(ctx.getOuterContext(), service));
+ }});
registerService(Context.WIFI_RTT_RANGING_SERVICE, WifiRttManager.class,
new CachedServiceFetcher<WifiRttManager>() {
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index 08effd9..202b894 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -77,6 +77,7 @@
TAG_KEY_DESTRUCTION,
TAG_CERT_AUTHORITY_INSTALLED,
TAG_CERT_AUTHORITY_REMOVED,
+ TAG_CRYPTO_SELF_TEST_COMPLETED,
})
public @interface SecurityLogTag {}
@@ -400,6 +401,14 @@
SecurityLogTags.SECURITY_USER_RESTRICTION_REMOVED;
/**
+ * Indicates that cryptographic functionality self test has completed. The log entry contains an
+ * {@code Integer} payload, indicating the result of the test (0 if the test failed, 1 if
+ * succeeded) and accessible via {@link SecurityEvent#getData()}.
+ */
+ public static final int TAG_CRYPTO_SELF_TEST_COMPLETED =
+ SecurityLogTags.SECURITY_CRYPTO_SELF_TEST_COMPLETED;
+
+ /**
* Event severity level indicating that the event corresponds to normal workflow.
*/
public static final int LEVEL_INFO = 1;
@@ -529,6 +538,7 @@
case TAG_USER_RESTRICTION_REMOVED:
return LEVEL_INFO;
case TAG_CERT_AUTHORITY_REMOVED:
+ case TAG_CRYPTO_SELF_TEST_COMPLETED:
return getSuccess() ? LEVEL_INFO : LEVEL_ERROR;
case TAG_CERT_AUTHORITY_INSTALLED:
case TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT:
diff --git a/core/java/android/app/admin/SecurityLogTags.logtags b/core/java/android/app/admin/SecurityLogTags.logtags
index be62678..b64b7e3 100644
--- a/core/java/android/app/admin/SecurityLogTags.logtags
+++ b/core/java/android/app/admin/SecurityLogTags.logtags
@@ -34,4 +34,5 @@
210027 security_user_restriction_added (package|3),(admin_user|1),(restriction|3)
210028 security_user_restriction_removed (package|3),(admin_user|1),(restriction|3)
210029 security_cert_authority_installed (success|1),(subject|3)
-210030 security_cert_authority_removed (success|1),(subject|3)
\ No newline at end of file
+210030 security_cert_authority_removed (success|1),(subject|3)
+210031 security_crypto_self_test_completed (success|1)
\ No newline at end of file
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 09a46b8..0342c93 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -468,6 +468,18 @@
dest.writeBoolean(mOverlayIsStatic);
dest.writeInt(compileSdkVersion);
dest.writeString(compileSdkVersionCodename);
+ writeSigningCertificateHistoryToParcel(dest, parcelableFlags);
+ }
+
+ private void writeSigningCertificateHistoryToParcel(Parcel dest, int parcelableFlags) {
+ if (signingCertificateHistory != null) {
+ dest.writeInt(signingCertificateHistory.length);
+ for (int i = 0; i < signingCertificateHistory.length; i++) {
+ dest.writeTypedArray(signingCertificateHistory[i], parcelableFlags);
+ }
+ } else {
+ dest.writeInt(-1);
+ }
}
public static final Parcelable.Creator<PackageInfo> CREATOR
@@ -523,6 +535,7 @@
mOverlayIsStatic = source.readBoolean();
compileSdkVersion = source.readInt();
compileSdkVersionCodename = source.readString();
+ readSigningCertificateHistoryFromParcel(source);
// The component lists were flattened with the redundant ApplicationInfo
// instances omitted. Distribute the canonical one here as appropriate.
@@ -534,6 +547,16 @@
}
}
+ private void readSigningCertificateHistoryFromParcel(Parcel source) {
+ int len = source.readInt();
+ if (len != -1) {
+ signingCertificateHistory = new Signature[len][];
+ for (int i = 0; i < len; i++) {
+ signingCertificateHistory[i] = source.createTypedArray(Signature.CREATOR);
+ }
+ }
+ }
+
private void propagateApplicationInfo(ApplicationInfo appInfo, ComponentInfo[] components) {
if (components != null) {
for (ComponentInfo ci : components) {
diff --git a/core/java/android/content/pm/VerifierDeviceIdentity.java b/core/java/android/content/pm/VerifierDeviceIdentity.java
index a8cdb6a..90be6f31 100644
--- a/core/java/android/content/pm/VerifierDeviceIdentity.java
+++ b/core/java/android/content/pm/VerifierDeviceIdentity.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;
import java.util.Random;
@@ -86,6 +88,7 @@
* @return verifier device identity based on the input from the provided
* random number generator
*/
+ @VisibleForTesting
static VerifierDeviceIdentity generate(Random rng) {
long identity = rng.nextLong();
return new VerifierDeviceIdentity(identity);
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index fd0e5ae..5d7cf1e 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1537,6 +1537,7 @@
public static final int STATE2_BLUETOOTH_ON_FLAG = 1<<22;
public static final int STATE2_CAMERA_FLAG = 1<<21;
public static final int STATE2_BLUETOOTH_SCAN_FLAG = 1 << 20;
+ public static final int STATE2_CELLULAR_HIGH_TX_POWER_FLAG = 1 << 19;
public static final int MOST_INTERESTING_STATES2 =
STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_MASK
@@ -2353,9 +2354,11 @@
WIFI_SUPPL_STATE_NAMES, WIFI_SUPPL_STATE_SHORT_NAMES),
new BitDescription(HistoryItem.STATE2_CAMERA_FLAG, "camera", "ca"),
new BitDescription(HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG, "ble_scan", "bles"),
+ new BitDescription(HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG,
+ "cellular_high_tx_power", "Chtp"),
new BitDescription(HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK,
HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT, "gps_signal_quality", "Gss",
- new String[] { "poor", "good"}, new String[] { "poor", "good"}),
+ new String[] { "poor", "good"}, new String[] { "poor", "good"})
};
public static final String[] HISTORY_EVENT_NAMES = new String[] {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7fd75c9..84996e0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7396,7 +7396,8 @@
*/
public static final String NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode";
- private static final Validator NIGHT_DISPLAY_AUTO_MODE_VALIDATOR = BOOLEAN_VALIDATOR;
+ private static final Validator NIGHT_DISPLAY_AUTO_MODE_VALIDATOR =
+ new SettingsValidators.InclusiveIntegerRangeValidator(0, 2);
/**
* Control the color temperature of Night Display, represented in Kelvin.
diff --git a/core/java/android/transition/ArcMotion.java b/core/java/android/transition/ArcMotion.java
index da14834..172c837 100644
--- a/core/java/android/transition/ArcMotion.java
+++ b/core/java/android/transition/ArcMotion.java
@@ -216,7 +216,13 @@
boolean isMovingUpwards = startY > endY;
- if ((Math.abs(deltaX) < Math.abs(deltaY))) {
+ if (deltaY == 0) {
+ ex = dx;
+ ey = dy + (Math.abs(deltaX) * 0.5f * mMinimumHorizontalTangent);
+ } else if (deltaX == 0) {
+ ex = dx + (Math.abs(deltaY) * 0.5f * mMinimumVerticalTangent);
+ ey = dy;
+ } else if ((Math.abs(deltaX) < Math.abs(deltaY))) {
// Similar triangles bfa and bde mean that (ab/fb = eb/bd)
// Therefore, eb = ab * bd / fb
// ab = hypotenuse
@@ -254,7 +260,7 @@
float maximumArcDist2 = midDist2 * mMaximumTangent * mMaximumTangent;
float newArcDistance2 = 0;
- if (arcDist2 < minimumArcDist2) {
+ if (arcDist2 != 0 && arcDist2 < minimumArcDist2) {
newArcDistance2 = minimumArcDist2;
} else if (arcDist2 > maximumArcDist2) {
newArcDistance2 = maximumArcDist2;
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 410cdc6..1ead0b4 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -37,13 +37,13 @@
private static final Map<String, String> DEFAULT_FLAGS;
static {
DEFAULT_FLAGS = new HashMap<>();
- DEFAULT_FLAGS.put("device_info_v2", "true");
DEFAULT_FLAGS.put("settings_connected_device_v2", "true");
DEFAULT_FLAGS.put("settings_battery_v2", "true");
DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
DEFAULT_FLAGS.put("settings_zone_picker_v2", "true");
- DEFAULT_FLAGS.put("settings_about_phone_v2", "false");
+ DEFAULT_FLAGS.put("settings_about_phone_v2", "true");
DEFAULT_FLAGS.put("settings_bluetooth_while_driving", "false");
+ DEFAULT_FLAGS.put("settings_data_usage_v2", "false");
}
/**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 79fc134..0910c11 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -17,6 +17,7 @@
package android.view;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
+
import static java.lang.Math.max;
import android.animation.AnimatorInflater;
@@ -906,6 +907,13 @@
*/
private static boolean sThrowOnInvalidFloatProperties;
+ /**
+ * Prior to P, {@code #startDragAndDrop} accepts a builder which produces an empty drag shadow.
+ * Currently zero size SurfaceControl cannot be created thus we create a dummy 1x1 surface
+ * instead.
+ */
+ private static boolean sAcceptZeroSizeDragShadow;
+
/** @hide */
@IntDef({NOT_FOCUSABLE, FOCUSABLE, FOCUSABLE_AUTO})
@Retention(RetentionPolicy.SOURCE)
@@ -4840,6 +4848,8 @@
sAlwaysAssignFocus = targetSdkVersion < Build.VERSION_CODES.P;
+ sAcceptZeroSizeDragShadow = targetSdkVersion < Build.VERSION_CODES.P;
+
sCompatibilityDone = true;
}
}
@@ -8378,7 +8388,7 @@
AccessibilityNodeProvider provider, AccessibilityNodeInfo info,
boolean forAutofill) {
structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()),
- null, null, null);
+ null, null, info.getViewIdResourceName());
Rect rect = structure.getTempRect();
info.getBoundsInParent(rect);
structure.setDimens(rect.left, rect.top, 0, 0, rect.width(), rect.height());
@@ -8418,6 +8428,13 @@
CharSequence cname = info.getClassName();
structure.setClassName(cname != null ? cname.toString() : null);
structure.setContentDescription(info.getContentDescription());
+ if (forAutofill) {
+ final int maxTextLength = info.getMaxTextLength();
+ if (maxTextLength != -1) {
+ structure.setMaxTextLength(maxTextLength);
+ }
+ structure.setHint(info.getHintText());
+ }
if ((info.getText() != null || info.getError() != null)) {
structure.setText(info.getText(), info.getTextSelectionStart(),
info.getTextSelectionEnd());
@@ -8428,7 +8445,8 @@
final AutofillValue autofillValue = AutofillValue.forText(structure.getText());
structure.setAutofillValue(autofillValue);
if (info.isPassword()) {
- structure.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ structure.setInputType(InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_VARIATION_PASSWORD);
}
} else {
structure.setDataIsSensitive(false);
@@ -23619,8 +23637,7 @@
* constructor variant is only useful when the {@link #onProvideShadowMetrics(Point, Point)}
* and {@link #onDrawShadow(Canvas)} methods are also overridden in order
* to supply the drag shadow's dimensions and appearance without
- * reference to any View object. If they are not overridden, then the result is an
- * invisible drag shadow.
+ * reference to any View object.
*/
public DragShadowBuilder() {
mView = new WeakReference<View>(null);
@@ -23774,6 +23791,9 @@
// Create 1x1 surface when zero surface size is specified because SurfaceControl.Builder
// does not accept zero size surface.
if (shadowSize.x == 0 || shadowSize.y == 0) {
+ if (!sAcceptZeroSizeDragShadow) {
+ throw new IllegalStateException("Drag shadow dimensions must be positive");
+ }
shadowSize.x = 1;
shadowSize.y = 1;
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 23e7d61..417a725 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3173,6 +3173,15 @@
*/
@Override
public void writeToParcel(Parcel parcel, int flags) {
+ writeToParcelNoRecycle(parcel, flags);
+ // Since instances of this class are fetched via synchronous i.e. blocking
+ // calls in IPCs we always recycle as soon as the instance is marshaled.
+ recycle();
+ }
+
+ /** @hide */
+ @TestApi
+ public void writeToParcelNoRecycle(Parcel parcel, int flags) {
// Write bit set of indices of fields with values differing from default
long nonDefaultFields = 0;
int fieldIndex = 0; // index of the current field
@@ -3406,10 +3415,6 @@
+ " vs " + fieldIndex);
}
}
-
- // Since instances of this class are fetched via synchronous i.e. blocking
- // calls in IPCs we always recycle as soon as the instance is marshaled.
- recycle();
}
/**
@@ -3557,7 +3562,7 @@
if (isBitSet(nonDefaultFields, fieldIndex++)) {
mContentDescription = parcel.readCharSequence();
}
- if (isBitSet(nonDefaultFields, fieldIndex++)) mPaneTitle = parcel.readString();
+ if (isBitSet(nonDefaultFields, fieldIndex++)) mPaneTitle = parcel.readCharSequence();
if (isBitSet(nonDefaultFields, fieldIndex++)) mTooltipText = parcel.readCharSequence();
if (isBitSet(nonDefaultFields, fieldIndex++)) mViewIdResourceName = parcel.readString();
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 63a9990..5131a8a 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -16,6 +16,10 @@
package android.view.autofill;
+import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
+import static android.view.autofill.Helper.sDebug;
+import static android.view.autofill.Helper.sVerbose;
+
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -47,13 +51,14 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.AccessibilityWindowInfo;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
+
import org.xmlpull.v1.XmlPullParserException;
-import sun.misc.Cleaner;
import java.io.IOException;
import java.io.PrintWriter;
@@ -66,9 +71,7 @@
import java.util.List;
import java.util.Objects;
-import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
-import static android.view.autofill.Helper.sDebug;
-import static android.view.autofill.Helper.sVerbose;
+import sun.misc.Cleaner;
// TODO: use java.lang.ref.Cleaner once Android supports Java 9
@@ -616,10 +619,8 @@
/**
* @hide
*/
- public boolean isCompatibilityModeEnabled() {
- synchronized (mLock) {
- return mCompatibilityBridge != null;
- }
+ public boolean isCompatibilityModeEnabledLocked() {
+ return mCompatibilityBridge != null;
}
/**
@@ -1381,7 +1382,8 @@
@NonNull AutofillValue value, int flags) {
if (sVerbose) {
Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
- + ", flags=" + flags + ", state=" + getStateAsStringLocked());
+ + ", flags=" + flags + ", state=" + getStateAsStringLocked()
+ + ", compatMode=" + isCompatibilityModeEnabledLocked());
}
if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
if (sVerbose) {
@@ -1392,7 +1394,7 @@
}
try {
final AutofillClient client = getClient();
- if (client == null) return; // NOTE: getClient() already logd it..
+ if (client == null) return; // NOTE: getClient() already logged it..
mSessionId = mService.startSession(client.autofillClientGetActivityToken(),
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
@@ -1939,7 +1941,8 @@
pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
- pw.print(pfx); pw.print("compat mode enabled: "); pw.println(isCompatibilityModeEnabled());
+ pw.print(pfx); pw.print("compat mode enabled: "); pw.println(
+ isCompatibilityModeEnabledLocked());
pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
pw.print(" verbose: "); pw.println(sVerbose);
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index ecab15f..467e2a4 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -678,7 +678,8 @@
StopwatchTimer mCameraOnTimer;
int mGpsSignalQualityBin = -1;
- final StopwatchTimer[] mGpsSignalQualityTimer =
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ protected final StopwatchTimer[] mGpsSignalQualityTimer =
new StopwatchTimer[GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS];
int mPhoneSignalStrengthBin = -1;
@@ -760,6 +761,8 @@
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
protected StopwatchTimer mBluetoothScanTimer;
+ boolean mIsCellularTxPowerHigh = false;
+
int mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
long mMobileRadioActiveStartTime;
StopwatchTimer mMobileRadioActiveTimer;
@@ -11211,6 +11214,9 @@
Slog.d(TAG, "Updating mobile radio stats with " + activityInfo);
}
+ // Add modem tx power to history.
+ addModemTxPowerToHistory(activityInfo);
+
// Grab a separate lock to acquire the network stats, which may do I/O.
NetworkStats delta = null;
synchronized (mModemNetworkLock) {
@@ -11385,6 +11391,44 @@
new BluetoothActivityEnergyInfo(0, 0, 0, 0, 0, 0);
/**
+ * Add modem tx power to history
+ * Device is said to be in high cellular transmit power when it has spent most of the transmit
+ * time at the highest power level.
+ * @param activityInfo
+ */
+ private void addModemTxPowerToHistory(final ModemActivityInfo activityInfo) {
+ if (activityInfo == null) {
+ return;
+ }
+ int[] txTimeMs = activityInfo.getTxTimeMillis();
+ if (txTimeMs == null || txTimeMs.length != ModemActivityInfo.TX_POWER_LEVELS) {
+ return;
+ }
+ final long elapsedRealtime = mClocks.elapsedRealtime();
+ final long uptime = mClocks.uptimeMillis();
+ int levelMaxTimeSpent = 0;
+ for (int i = 1; i < txTimeMs.length; i++) {
+ if (txTimeMs[i] > txTimeMs[levelMaxTimeSpent]) {
+ levelMaxTimeSpent = i;
+ }
+ }
+ if (levelMaxTimeSpent == ModemActivityInfo.TX_POWER_LEVELS - 1) {
+ if (!mIsCellularTxPowerHigh) {
+ mHistoryCur.states2 |= HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG;
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ mIsCellularTxPowerHigh = true;
+ }
+ return;
+ }
+ if (mIsCellularTxPowerHigh) {
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG;
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ mIsCellularTxPowerHigh = false;
+ }
+ return;
+ }
+
+ /**
* Distribute Bluetooth energy info and network traffic to apps.
* @param info The energy information from the bluetooth controller.
*/
@@ -13537,6 +13581,7 @@
mCameraOnTimer.readSummaryFromParcelLocked(in);
mBluetoothScanNesting = 0;
mBluetoothScanTimer.readSummaryFromParcelLocked(in);
+ mIsCellularTxPowerHigh = false;
int NRPMS = in.readInt();
if (NRPMS > 10000) {
@@ -14473,6 +14518,7 @@
mCameraOnTimer = new StopwatchTimer(mClocks, null, -13, null, mOnBatteryTimeBase, in);
mBluetoothScanNesting = 0;
mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase, in);
+ mIsCellularTxPowerHigh = false;
mDischargeUnplugLevel = in.readInt();
mDischargePlugLevel = in.readInt();
mDischargeCurrentLevel = in.readInt();
diff --git a/core/java/com/android/internal/widget/VerifyCredentialResponse.java b/core/java/com/android/internal/widget/VerifyCredentialResponse.java
index ad6020c..7d1c706 100644
--- a/core/java/com/android/internal/widget/VerifyCredentialResponse.java
+++ b/core/java/com/android/internal/widget/VerifyCredentialResponse.java
@@ -98,6 +98,8 @@
if (mPayload != null) {
dest.writeInt(mPayload.length);
dest.writeByteArray(mPayload);
+ } else {
+ dest.writeInt(0);
}
}
}
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index ed032c7..f6223fa 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -90,7 +90,7 @@
}
static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp<SkData>&& data, int ttcIndex,
- jint weight, jint italic) {
+ jint givenWeight, jint givenItalic) {
uirenderer::FatVector<SkFontArguments::Axis, 2> skiaAxes;
for (const auto& axis : builder->axes) {
skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value});
@@ -114,15 +114,27 @@
std::shared_ptr<minikin::MinikinFont> minikinFont =
std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize, ttcIndex,
builder->axes);
- minikin::Font::Builder fontBuilder(minikinFont);
- if (weight != RESOLVE_BY_FONT_TABLE) {
- fontBuilder.setWeight(weight);
+ int weight = givenWeight;
+ bool italic = givenItalic == 1;
+ if (givenWeight == RESOLVE_BY_FONT_TABLE || givenItalic == RESOLVE_BY_FONT_TABLE) {
+ int os2Weight;
+ bool os2Italic;
+ if (!minikin::FontFamily::analyzeStyle(minikinFont, &os2Weight, &os2Italic)) {
+ ALOGE("analyzeStyle failed. Using default style");
+ os2Weight = 400;
+ os2Italic = false;
+ }
+ if (givenWeight == RESOLVE_BY_FONT_TABLE) {
+ weight = os2Weight;
+ }
+ if (givenItalic == RESOLVE_BY_FONT_TABLE) {
+ italic = os2Italic;
+ }
}
- if (italic != RESOLVE_BY_FONT_TABLE) {
- fontBuilder.setSlant(static_cast<minikin::FontStyle::Slant>(italic != 0));
- }
- builder->fonts.push_back(fontBuilder.build());
+
+ builder->fonts.push_back(minikin::Font(minikinFont,
+ minikin::FontStyle(weight, static_cast<minikin::FontStyle::Slant>(italic))));
builder->axes.clear();
return true;
}
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 482d028..115d0d5 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -576,7 +576,7 @@
minikin::FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle);
float saveSkewX = paint->getTextSkewX();
bool savefakeBold = paint->isFakeBoldText();
- MinikinFontSkia::populateSkPaint(paint, baseFont.font->typeface().get(), baseFont.fakery);
+ MinikinFontSkia::populateSkPaint(paint, baseFont.font, baseFont.fakery);
SkScalar spacing = paint->getFontMetrics(metrics);
// The populateSkPaint call may have changed fake bold / text skew
// because we want to measure with those effects applied, so now
diff --git a/core/proto/android/server/alarmmanagerservice.proto b/core/proto/android/server/alarmmanagerservice.proto
index 0342c9c..d1c5db6 100644
--- a/core/proto/android/server/alarmmanagerservice.proto
+++ b/core/proto/android/server/alarmmanagerservice.proto
@@ -104,11 +104,6 @@
repeated InFlightProto outstanding_deliveries = 34;
- // Minimum time between ALLOW_WHILE_IDLE alarms when system is idling. It
- // should be either CosntantsProto.allow_while_idle_short_duration_ms or
- // ConstantsProto.allow_while_idle_long_duration_ms.
- optional int64 allow_while_idle_min_duration_ms = 35;
-
message LastAllowWhileIdleDispatch {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -121,7 +116,7 @@
}
// Whether the short or long while-idle timeout should be used for each UID.
- repeated int32 use_allow_while_idle_short_time = 42;
+ repeated int32 use_allow_while_idle_short_time = 35;
// For each uid, this is the last time we dispatched an "allow while idle"
// alarm, used to determine the earliest we can dispatch the next such alarm.
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index b5c3ac0..5cb5319 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -227,7 +227,6 @@
optional int32 setting_minimum = 1;
optional int32 setting_maximum = 2;
optional int32 setting_default = 3;
- optional int32 setting_for_vr_default = 4;
}
// True to decouple auto-suspend mode from the display state.
@@ -293,44 +292,27 @@
// The stay on while plugged in setting.
// A set of battery conditions under which to make the screen stay on.
optional StayOnWhilePluggedInProto stay_on_while_plugged_in = 29;
- // The screen brightness setting, from 0 to 255.
- // Use -1 if no value has been set.
- optional sint32 screen_brightness_setting = 30;
- // The screen auto-brightness adjustment setting, from -1 to 1.
- // Use 0 if there is no adjustment.
- optional float screen_auto_brightness_adjustment_setting = 31;
// The screen brightness mode.
- optional .android.providers.settings.SettingsProto.ScreenBrightnessMode screen_brightness_mode_setting = 32;
+ optional .android.providers.settings.SettingsProto.ScreenBrightnessMode screen_brightness_mode_setting = 30;
// The screen brightness setting override from the window manager
// to allow the current foreground activity to override the brightness.
// Use -1 to disable.
- optional sint32 screen_brightness_override_from_window_manager = 33;
+ optional sint32 screen_brightness_override_from_window_manager = 31;
// The user activity timeout override from the window manager
// to allow the current foreground activity to override the user activity
// timeout. Use -1 to disable.
- optional sint64 user_activity_timeout_override_from_window_manager_ms = 34;
+ optional sint64 user_activity_timeout_override_from_window_manager_ms = 32;
// The window manager has determined the user to be inactive via other means.
// Set this to false to disable.
- optional bool is_user_inactive_override_from_window_manager = 35;
- // The screen brightness setting override from the settings application
- // to temporarily adjust the brightness until next updated,
- // Use -1 to disable.
- optional sint32 temporary_screen_brightness_setting_override = 36;
- // The screen brightness adjustment setting override from the settings
- // application to temporarily adjust the auto-brightness adjustment factor
- // until next updated, in the range -1..1.
- // Use NaN to disable.
- optional float temporary_screen_auto_brightness_adjustment_setting_override = 37;
+ optional bool is_user_inactive_override_from_window_manager = 33;
// The screen state to use while dozing.
- optional .android.view.DisplayStateEnum doze_screen_state_override_from_dream_manager = 38;
+ optional .android.view.DisplayStateEnum doze_screen_state_override_from_dream_manager = 34;
// The screen brightness to use while dozing.
- optional float dozed_screen_brightness_override_from_dream_manager = 39;
+ optional float dozed_screen_brightness_override_from_dream_manager = 35;
// Screen brightness settings limits.
- optional ScreenBrightnessSettingLimitsProto screen_brightness_setting_limits = 40;
- // The screen brightness setting, from 0 to 255, to be used while in VR Mode.
- optional int32 screen_brightness_for_vr_setting = 41;
+ optional ScreenBrightnessSettingLimitsProto screen_brightness_setting_limits = 36;
// True if double tap to wake is enabled
- optional bool is_double_tap_wake_enabled = 42;
+ optional bool is_double_tap_wake_enabled = 37;
// True if we are currently in VR Mode.
- optional bool is_vr_mode_enabled = 43;
+ optional bool is_vr_mode_enabled = 38;
}
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index 660c744..7b239f0 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -20,6 +20,7 @@
import android.os.Looper;
import android.util.SparseIntArray;
+import com.android.internal.location.gnssmetrics.GnssMetrics;
import java.util.ArrayList;
import java.util.concurrent.Future;
@@ -40,6 +41,11 @@
mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase);
setExternalStatsSyncLocked(new DummyExternalStatsSync());
+ for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+ mGpsSignalQualityTimer[i] = new StopwatchTimer(clocks, null, -1000-i, null,
+ mOnBatteryTimeBase);
+ }
+
// A no-op handler.
mHandler = new Handler(Looper.getMainLooper()) {};
}
diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm
index 1ef74ba..d0565ca 100644
--- a/data/keyboards/Generic.kcm
+++ b/data/keyboards/Generic.kcm
@@ -472,11 +472,15 @@
### Non-printing keys ###
key ESCAPE {
- base: fallback BACK
+ base: none
alt, meta: fallback HOME
ctrl: fallback MENU
}
+key DEL {
+ ctrl+alt: fallback BACK
+}
+
### Gamepad buttons ###
key BUTTON_A {
diff --git a/data/keyboards/Virtual.kcm b/data/keyboards/Virtual.kcm
index c4647e0..c763cc09 100644
--- a/data/keyboards/Virtual.kcm
+++ b/data/keyboards/Virtual.kcm
@@ -469,11 +469,15 @@
### Non-printing keys ###
key ESCAPE {
- base: fallback BACK
+ base: none
alt, meta: fallback HOME
ctrl: fallback MENU
}
+key DEL {
+ ctrl+alt: fallback BACK
+}
+
### Gamepad buttons ###
key BUTTON_A {
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index f5a6f49..8b5114c 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -825,6 +825,14 @@
mFillPaint.setXfermode(mode);
}
+ /**
+ * @param aa to draw this drawable with
+ * @hide
+ */
+ public void setAntiAlias(boolean aa) {
+ mFillPaint.setAntiAlias(aa);
+ }
+
private void buildPathIfDirty() {
final GradientState st = mGradientState;
if (mPathIsDirty) {
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index dca9ef5..091b526 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -132,8 +132,8 @@
bool italicFromFont;
const minikin::FontStyle defaultStyle;
- const minikin::MinikinFont* mf = families.empty() ? nullptr
- : families[0]->getClosestMatch(defaultStyle).font->typeface().get();
+ const minikin::MinikinFont* mf =
+ families.empty() ? nullptr : families[0]->getClosestMatch(defaultStyle).font;
if (mf != nullptr) {
SkTypeface* skTypeface = reinterpret_cast<const MinikinFontSkia*>(mf)->GetSkTypeface();
const SkFontStyle& style = skTypeface->fontStyle();
@@ -183,7 +183,7 @@
std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>());
std::vector<minikin::Font> fonts;
- fonts.push_back(minikin::Font::Builder(font).build());
+ fonts.push_back(minikin::Font(std::move(font), minikin::FontStyle()));
std::shared_ptr<minikin::FontCollection> collection = std::make_shared<minikin::FontCollection>(
std::make_shared<minikin::FontFamily>(std::move(fonts)));
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index e424a26..2232c25 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -57,7 +57,7 @@
std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>());
std::vector<minikin::Font> fonts;
- fonts.push_back(minikin::Font::Builder(font).build());
+ fonts.push_back(minikin::Font(std::move(font), minikin::FontStyle()));
return std::make_shared<minikin::FontFamily>(std::move(fonts));
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index d3c6edd..d194796 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1271,6 +1271,9 @@
final String allowedProviders = Settings.Secure.getStringForUser(
mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
userHandle.getIdentifier());
+ if (allowedProviders == null) {
+ return false;
+ }
final List<String> providerList = Arrays.asList(allowedProviders.split(","));
for(String provider : getAllProviders()) {
if (provider.equals(PASSIVE_PROVIDER)) {
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index d0963cb..3847530 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -1416,6 +1416,7 @@
/*
* Call BEFORE adding a routing callback handler.
*/
+ @GuardedBy("mRoutingChangeListeners")
private void testEnableNativeRoutingCallbacksLocked() {
if (mRoutingChangeListeners.size() == 0) {
native_enableDeviceCallback();
@@ -1425,6 +1426,7 @@
/*
* Call AFTER removing a routing callback handler.
*/
+ @GuardedBy("mRoutingChangeListeners")
private void testDisableNativeRoutingCallbacksLocked() {
if (mRoutingChangeListeners.size() == 0) {
native_disableDeviceCallback();
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 8e822a5..2d5fad5 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -2821,6 +2821,7 @@
/*
* Call BEFORE adding a routing callback handler.
*/
+ @GuardedBy("mRoutingChangeListeners")
private void testEnableNativeRoutingCallbacksLocked() {
if (mRoutingChangeListeners.size() == 0) {
native_enableDeviceCallback();
@@ -2830,6 +2831,7 @@
/*
* Call AFTER removing a routing callback handler.
*/
+ @GuardedBy("mRoutingChangeListeners")
private void testDisableNativeRoutingCallbacksLocked() {
if (mRoutingChangeListeners.size() == 0) {
native_disableDeviceCallback();
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index e9ffe60..bd6c7e6 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -30,7 +30,7 @@
import android.media.session.MediaSessionManager;
import android.media.update.ApiLoader;
import android.media.update.MediaController2Provider;
-import android.media.update.PlaybackInfoProvider;
+import android.media.update.MediaController2Provider.PlaybackInfoProvider;
import android.net.Uri;
import android.os.Bundle;
import android.os.ResultReceiver;
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 1bc3dfa..fe5e822 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1484,6 +1484,7 @@
/*
* Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler.
*/
+ @GuardedBy("mRoutingChangeListeners")
private void enableNativeRoutingCallbacksLocked(boolean enabled) {
if (mRoutingChangeListeners.size() == 0) {
native_enableDeviceCallback(enabled);
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index e3d5ac0..d4e9aac 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -1417,6 +1417,7 @@
/*
* Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler.
*/
+ @GuardedBy("mRoutingChangeListeners")
private void enableNativeRoutingCallbacksLocked(boolean enabled) {
if (mRoutingChangeListeners.size() == 0) {
native_enableDeviceCallback(enabled);
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 62240ce..823410f 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -1353,6 +1353,7 @@
/*
* Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler.
*/
+ @GuardedBy("mRoutingChangeListeners")
private void enableNativeRoutingCallbacksLocked(boolean enabled) {
if (mRoutingChangeListeners.size() == 0) {
native_enableDeviceCallback(enabled);
diff --git a/media/java/android/media/update/MediaController2Provider.java b/media/java/android/media/update/MediaController2Provider.java
index 71bc64a..c492d307 100644
--- a/media/java/android/media/update/MediaController2Provider.java
+++ b/media/java/android/media/update/MediaController2Provider.java
@@ -18,6 +18,7 @@
import android.annotation.SystemApi;
import android.app.PendingIntent;
+import android.media.AudioAttributes;
import android.media.MediaController2.PlaybackInfo;
import android.media.MediaItem2;
import android.media.MediaSession2.Command;
@@ -65,4 +66,12 @@
PlaylistParams getPlaylistParams_impl();
void setPlaylistParams_impl(PlaylistParams params);
PlaybackState2 getPlaybackState_impl();
+
+ interface PlaybackInfoProvider {
+ int getPlaybackType_impl();
+ AudioAttributes getAudioAttributes_impl();
+ int getControlType_impl();
+ int getMaxVolume_impl();
+ int getCurrentVolume_impl();
+ }
}
diff --git a/media/java/android/media/update/PlaybackInfoProvider.java b/media/java/android/media/update/PlaybackInfoProvider.java
deleted file mode 100644
index 36eb58a..0000000
--- a/media/java/android/media/update/PlaybackInfoProvider.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.
- */
-
-package android.media.update;
-
-import android.media.AudioAttributes;
-
-/**
- * @hide
- */
-// TODO(jaewan): @SystemApi
-public interface PlaybackInfoProvider {
- int getPlaybackType_impl();
- AudioAttributes getAudioAttributes_impl();
- int getControlType_impl();
- int getMaxVolume_impl();
- int getCurrentVolume_impl();
-}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 6ef3fac..1cff59c 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -865,30 +865,49 @@
<!-- Summary shown for color space correction preference when its value is overridden by another preference [CHAR LIMIT=35] -->
<string name="daltonizer_type_overridden">Overridden by <xliff:g id="title" example="Simulate color space">%1$s</xliff:g></string>
- <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery discharging -->
- <string name="power_remaining_duration_only">About <xliff:g id="time">^1</xliff:g> left</string>
- <!-- [CHAR_LIMIT=60] Label for estimated remaining duration of battery discharging -->
- <string name="power_remaining_duration_only_enhanced">About <xliff:g id="time">^1</xliff:g> left based on your usage</string>
- <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery charging -->
- <string name="power_remaining_charging_duration_only"><xliff:g id="time">^1</xliff:g> left until fully charged</string>
+ <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery discharging -->
+ <string name="power_remaining_duration_only">About <xliff:g id="time">%1$s</xliff:g> left</string>
+ <!-- [CHAR_LIMIT=60] Label for estimated remaining duration of battery discharging -->
+ <string name="power_remaining_duration_only_enhanced">About <xliff:g id="time">%1$s</xliff:g> left based on your usage</string>
+ <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery charging -->
+ <string name="power_remaining_charging_duration_only"><xliff:g id="time">%1$s</xliff:g> left until fully charged</string>
- <!-- [CHAR_LIMIT=40] Short label for estimated remaining duration of battery charging/discharging -->
- <string name="power_remaining_duration_only_short"><xliff:g id="time">^1</xliff:g> left</string>
- <!-- [CHAR_LIMIT=60] Short label for estimated remaining duration of battery charging/discharging -->
- <string name="power_remaining_duration_only_short_enhanced"><xliff:g id="time">^1</xliff:g> left based on your usage</string>
+ <!-- [CHAR_LIMIT=40] Short label for estimated remaining duration of battery charging/discharging -->
+ <string name="power_remaining_duration_only_short"><xliff:g id="time">%1$s</xliff:g> left</string>
- <!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
- <string name="power_discharging_duration"><xliff:g id="level">^1</xliff:g> - about <xliff:g id="time">^2</xliff:g> left</string>
- <!-- [CHAR_LIMIT=60] Label for battery level chart when discharging with duration and using enhanced estimate -->
- <string name="power_discharging_duration_enhanced"><xliff:g id="level">^1</xliff:g> - about <xliff:g id="time">^2</xliff:g> left based on your usage</string>
+ <!-- [CHAR_LIMIT=60] label for estimated remaining duration of battery when under a certain amount -->
+ <string name="power_remaining_less_than_duration_only">Less than <xliff:g id="threshold">%1$s</xliff:g> remaining</string>
+ <!-- [CHAR_LIMIT=60] label for estimated remaining duration of battery when under a certain amount with the percentage -->
+ <string name="power_remaining_less_than_duration"><xliff:g id="level">%1$s</xliff:g> - Less than <xliff:g id="threshold">%2$s</xliff:g> remaining</string>
- <!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
- <string name="power_discharging_duration_short"><xliff:g id="level">^1</xliff:g> - <xliff:g id="time">^2</xliff:g> left</string>
+ <!-- Used to let users know that they have more than some amount of battery life remaining with percentage. ex: 75% - more than 1 day remaining [CHAR LIMIT = 80] -->
+ <string name="power_remaining_more_than_subtext"><xliff:g id="level">%1$s</xliff:g>more than <xliff:g id="time_remaining">%2$s</xliff:g> remaining</string>
+ <!-- Used to let users know that they have more than some amount of battery life remaining. ex: more than 1 day remaining [CHAR LIMIT = 40] -->
+ <string name="power_remaining_only_more_than_subtext">more than <xliff:g id="time_remaining">%1$s</xliff:g> remaining</string>
+
+ <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
+ <string name="power_remaining_duration_only_shutdown_imminent" product="default">phone may shutdown soon</string>
+ <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
+ <string name="power_remaining_duration_only_shutdown_imminent" product="tablet">tablet may shutdown soon</string>
+ <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
+ <string name="power_remaining_duration_only_shutdown_imminent" product="device">device may shutdown soon</string>
+
+ <!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
+ <string name="power_discharging_duration"><xliff:g id="level">%1$s</xliff:g> - about <xliff:g id="time">%2$s</xliff:g> left</string>
+ <!-- [CHAR_LIMIT=60] Label for battery level chart when discharging with duration and using enhanced estimate -->
+ <string name="power_discharging_duration_enhanced"><xliff:g id="level">%1$s</xliff:g> - about <xliff:g id="time">%2$s</xliff:g> left based on your usage</string>
+
+ <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
+ <string name="power_remaining_duration_shutdown_imminent" product="default"><xliff:g id="level">%1$s</xliff:g> - phone may shutdown soon</string>
+ <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
+ <string name="power_remaining_duration_shutdown_imminent" product="tablet"><xliff:g id="level">%1$s</xliff:g> - tablet may shutdown soon</string>
+ <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
+ <string name="power_remaining_duration_shutdown_imminent" product="device"><xliff:g id="level">%1$s</xliff:g> - device may shutdown soon</string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging -->
<string name="power_charging"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="state">%2$s</xliff:g></string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
- <string name="power_charging_duration"><xliff:g id="level">^1</xliff:g> - <xliff:g id="time">^2</xliff:g> until fully charged</string>
+ <string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="time">%2$s</xliff:g> until fully charged</string>
<!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="battery_info_status_unknown">Unknown</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index 190f5e6..68ead09 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -17,7 +17,6 @@
import android.annotation.LayoutRes;
import android.annotation.Nullable;
-import android.app.ActionBar;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -72,9 +71,9 @@
requestWindowFeature(Window.FEATURE_NO_TITLE);
}
super.setContentView(R.layout.settings_with_drawer);
- mContentHeaderContainer = (FrameLayout) findViewById(R.id.content_header_container);
+ mContentHeaderContainer = findViewById(R.id.content_header_container);
- Toolbar toolbar = (Toolbar) findViewById(R.id.action_bar);
+ Toolbar toolbar = findViewById(R.id.action_bar);
if (theme.getBoolean(android.R.styleable.Theme_windowNoTitle, false)) {
toolbar.setVisibility(View.GONE);
return;
@@ -89,7 +88,9 @@
@Override
public boolean onNavigateUp() {
- finish();
+ if (!super.onNavigateUp()) {
+ finish();
+ }
return true;
}
@@ -104,11 +105,6 @@
registerReceiver(mPackageReceiver, filter);
new CategoriesUpdateTask().execute();
- final Intent intent = getIntent();
- if (intent != null && intent.getBooleanExtra(EXTRA_SHOW_MENU, false)) {
- // Intent explicitly set to show menu.
- showMenuIcon();
- }
}
@Override
@@ -125,13 +121,6 @@
mCategoryListeners.remove(listener);
}
- public void setContentHeaderView(View headerView) {
- mContentHeaderContainer.removeAllViews();
- if (headerView != null) {
- mContentHeaderContainer.addView(headerView);
- }
- }
-
@Override
public void setContentView(@LayoutRes int layoutResID) {
final ViewGroup parent = findViewById(R.id.content_frame);
@@ -151,13 +140,6 @@
((ViewGroup) findViewById(R.id.content_frame)).addView(view, params);
}
- private void showMenuIcon() {
- final ActionBar actionBar = getActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- }
- }
-
private void onCategoriesChanged() {
final int N = mCategoryListeners.size();
for (int i = 0; i < N; i++) {
@@ -165,10 +147,6 @@
}
}
- public void onProfileTileOpen() {
- finish();
- }
-
/**
* @return whether or not the enabled state actually changed.
*/
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
new file mode 100644
index 0000000..346ca66
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
@@ -0,0 +1,143 @@
+/*
+ * 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.settingslib.utils;
+
+import android.content.Context;
+import android.icu.text.MeasureFormat;
+import android.icu.text.MeasureFormat.FormatWidth;
+import android.icu.util.Measure;
+import android.icu.util.MeasureUnit;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import com.android.settingslib.R;
+import com.android.settingslib.utils.StringUtil;
+import java.util.Locale;
+import java.util.concurrent.TimeUnit;
+
+/** Utility class for keeping power related strings consistent**/
+public class PowerUtil {
+ private static final long SEVEN_MINUTES_MILLIS = TimeUnit.MINUTES.toMillis(7);
+ private static final long FIFTEEN_MINUTES_MILLIS = TimeUnit.MINUTES.toMillis(15);
+ private static final long ONE_DAY_MILLIS = TimeUnit.DAYS.toMillis(1);
+
+ /**
+ * This method produces the text used in various places throughout the system to describe the
+ * remaining battery life of the phone in a consistent manner.
+ *
+ * @param context
+ * @param drainTimeMs The estimated time remaining before the phone dies in milliseconds.
+ * @param percentageString An optional percentage of battery remaining string.
+ * @param basedOnUsage Whether this estimate is based on usage or simple extrapolation.
+ * @return a properly formatted and localized string describing how much time remains
+ * before the battery runs out.
+ */
+ public static String getBatteryRemainingStringFormatted(Context context, long drainTimeMs,
+ @Nullable String percentageString, boolean basedOnUsage) {
+ if (drainTimeMs > 0) {
+ if (drainTimeMs <= SEVEN_MINUTES_MILLIS) {
+ // show a imminent shutdown warning if less than 7 minutes remain
+ return getShutdownImminentString(context, percentageString);
+ } else if (drainTimeMs <= FIFTEEN_MINUTES_MILLIS) {
+ // show a less than 15 min remaining warning if appropriate
+ CharSequence timeString = StringUtil.formatElapsedTime(context,
+ FIFTEEN_MINUTES_MILLIS,
+ false /* withSeconds */);
+ return getUnderFifteenString(context, timeString, percentageString);
+ } else if (drainTimeMs >= ONE_DAY_MILLIS) {
+ // just say more than one day if over 24 hours
+ return getMoreThanOneDayString(context, percentageString);
+ } else {
+ // show a regular time remaining string
+ return getRegularTimeRemainingString(context, drainTimeMs,
+ percentageString, basedOnUsage);
+ }
+ }
+ return null;
+ }
+
+ private static String getShutdownImminentString(Context context, String percentageString) {
+ return TextUtils.isEmpty(percentageString)
+ ? context.getString(R.string.power_remaining_duration_only_shutdown_imminent)
+ : context.getString(
+ R.string.power_remaining_duration_shutdown_imminent,
+ percentageString);
+ }
+
+ private static String getUnderFifteenString(Context context, CharSequence timeString,
+ String percentageString) {
+ return TextUtils.isEmpty(percentageString)
+ ? context.getString(R.string.power_remaining_less_than_duration_only, timeString)
+ : context.getString(
+ R.string.power_remaining_less_than_duration,
+ percentageString,
+ timeString);
+
+ }
+
+ private static String getMoreThanOneDayString(Context context, String percentageString) {
+ final Locale currentLocale = context.getResources().getConfiguration().getLocales().get(0);
+ final MeasureFormat frmt = MeasureFormat.getInstance(currentLocale, FormatWidth.SHORT);
+
+ final Measure daysMeasure = new Measure(1, MeasureUnit.DAY);
+
+ return TextUtils.isEmpty(percentageString)
+ ? context.getString(R.string.power_remaining_only_more_than_subtext,
+ frmt.formatMeasures(daysMeasure))
+ : context.getString(
+ R.string.power_remaining_more_than_subtext,
+ percentageString,
+ frmt.formatMeasures(daysMeasure));
+ }
+
+ private static String getRegularTimeRemainingString(Context context, long drainTimeMs,
+ String percentageString, boolean basedOnUsage) {
+ // round to the nearest 15 min to not appear oversly precise
+ final long roundedTimeMs = roundToNearestThreshold(drainTimeMs,
+ FIFTEEN_MINUTES_MILLIS);
+ CharSequence timeString = StringUtil.formatElapsedTime(context,
+ roundedTimeMs,
+ false /* withSeconds */);
+ if (TextUtils.isEmpty(percentageString)) {
+ int id = basedOnUsage
+ ? R.string.power_remaining_duration_only_enhanced
+ : R.string.power_remaining_duration_only;
+ return context.getString(id, timeString);
+ } else {
+ int id = basedOnUsage
+ ? R.string.power_discharging_duration_enhanced
+ : R.string.power_discharging_duration;
+ return context.getString(id, percentageString, timeString);
+ }
+ }
+
+ public static long convertUsToMs(long timeUs) {
+ return timeUs / 1000;
+ }
+
+ public static long convertMsToUs(long timeMs) {
+ return timeMs * 1000;
+ }
+
+ private static long roundToNearestThreshold(long drainTime, long threshold) {
+ final long remainder = drainTime % threshold;
+ if (remainder < threshold / 2) {
+ return drainTime - remainder;
+ } else {
+ return drainTime - remainder + threshold;
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
new file mode 100644
index 0000000..45fdd78
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
@@ -0,0 +1,149 @@
+/*
+ * 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.settingslib.utils;
+
+import android.content.Context;
+import android.icu.text.MeasureFormat;
+import android.icu.text.MeasureFormat.FormatWidth;
+import android.icu.text.RelativeDateTimeFormatter;
+import android.icu.text.RelativeDateTimeFormatter.RelativeUnit;
+import android.icu.util.Measure;
+import android.icu.util.MeasureUnit;
+import android.icu.util.ULocale;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.TtsSpan;
+import java.util.ArrayList;
+import java.util.Locale;
+
+/** Utility class for generally useful string methods **/
+public class StringUtil {
+
+ public static final int SECONDS_PER_MINUTE = 60;
+ public static final int SECONDS_PER_HOUR = 60 * 60;
+ public static final int SECONDS_PER_DAY = 24 * 60 * 60;
+
+ /**
+ * Returns elapsed time for the given millis, in the following format:
+ * 2d 5h 40m 29s
+ * @param context the application context
+ * @param millis the elapsed time in milli seconds
+ * @param withSeconds include seconds?
+ * @return the formatted elapsed time
+ */
+ public static CharSequence formatElapsedTime(Context context, double millis,
+ boolean withSeconds) {
+ SpannableStringBuilder sb = new SpannableStringBuilder();
+ int seconds = (int) Math.floor(millis / 1000);
+ if (!withSeconds) {
+ // Round up.
+ seconds += 30;
+ }
+
+ int days = 0, hours = 0, minutes = 0;
+ if (seconds >= SECONDS_PER_DAY) {
+ days = seconds / SECONDS_PER_DAY;
+ seconds -= days * SECONDS_PER_DAY;
+ }
+ if (seconds >= SECONDS_PER_HOUR) {
+ hours = seconds / SECONDS_PER_HOUR;
+ seconds -= hours * SECONDS_PER_HOUR;
+ }
+ if (seconds >= SECONDS_PER_MINUTE) {
+ minutes = seconds / SECONDS_PER_MINUTE;
+ seconds -= minutes * SECONDS_PER_MINUTE;
+ }
+
+ final ArrayList<Measure> measureList = new ArrayList(4);
+ if (days > 0) {
+ measureList.add(new Measure(days, MeasureUnit.DAY));
+ }
+ if (hours > 0) {
+ measureList.add(new Measure(hours, MeasureUnit.HOUR));
+ }
+ if (minutes > 0) {
+ measureList.add(new Measure(minutes, MeasureUnit.MINUTE));
+ }
+ if (withSeconds && seconds > 0) {
+ measureList.add(new Measure(seconds, MeasureUnit.SECOND));
+ }
+ if (measureList.size() == 0) {
+ // Everything addable was zero, so nothing was added. We add a zero.
+ measureList.add(new Measure(0, withSeconds ? MeasureUnit.SECOND : MeasureUnit.MINUTE));
+ }
+ final Measure[] measureArray = measureList.toArray(new Measure[measureList.size()]);
+
+ final Locale locale = context.getResources().getConfiguration().locale;
+ final MeasureFormat measureFormat = MeasureFormat.getInstance(
+ locale, FormatWidth.NARROW);
+ sb.append(measureFormat.formatMeasures(measureArray));
+
+ if (measureArray.length == 1 && MeasureUnit.MINUTE.equals(measureArray[0].getUnit())) {
+ // Add ttsSpan if it only have minute value, because it will be read as "meters"
+ final TtsSpan ttsSpan = new TtsSpan.MeasureBuilder().setNumber(minutes)
+ .setUnit("minute").build();
+ sb.setSpan(ttsSpan, 0, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ return sb;
+ }
+
+ /**
+ * Returns relative time for the given millis in the past, in a short format such as "2 days
+ * ago", "5 hr. ago", "40 min. ago", or "29 sec. ago".
+ *
+ * <p>The unit is chosen to have good information value while only using one unit. So 27 hours
+ * and 50 minutes would be formatted as "28 hr. ago", while 50 hours would be formatted as
+ * "2 days ago".
+ *
+ * @param context the application context
+ * @param millis the elapsed time in milli seconds
+ * @param withSeconds include seconds?
+ * @return the formatted elapsed time
+ */
+ public static CharSequence formatRelativeTime(Context context, double millis,
+ boolean withSeconds) {
+ final int seconds = (int) Math.floor(millis / 1000);
+ final RelativeUnit unit;
+ final int value;
+ if (withSeconds && seconds < 2 * SECONDS_PER_MINUTE) {
+ unit = RelativeUnit.SECONDS;
+ value = seconds;
+ } else if (seconds < 2 * SECONDS_PER_HOUR) {
+ unit = RelativeUnit.MINUTES;
+ value = (seconds + SECONDS_PER_MINUTE / 2)
+ / SECONDS_PER_MINUTE;
+ } else if (seconds < 2 * SECONDS_PER_DAY) {
+ unit = RelativeUnit.HOURS;
+ value = (seconds + SECONDS_PER_HOUR / 2)
+ / SECONDS_PER_HOUR;
+ } else {
+ unit = RelativeUnit.DAYS;
+ value = (seconds + SECONDS_PER_DAY / 2)
+ / SECONDS_PER_DAY;
+ }
+
+ final Locale locale = context.getResources().getConfiguration().locale;
+ final RelativeDateTimeFormatter formatter = RelativeDateTimeFormatter.getInstance(
+ ULocale.forLocale(locale),
+ null /* default NumberFormat */,
+ RelativeDateTimeFormatter.Style.SHORT,
+ android.icu.text.DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE);
+
+ return formatter.format(value, RelativeDateTimeFormatter.Direction.LAST, unit);
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java
index 003f905..2f417ad 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java
@@ -18,8 +18,6 @@
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
import android.app.Instrumentation;
@@ -49,42 +47,22 @@
}
@Test
- public void startActivityWithNoExtra_showNoNavUp() {
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- instrumentation.startActivitySync(new Intent(instrumentation.getTargetContext(),
- TestActivity.class));
-
- onView(withContentDescription(com.android.internal.R.string.action_bar_up_description))
- .check(doesNotExist());
- }
-
- @Test
- public void startActivityWithExtraToHideMenu_showNavUp() {
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- Intent intent = new Intent(instrumentation.getTargetContext(), TestActivity.class)
- .putExtra(TestActivity.EXTRA_SHOW_MENU, false);
+ public void startActivity_doNotShowNavUp() {
+ final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ final Intent intent = new Intent(instrumentation.getTargetContext(), TestActivity.class)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
instrumentation.startActivitySync(intent);
onView(withContentDescription(com.android.internal.R.string.action_bar_up_description))
.check(doesNotExist());
}
- @Test
- public void startActivityWithExtraToShowMenu_showNavUp() {
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- Intent intent = new Intent(instrumentation.getTargetContext(), TestActivity.class)
- .putExtra(TestActivity.EXTRA_SHOW_MENU, true);
- instrumentation.startActivitySync(intent);
-
- onView(withContentDescription(com.android.internal.R.string.action_bar_up_description))
- .check(matches(isDisplayed()));
- }
-
/**
* Test Activity in this test.
*
* Use this activity because SettingsDrawerActivity hasn't been registered in its
* AndroidManifest.xml
*/
- public static class TestActivity extends SettingsDrawerActivity {}
+ public static class TestActivity extends SettingsDrawerActivity {
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
new file mode 100644
index 0000000..f93210f
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
@@ -0,0 +1,162 @@
+/*
+ * 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.settingslib.utils;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import com.android.settingslib.R;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import com.android.settingslib.utils.PowerUtil;
+import java.time.Duration;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class PowerUtilTest {
+ public static final String TEST_BATTERY_LEVEL_10 = "10%";
+ public static final String FIFTEEN_MIN_FORMATTED = "15m";
+ public static final long SEVENTEEN_MIN_MILLIS = Duration.ofMinutes(17).toMillis();
+ public static final long FIVE_MINUTES_MILLIS = Duration.ofMinutes(5).toMillis();
+ public static final long TEN_MINUTES_MILLIS = Duration.ofMinutes(10).toMillis();
+ public static final long TWO_DAYS_MILLIS = Duration.ofDays(2).toMillis();
+ public static final String ONE_DAY_FORMATTED = "1 day";
+
+ private Context mContext;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ }
+
+ @Test
+ public void testGetBatteryRemainingStringFormatted_moreThanFifteenMinutes_withPercentage() {
+ String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ SEVENTEEN_MIN_MILLIS,
+ TEST_BATTERY_LEVEL_10,
+ true /* basedOnUsage */);
+ String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ SEVENTEEN_MIN_MILLIS,
+ TEST_BATTERY_LEVEL_10,
+ false /* basedOnUsage */);
+
+ // We only add special mention for the long string
+ assertThat(info).isEqualTo(mContext.getString(
+ R.string.power_discharging_duration_enhanced,
+ TEST_BATTERY_LEVEL_10,
+ FIFTEEN_MIN_FORMATTED));
+ // shortened string should not have extra text
+ assertThat(info2).isEqualTo(mContext.getString(
+ R.string.power_discharging_duration,
+ TEST_BATTERY_LEVEL_10,
+ FIFTEEN_MIN_FORMATTED));
+ }
+
+ @Test
+ public void testGetBatteryRemainingStringFormatted_moreThanFifteenMinutes_noPercentage() {
+ String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ SEVENTEEN_MIN_MILLIS,
+ null /* percentageString */,
+ true /* basedOnUsage */);
+ String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ SEVENTEEN_MIN_MILLIS,
+ null /* percentageString */,
+ false /* basedOnUsage */);
+
+ // We only add special mention for the long string
+ assertThat(info).isEqualTo(mContext.getString(
+ R.string.power_remaining_duration_only_enhanced,
+ FIFTEEN_MIN_FORMATTED));
+ // shortened string should not have extra text
+ assertThat(info2).isEqualTo(mContext.getString(
+ R.string.power_remaining_duration_only,
+ FIFTEEN_MIN_FORMATTED));
+ }
+
+
+ @Test
+ public void testGetBatteryRemainingStringFormatted_lessThanSevenMinutes_usesCorrectString() {
+ String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ FIVE_MINUTES_MILLIS,
+ TEST_BATTERY_LEVEL_10 /* percentageString */,
+ true /* basedOnUsage */);
+ String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ FIVE_MINUTES_MILLIS,
+ null /* percentageString */,
+ true /* basedOnUsage */);
+
+ // additional battery percentage in this string
+ assertThat(info).isEqualTo(mContext.getString(
+ R.string.power_remaining_duration_shutdown_imminent,
+ TEST_BATTERY_LEVEL_10));
+ // shortened string should not have percentage
+ assertThat(info2).isEqualTo(mContext.getString(
+ R.string.power_remaining_duration_only_shutdown_imminent));
+ }
+
+ @Test
+ public void testGetBatteryRemainingStringFormatted_betweenSevenAndFifteenMinutes_usesCorrectString() {
+ String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ TEN_MINUTES_MILLIS,
+ null /* percentageString */,
+ true /* basedOnUsage */);
+ String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ TEN_MINUTES_MILLIS,
+ TEST_BATTERY_LEVEL_10 /* percentageString */,
+ true /* basedOnUsage */);
+
+ // shortened string should not have percentage
+ assertThat(info).isEqualTo(mContext.getString(
+ R.string.power_remaining_less_than_duration_only,
+ FIFTEEN_MIN_FORMATTED));
+ // Add percentage to string when provided
+ assertThat(info2).isEqualTo(mContext.getString(
+ R.string.power_remaining_less_than_duration,
+ TEST_BATTERY_LEVEL_10,
+ FIFTEEN_MIN_FORMATTED));
+ }
+
+ @Test
+ public void testGetBatteryRemainingStringFormatted_moreThanOneDay_usesCorrectString() {
+ String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ TWO_DAYS_MILLIS,
+ null /* percentageString */,
+ true /* basedOnUsage */);
+ String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ TWO_DAYS_MILLIS,
+ TEST_BATTERY_LEVEL_10 /* percentageString */,
+ true /* basedOnUsage */);
+
+ // shortened string should not have percentage
+ assertThat(info).isEqualTo(mContext.getString(
+ R.string.power_remaining_only_more_than_subtext,
+ ONE_DAY_FORMATTED));
+ // Add percentage to string when provided
+ assertThat(info2).isEqualTo(mContext.getString(
+ R.string.power_remaining_more_than_subtext,
+ TEST_BATTERY_LEVEL_10,
+ ONE_DAY_FORMATTED));
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
new file mode 100644
index 0000000..d5e3cdb
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
@@ -0,0 +1,213 @@
+/*
+ * 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.settingslib.utils;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.text.SpannableStringBuilder;
+import android.text.format.DateUtils;
+import android.text.style.TtsSpan;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class StringUtilTest {
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = spy(RuntimeEnvironment.application);
+ }
+
+ @Test
+ public void testFormatElapsedTime_WithSeconds_ShowSeconds() {
+ final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS + 30 * DateUtils.SECOND_IN_MILLIS;
+ final String expectedTime = "5m 30s";
+
+ assertThat(StringUtil.formatElapsedTime(mContext, testMillis, true).toString())
+ .isEqualTo(expectedTime);
+ }
+
+ @Test
+ public void testFormatElapsedTime_NoSeconds_DoNotShowSeconds() {
+ final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS + 30 * DateUtils.SECOND_IN_MILLIS;
+ final String expectedTime = "6m";
+
+ assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+ .isEqualTo(expectedTime);
+ }
+
+ @Test
+ public void testFormatElapsedTime_TimeMoreThanOneDay_ShowCorrectly() {
+ final double testMillis = 2 * DateUtils.DAY_IN_MILLIS
+ + 4 * DateUtils.HOUR_IN_MILLIS + 15 * DateUtils.MINUTE_IN_MILLIS;
+ final String expectedTime = "2d 4h 15m";
+
+ assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+ .isEqualTo(expectedTime);
+ }
+
+ @Test
+ public void testFormatElapsedTime_ZeroFieldsInTheMiddleDontShow() {
+ final double testMillis = 2 * DateUtils.DAY_IN_MILLIS + 15 * DateUtils.MINUTE_IN_MILLIS;
+ final String expectedTime = "2d 15m";
+
+ assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+ .isEqualTo(expectedTime);
+ }
+
+ @Test
+ public void testFormatElapsedTime_FormatZero_WithSeconds() {
+ final double testMillis = 0;
+ final String expectedTime = "0s";
+
+ assertThat(StringUtil.formatElapsedTime(mContext, testMillis, true).toString())
+ .isEqualTo(expectedTime);
+ }
+
+ @Test
+ public void testFormatElapsedTime_FormatZero_NoSeconds() {
+ final double testMillis = 0;
+ final String expectedTime = "0m";
+
+ assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+ .isEqualTo(expectedTime);
+ }
+
+ @Test
+ public void testFormatElapsedTime_onlyContainsMinute_hasTtsSpan() {
+ final double testMillis = 15 * DateUtils.MINUTE_IN_MILLIS;
+
+ final CharSequence charSequence =
+ StringUtil.formatElapsedTime(mContext, testMillis, false);
+ assertThat(charSequence).isInstanceOf(SpannableStringBuilder.class);
+
+ final SpannableStringBuilder expectedString = (SpannableStringBuilder) charSequence;
+ final TtsSpan[] ttsSpans = expectedString.getSpans(0, expectedString.length(),
+ TtsSpan.class);
+
+ assertThat(ttsSpans).asList().hasSize(1);
+ assertThat(ttsSpans[0].getType()).isEqualTo(TtsSpan.TYPE_MEASURE);
+ }
+
+ @Test
+ public void testFormatRelativeTime_WithSeconds_ShowSeconds() {
+ final double testMillis = 40 * DateUtils.SECOND_IN_MILLIS;
+ final String expectedTime = "40 sec. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_NoSeconds_DoNotShowSeconds() {
+ final double testMillis = 40 * DateUtils.SECOND_IN_MILLIS;
+ final String expectedTime = "1 min. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, false).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_LessThanTwoMinutes_withSeconds() {
+ final double testMillis = 119 * DateUtils.SECOND_IN_MILLIS;
+ final String expectedTime = "119 sec. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_LessThanTwoMinutes_NoSeconds() {
+ final double testMillis = 119 * DateUtils.SECOND_IN_MILLIS;
+ final String expectedTime = "2 min. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, false).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_TwoMinutes_withSeconds() {
+ final double testMillis = 2 * DateUtils.MINUTE_IN_MILLIS;
+ final String expectedTime = "2 min. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_LessThanTwoHours_withSeconds() {
+ final double testMillis = 119 * DateUtils.MINUTE_IN_MILLIS;
+ final String expectedTime = "119 min. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_TwoHours_withSeconds() {
+ final double testMillis = 2 * DateUtils.HOUR_IN_MILLIS;
+ final String expectedTime = "2 hr. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_LessThanTwoDays_withSeconds() {
+ final double testMillis = 47 * DateUtils.HOUR_IN_MILLIS;
+ final String expectedTime = "47 hr. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_TwoDays_withSeconds() {
+ final double testMillis = 2 * DateUtils.DAY_IN_MILLIS;
+ final String expectedTime = "2 days ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_FormatZero_WithSeconds() {
+ final double testMillis = 0;
+ final String expectedTime = "0 sec. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_FormatZero_NoSeconds() {
+ final double testMillis = 0;
+ final String expectedTime = "0 min. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, false).toString()).isEqualTo(
+ expectedTime);
+ }
+}
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 2bcf4ef..1e48213 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -56,7 +56,6 @@
SystemUI-proto
LOCAL_JAVA_LIBRARIES := telephony-common
-LOCAL_JAVA_LIBRARIES += android.car
LOCAL_PACKAGE_NAME := SystemUI
LOCAL_CERTIFICATE := platform
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 9613a6a..cbb3e8f 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -176,9 +176,6 @@
<!-- It's like, reality, but, you know, virtual -->
<uses-permission android:name="android.permission.ACCESS_VR_MANAGER" />
- <!-- To control car audio module volume -->
- <uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" />
-
<!-- the ability to rename notifications posted by other apps -->
<uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
diff --git a/packages/SystemUI/shared/tests/Android.mk b/packages/SystemUI/shared/tests/Android.mk
index 239a4e3..1715983 100644
--- a/packages/SystemUI/shared/tests/Android.mk
+++ b/packages/SystemUI/shared/tests/Android.mk
@@ -41,7 +41,7 @@
testables \
truth-prebuilt \
-LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common android.car
+LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common
# sign this with platform cert, so this test is allowed to inject key events into
# UI it doesn't own. This is necessary to allow screenshots to be taken
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index aa56694..3a2b12f 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -26,10 +26,6 @@
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.content.IntentFilter;
-import android.icu.text.MeasureFormat;
-import android.icu.text.MeasureFormat.FormatWidth;
-import android.icu.util.Measure;
-import android.icu.util.MeasureUnit;
import android.media.AudioAttributes;
import android.os.AsyncTask;
import android.os.Handler;
@@ -37,11 +33,11 @@
import android.os.PowerManager;
import android.os.UserHandle;
import android.support.annotation.VisibleForTesting;
-import android.text.format.DateUtils;
import android.util.Slog;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.settingslib.Utils;
+import com.android.settingslib.utils.PowerUtil;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.statusbar.phone.SystemUIDialog;
@@ -49,8 +45,6 @@
import java.io.PrintWriter;
import java.text.NumberFormat;
-import java.util.Locale;
-import java.util.concurrent.TimeUnit;
public class PowerNotificationWarnings implements PowerUI.WarningsUI {
private static final String TAG = PowerUI.TAG + ".Notification";
@@ -200,12 +194,7 @@
// override notification copy if hybrid notification enabled
if (mEstimate != null) {
title = mContext.getString(R.string.battery_low_title_hybrid);
- contentText = mContext.getString(
- mEstimate.isBasedOnUsage
- ? R.string.battery_low_percent_format_hybrid
- : R.string.battery_low_percent_format_hybrid_short,
- percentage,
- getTimeRemainingFormatted());
+ contentText = getHybridContentString(percentage);
}
final Notification.Builder nb =
@@ -239,21 +228,12 @@
mNoMan.notifyAsUser(TAG_BATTERY, SystemMessage.NOTE_POWER_LOW, n, UserHandle.ALL);
}
- @VisibleForTesting
- String getTimeRemainingFormatted() {
- final Locale currentLocale = mContext.getResources().getConfiguration().getLocales().get(0);
- MeasureFormat frmt = MeasureFormat.getInstance(currentLocale, FormatWidth.NARROW);
-
- final long remainder = mEstimate.estimateMillis % DateUtils.HOUR_IN_MILLIS;
- final long hours = TimeUnit.MILLISECONDS.toHours(
- mEstimate.estimateMillis - remainder);
- // round down to the nearest 15 min for now to not appear overly precise
- final long minutes = TimeUnit.MILLISECONDS.toMinutes(
- remainder - (remainder % TimeUnit.MINUTES.toMillis(15)));
- final Measure hoursMeasure = new Measure(hours, MeasureUnit.HOUR);
- final Measure minutesMeasure = new Measure(minutes, MeasureUnit.MINUTE);
-
- return frmt.formatMeasures(hoursMeasure, minutesMeasure);
+ private String getHybridContentString(String percentage) {
+ return PowerUtil.getBatteryRemainingStringFormatted(
+ mContext,
+ mEstimate.estimateMillis,
+ percentage,
+ mEstimate.isBasedOnUsage);
}
private PendingIntent pendingBroadcast(String action) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSScrollLayout.java b/packages/SystemUI/src/com/android/systemui/qs/QSScrollLayout.java
index a44fd9a..b8f6784 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSScrollLayout.java
@@ -14,8 +14,11 @@
package com.android.systemui.qs;
+import android.animation.ObjectAnimator;
import android.content.Context;
+import android.graphics.Canvas;
import android.support.v4.widget.NestedScrollView;
+import android.util.Property;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
@@ -23,6 +26,8 @@
import android.widget.LinearLayout;
import com.android.systemui.R;
+import com.android.systemui.qs.touch.OverScroll;
+import com.android.systemui.qs.touch.SwipeDetector;
/**
* Quick setting scroll view containing the brightness slider and the QS tiles.
@@ -35,6 +40,9 @@
private final int mTouchSlop;
private final int mFooterHeight;
private int mLastMotionY;
+ private final SwipeDetector mSwipeDetector;
+ private final OverScrollHelper mOverScrollHelper;
+ private float mContentTranslationY;
public QSScrollLayout(Context context, View... children) {
super(context);
@@ -49,15 +57,19 @@
linearLayout.addView(view);
}
addView(linearLayout);
+ setOverScrollMode(OVER_SCROLL_NEVER);
+ mOverScrollHelper = new OverScrollHelper();
+ mSwipeDetector = new SwipeDetector(context, mOverScrollHelper, SwipeDetector.VERTICAL);
+ mSwipeDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, true);
}
-
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (canScrollVertically(1) || canScrollVertically(-1)) {
return super.onInterceptTouchEvent(ev);
}
- return false;
+ mSwipeDetector.onTouchEvent(ev);
+ return super.onInterceptTouchEvent(ev) || mOverScrollHelper.isInOverScroll();
}
@Override
@@ -65,7 +77,15 @@
if (canScrollVertically(1) || canScrollVertically(-1)) {
return super.onTouchEvent(ev);
}
- return false;
+ mSwipeDetector.onTouchEvent(ev);
+ return super.onTouchEvent(ev);
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ canvas.translate(0, mContentTranslationY);
+ super.dispatchDraw(canvas);
+ canvas.translate(0, -mContentTranslationY);
}
public boolean shouldIntercept(MotionEvent ev) {
@@ -98,4 +118,81 @@
parent.requestDisallowInterceptTouchEvent(disallowIntercept);
}
}
+
+ private void setContentTranslationY(float contentTranslationY) {
+ mContentTranslationY = contentTranslationY;
+ invalidate();
+ }
+
+ private static final Property<QSScrollLayout, Float> CONTENT_TRANS_Y =
+ new Property<QSScrollLayout, Float>(Float.class, "qsScrollLayoutContentTransY") {
+ @Override
+ public Float get(QSScrollLayout qsScrollLayout) {
+ return qsScrollLayout.mContentTranslationY;
+ }
+
+ @Override
+ public void set(QSScrollLayout qsScrollLayout, Float y) {
+ qsScrollLayout.setContentTranslationY(y);
+ }
+ };
+
+ private class OverScrollHelper implements SwipeDetector.Listener {
+ private boolean mIsInOverScroll;
+
+ // We use this value to calculate the actual amount the user has overscrolled.
+ private float mFirstDisplacement = 0;
+
+ @Override
+ public void onDragStart(boolean start) {}
+
+ @Override
+ public boolean onDrag(float displacement, float velocity) {
+ // Only overscroll if the user is scrolling down when they're already at the bottom
+ // or scrolling up when they're already at the top.
+ boolean wasInOverScroll = mIsInOverScroll;
+ mIsInOverScroll = (!canScrollVertically(1) && displacement < 0) ||
+ (!canScrollVertically(-1) && displacement > 0);
+
+ if (wasInOverScroll && !mIsInOverScroll) {
+ // Exit overscroll. This can happen when the user is in overscroll and then
+ // scrolls the opposite way. Note that this causes the reset translation animation
+ // to run while the user is dragging, which feels a bit unnatural.
+ reset();
+ } else if (mIsInOverScroll) {
+ if (Float.compare(mFirstDisplacement, 0) == 0) {
+ // Because users can scroll before entering overscroll, we need to
+ // subtract the amount where the user was not in overscroll.
+ mFirstDisplacement = displacement;
+ }
+ float overscrollY = displacement - mFirstDisplacement;
+ setContentTranslationY(getDampedOverScroll(overscrollY));
+ }
+
+ return mIsInOverScroll;
+ }
+
+ @Override
+ public void onDragEnd(float velocity, boolean fling) {
+ reset();
+ }
+
+ private void reset() {
+ if (Float.compare(mContentTranslationY, 0) != 0) {
+ ObjectAnimator.ofFloat(QSScrollLayout.this, CONTENT_TRANS_Y, 0)
+ .setDuration(100)
+ .start();
+ }
+ mIsInOverScroll = false;
+ mFirstDisplacement = 0;
+ }
+
+ public boolean isInOverScroll() {
+ return mIsInOverScroll;
+ }
+
+ private float getDampedOverScroll(float y) {
+ return OverScroll.dampedScroll(y, getHeight());
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 3b9e7bc..65135ab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -104,6 +104,11 @@
setMeasuredDimension(width, height);
}
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+
private static int exactly(int size) {
return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/touch/OverScroll.java b/packages/SystemUI/src/com/android/systemui/qs/touch/OverScroll.java
new file mode 100644
index 0000000..0464886
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/touch/OverScroll.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.qs.touch;
+
+/**
+ * Utility methods for overscroll damping and related effect.
+ *
+ * Copied from packages/apps/Launcher3/src/com/android/launcher3/touch/OverScroll.java
+ */
+public class OverScroll {
+
+ private static final float OVERSCROLL_DAMP_FACTOR = 0.07f;
+
+ /**
+ * This curve determines how the effect of scrolling over the limits of the page diminishes
+ * as the user pulls further and further from the bounds
+ *
+ * @param f The percentage of how much the user has overscrolled.
+ * @return A transformed percentage based on the influence curve.
+ */
+ private static float overScrollInfluenceCurve(float f) {
+ f -= 1.0f;
+ return f * f * f + 1.0f;
+ }
+
+ /**
+ * @param amount The original amount overscrolled.
+ * @param max The maximum amount that the View can overscroll.
+ * @return The dampened overscroll amount.
+ */
+ public static int dampedScroll(float amount, int max) {
+ if (Float.compare(amount, 0) == 0) return 0;
+
+ float f = amount / max;
+ f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
+
+ // Clamp this factor, f, to -1 < f < 1
+ if (Math.abs(f) >= 1) {
+ f /= Math.abs(f);
+ }
+
+ return Math.round(OVERSCROLL_DAMP_FACTOR * f * max);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/touch/SwipeDetector.java b/packages/SystemUI/src/com/android/systemui/qs/touch/SwipeDetector.java
new file mode 100644
index 0000000..2522052
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/touch/SwipeDetector.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.qs.touch;
+
+import static android.view.MotionEvent.INVALID_POINTER_ID;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.support.annotation.NonNull;
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+/**
+ * One dimensional scroll/drag/swipe gesture detector.
+ *
+ * Definition of swipe is different from android system in that this detector handles
+ * 'swipe to dismiss', 'swiping up/down a container' but also keeps scrolling state before
+ * swipe action happens
+ *
+ * Copied from packages/apps/Launcher3/src/com/android/launcher3/touch/SwipeDetector.java
+ */
+public class SwipeDetector {
+
+ private static final boolean DBG = false;
+ private static final String TAG = "SwipeDetector";
+
+ private int mScrollConditions;
+ public static final int DIRECTION_POSITIVE = 1 << 0;
+ public static final int DIRECTION_NEGATIVE = 1 << 1;
+ public static final int DIRECTION_BOTH = DIRECTION_NEGATIVE | DIRECTION_POSITIVE;
+
+ private static final float ANIMATION_DURATION = 1200;
+
+ protected int mActivePointerId = INVALID_POINTER_ID;
+
+ /**
+ * The minimum release velocity in pixels per millisecond that triggers fling..
+ */
+ public static final float RELEASE_VELOCITY_PX_MS = 1.0f;
+
+ /**
+ * The time constant used to calculate dampening in the low-pass filter of scroll velocity.
+ * Cutoff frequency is set at 10 Hz.
+ */
+ public static final float SCROLL_VELOCITY_DAMPENING_RC = 1000f / (2f * (float) Math.PI * 10);
+
+ /* Scroll state, this is set to true during dragging and animation. */
+ private ScrollState mState = ScrollState.IDLE;
+
+ enum ScrollState {
+ IDLE,
+ DRAGGING, // onDragStart, onDrag
+ SETTLING // onDragEnd
+ }
+
+ public static abstract class Direction {
+
+ abstract float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint);
+
+ /**
+ * Distance in pixels a touch can wander before we think the user is scrolling.
+ */
+ abstract float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos);
+ }
+
+ public static final Direction VERTICAL = new Direction() {
+
+ @Override
+ float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint) {
+ return ev.getY(pointerIndex) - refPoint.y;
+ }
+
+ @Override
+ float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) {
+ return Math.abs(ev.getX(pointerIndex) - downPos.x);
+ }
+ };
+
+ public static final Direction HORIZONTAL = new Direction() {
+
+ @Override
+ float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint) {
+ return ev.getX(pointerIndex) - refPoint.x;
+ }
+
+ @Override
+ float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) {
+ return Math.abs(ev.getY(pointerIndex) - downPos.y);
+ }
+ };
+
+ //------------------- ScrollState transition diagram -----------------------------------
+ //
+ // IDLE -> (mDisplacement > mTouchSlop) -> DRAGGING
+ // DRAGGING -> (MotionEvent#ACTION_UP, MotionEvent#ACTION_CANCEL) -> SETTLING
+ // SETTLING -> (MotionEvent#ACTION_DOWN) -> DRAGGING
+ // SETTLING -> (View settled) -> IDLE
+
+ private void setState(ScrollState newState) {
+ if (DBG) {
+ Log.d(TAG, "setState:" + mState + "->" + newState);
+ }
+ // onDragStart and onDragEnd is reported ONLY on state transition
+ if (newState == ScrollState.DRAGGING) {
+ initializeDragging();
+ if (mState == ScrollState.IDLE) {
+ reportDragStart(false /* recatch */);
+ } else if (mState == ScrollState.SETTLING) {
+ reportDragStart(true /* recatch */);
+ }
+ }
+ if (newState == ScrollState.SETTLING) {
+ reportDragEnd();
+ }
+
+ mState = newState;
+ }
+
+ public boolean isDraggingOrSettling() {
+ return mState == ScrollState.DRAGGING || mState == ScrollState.SETTLING;
+ }
+
+ /**
+ * There's no touch and there's no animation.
+ */
+ public boolean isIdleState() {
+ return mState == ScrollState.IDLE;
+ }
+
+ public boolean isSettlingState() {
+ return mState == ScrollState.SETTLING;
+ }
+
+ public boolean isDraggingState() {
+ return mState == ScrollState.DRAGGING;
+ }
+
+ private final PointF mDownPos = new PointF();
+ private final PointF mLastPos = new PointF();
+ private final Direction mDir;
+
+ private final float mTouchSlop;
+
+ /* Client of this gesture detector can register a callback. */
+ private final Listener mListener;
+
+ private long mCurrentMillis;
+
+ private float mVelocity;
+ private float mLastDisplacement;
+ private float mDisplacement;
+
+ private float mSubtractDisplacement;
+ private boolean mIgnoreSlopWhenSettling;
+
+ public interface Listener {
+ void onDragStart(boolean start);
+
+ boolean onDrag(float displacement, float velocity);
+
+ void onDragEnd(float velocity, boolean fling);
+ }
+
+ public SwipeDetector(@NonNull Context context, @NonNull Listener l, @NonNull Direction dir) {
+ this(ViewConfiguration.get(context).getScaledTouchSlop(), l, dir);
+ }
+
+ @VisibleForTesting
+ protected SwipeDetector(float touchSlope, @NonNull Listener l, @NonNull Direction dir) {
+ mTouchSlop = touchSlope;
+ mListener = l;
+ mDir = dir;
+ }
+
+ public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
+ mScrollConditions = scrollDirectionFlags;
+ mIgnoreSlopWhenSettling = ignoreSlop;
+ }
+
+ private boolean shouldScrollStart(MotionEvent ev, int pointerIndex) {
+ // reject cases where the angle or slop condition is not met.
+ if (Math.max(mDir.getActiveTouchSlop(ev, pointerIndex, mDownPos), mTouchSlop)
+ > Math.abs(mDisplacement)) {
+ return false;
+ }
+
+ // Check if the client is interested in scroll in current direction.
+ if (((mScrollConditions & DIRECTION_NEGATIVE) > 0 && mDisplacement > 0) ||
+ ((mScrollConditions & DIRECTION_POSITIVE) > 0 && mDisplacement < 0)) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean onTouchEvent(MotionEvent ev) {
+ switch (ev.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ mActivePointerId = ev.getPointerId(0);
+ mDownPos.set(ev.getX(), ev.getY());
+ mLastPos.set(mDownPos);
+ mLastDisplacement = 0;
+ mDisplacement = 0;
+ mVelocity = 0;
+
+ if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
+ setState(ScrollState.DRAGGING);
+ }
+ break;
+ //case MotionEvent.ACTION_POINTER_DOWN:
+ case MotionEvent.ACTION_POINTER_UP:
+ int ptrIdx = ev.getActionIndex();
+ int ptrId = ev.getPointerId(ptrIdx);
+ if (ptrId == mActivePointerId) {
+ final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
+ mDownPos.set(
+ ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
+ ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
+ mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
+ mActivePointerId = ev.getPointerId(newPointerIdx);
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == INVALID_POINTER_ID) {
+ break;
+ }
+ mDisplacement = mDir.getDisplacement(ev, pointerIndex, mDownPos);
+ computeVelocity(mDir.getDisplacement(ev, pointerIndex, mLastPos),
+ ev.getEventTime());
+
+ // handle state and listener calls.
+ if (mState != ScrollState.DRAGGING && shouldScrollStart(ev, pointerIndex)) {
+ setState(ScrollState.DRAGGING);
+ }
+ if (mState == ScrollState.DRAGGING) {
+ reportDragging();
+ }
+ mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ // These are synthetic events and there is no need to update internal values.
+ if (mState == ScrollState.DRAGGING) {
+ setState(ScrollState.SETTLING);
+ }
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+
+ public void finishedScrolling() {
+ setState(ScrollState.IDLE);
+ }
+
+ private boolean reportDragStart(boolean recatch) {
+ mListener.onDragStart(!recatch);
+ if (DBG) {
+ Log.d(TAG, "onDragStart recatch:" + recatch);
+ }
+ return true;
+ }
+
+ private void initializeDragging() {
+ if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
+ mSubtractDisplacement = 0;
+ }
+ if (mDisplacement > 0) {
+ mSubtractDisplacement = mTouchSlop;
+ } else {
+ mSubtractDisplacement = -mTouchSlop;
+ }
+ }
+
+ private boolean reportDragging() {
+ if (mDisplacement != mLastDisplacement) {
+ if (DBG) {
+ Log.d(TAG, String.format("onDrag disp=%.1f, velocity=%.1f",
+ mDisplacement, mVelocity));
+ }
+
+ mLastDisplacement = mDisplacement;
+ return mListener.onDrag(mDisplacement - mSubtractDisplacement, mVelocity);
+ }
+ return true;
+ }
+
+ private void reportDragEnd() {
+ if (DBG) {
+ Log.d(TAG, String.format("onScrollEnd disp=%.1f, velocity=%.1f",
+ mDisplacement, mVelocity));
+ }
+ mListener.onDragEnd(mVelocity, Math.abs(mVelocity) > RELEASE_VELOCITY_PX_MS);
+
+ }
+
+ /**
+ * Computes the damped velocity.
+ */
+ public float computeVelocity(float delta, long currentMillis) {
+ long previousMillis = mCurrentMillis;
+ mCurrentMillis = currentMillis;
+
+ float deltaTimeMillis = mCurrentMillis - previousMillis;
+ float velocity = (deltaTimeMillis > 0) ? (delta / deltaTimeMillis) : 0;
+ if (Math.abs(mVelocity) < 0.001f) {
+ mVelocity = velocity;
+ } else {
+ float alpha = computeDampeningFactor(deltaTimeMillis);
+ mVelocity = interpolate(mVelocity, velocity, alpha);
+ }
+ return mVelocity;
+ }
+
+ /**
+ * Returns a time-dependent dampening factor using delta time.
+ */
+ private static float computeDampeningFactor(float deltaTime) {
+ return deltaTime / (SCROLL_VELOCITY_DAMPENING_RC + deltaTime);
+ }
+
+ /**
+ * Returns the linear interpolation between two values
+ */
+ private static float interpolate(float from, float to, float alpha) {
+ return (1.0f - alpha) * from + alpha * to;
+ }
+
+ public static long calculateDuration(float velocity, float progressNeeded) {
+ // TODO: make these values constants after tuning.
+ float velocityDivisor = Math.max(2f, Math.abs(0.5f * velocity));
+ float travelDistance = Math.max(0.2f, progressNeeded);
+ long duration = (long) Math.max(100, ANIMATION_DURATION / velocityDivisor * travelDistance);
+ if (DBG) {
+ Log.d(TAG, String.format("calculateDuration=%d, v=%f, d=%f", duration, velocity, progressNeeded));
+ }
+ return duration;
+ }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
index d6beb7f..ab89a52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
@@ -249,6 +249,9 @@
(GradientDrawable) ((LayerDrawable) mBackground).getDrawable(0);
gradientDrawable.setXfermode(
running ? new PorterDuffXfermode(PorterDuff.Mode.SRC) : null);
+ // Speed optimization: disable AA if transfer mode is not SRC_OVER. AA is not easy to
+ // spot during animation anyways.
+ gradientDrawable.setAntiAlias(!running);
}
if (!mExpandAnimationRunning) {
setDrawableAlpha(mDrawableAlpha);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 3ebeb4d..3dfb913 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -379,7 +379,7 @@
// Because space is usually constrained in the auto use-case, there should not be a
// pinned notification when the shade has been expanded. Ensure this by removing all heads-
// up notifications.
- mHeadsUpManager.removeAllHeadsUpEntries();
+ mHeadsUpManager.releaseAllImmediately();
super.animateExpandNotificationsPanel();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
new file mode 100644
index 0000000..aba5cdf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.support.v4.util.ArraySet;
+import android.util.Log;
+import android.util.Pools;
+import android.view.View;
+import android.view.ViewTreeObserver;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dumpable;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Stack;
+
+/**
+ * A implementation of HeadsUpManager for phone and car.
+ */
+public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
+ ViewTreeObserver.OnComputeInternalInsetsListener, VisualStabilityManager.Callback,
+ OnHeadsUpChangedListener {
+ private static final String TAG = "HeadsUpManagerPhone";
+ private static final boolean DEBUG = false;
+
+ private final View mStatusBarWindowView;
+ private final int mStatusBarHeight;
+ private final NotificationGroupManager mGroupManager;
+ private final StatusBar mBar;
+ private final VisualStabilityManager mVisualStabilityManager;
+
+ private boolean mReleaseOnExpandFinish;
+ private boolean mTrackingHeadsUp;
+ private HashSet<String> mSwipedOutKeys = new HashSet<>();
+ private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
+ private ArraySet<NotificationData.Entry> mEntriesToRemoveWhenReorderingAllowed
+ = new ArraySet<>();
+ private boolean mIsExpanded;
+ private int[] mTmpTwoArray = new int[2];
+ private boolean mHeadsUpGoingAway;
+ private boolean mWaitingOnCollapseWhenGoingAway;
+ private boolean mIsObserving;
+ private int mStatusBarState;
+
+ private final Pools.Pool<HeadsUpEntryPhone> mEntryPool = new Pools.Pool<HeadsUpEntryPhone>() {
+ private Stack<HeadsUpEntryPhone> mPoolObjects = new Stack<>();
+
+ @Override
+ public HeadsUpEntryPhone acquire() {
+ if (!mPoolObjects.isEmpty()) {
+ return mPoolObjects.pop();
+ }
+ return new HeadsUpEntryPhone();
+ }
+
+ @Override
+ public boolean release(@NonNull HeadsUpEntryPhone instance) {
+ mPoolObjects.push(instance);
+ return true;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // Constructor:
+
+ public HeadsUpManagerPhone(@NonNull final Context context, @NonNull View statusBarWindowView,
+ @NonNull NotificationGroupManager groupManager, @NonNull StatusBar bar,
+ @NonNull VisualStabilityManager visualStabilityManager) {
+ super(context);
+
+ mStatusBarWindowView = statusBarWindowView;
+ mGroupManager = groupManager;
+ mBar = bar;
+ mVisualStabilityManager = visualStabilityManager;
+
+ Resources resources = mContext.getResources();
+ mStatusBarHeight = resources.getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height);
+
+ addListener(new OnHeadsUpChangedListener() {
+ @Override
+ public void onHeadsUpPinnedModeChanged(boolean hasPinnedNotification) {
+ if (DEBUG) Log.w(TAG, "onHeadsUpPinnedModeChanged");
+ updateTouchableRegionListener();
+ }
+ });
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // Public methods:
+
+ /**
+ * Decides whether a click is invalid for a notification, i.e it has not been shown long enough
+ * that a user might have consciously clicked on it.
+ *
+ * @param key the key of the touched notification
+ * @return whether the touch is invalid and should be discarded
+ */
+ public boolean shouldSwallowClick(@NonNull String key) {
+ HeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key);
+ return entry != null && mClock.currentTimeMillis() < entry.postTime;
+ }
+
+ public void onExpandingFinished() {
+ if (mReleaseOnExpandFinish) {
+ releaseAllImmediately();
+ mReleaseOnExpandFinish = false;
+ } else {
+ for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
+ if (isHeadsUp(entry.key)) {
+ // Maybe the heads-up was removed already
+ removeHeadsUpEntry(entry);
+ }
+ }
+ }
+ mEntriesToRemoveAfterExpand.clear();
+ }
+
+ /**
+ * Sets the tracking-heads-up flag. If the flag is true, HeadsUpManager doesn't remove the entry
+ * from the list even after a Heads Up Notification is gone.
+ */
+ public void setTrackingHeadsUp(boolean trackingHeadsUp) {
+ mTrackingHeadsUp = trackingHeadsUp;
+ }
+
+ /**
+ * Notify that the status bar panel gets expanded or collapsed.
+ *
+ * @param isExpanded True to notify expanded, false to notify collapsed.
+ */
+ public void setIsPanelExpanded(boolean isExpanded) {
+ if (isExpanded != mIsExpanded) {
+ mIsExpanded = isExpanded;
+ if (isExpanded) {
+ // make sure our state is sane
+ mWaitingOnCollapseWhenGoingAway = false;
+ mHeadsUpGoingAway = false;
+ updateTouchableRegionListener();
+ }
+ }
+ }
+
+ /**
+ * Set the current state of the statusbar.
+ */
+ public void setStatusBarState(int statusBarState) {
+ mStatusBarState = statusBarState;
+ }
+
+ /**
+ * Set that we are exiting the headsUp pinned mode, but some notifications might still be
+ * animating out. This is used to keep the touchable regions in a sane state.
+ */
+ public void setHeadsUpGoingAway(boolean headsUpGoingAway) {
+ if (headsUpGoingAway != mHeadsUpGoingAway) {
+ mHeadsUpGoingAway = headsUpGoingAway;
+ if (!headsUpGoingAway) {
+ waitForStatusBarLayout();
+ }
+ updateTouchableRegionListener();
+ }
+ }
+
+ /**
+ * Notifies that a remote input textbox in notification gets active or inactive.
+ * @param entry The entry of the target notification.
+ * @param remoteInputActive True to notify active, False to notify inactive.
+ */
+ public void setRemoteInputActive(
+ @NonNull NotificationData.Entry entry, boolean remoteInputActive) {
+ HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.key);
+ if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
+ headsUpEntry.remoteInputActive = remoteInputActive;
+ if (remoteInputActive) {
+ headsUpEntry.removeAutoRemovalCallbacks();
+ } else {
+ headsUpEntry.updateEntry(false /* updatePostTime */);
+ }
+ }
+ }
+
+ @VisibleForTesting
+ public void removeMinimumDisplayTimeForTesting() {
+ mMinimumDisplayTime = 0;
+ mHeadsUpNotificationDecay = 0;
+ mTouchAcceptanceDelay = 0;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // HeadsUpManager public methods overrides:
+
+ @Override
+ public boolean isTrackingHeadsUp() {
+ return mTrackingHeadsUp;
+ }
+
+ @Override
+ public void snooze() {
+ super.snooze();
+ mReleaseOnExpandFinish = true;
+ }
+
+ /**
+ * React to the removal of the notification in the heads up.
+ *
+ * @return true if the notification was removed and false if it still needs to be kept around
+ * for a bit since it wasn't shown long enough
+ */
+ @Override
+ public boolean removeNotification(@NonNull String key, boolean ignoreEarliestRemovalTime) {
+ if (wasShownLongEnough(key) || ignoreEarliestRemovalTime) {
+ return super.removeNotification(key, ignoreEarliestRemovalTime);
+ } else {
+ HeadsUpEntryPhone entry = getHeadsUpEntryPhone(key);
+ entry.removeAsSoonAsPossible();
+ return false;
+ }
+ }
+
+ public void addSwipedOutNotification(@NonNull String key) {
+ mSwipedOutKeys.add(key);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // Dumpable overrides:
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("HeadsUpManagerPhone state:");
+ dumpInternal(fd, pw, args);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // ViewTreeObserver.OnComputeInternalInsetsListener overrides:
+
+ /**
+ * Overridden from TreeObserver.
+ */
+ @Override
+ public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
+ if (mIsExpanded || mBar.isBouncerShowing()) {
+ // The touchable region is always the full area when expanded
+ return;
+ }
+ if (hasPinnedHeadsUp()) {
+ ExpandableNotificationRow topEntry = getTopEntry().row;
+ if (topEntry.isChildInGroup()) {
+ final ExpandableNotificationRow groupSummary
+ = mGroupManager.getGroupSummary(topEntry.getStatusBarNotification());
+ if (groupSummary != null) {
+ topEntry = groupSummary;
+ }
+ }
+ topEntry.getLocationOnScreen(mTmpTwoArray);
+ int minX = mTmpTwoArray[0];
+ int maxX = mTmpTwoArray[0] + topEntry.getWidth();
+ int maxY = topEntry.getIntrinsicHeight();
+
+ info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ info.touchableRegion.set(minX, 0, maxX, maxY);
+ } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) {
+ info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // VisualStabilityManager.Callback overrides:
+
+ @Override
+ public void onReorderingAllowed() {
+ mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(false);
+ for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) {
+ if (isHeadsUp(entry.key)) {
+ // Maybe the heads-up was removed already
+ removeHeadsUpEntry(entry);
+ }
+ }
+ mEntriesToRemoveWhenReorderingAllowed.clear();
+ mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(true);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // HeadsUpManager utility (protected) methods overrides:
+
+ @Override
+ protected HeadsUpEntry createHeadsUpEntry() {
+ return mEntryPool.acquire();
+ }
+
+ @Override
+ protected void releaseHeadsUpEntry(HeadsUpEntry entry) {
+ entry.reset();
+ mEntryPool.release((HeadsUpEntryPhone) entry);
+ }
+
+ @Override
+ protected boolean shouldHeadsUpBecomePinned(NotificationData.Entry entry) {
+ return mStatusBarState != StatusBarState.KEYGUARD && !mIsExpanded
+ || super.shouldHeadsUpBecomePinned(entry);
+ }
+
+ @Override
+ protected void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
+ super.dumpInternal(fd, pw, args);
+ pw.print(" mStatusBarState="); pw.println(mStatusBarState);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // Private utility methods:
+
+ @Nullable
+ private HeadsUpEntryPhone getHeadsUpEntryPhone(@NonNull String key) {
+ return (HeadsUpEntryPhone) getHeadsUpEntry(key);
+ }
+
+ @Nullable
+ private HeadsUpEntryPhone getTopHeadsUpEntryPhone() {
+ return (HeadsUpEntryPhone) getTopHeadsUpEntry();
+ }
+
+ private boolean wasShownLongEnough(@NonNull String key) {
+ if (mSwipedOutKeys.contains(key)) {
+ // We always instantly dismiss views being manually swiped out.
+ mSwipedOutKeys.remove(key);
+ return true;
+ }
+
+ HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(key);
+ HeadsUpEntryPhone topEntry = getTopHeadsUpEntryPhone();
+ return headsUpEntry != topEntry || headsUpEntry.wasShownLongEnough();
+ }
+
+ /**
+ * We need to wait on the whole panel to collapse, before we can remove the touchable region
+ * listener.
+ */
+ private void waitForStatusBarLayout() {
+ mWaitingOnCollapseWhenGoingAway = true;
+ mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft,
+ int oldTop, int oldRight, int oldBottom) {
+ if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) {
+ mStatusBarWindowView.removeOnLayoutChangeListener(this);
+ mWaitingOnCollapseWhenGoingAway = false;
+ updateTouchableRegionListener();
+ }
+ }
+ });
+ }
+
+ private void updateTouchableRegionListener() {
+ boolean shouldObserve = hasPinnedHeadsUp() || mHeadsUpGoingAway
+ || mWaitingOnCollapseWhenGoingAway;
+ if (shouldObserve == mIsObserving) {
+ return;
+ }
+ if (shouldObserve) {
+ mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+ mStatusBarWindowView.requestLayout();
+ } else {
+ mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
+ }
+ mIsObserving = shouldObserve;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // HeadsUpEntryPhone:
+
+ protected class HeadsUpEntryPhone extends HeadsUpManager.HeadsUpEntry {
+ public void setEntry(@NonNull final NotificationData.Entry entry) {
+ Runnable removeHeadsUpRunnable = () -> {
+ if (!mVisualStabilityManager.isReorderingAllowed()) {
+ mEntriesToRemoveWhenReorderingAllowed.add(entry);
+ mVisualStabilityManager.addReorderingAllowedCallback(
+ HeadsUpManagerPhone.this);
+ } else if (!mTrackingHeadsUp) {
+ removeHeadsUpEntry(entry);
+ } else {
+ mEntriesToRemoveAfterExpand.add(entry);
+ }
+ };
+
+ super.setEntry(entry, removeHeadsUpRunnable);
+ }
+
+ public boolean wasShownLongEnough() {
+ return earliestRemovaltime < mClock.currentTimeMillis();
+ }
+
+ @Override
+ public void updateEntry(boolean updatePostTime) {
+ super.updateEntry(updatePostTime);
+
+ if (mEntriesToRemoveAfterExpand.contains(entry)) {
+ mEntriesToRemoveAfterExpand.remove(entry);
+ }
+ if (mEntriesToRemoveWhenReorderingAllowed.contains(entry)) {
+ mEntriesToRemoveWhenReorderingAllowed.remove(entry);
+ }
+ }
+
+ @Override
+ public void expanded(boolean expanded) {
+ if (this.expanded == expanded) {
+ return;
+ }
+
+ this.expanded = expanded;
+ if (expanded) {
+ removeAutoRemovalCallbacks();
+ } else {
+ updateEntry(false /* updatePostTime */);
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index c85571c..2bfdefe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -23,7 +23,7 @@
import com.android.systemui.Gefingerpoken;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
/**
@@ -31,7 +31,7 @@
*/
public class HeadsUpTouchHelper implements Gefingerpoken {
- private HeadsUpManager mHeadsUpManager;
+ private HeadsUpManagerPhone mHeadsUpManager;
private NotificationStackScrollLayout mStackScroller;
private int mTrackingPointer;
private float mTouchSlop;
@@ -43,7 +43,7 @@
private NotificationPanelView mPanel;
private ExpandableNotificationRow mPickedChild;
- public HeadsUpTouchHelper(HeadsUpManager headsUpManager,
+ public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager,
NotificationStackScrollLayout stackScroller,
NotificationPanelView notificationPanelView) {
mHeadsUpManager = headsUpManager;
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 cd2e77a..52d005c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -68,14 +68,12 @@
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.StackStateAnimator;
import java.util.List;
-import java.util.Collection;
public class NotificationPanelView extends PanelView implements
ExpandableView.OnHeightChangedListener,
@@ -1571,7 +1569,7 @@
private void updatePanelExpanded() {
boolean isExpanded = !isFullyCollapsed();
if (mPanelExpanded != isExpanded) {
- mHeadsUpManager.setIsExpanded(isExpanded);
+ mHeadsUpManager.setIsPanelExpanded(isExpanded);
mStatusBar.setPanelExpanded(isExpanded);
mPanelExpanded = isExpanded;
}
@@ -2338,7 +2336,7 @@
}
@Override
- public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+ public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
super.setHeadsUpManager(headsUpManager);
mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager, mNotificationStackScroller,
this);
@@ -2630,8 +2628,8 @@
}
}
- public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) {
- mKeyguardStatusView.setPulsing(pulsing != null);
+ public void setPulsing(boolean pulsing) {
+ mKeyguardStatusView.setPulsing(pulsing);
positionClockAndNotifications();
mNotificationStackScroller.setPulsing(pulsing, mKeyguardStatusView.getLocationOnScreen()[1]
+ mKeyguardStatusView.getClockBottom());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 2b7e474..6daabed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -50,7 +50,7 @@
import com.android.systemui.doze.DozeLog;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -75,7 +75,7 @@
}
protected StatusBar mStatusBar;
- protected HeadsUpManager mHeadsUpManager;
+ protected HeadsUpManagerPhone mHeadsUpManager;
private float mPeekHeight;
private float mHintDistance;
@@ -1252,7 +1252,7 @@
*/
protected abstract int getClearAllHeight();
- public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+ public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
mHeadsUpManager = headsUpManager;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 1bf719a..3777a6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -208,6 +208,7 @@
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -219,6 +220,7 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
@@ -809,15 +811,14 @@
.commit();
mIconController = Dependency.get(StatusBarIconController.class);
- mHeadsUpManager = new HeadsUpManager(context, mStatusBarWindow, mGroupManager);
- mHeadsUpManager.setBar(this);
+ mHeadsUpManager = new HeadsUpManagerPhone(context, mStatusBarWindow, mGroupManager, this,
+ mVisualStabilityManager);
mHeadsUpManager.addListener(this);
mHeadsUpManager.addListener(mNotificationPanel);
mHeadsUpManager.addListener(mGroupManager);
mHeadsUpManager.addListener(mVisualStabilityManager);
mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
mGroupManager.setHeadsUpManager(mHeadsUpManager);
- mHeadsUpManager.setVisualStabilityManager(mVisualStabilityManager);
putComponent(HeadsUpManager.class, mHeadsUpManager);
mEntryManager.setUpWithPresenter(this, mStackScroller, this, mHeadsUpManager);
@@ -1348,7 +1349,8 @@
@Override
public void onPerformRemoveNotification(StatusBarNotification n) {
- if (mStackScroller.hasPulsingNotifications() && mHeadsUpManager.getAllEntries().isEmpty()) {
+ if (mStackScroller.hasPulsingNotifications() &&
+ !mHeadsUpManager.hasHeadsUpNotifications()) {
// We were showing a pulse for a notification, but no notifications are pulsing anymore.
// Finish the pulse.
mDozeScrimController.pulseOutNow();
@@ -2097,9 +2099,8 @@
}
public void maybeEscalateHeadsUp() {
- Collection<HeadsUpManager.HeadsUpEntry> entries = mHeadsUpManager.getAllEntries();
- for (HeadsUpManager.HeadsUpEntry entry : entries) {
- final StatusBarNotification sbn = entry.entry.notification;
+ mHeadsUpManager.getAllEntries().forEach(entry -> {
+ final StatusBarNotification sbn = entry.notification;
final Notification notification = sbn.getNotification();
if (notification.fullScreenIntent != null) {
if (DEBUG) {
@@ -2109,11 +2110,11 @@
EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION,
sbn.getKey());
notification.fullScreenIntent.send();
- entry.entry.notifyFullScreenIntentLaunched();
+ entry.notifyFullScreenIntentLaunched();
} catch (PendingIntent.CanceledException e) {
}
}
- }
+ });
mHeadsUpManager.releaseAllImmediately();
}
@@ -4658,24 +4659,22 @@
@Override
public void onPulseStarted() {
callback.onPulseStarted();
- Collection<HeadsUpManager.HeadsUpEntry> pulsingEntries =
- mHeadsUpManager.getAllEntries();
- if (!pulsingEntries.isEmpty()) {
+ if (mHeadsUpManager.hasHeadsUpNotifications()) {
// Only pulse the stack scroller if there's actually something to show.
// Otherwise just show the always-on screen.
- setPulsing(pulsingEntries);
+ setPulsing(true);
}
}
@Override
public void onPulseFinished() {
callback.onPulseFinished();
- setPulsing(null);
+ setPulsing(false);
}
- private void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) {
+ private void setPulsing(boolean pulsing) {
mNotificationPanel.setPulsing(pulsing);
- mVisualStabilityManager.setPulsing(pulsing != null);
+ mVisualStabilityManager.setPulsing(pulsing);
mIgnoreTouchWhilePulsing = false;
}
}, reason);
@@ -4823,7 +4822,7 @@
// for heads up notifications
- protected HeadsUpManager mHeadsUpManager;
+ protected HeadsUpManagerPhone mHeadsUpManager;
private AboveShelfObserver mAboveShelfObserver;
@@ -4926,7 +4925,7 @@
// Release the HUN notification to the shade.
if (isPresenterFullyCollapsed()) {
- HeadsUpManager.setIsClickedNotification(row, true);
+ HeadsUpUtil.setIsClickedHeadsUpNotification(row, true);
}
//
// In most cases, when FLAG_AUTO_CANCEL is set, the notification will
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 53dfb24..040d7ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -16,119 +16,69 @@
package com.android.systemui.statusbar.policy;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.os.SystemClock;
import android.os.Handler;
import android.os.Looper;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.support.v4.util.ArraySet;
import android.util.ArrayMap;
+import android.provider.Settings;
import android.util.Log;
-import android.util.Pools;
-import android.view.View;
-import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityEvent;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.StatusBar;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collection;
+import java.util.Iterator;
+import java.util.stream.Stream;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Stack;
/**
* A manager which handles heads up notifications which is a special mode where
* they simply peek from the top of the screen.
*/
-public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsListener,
- VisualStabilityManager.Callback {
+public class HeadsUpManager {
private static final String TAG = "HeadsUpManager";
private static final boolean DEBUG = false;
private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms";
- private static final int TAG_CLICKED_NOTIFICATION = R.id.is_clicked_heads_up_tag;
- private final int mHeadsUpNotificationDecay;
- private final int mMinimumDisplayTime;
+ protected final Clock mClock = new Clock();
+ protected final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>();
+ protected final Handler mHandler = new Handler(Looper.getMainLooper());
- private final int mTouchAcceptanceDelay;
+ protected final Context mContext;
+
+ protected int mHeadsUpNotificationDecay;
+ protected int mMinimumDisplayTime;
+ protected int mTouchAcceptanceDelay;
+ protected int mSnoozeLengthMs;
+ protected boolean mHasPinnedNotification;
+ protected int mUser;
+
+ private final HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>();
private final ArrayMap<String, Long> mSnoozedPackages;
- private final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>();
- private final int mDefaultSnoozeLengthMs;
- private final Handler mHandler = new Handler(Looper.getMainLooper());
- private final Pools.Pool<HeadsUpEntry> mEntryPool = new Pools.Pool<HeadsUpEntry>() {
- private Stack<HeadsUpEntry> mPoolObjects = new Stack<>();
-
- @Override
- public HeadsUpEntry acquire() {
- if (!mPoolObjects.isEmpty()) {
- return mPoolObjects.pop();
- }
- return new HeadsUpEntry();
- }
-
- @Override
- public boolean release(HeadsUpEntry instance) {
- instance.reset();
- mPoolObjects.push(instance);
- return true;
- }
- };
-
- private final View mStatusBarWindowView;
- private final int mStatusBarHeight;
- private final Context mContext;
- private final NotificationGroupManager mGroupManager;
- private StatusBar mBar;
- private int mSnoozeLengthMs;
- private ContentObserver mSettingsObserver;
- private HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>();
- private HashSet<String> mSwipedOutKeys = new HashSet<>();
- private int mUser;
- private Clock mClock;
- private boolean mReleaseOnExpandFinish;
- private boolean mTrackingHeadsUp;
- private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
- private ArraySet<NotificationData.Entry> mEntriesToRemoveWhenReorderingAllowed
- = new ArraySet<>();
- private boolean mIsExpanded;
- private boolean mHasPinnedNotification;
- private int[] mTmpTwoArray = new int[2];
- private boolean mHeadsUpGoingAway;
- private boolean mWaitingOnCollapseWhenGoingAway;
- private boolean mIsObserving;
- private boolean mRemoteInputActive;
- private float mExpandedHeight;
- private VisualStabilityManager mVisualStabilityManager;
- private int mStatusBarState;
-
- public HeadsUpManager(final Context context, View statusBarWindowView,
- NotificationGroupManager groupManager) {
+ public HeadsUpManager(@NonNull final Context context) {
mContext = context;
- Resources resources = mContext.getResources();
- mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay);
- mSnoozedPackages = new ArrayMap<>();
- mDefaultSnoozeLengthMs = resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
- mSnoozeLengthMs = mDefaultSnoozeLengthMs;
+ Resources resources = context.getResources();
mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
mHeadsUpNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay);
- mClock = new Clock();
+ mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay);
+ mSnoozedPackages = new ArrayMap<>();
+ int defaultSnoozeLengthMs =
+ resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(),
- SETTING_HEADS_UP_SNOOZE_LENGTH_MS, mDefaultSnoozeLengthMs);
- mSettingsObserver = new ContentObserver(mHandler) {
+ SETTING_HEADS_UP_SNOOZE_LENGTH_MS, defaultSnoozeLengthMs);
+ ContentObserver settingsObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
final int packageSnoozeLengthMs = Settings.Global.getInt(
@@ -141,48 +91,27 @@
};
context.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false,
- mSettingsObserver);
- mStatusBarWindowView = statusBarWindowView;
- mGroupManager = groupManager;
- mStatusBarHeight = resources.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height);
+ settingsObserver);
}
- private void updateTouchableRegionListener() {
- boolean shouldObserve = mHasPinnedNotification || mHeadsUpGoingAway
- || mWaitingOnCollapseWhenGoingAway;
- if (shouldObserve == mIsObserving) {
- return;
- }
- if (shouldObserve) {
- mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
- mStatusBarWindowView.requestLayout();
- } else {
- mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
- }
- mIsObserving = shouldObserve;
- }
-
- public void setBar(StatusBar bar) {
- mBar = bar;
- }
-
- public void addListener(OnHeadsUpChangedListener listener) {
+ /**
+ * Adds an OnHeadUpChangedListener to observe events.
+ */
+ public void addListener(@NonNull OnHeadsUpChangedListener listener) {
mListeners.add(listener);
}
- public void removeListener(OnHeadsUpChangedListener listener) {
+ /**
+ * Removes the OnHeadUpChangedListener from the observer list.
+ */
+ public void removeListener(@NonNull OnHeadsUpChangedListener listener) {
mListeners.remove(listener);
}
- public StatusBar getBar() {
- return mBar;
- }
-
/**
* Called when posting a new notification to the heads up.
*/
- public void showNotification(NotificationData.Entry headsUp) {
+ public void showNotification(@NonNull NotificationData.Entry headsUp) {
if (DEBUG) Log.v(TAG, "showNotification");
addHeadsUpEntry(headsUp);
updateNotification(headsUp, true);
@@ -192,7 +121,7 @@
/**
* Called when updating or posting a notification to the heads up.
*/
- public void updateNotification(NotificationData.Entry headsUp, boolean alert) {
+ public void updateNotification(@NonNull NotificationData.Entry headsUp, boolean alert) {
if (DEBUG) Log.v(TAG, "updateNotification");
headsUp.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
@@ -204,14 +133,13 @@
// with the groupmanager
return;
}
- headsUpEntry.updateEntry();
+ headsUpEntry.updateEntry(true /* updatePostTime */);
setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUp));
}
}
- private void addHeadsUpEntry(NotificationData.Entry entry) {
- HeadsUpEntry headsUpEntry = mEntryPool.acquire();
-
+ private void addHeadsUpEntry(@NonNull NotificationData.Entry entry) {
+ HeadsUpEntry headsUpEntry = createHeadsUpEntry();
// This will also add the entry to the sortedList
headsUpEntry.setEntry(entry);
mHeadsUpEntries.put(entry.key, headsUpEntry);
@@ -223,16 +151,17 @@
entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
}
- private boolean shouldHeadsUpBecomePinned(NotificationData.Entry entry) {
- return mStatusBarState != StatusBarState.KEYGUARD
- && !mIsExpanded || hasFullScreenIntent(entry);
+ protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationData.Entry entry) {
+ return hasFullScreenIntent(entry);
}
- private boolean hasFullScreenIntent(NotificationData.Entry entry) {
+ protected boolean hasFullScreenIntent(@NonNull NotificationData.Entry entry) {
return entry.notification.getNotification().fullScreenIntent != null;
}
- private void setEntryPinned(HeadsUpEntry headsUpEntry, boolean isPinned) {
+ protected void setEntryPinned(
+ @NonNull HeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned) {
+ if (DEBUG) Log.v(TAG, "setEntryPinned: " + isPinned);
ExpandableNotificationRow row = headsUpEntry.entry.row;
if (row.isPinned() != isPinned) {
row.setPinned(isPinned);
@@ -247,33 +176,35 @@
}
}
- private void removeHeadsUpEntry(NotificationData.Entry entry) {
+ protected void removeHeadsUpEntry(@NonNull NotificationData.Entry entry) {
HeadsUpEntry remove = mHeadsUpEntries.remove(entry.key);
+ onHeadsUpEntryRemoved(remove);
+ }
+
+ protected void onHeadsUpEntryRemoved(@NonNull HeadsUpEntry remove) {
+ NotificationData.Entry entry = remove.entry;
entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
entry.row.setHeadsUp(false);
setEntryPinned(remove, false /* isPinned */);
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpStateChanged(entry, false);
}
- mEntryPool.release(remove);
+ releaseHeadsUpEntry(remove);
}
- public void removeAllHeadsUpEntries() {
- for (String key : mHeadsUpEntries.keySet()) {
- removeHeadsUpEntry(mHeadsUpEntries.get(key).entry);
- }
- }
-
- private void updatePinnedMode() {
+ protected void updatePinnedMode() {
boolean hasPinnedNotification = hasPinnedNotificationInternal();
if (hasPinnedNotification == mHasPinnedNotification) {
return;
}
+ if (DEBUG) {
+ Log.v(TAG, "Pinned mode changed: " + mHasPinnedNotification + " -> " +
+ hasPinnedNotification);
+ }
mHasPinnedNotification = hasPinnedNotification;
if (mHasPinnedNotification) {
MetricsLogger.count(mContext, "note_peek", 1);
}
- updateTouchableRegionListener();
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpPinnedModeChanged(hasPinnedNotification);
}
@@ -285,47 +216,36 @@
* @return true if the notification was removed and false if it still needs to be kept around
* for a bit since it wasn't shown long enough
*/
- public boolean removeNotification(String key, boolean ignoreEarliestRemovalTime) {
- if (DEBUG) Log.v(TAG, "remove");
- if (wasShownLongEnough(key) || ignoreEarliestRemovalTime) {
- releaseImmediately(key);
- return true;
- } else {
- getHeadsUpEntry(key).removeAsSoonAsPossible();
- return false;
- }
+ public boolean removeNotification(@NonNull String key, boolean ignoreEarliestRemovalTime) {
+ if (DEBUG) Log.v(TAG, "removeNotification");
+ releaseImmediately(key);
+ return true;
}
- private boolean wasShownLongEnough(String key) {
- HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
- HeadsUpEntry topEntry = getTopEntry();
- if (mSwipedOutKeys.contains(key)) {
- // We always instantly dismiss views being manually swiped out.
- mSwipedOutKeys.remove(key);
- return true;
- }
- if (headsUpEntry != topEntry) {
- return true;
- }
- return headsUpEntry.wasShownLongEnough();
- }
-
- public boolean isHeadsUp(String key) {
+ /**
+ * Returns if the given notification is in the Heads Up Notification list or not.
+ */
+ public boolean isHeadsUp(@NonNull String key) {
return mHeadsUpEntries.containsKey(key);
}
/**
- * Push any current Heads Up notification down into the shade.
+ * Pushes any current Heads Up notification down into the shade.
*/
public void releaseAllImmediately() {
if (DEBUG) Log.v(TAG, "releaseAllImmediately");
- ArrayList<String> keys = new ArrayList<>(mHeadsUpEntries.keySet());
- for (String key : keys) {
- releaseImmediately(key);
+ Iterator<HeadsUpEntry> iterator = mHeadsUpEntries.values().iterator();
+ while (iterator.hasNext()) {
+ HeadsUpEntry entry = iterator.next();
+ iterator.remove();
+ onHeadsUpEntryRemoved(entry);
}
}
- public void releaseImmediately(String key) {
+ /**
+ * Pushes the given Heads Up notification down into the shade.
+ */
+ public void releaseImmediately(@NonNull String key) {
HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
if (headsUpEntry == null) {
return;
@@ -334,11 +254,14 @@
removeHeadsUpEntry(shadeEntry);
}
- public boolean isSnoozed(String packageName) {
+ /**
+ * Returns if the given notification is snoozed or not.
+ */
+ public boolean isSnoozed(@NonNull String packageName) {
final String key = snoozeKey(packageName, mUser);
Long snoozedUntil = mSnoozedPackages.get(key);
if (snoozedUntil != null) {
- if (snoozedUntil > SystemClock.elapsedRealtime()) {
+ if (snoozedUntil > mClock.currentTimeMillis()) {
if (DEBUG) Log.v(TAG, key + " snoozed");
return true;
}
@@ -347,39 +270,71 @@
return false;
}
+ /**
+ * Snoozes all current Heads Up Notifications.
+ */
public void snooze() {
for (String key : mHeadsUpEntries.keySet()) {
HeadsUpEntry entry = mHeadsUpEntries.get(key);
String packageName = entry.entry.notification.getPackageName();
mSnoozedPackages.put(snoozeKey(packageName, mUser),
- SystemClock.elapsedRealtime() + mSnoozeLengthMs);
+ mClock.currentTimeMillis() + mSnoozeLengthMs);
}
- mReleaseOnExpandFinish = true;
}
- private static String snoozeKey(String packageName, int user) {
+ @NonNull
+ private static String snoozeKey(@NonNull String packageName, int user) {
return user + "," + packageName;
}
- private HeadsUpEntry getHeadsUpEntry(String key) {
+ @Nullable
+ protected HeadsUpEntry getHeadsUpEntry(@NonNull String key) {
return mHeadsUpEntries.get(key);
}
- public NotificationData.Entry getEntry(String key) {
- return mHeadsUpEntries.get(key).entry;
+ /**
+ * Returns the entry of given Heads Up Notification.
+ *
+ * @param key Key of heads up notification
+ */
+ @Nullable
+ public NotificationData.Entry getEntry(@NonNull String key) {
+ HeadsUpEntry entry = mHeadsUpEntries.get(key);
+ return entry != null ? entry.entry : null;
}
- public Collection<HeadsUpEntry> getAllEntries() {
- return mHeadsUpEntries.values();
+ /**
+ * Returns the stream of all current Heads Up Notifications.
+ */
+ @NonNull
+ public Stream<NotificationData.Entry> getAllEntries() {
+ return mHeadsUpEntries.values().stream().map(headsUpEntry -> headsUpEntry.entry);
}
- public HeadsUpEntry getTopEntry() {
+ /**
+ * Returns the top Heads Up Notification, which appeares to show at first.
+ */
+ @Nullable
+ public NotificationData.Entry getTopEntry() {
+ HeadsUpEntry topEntry = getTopHeadsUpEntry();
+ return (topEntry != null) ? topEntry.entry : null;
+ }
+
+ /**
+ * Returns if any heads up notification is available or not.
+ */
+ public boolean hasHeadsUpNotifications() {
+ return !mHeadsUpEntries.isEmpty();
+ }
+
+ @Nullable
+ protected HeadsUpEntry getTopHeadsUpEntry() {
if (mHeadsUpEntries.isEmpty()) {
return null;
}
HeadsUpEntry topEntry = null;
for (HeadsUpEntry entry: mHeadsUpEntries.values()) {
- if (topEntry == null || entry.compareTo(topEntry) == -1) {
+ if (topEntry == null || entry.compareTo(topEntry) < 0) {
topEntry = entry;
}
}
@@ -387,56 +342,22 @@
}
/**
- * Decides whether a click is invalid for a notification, i.e it has not been shown long enough
- * that a user might have consciously clicked on it.
- *
- * @param key the key of the touched notification
- * @return whether the touch is invalid and should be discarded
+ * Sets the current user.
*/
- public boolean shouldSwallowClick(String key) {
- HeadsUpEntry entry = mHeadsUpEntries.get(key);
- if (entry != null && mClock.currentTimeMillis() < entry.postTime) {
- return true;
- }
- return false;
- }
-
- public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
- if (mIsExpanded || mBar.isBouncerShowing()) {
- // The touchable region is always the full area when expanded
- return;
- }
- if (mHasPinnedNotification) {
- ExpandableNotificationRow topEntry = getTopEntry().entry.row;
- if (topEntry.isChildInGroup()) {
- final ExpandableNotificationRow groupSummary
- = mGroupManager.getGroupSummary(topEntry.getStatusBarNotification());
- if (groupSummary != null) {
- topEntry = groupSummary;
- }
- }
- topEntry.getLocationOnScreen(mTmpTwoArray);
- int minX = mTmpTwoArray[0];
- int maxX = mTmpTwoArray[0] + topEntry.getWidth();
- int maxY = topEntry.getIntrinsicHeight();
-
- info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- info.touchableRegion.set(minX, 0, maxX, maxY);
- } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) {
- info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
- }
- }
-
public void setUser(int user) {
mUser = user;
}
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("HeadsUpManager state:");
+ dumpInternal(fd, pw, args);
+ }
+
+ protected void dumpInternal(
+ @NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.print(" mTouchAcceptanceDelay="); pw.println(mTouchAcceptanceDelay);
pw.print(" mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
- pw.print(" now="); pw.println(SystemClock.elapsedRealtime());
+ pw.print(" now="); pw.println(mClock.currentTimeMillis());
pw.print(" mUser="); pw.println(mUser);
for (HeadsUpEntry entry: mHeadsUpEntries.values()) {
pw.print(" HeadsUpEntry="); pw.println(entry.entry);
@@ -449,6 +370,9 @@
}
}
+ /**
+ * Returns if there are any pinned Heads Up Notifications or not.
+ */
public boolean hasPinnedHeadsUp() {
return mHasPinnedNotification;
}
@@ -464,14 +388,8 @@
}
/**
- * Notifies that a notification was swiped out and will be removed.
- *
- * @param key the notification key
+ * Unpins all pinned Heads Up Notifications.
*/
- public void addSwipedOutNotification(String key) {
- mSwipedOutKeys.add(key);
- }
-
public void unpinAll() {
for (String key : mHeadsUpEntries.keySet()) {
HeadsUpEntry entry = mHeadsUpEntries.get(key);
@@ -481,60 +399,13 @@
}
}
- public void onExpandingFinished() {
- if (mReleaseOnExpandFinish) {
- releaseAllImmediately();
- mReleaseOnExpandFinish = false;
- } else {
- for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
- if (isHeadsUp(entry.key)) {
- // Maybe the heads-up was removed already
- removeHeadsUpEntry(entry);
- }
- }
- }
- mEntriesToRemoveAfterExpand.clear();
- }
-
- public void setTrackingHeadsUp(boolean trackingHeadsUp) {
- mTrackingHeadsUp = trackingHeadsUp;
- }
-
- public boolean isTrackingHeadsUp() {
- return mTrackingHeadsUp;
- }
-
- public void setIsExpanded(boolean isExpanded) {
- if (isExpanded != mIsExpanded) {
- mIsExpanded = isExpanded;
- if (isExpanded) {
- // make sure our state is sane
- mWaitingOnCollapseWhenGoingAway = false;
- mHeadsUpGoingAway = false;
- updateTouchableRegionListener();
- }
- }
- }
-
/**
- * @return the height of the top heads up notification when pinned. This is different from the
- * intrinsic height, which also includes whether the notification is system expanded and
- * is mainly used when dragging down from a heads up notification.
+ * Returns the value of the tracking-heads-up flag. See the doc of {@code setTrackingHeadsUp} as
+ * well.
*/
- public int getTopHeadsUpPinnedHeight() {
- HeadsUpEntry topEntry = getTopEntry();
- if (topEntry == null || topEntry.entry == null) {
- return 0;
- }
- ExpandableNotificationRow row = topEntry.entry.row;
- if (row.isChildInGroup()) {
- final ExpandableNotificationRow groupSummary
- = mGroupManager.getGroupSummary(row.getStatusBarNotification());
- if (groupSummary != null) {
- row = groupSummary;
- }
- }
- return row.getPinnedHeadsUpHeight();
+ public boolean isTrackingHeadsUp() {
+ // Might be implemented in subclass.
+ return false;
}
/**
@@ -543,7 +414,7 @@
* @return -1 if the first argument should be ranked higher than the second, 1 if the second
* one should be ranked higher and 0 if they are equal.
*/
- public int compare(NotificationData.Entry a, NotificationData.Entry b) {
+ public int compare(@NonNull NotificationData.Entry a, @NonNull NotificationData.Entry b) {
HeadsUpEntry aEntry = getHeadsUpEntry(a.key);
HeadsUpEntry bEntry = getHeadsUpEntry(b.key);
if (aEntry == null || bEntry == null) {
@@ -553,147 +424,62 @@
}
/**
- * Set that we are exiting the headsUp pinned mode, but some notifications might still be
- * animating out. This is used to keep the touchable regions in a sane state.
- */
- public void setHeadsUpGoingAway(boolean headsUpGoingAway) {
- if (headsUpGoingAway != mHeadsUpGoingAway) {
- mHeadsUpGoingAway = headsUpGoingAway;
- if (!headsUpGoingAway) {
- waitForStatusBarLayout();
- }
- updateTouchableRegionListener();
- }
- }
-
- /**
- * We need to wait on the whole panel to collapse, before we can remove the touchable region
- * listener.
- */
- private void waitForStatusBarLayout() {
- mWaitingOnCollapseWhenGoingAway = true;
- mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom,
- int oldLeft,
- int oldTop, int oldRight, int oldBottom) {
- if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) {
- mStatusBarWindowView.removeOnLayoutChangeListener(this);
- mWaitingOnCollapseWhenGoingAway = false;
- updateTouchableRegionListener();
- }
- }
- });
- }
-
- public static void setIsClickedNotification(View child, boolean clicked) {
- child.setTag(TAG_CLICKED_NOTIFICATION, clicked ? true : null);
- }
-
- public static boolean isClickedHeadsUpNotification(View child) {
- Boolean clicked = (Boolean) child.getTag(TAG_CLICKED_NOTIFICATION);
- return clicked != null && clicked;
- }
-
- public void setRemoteInputActive(NotificationData.Entry entry, boolean remoteInputActive) {
- HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key);
- if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
- headsUpEntry.remoteInputActive = remoteInputActive;
- if (remoteInputActive) {
- headsUpEntry.removeAutoRemovalCallbacks();
- } else {
- headsUpEntry.updateEntry(false /* updatePostTime */);
- }
- }
- }
-
- /**
* Set an entry to be expanded and therefore stick in the heads up area if it's pinned
* until it's collapsed again.
*/
- public void setExpanded(NotificationData.Entry entry, boolean expanded) {
- HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key);
- if (headsUpEntry != null && headsUpEntry.expanded != expanded && entry.row.isPinned()) {
- headsUpEntry.expanded = expanded;
- if (expanded) {
- headsUpEntry.removeAutoRemovalCallbacks();
- } else {
- headsUpEntry.updateEntry(false /* updatePostTime */);
- }
+ public void setExpanded(@NonNull NotificationData.Entry entry, boolean expanded) {
+ HeadsUpManager.HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key);
+ if (headsUpEntry != null && entry.row.isPinned()) {
+ headsUpEntry.expanded(expanded);
}
}
- @Override
- public void onReorderingAllowed() {
- mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(false);
- for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) {
- if (isHeadsUp(entry.key)) {
- // Maybe the heads-up was removed already
- removeHeadsUpEntry(entry);
- }
- }
- mEntriesToRemoveWhenReorderingAllowed.clear();
- mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(true);
+ @NonNull
+ protected HeadsUpEntry createHeadsUpEntry() {
+ return new HeadsUpEntry();
}
- public void setVisualStabilityManager(VisualStabilityManager visualStabilityManager) {
- mVisualStabilityManager = visualStabilityManager;
- }
-
- public void setStatusBarState(int statusBarState) {
- mStatusBarState = statusBarState;
+ protected void releaseHeadsUpEntry(@NonNull HeadsUpEntry entry) {
+ entry.reset();
}
/**
* This represents a notification and how long it is in a heads up mode. It also manages its
* lifecycle automatically when created.
*/
- public class HeadsUpEntry implements Comparable<HeadsUpEntry> {
- public NotificationData.Entry entry;
+ protected class HeadsUpEntry implements Comparable<HeadsUpEntry> {
+ @Nullable public NotificationData.Entry entry;
public long postTime;
- public long earliestRemovaltime;
- private Runnable mRemoveHeadsUpRunnable;
public boolean remoteInputActive;
+ public long earliestRemovaltime;
public boolean expanded;
- public void setEntry(final NotificationData.Entry entry) {
+ @Nullable private Runnable mRemoveHeadsUpRunnable;
+
+ public void setEntry(@Nullable final NotificationData.Entry entry) {
+ setEntry(entry, null);
+ }
+
+ public void setEntry(@Nullable final NotificationData.Entry entry,
+ @Nullable Runnable removeHeadsUpRunnable) {
this.entry = entry;
+ this.mRemoveHeadsUpRunnable = removeHeadsUpRunnable;
// The actual post time will be just after the heads-up really slided in
postTime = mClock.currentTimeMillis() + mTouchAcceptanceDelay;
- mRemoveHeadsUpRunnable = new Runnable() {
- @Override
- public void run() {
- if (!mVisualStabilityManager.isReorderingAllowed()) {
- mEntriesToRemoveWhenReorderingAllowed.add(entry);
- mVisualStabilityManager.addReorderingAllowedCallback(HeadsUpManager.this);
- } else if (!mTrackingHeadsUp) {
- removeHeadsUpEntry(entry);
- } else {
- mEntriesToRemoveAfterExpand.add(entry);
- }
- }
- };
- updateEntry();
- }
-
- public void updateEntry() {
- updateEntry(true);
+ updateEntry(true /* updatePostTime */);
}
public void updateEntry(boolean updatePostTime) {
+ if (DEBUG) Log.v(TAG, "updateEntry");
+
long currentTime = mClock.currentTimeMillis();
earliestRemovaltime = currentTime + mMinimumDisplayTime;
if (updatePostTime) {
postTime = Math.max(postTime, currentTime);
}
removeAutoRemovalCallbacks();
- if (mEntriesToRemoveAfterExpand.contains(entry)) {
- mEntriesToRemoveAfterExpand.remove(entry);
- }
- if (mEntriesToRemoveWhenReorderingAllowed.contains(entry)) {
- mEntriesToRemoveWhenReorderingAllowed.remove(entry);
- }
+
if (!isSticky()) {
long finishTime = postTime + mHeadsUpNotificationDecay;
long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
@@ -707,7 +493,7 @@
}
@Override
- public int compareTo(HeadsUpEntry o) {
+ public int compareTo(@NonNull HeadsUpEntry o) {
boolean isPinned = entry.row.isPinned();
boolean otherPinned = o.entry.row.isPinned();
if (isPinned && !otherPinned) {
@@ -734,26 +520,29 @@
: -1;
}
- public void removeAutoRemovalCallbacks() {
- mHandler.removeCallbacks(mRemoveHeadsUpRunnable);
- }
-
- public boolean wasShownLongEnough() {
- return earliestRemovaltime < mClock.currentTimeMillis();
- }
-
- public void removeAsSoonAsPossible() {
- removeAutoRemovalCallbacks();
- mHandler.postDelayed(mRemoveHeadsUpRunnable,
- earliestRemovaltime - mClock.currentTimeMillis());
+ public void expanded(boolean expanded) {
+ this.expanded = expanded;
}
public void reset() {
- removeAutoRemovalCallbacks();
entry = null;
- mRemoveHeadsUpRunnable = null;
expanded = false;
remoteInputActive = false;
+ removeAutoRemovalCallbacks();
+ mRemoveHeadsUpRunnable = null;
+ }
+
+ public void removeAutoRemovalCallbacks() {
+ if (mRemoveHeadsUpRunnable != null)
+ mHandler.removeCallbacks(mRemoveHeadsUpRunnable);
+ }
+
+ public void removeAsSoonAsPossible() {
+ if (mRemoveHeadsUpRunnable != null) {
+ removeAutoRemovalCallbacks();
+ mHandler.postDelayed(mRemoveHeadsUpRunnable,
+ earliestRemovaltime - mClock.currentTimeMillis());
+ }
}
}
@@ -762,5 +551,4 @@
return SystemClock.elapsedRealtime();
}
}
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java
new file mode 100644
index 0000000..1e3c123c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java
@@ -0,0 +1,47 @@
+/*
+ * 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.systemui.statusbar.policy;
+
+import android.view.View;
+
+import com.android.systemui.R;
+
+/**
+ * A class of utility static methods for heads up notifications.
+ */
+public final class HeadsUpUtil {
+ private static final int TAG_CLICKED_NOTIFICATION = R.id.is_clicked_heads_up_tag;
+
+ /**
+ * Set the given view as clicked or not-clicked.
+ * @param view The view to be set the flag to.
+ * @param clicked True to set as clicked. False to not-clicked.
+ */
+ public static void setIsClickedHeadsUpNotification(View view, boolean clicked) {
+ view.setTag(TAG_CLICKED_NOTIFICATION, clicked ? true : null);
+ }
+
+ /**
+ * Check if the given view has the flag of "clicked notification"
+ * @param view The view to be checked.
+ * @return True if the view has clicked. False othrewise.
+ */
+ public static boolean isClickedHeadsUpNotification(View view) {
+ Boolean clicked = (Boolean) view.getTag(TAG_CLICKED_NOTIFICATION);
+ return clicked != null && clicked;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 077c6c3..ea449c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -28,6 +28,8 @@
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.HapticFeedbackConstants;
@@ -45,6 +47,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
+import com.android.systemui.OverviewProxyService;
import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface;
@@ -58,12 +61,16 @@
private long mDownTime;
private int mCode;
private int mTouchSlop;
+ private int mTouchDownX;
+ private int mTouchDownY;
private boolean mSupportsLongpress = true;
private AudioManager mAudioManager;
private boolean mGestureAborted;
private boolean mLongClicked;
private OnClickListener mOnClickListener;
private final KeyButtonRipple mRipple;
+ private final OverviewProxyService mOverviewProxyService;
+ private final Vibrator mVibrator;
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private final Runnable mCheckLongPress = new Runnable() {
@@ -110,6 +117,8 @@
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mRipple = new KeyButtonRipple(context, this);
+ mVibrator = mContext.getSystemService(Vibrator.class);
+ mOverviewProxyService = Dependency.get(OverviewProxyService.class);
setBackground(mRipple);
}
@@ -189,6 +198,7 @@
}
public boolean onTouchEvent(MotionEvent ev) {
+ final boolean isProxyConnected = mOverviewProxyService.getProxy() != null;
final int action = ev.getAction();
int x, y;
if (action == MotionEvent.ACTION_DOWN) {
@@ -203,23 +213,34 @@
mDownTime = SystemClock.uptimeMillis();
mLongClicked = false;
setPressed(true);
+ mTouchDownX = (int) ev.getX();
+ mTouchDownY = (int) ev.getY();
if (mCode != 0) {
sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime);
} else {
// Provide the same haptic feedback that the system offers for virtual keys.
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
}
- playSoundEffect(SoundEffectConstants.CLICK);
+ if (isProxyConnected) {
+ // Provide small vibration for quick step or immediate down feedback
+ AsyncTask.execute(() ->
+ mVibrator.vibrate(VibrationEffect
+ .get(VibrationEffect.EFFECT_TICK, false)));
+ } else {
+ playSoundEffect(SoundEffectConstants.CLICK);
+ }
removeCallbacks(mCheckLongPress);
postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
break;
case MotionEvent.ACTION_MOVE:
x = (int)ev.getX();
y = (int)ev.getY();
- setPressed(x >= -mTouchSlop
- && x < getWidth() + mTouchSlop
- && y >= -mTouchSlop
- && y < getHeight() + mTouchSlop);
+ boolean exceededTouchSlopX = Math.abs(x - mTouchDownX) > mTouchSlop;
+ boolean exceededTouchSlopY = Math.abs(y - mTouchDownY) > mTouchSlop;
+ if (exceededTouchSlopX || exceededTouchSlopY) {
+ setPressed(false);
+ removeCallbacks(mCheckLongPress);
+ }
break;
case MotionEvent.ACTION_CANCEL:
setPressed(false);
@@ -231,9 +252,14 @@
case MotionEvent.ACTION_UP:
final boolean doIt = isPressed() && !mLongClicked;
setPressed(false);
- // Always send a release ourselves because it doesn't seem to be sent elsewhere
- // and it feels weird to sometimes get a release haptic and other times not.
- if ((SystemClock.uptimeMillis() - mDownTime) > 150 && !mLongClicked) {
+ if (isProxyConnected) {
+ if (doIt) {
+ performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
+ playSoundEffect(SoundEffectConstants.CLICK);
+ }
+ } else if ((SystemClock.uptimeMillis() - mDownTime) > 150 && !mLongClicked) {
+ // Always send a release ourselves because it doesn't seem to be sent elsewhere
+ // and it feels weird to sometimes get a release haptic and other times not.
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE);
}
if (mCode != 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index 424858a..d7a810e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -64,7 +64,7 @@
private boolean mPanelTracking;
private boolean mExpansionChanging;
private boolean mPanelFullWidth;
- private Collection<HeadsUpManager.HeadsUpEntry> mPulsing;
+ private boolean mPulsing;
private boolean mUnlockHintRunning;
private boolean mQsCustomizerShowing;
private int mIntrinsicPadding;
@@ -315,23 +315,18 @@
}
public boolean hasPulsingNotifications() {
- return mPulsing != null;
+ return mPulsing;
}
- public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> hasPulsing) {
+ public void setPulsing(boolean hasPulsing) {
mPulsing = hasPulsing;
}
public boolean isPulsing(NotificationData.Entry entry) {
- if (mPulsing == null) {
+ if (!mPulsing || mHeadsUpManager == null) {
return false;
}
- for (HeadsUpManager.HeadsUpEntry e : mPulsing) {
- if (e.entry == entry) {
- return true;
- }
- }
- return false;
+ return mHeadsUpManager.getAllEntries().anyMatch(e -> (e == entry));
}
public boolean isPanelTracking() {
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 c114a6f..1b55a5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -92,10 +92,11 @@
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.ScrollAdapter;
import android.support.v4.graphics.ColorUtils;
@@ -288,7 +289,7 @@
private HashSet<View> mClearOverlayViewsWhenFinished = new HashSet<>();
private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
= new HashSet<>();
- private HeadsUpManager mHeadsUpManager;
+ private HeadsUpManagerPhone mHeadsUpManager;
private boolean mTrackingHeadsUp;
private ScrimController mScrimController;
private boolean mForceNoOverlappingRendering;
@@ -358,7 +359,7 @@
}
};
private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
- private Collection<HeadsUpManager.HeadsUpEntry> mPulsing;
+ private boolean mPulsing;
private boolean mDrawBackgroundAsSrc;
private boolean mFadingOut;
private boolean mParentNotFullyVisible;
@@ -690,7 +691,7 @@
}
private void updateAlgorithmHeightAndPadding() {
- if (mPulsing != null) {
+ if (mPulsing) {
mTopPadding = mClockBottom;
} else {
mTopPadding = mAmbientState.isDark() ? mDarkTopPadding : mRegularTopPadding;
@@ -920,6 +921,27 @@
}
/**
+ * @return the height of the top heads up notification when pinned. This is different from the
+ * intrinsic height, which also includes whether the notification is system expanded and
+ * is mainly used when dragging down from a heads up notification.
+ */
+ private int getTopHeadsUpPinnedHeight() {
+ NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
+ if (topEntry == null) {
+ return 0;
+ }
+ ExpandableNotificationRow row = topEntry.row;
+ if (row.isChildInGroup()) {
+ final ExpandableNotificationRow groupSummary
+ = mGroupManager.getGroupSummary(row.getStatusBarNotification());
+ if (groupSummary != null) {
+ row = groupSummary;
+ }
+ }
+ return row.getPinnedHeadsUpHeight();
+ }
+
+ /**
* @return the position from where the appear transition ends when expanding.
* Measured in absolute height.
*/
@@ -930,7 +952,7 @@
int minNotificationsForShelf = 1;
if (mTrackingHeadsUp
|| (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
- appearPosition = mHeadsUpManager.getTopHeadsUpPinnedHeight();
+ appearPosition = getTopHeadsUpPinnedHeight();
minNotificationsForShelf = 2;
} else {
appearPosition = 0;
@@ -1198,9 +1220,9 @@
if (slidingChild instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
- && mHeadsUpManager.getTopEntry().entry.row != row
+ && mHeadsUpManager.getTopEntry().row != row
&& mGroupManager.getGroupSummary(
- mHeadsUpManager.getTopEntry().entry.row.getStatusBarNotification())
+ mHeadsUpManager.getTopEntry().row.getStatusBarNotification())
!= row) {
continue;
}
@@ -2120,7 +2142,7 @@
@Override
public boolean hasPulsingNotifications() {
- return mPulsing != null;
+ return mPulsing;
}
private void updateScrollability() {
@@ -2753,7 +2775,7 @@
}
private boolean isClickedHeadsUp(View child) {
- return HeadsUpManager.isClickedHeadsUpNotification(child);
+ return HeadsUpUtil.isClickedHeadsUpNotification(child);
}
/**
@@ -4258,7 +4280,7 @@
mAnimationFinishedRunnables.add(runnable);
}
- public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+ public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
mHeadsUpManager = headsUpManager;
mAmbientState.setHeadsUpManager(headsUpManager);
}
@@ -4326,8 +4348,8 @@
return mIsExpanded;
}
- public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing, int clockBottom) {
- if (mPulsing == null && pulsing == null) {
+ public void setPulsing(boolean pulsing, int clockBottom) {
+ if (!mPulsing && !pulsing) {
return;
}
mPulsing = pulsing;
@@ -4466,7 +4488,7 @@
pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
+ " alpha:%f scrollY:%d]",
this.getClass().getSimpleName(),
- mPulsing != null ?"T":"f",
+ mPulsing ? "T":"f",
mAmbientState.isQsCustomizerShowing() ? "T":"f",
getVisibility() == View.VISIBLE ? "visible"
: getVisibility() == View.GONE ? "gone"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
index 682b849..04a7bd7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
@@ -30,7 +30,7 @@
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.HeadsUpUtil;
/**
* A state of a view. This can be used to apply a set of view properties to a view with
@@ -582,7 +582,7 @@
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- HeadsUpManager.setIsClickedNotification(child, false);
+ HeadsUpUtil.setIsClickedHeadsUpNotification(child, false);
child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null);
child.setTag(TAG_START_TRANSLATION_Y, null);
child.setTag(TAG_END_TRANSLATION_Y, null);
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index 936ff51..59a7da6 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -65,7 +65,6 @@
LOCAL_JAVA_LIBRARIES := \
android.test.runner \
telephony-common \
- android.car \
android.test.base \
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
index 3e37cfe..bf6cc53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
@@ -151,22 +151,4 @@
verify(mMockNotificationManager, times(1)).cancelAsUser(anyString(),
eq(SystemMessage.NOTE_THERMAL_SHUTDOWN), any());
}
-
- @Test
- public void testGetTimeRemainingFormatted_roundsDownTo15() {
- mPowerNotificationWarnings.updateEstimate(
- new Estimate(TimeUnit.MINUTES.toMillis(57), true));
- String time = mPowerNotificationWarnings.getTimeRemainingFormatted();
-
- assertTrue("time:" + time + ", expected: " + FORMATTED_45M, time.equals(FORMATTED_45M));
- }
-
- @Test
- public void testGetTimeRemainingFormatted_keepsMinutesWhenZero() {
- mPowerNotificationWarnings.updateEstimate(
- new Estimate(TimeUnit.MINUTES.toMillis(65), true));
- String time = mPowerNotificationWarnings.getTimeRemainingFormatted();
-
- assertTrue("time:" + time + ", expected: " + FORMATTED_HOUR, time.equals(FORMATTED_HOUR));
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index 6e7477f..f3c1171 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -32,6 +32,7 @@
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.NotificationInflaterTest;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -51,7 +52,7 @@
public NotificationTestHelper(Context context) {
mContext = context;
mInstrumentation = InstrumentationRegistry.getInstrumentation();
- mHeadsUpManager = new HeadsUpManager(mContext, null, mGroupManager);
+ mHeadsUpManager = new HeadsUpManagerPhone(mContext, null, mGroupManager, null, null);
}
public ExpandableNotificationRow createRow() throws Exception {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
new file mode 100644
index 0000000..aa991cb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.app.ActivityManager;
+import android.app.Instrumentation;
+import android.app.Notification;
+import android.os.UserHandle;
+import android.view.View;
+import android.service.notification.StatusBarNotification;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.assertFalse;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class HeadsUpManagerPhoneTest extends SysuiTestCase {
+ @Rule public MockitoRule rule = MockitoJUnit.rule();
+
+ private static final String TEST_PACKAGE_NAME = "test";
+ private static final int TEST_UID = 0;
+
+ private HeadsUpManagerPhone mHeadsUpManager;
+
+ private NotificationData.Entry mEntry;
+ private StatusBarNotification mSbn;
+
+ @Mock private NotificationGroupManager mGroupManager;
+ @Mock private View mStatusBarWindowView;
+ @Mock private StatusBar mBar;
+ @Mock private ExpandableNotificationRow mRow;
+ @Mock private VisualStabilityManager mVSManager;
+
+ @Before
+ public void setUp() {
+ when(mVSManager.isReorderingAllowed()).thenReturn(true);
+
+ mHeadsUpManager = new HeadsUpManagerPhone(
+ mContext, mStatusBarWindowView, mGroupManager, mBar, mVSManager);
+
+ Notification.Builder n = new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setContentTitle("Title")
+ .setContentText("Text");
+ mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
+ 0, n.build(), new UserHandle(ActivityManager.getCurrentUser()), null, 0);
+
+ mEntry = new NotificationData.Entry(mSbn);
+ mEntry.row = mRow;
+ mEntry.expandedIcon = mock(StatusBarIconView.class);
+ }
+
+ @Test
+ public void testBasicOperations() {
+ // Check the initial state.
+ assertNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNull(mHeadsUpManager.getTopEntry());
+ assertEquals(0, mHeadsUpManager.getAllEntries().count());
+ assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
+
+ // Add a notification.
+ mHeadsUpManager.showNotification(mEntry);
+
+ assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
+ assertEquals(mEntry, mHeadsUpManager.getTopEntry());
+ assertEquals(1, mHeadsUpManager.getAllEntries().count());
+ assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
+
+ // Update the notification.
+ mHeadsUpManager.updateNotification(mEntry, false);
+
+ assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
+ assertEquals(mEntry, mHeadsUpManager.getTopEntry());
+ assertEquals(1, mHeadsUpManager.getAllEntries().count());
+ assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
+
+ // Try to remove but defer, since the notification is currenlt visible on display.
+ mHeadsUpManager.removeNotification(mEntry.key, false /* ignoreEarliestRemovalTime */);
+
+ assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
+ assertEquals(mEntry, mHeadsUpManager.getTopEntry());
+ assertEquals(1, mHeadsUpManager.getAllEntries().count());
+ assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
+
+ // Remove forcibly with ignoreEarliestRemovalTime = true.
+ mHeadsUpManager.removeNotification(mEntry.key, true /* ignoreEarliestRemovalTime */);
+
+ // Check the initial state.
+ assertNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNull(mHeadsUpManager.getTopEntry());
+ assertEquals(0, mHeadsUpManager.getAllEntries().count());
+ assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
+ }
+
+ @Test
+ public void testsTimeoutRemoval() {
+ mHeadsUpManager.removeMinimumDisplayTimeForTesting();
+
+ // Check the initial state.
+ assertNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNull(mHeadsUpManager.getTopEntry());
+ assertEquals(0, mHeadsUpManager.getAllEntries().count());
+ assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
+
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+
+ // Run the code on the main thready, not to run an async operations.
+ instrumentation.runOnMainSync(() -> {
+ // Add a notification.
+ mHeadsUpManager.showNotification(mEntry);
+
+ // Ensure the head up is visible before timeout.
+ assertNotNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNotNull(mHeadsUpManager.getTopEntry());
+ assertEquals(1, mHeadsUpManager.getAllEntries().count());
+ assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
+ });
+ // Wait for the async operations, which removes the heads up notification.
+ waitForIdleSync();
+
+ assertNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNull(mHeadsUpManager.getTopEntry());
+ assertEquals(0, mHeadsUpManager.getAllEntries().count());
+ assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
+ }
+
+ @Test
+ public void releaseImmediately() {
+ // Check the initial state.
+ assertNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNull(mHeadsUpManager.getTopEntry());
+ assertEquals(0, mHeadsUpManager.getAllEntries().count());
+ assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
+
+ // Add a notification.
+ mHeadsUpManager.showNotification(mEntry);
+
+ assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
+ assertEquals(mEntry, mHeadsUpManager.getTopEntry());
+ assertEquals(1, mHeadsUpManager.getAllEntries().count());
+ assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
+
+ // Remove but defer, since the notification is visible on display.
+ mHeadsUpManager.releaseImmediately(mEntry.key);
+
+ assertNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNull(mHeadsUpManager.getTopEntry());
+ assertEquals(0, mHeadsUpManager.getAllEntries().count());
+ assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
+ }
+
+ @Test
+ public void releaseAllImmediately() {
+ // Check the initial state.
+ assertNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNull(mHeadsUpManager.getTopEntry());
+ assertEquals(0, mHeadsUpManager.getAllEntries().count());
+ assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
+
+ // Add a notification.
+ mHeadsUpManager.showNotification(mEntry);
+
+ assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
+ assertEquals(mEntry, mHeadsUpManager.getTopEntry());
+ assertEquals(1, mHeadsUpManager.getAllEntries().count());
+ assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
+
+ // Remove but defer, since the notification is visible on display.
+ mHeadsUpManager.releaseAllImmediately();
+
+ assertNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNull(mHeadsUpManager.getTopEntry());
+ assertEquals(0, mHeadsUpManager.getAllEntries().count());
+ assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index bdf9b1f..31442af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -86,8 +86,8 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -110,7 +110,7 @@
@Mock private UnlockMethodCache mUnlockMethodCache;
@Mock private KeyguardIndicationController mKeyguardIndicationController;
@Mock private NotificationStackScrollLayout mStackScroller;
- @Mock private HeadsUpManager mHeadsUpManager;
+ @Mock private HeadsUpManagerPhone mHeadsUpManager;
@Mock private SystemServicesProxy mSystemServicesProxy;
@Mock private NotificationPanelView mNotificationPanelView;
@Mock private IStatusBarService mBarService;
@@ -588,7 +588,7 @@
static class TestableStatusBar extends StatusBar {
public TestableStatusBar(StatusBarKeyguardViewManager man,
UnlockMethodCache unlock, KeyguardIndicationController key,
- NotificationStackScrollLayout stack, HeadsUpManager hum,
+ NotificationStackScrollLayout stack, HeadsUpManagerPhone hum,
PowerManager pm, NotificationPanelView panelView,
IStatusBarService barService, NotificationListener notificationListener,
NotificationLogger notificationLogger,
@@ -650,7 +650,7 @@
public void setUpForTest(NotificationPresenter presenter,
NotificationListContainer listContainer,
Callback callback,
- HeadsUpManager headsUpManager,
+ HeadsUpManagerPhone headsUpManager,
NotificationData notificationData) {
super.setUpWithPresenter(presenter, listContainer, callback, headsUpManager);
mNotificationData = notificationData;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
index a10bebf..e1b97bda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
@@ -29,12 +29,14 @@
import com.android.systemui.statusbar.policy.LocationController.LocationChangeCallback;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
+@Ignore
public class LocationControllerImplTest extends SysuiTestCase {
private LocationControllerImpl mLocationController;
@@ -79,4 +81,4 @@
TestableLooper.get(this).processAllMessages();
}
-}
\ No newline at end of file
+}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 72c3c94..d5a722b 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -306,6 +306,7 @@
*
* @return service instance.
*/
+ @GuardedBy("mLock")
@NonNull
AutofillManagerServiceImpl getServiceForUserLocked(int userId) {
final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
@@ -325,6 +326,7 @@
*
* @return service instance or {@code null} if not already present
*/
+ @GuardedBy("mLock")
@Nullable
AutofillManagerServiceImpl peekServiceForUserLocked(int userId) {
final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
@@ -474,6 +476,7 @@
/**
* Removes a cached service for a given user.
*/
+ @GuardedBy("mLock")
private void removeCachedServiceLocked(int userId) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
@@ -485,6 +488,7 @@
/**
* Updates a cached service for a given user.
*/
+ @GuardedBy("mLock")
private void updateCachedServiceLocked(int userId) {
updateCachedServiceLocked(userId, mDisabledUsers.get(userId));
}
@@ -492,6 +496,7 @@
/**
* Updates a cached service for a given user.
*/
+ @GuardedBy("mLock")
private void updateCachedServiceLocked(int userId, boolean disabled) {
AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
if (service != null) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 989a7b5..2dcc6da 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -211,6 +211,7 @@
}
}
+ @GuardedBy("mLock")
private int getServiceUidLocked() {
if (mInfo == null) {
Slog.w(TAG, "getServiceUidLocked(): no mInfo");
@@ -248,6 +249,7 @@
mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, mUserId);
}
+ @GuardedBy("mLock")
void updateLocked(boolean disabled) {
final boolean wasEnabled = isEnabledLocked();
if (sVerbose) {
@@ -300,6 +302,7 @@
}
}
+ @GuardedBy("mLock")
boolean addClientLocked(IAutoFillManagerClient client) {
if (mClients == null) {
mClients = new RemoteCallbackList<>();
@@ -308,12 +311,14 @@
return isEnabledLocked();
}
+ @GuardedBy("mLock")
void removeClientLocked(IAutoFillManagerClient client) {
if (mClients != null) {
mClients.unregister(client);
}
}
+ @GuardedBy("mLock")
void setAuthenticationResultLocked(Bundle data, int sessionId, int authenticationId, int uid) {
if (!isEnabledLocked()) {
return;
@@ -336,6 +341,7 @@
}
}
+ @GuardedBy("mLock")
int startSessionLocked(@NonNull IBinder activityToken, int uid,
@NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId,
@NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback,
@@ -389,6 +395,7 @@
/**
* Remove abandoned sessions if needed.
*/
+ @GuardedBy("mLock")
private void pruneAbandonedSessionsLocked() {
long now = System.currentTimeMillis();
if (mLastPrune < now - MAX_ABANDONED_SESSION_MILLIS) {
@@ -400,6 +407,7 @@
}
}
+ @GuardedBy("mLock")
void finishSessionLocked(int sessionId, int uid) {
if (!isEnabledLocked()) {
return;
@@ -423,6 +431,7 @@
}
}
+ @GuardedBy("mLock")
void cancelSessionLocked(int sessionId, int uid) {
if (!isEnabledLocked()) {
return;
@@ -436,6 +445,7 @@
session.removeSelfLocked();
}
+ @GuardedBy("mLock")
void disableOwnedAutofillServicesLocked(int uid) {
Slog.i(TAG, "disableOwnedServices(" + uid + "): " + mInfo);
if (mInfo == null) return;
@@ -468,6 +478,7 @@
}
}
+ @GuardedBy("mLock")
private Session createSessionByTokenLocked(@NonNull IBinder activityToken, int uid,
@NonNull IBinder appCallbackToken, boolean hasCallback,
@NonNull ComponentName componentName, int flags) {
@@ -546,6 +557,7 @@
/**
* Updates a session and returns whether it should be restarted.
*/
+ @GuardedBy("mLock")
boolean updateSessionLocked(int sessionId, int uid, AutofillId autofillId, Rect virtualBounds,
AutofillValue value, int action, int flags) {
final Session session = mSessions.get(sessionId);
@@ -568,6 +580,7 @@
return false;
}
+ @GuardedBy("mLock")
void removeSessionLocked(int sessionId) {
mSessions.remove(sessionId);
}
@@ -603,6 +616,7 @@
}
}
+ @GuardedBy("mLock")
void destroyLocked() {
if (sVerbose) Slog.v(TAG, "destroyLocked()");
@@ -655,6 +669,7 @@
}
}
+ @GuardedBy("mLock")
private boolean isValidEventLocked(String method, int sessionId) {
if (mEventHistory == null) {
Slog.w(TAG, method + ": not logging event because history is null");
@@ -726,6 +741,7 @@
/**
* Updates the last fill response when an autofill context is committed.
*/
+ @GuardedBy("mLock")
void logContextCommittedLocked(int sessionId, @Nullable Bundle clientState,
@Nullable ArrayList<String> selectedDatasets,
@Nullable ArraySet<String> ignoredDatasets,
@@ -739,6 +755,7 @@
manuallyFilledDatasetIds, null, null, appPackageName);
}
+ @GuardedBy("mLock")
void logContextCommittedLocked(int sessionId, @Nullable Bundle clientState,
@Nullable ArrayList<String> selectedDatasets,
@Nullable ArraySet<String> ignoredDatasets,
@@ -847,6 +864,7 @@
}
}
+ @GuardedBy("mLock")
private boolean isCalledByServiceLocked(String methodName, int callingUid) {
if (getServiceUidLocked() != callingUid) {
Slog.w(TAG, methodName + "() called by UID " + callingUid
@@ -856,6 +874,7 @@
return true;
}
+ @GuardedBy("mLock")
void dumpLocked(String prefix, PrintWriter pw) {
final String prefix2 = prefix + " ";
@@ -965,6 +984,7 @@
mFieldClassificationStrategy.dump(prefix2, pw);
}
+ @GuardedBy("mLock")
void destroySessionsLocked() {
if (mSessions.size() == 0) {
mUi.destroyAll(null, null, false);
@@ -976,6 +996,7 @@
}
// TODO(b/64940307): remove this method if SaveUI is refactored to be attached on activities
+ @GuardedBy("mLock")
void destroyFinishedSessionsLocked() {
final int sessionCount = mSessions.size();
for (int i = sessionCount - 1; i >= 0; i--) {
@@ -987,6 +1008,7 @@
}
}
+ @GuardedBy("mLock")
void listSessionsLocked(ArrayList<String> output) {
final int numSessions = mSessions.size();
for (int i = 0; i < numSessions; i++) {
@@ -995,6 +1017,7 @@
}
}
+ @GuardedBy("mLock")
boolean isCompatibilityModeRequestedLocked(@NonNull String packageName,
long versionCode) {
if (mInfo == null || !mInfo.isCompatibilityModeRequested(packageName, versionCode)) {
@@ -1060,6 +1083,7 @@
}
}
+ @GuardedBy("mLock")
private boolean isClientSessionDestroyedLocked(IAutoFillManagerClient client) {
final int sessionCount = mSessions.size();
for (int i = 0; i < sessionCount; i++) {
@@ -1071,6 +1095,7 @@
return true;
}
+ @GuardedBy("mLock")
boolean isEnabledLocked() {
return mSetupComplete && mInfo != null && !mDisabled;
}
@@ -1123,6 +1148,7 @@
/**
* Checks if autofill is disabled by service to the given activity.
*/
+ @GuardedBy("mLock")
private boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) {
// Check activities first.
long elapsedTime = 0;
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index aea9ad0..fe6d4c4 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -475,6 +475,7 @@
return true;
}
+ @GuardedBy("mLock")
protected boolean isCancelledLocked() {
return mCancelled;
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 18f49ec..4a24704 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -300,6 +300,7 @@
/**
* Returns the ids of all entries in {@link #mViewStates} in the same order.
*/
+ @GuardedBy("mLock")
private AutofillId[] getIdsOfAllViewStatesLocked() {
final int numViewState = mViewStates.size();
final AutofillId[] ids = new AutofillId[numViewState];
@@ -346,6 +347,7 @@
* <p>Gets the value of a field, using either the {@code viewStates} or the {@code mContexts},
* or {@code null} when not found on either of them.
*/
+ @GuardedBy("mLock")
private AutofillValue findValueLocked(@NonNull AutofillId id) {
final ViewState state = mViewStates.get(id);
if (state == null) {
@@ -369,6 +371,7 @@
* @param fillContext The context to be filled
* @param flags The flags that started the session
*/
+ @GuardedBy("mLock")
private void fillContextWithAllowedValuesLocked(@NonNull FillContext fillContext, int flags) {
final ViewNode[] nodes = fillContext
.findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked());
@@ -409,6 +412,7 @@
/**
* Cancels the last request sent to the {@link #mRemoteFillService}.
*/
+ @GuardedBy("mLock")
private void cancelCurrentRequestLocked() {
final int canceledRequest = mRemoteFillService.cancelCurrentRequest();
@@ -430,6 +434,7 @@
/**
* Reads a new structure and then request a new fill response from the fill service.
*/
+ @GuardedBy("mLock")
private void requestNewFillResponseLocked(int flags) {
int requestId;
@@ -497,6 +502,7 @@
*
* @return The activity token
*/
+ @GuardedBy("mLock")
@NonNull IBinder getActivityTokenLocked() {
return mActivityToken;
}
@@ -663,6 +669,7 @@
*
* @return The context or {@code null} if there is no context
*/
+ @GuardedBy("mLock")
@Nullable private FillContext getFillContextByRequestIdLocked(int requestId) {
if (mContexts == null) {
return null;
@@ -820,6 +827,7 @@
});
}
+ @GuardedBy("mLock")
void setAuthenticationResultLocked(Bundle data, int authenticationId) {
if (mDestroyed) {
Slog.w(TAG, "Call to Session#setAuthenticationResultLocked() rejected - session: "
@@ -882,6 +890,7 @@
}
}
+ @GuardedBy("mLock")
void setHasCallbackLocked(boolean hasIt) {
if (mDestroyed) {
Slog.w(TAG, "Call to Session#setHasCallbackLocked() rejected - session: "
@@ -891,6 +900,7 @@
mHasCallback = hasIt;
}
+ @GuardedBy("mLock")
@Nullable
private FillResponse getLastResponseLocked(@Nullable String logPrefix) {
if (mContexts == null) {
@@ -923,6 +933,7 @@
return response;
}
+ @GuardedBy("mLock")
@Nullable
private SaveInfo getSaveInfoLocked() {
final FillResponse response = getLastResponseLocked(null);
@@ -941,6 +952,7 @@
});
}
+ @GuardedBy("mLock")
private void logContextCommittedLocked() {
final FillResponse lastResponse = getLastResponseLocked("logContextCommited()");
if (lastResponse == null) return;
@@ -1241,6 +1253,7 @@
*
* @return {@code true} if session is done, or {@code false} if it's pending user action.
*/
+ @GuardedBy("mLock")
public boolean showSaveLocked() {
if (mDestroyed) {
Slog.w(TAG, "Call to Session#showSaveLocked() rejected - session: "
@@ -1510,6 +1523,7 @@
/**
* Returns whether the session is currently showing the save UI
*/
+ @GuardedBy("mLock")
boolean isSavingLocked() {
return mIsSaving;
}
@@ -1517,6 +1531,7 @@
/**
* Gets the latest non-empty value for the given id in the autofill contexts.
*/
+ @GuardedBy("mLock")
@Nullable
private AutofillValue getValueFromContextsLocked(AutofillId id) {
final int numContexts = mContexts.size();
@@ -1539,6 +1554,7 @@
/**
* Gets the latest autofill options for the given id in the autofill contexts.
*/
+ @GuardedBy("mLock")
@Nullable
private CharSequence[] getAutofillOptionsFromContextsLocked(AutofillId id) {
final int numContexts = mContexts.size();
@@ -1556,6 +1572,7 @@
/**
* Calls service when user requested save.
*/
+ @GuardedBy("mLock")
void callSaveLocked() {
if (mDestroyed) {
Slog.w(TAG, "Call to Session#callSaveLocked() rejected - session: "
@@ -1646,6 +1663,7 @@
* @param viewState The view that is entered.
* @param flags The flag that was passed by the AutofillManager.
*/
+ @GuardedBy("mLock")
private void requestNewFillResponseOnViewEnteredIfNecessaryLocked(@NonNull AutofillId id,
@NonNull ViewState viewState, int flags) {
if ((flags & FLAG_MANUAL_REQUEST) != 0) {
@@ -1673,6 +1691,7 @@
*
* @return {@code true} iff a new partition should be started
*/
+ @GuardedBy("mLock")
private boolean shouldStartNewPartitionLocked(@NonNull AutofillId id) {
if (mResponses == null) {
return true;
@@ -1721,6 +1740,7 @@
return true;
}
+ @GuardedBy("mLock")
void updateLocked(AutofillId id, Rect virtualBounds, AutofillValue value, int action,
int flags) {
if (mDestroyed) {
@@ -1830,6 +1850,7 @@
/**
* Checks whether a view should be ignored.
*/
+ @GuardedBy("mLock")
private boolean isIgnoredLocked(AutofillId id) {
// Always check the latest response only
final FillResponse response = getLastResponseLocked(null);
@@ -1910,6 +1931,7 @@
}
}
+ @GuardedBy("mLock")
private void updateTrackedIdsLocked() {
// Only track the views of the last response as only those are reported back to the
// service, see #showSaveLocked
@@ -1982,6 +2004,7 @@
}
}
+ @GuardedBy("mLock")
private void replaceResponseLocked(@NonNull FillResponse oldResponse,
@NonNull FillResponse newResponse, @Nullable Bundle newClientState) {
// Disassociate view states with the old response
@@ -2005,6 +2028,7 @@
removeSelf();
}
+ @GuardedBy("mLock")
private void processResponseLocked(@NonNull FillResponse newResponse,
@Nullable Bundle newClientState, int flags) {
// Make sure we are hiding the UI which will be shown
@@ -2042,6 +2066,7 @@
/**
* Sets the state of all views in the given response.
*/
+ @GuardedBy("mLock")
private void setViewStatesLocked(FillResponse response, int state, boolean clearResponse) {
final List<Dataset> datasets = response.getDatasets();
if (datasets != null) {
@@ -2090,6 +2115,7 @@
/**
* Sets the state of all views in the given dataset and response.
*/
+ @GuardedBy("mLock")
private void setViewStatesLocked(@Nullable FillResponse response, @NonNull Dataset dataset,
int state, boolean clearResponse) {
final ArrayList<AutofillId> ids = dataset.getFieldIds();
@@ -2110,6 +2136,7 @@
}
}
+ @GuardedBy("mLock")
private ViewState createOrUpdateViewStateLocked(@NonNull AutofillId id, int state,
@Nullable AutofillValue value) {
ViewState viewState = mViewStates.get(id);
@@ -2171,6 +2198,7 @@
}
// TODO: this should never be null, but we got at least one occurrence, probably due to a race.
+ @GuardedBy("mLock")
@Nullable
private Intent createAuthFillInIntentLocked(int requestId, Bundle extras) {
final Intent fillInIntent = new Intent();
@@ -2203,6 +2231,7 @@
return "Session: [id=" + id + ", component=" + mComponentName + "]";
}
+ @GuardedBy("mLock")
void dumpLocked(String prefix, PrintWriter pw) {
final String prefix2 = prefix + " ";
pw.print(prefix); pw.print("id: "); pw.println(id);
@@ -2332,6 +2361,7 @@
* disabled it).
* </ul>
*/
+ @GuardedBy("mLock")
RemoteFillService destroyLocked() {
if (mDestroyed) {
return null;
@@ -2347,6 +2377,7 @@
* Cleans up this session and remove it from the service always, even if it does have a pending
* Save UI.
*/
+ @GuardedBy("mLock")
void forceRemoveSelfLocked() {
if (sVerbose) Slog.v(TAG, "forceRemoveSelfLocked(): " + mPendingSaveUi);
@@ -2376,6 +2407,7 @@
* Cleans up this session and remove it from the service, but but only if it does not have a
* pending Save UI.
*/
+ @GuardedBy("mLock")
void removeSelfLocked() {
if (sVerbose) Slog.v(TAG, "removeSelfLocked(): " + mPendingSaveUi);
if (mDestroyed) {
@@ -2404,6 +2436,7 @@
* a specific {@code token} created by
* {@link PendingUi#PendingUi(IBinder, int, IAutoFillManagerClient)}.
*/
+ @GuardedBy("mLock")
boolean isSaveUiPendingForTokenLocked(@NonNull IBinder token) {
return isSaveUiPendingLocked() && token.equals(mPendingSaveUi.getToken());
}
@@ -2411,10 +2444,12 @@
/**
* Checks whether this session is hiding the Save UI to handle a custom description link.
*/
+ @GuardedBy("mLock")
private boolean isSaveUiPendingLocked() {
return mPendingSaveUi != null && mPendingSaveUi.getState() == PendingUi.STATE_PENDING;
}
+ @GuardedBy("mLock")
private int getLastResponseIndexLocked() {
// The response ids are monotonically increasing so
// we just find the largest id which is the last. We
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 17c617c..45b2118 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -88,6 +88,7 @@
import java.util.function.Predicate;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE;
+import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
import static android.app.AlarmManager.RTC_WAKEUP;
import static android.app.AlarmManager.RTC;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
@@ -1011,7 +1012,7 @@
// Recurring alarms may have passed several alarm intervals while the
// alarm was kept pending. Send the appropriate trigger count.
if (alarm.repeatInterval > 0) {
- alarm.count += (nowELAPSED - alarm.requestedWhenElapsed) / alarm.repeatInterval;
+ alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval;
// Also schedule its next recurrence
final long delta = alarm.count * alarm.repeatInterval;
final long nextElapsed = alarm.whenElapsed + delta;
@@ -1507,24 +1508,19 @@
* Adjusts the alarm delivery time based on the current app standby bucket.
* @param alarm The alarm to adjust
* @return true if the alarm delivery time was updated.
- * TODO: Reduce the number of calls to getAppStandbyBucket by batching the calls per
- * {package, user} pairs
*/
private boolean adjustDeliveryTimeBasedOnStandbyBucketLocked(Alarm alarm) {
- if (alarm.alarmClock != null || UserHandle.isCore(alarm.creatorUid)) {
- return false;
- }
- // TODO: short term fix for b/72816079, remove after a proper fix is in place
- if ((alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
+ if (isExemptFromAppStandby(alarm)) {
return false;
}
if (mAppStandbyParole) {
- if (alarm.whenElapsed > alarm.requestedWhenElapsed) {
+ if (alarm.whenElapsed > alarm.expectedWhenElapsed) {
// We did defer this alarm earlier, restore original requirements
- alarm.whenElapsed = alarm.requestedWhenElapsed;
- alarm.maxWhenElapsed = alarm.requestedMaxWhenElapsed;
+ alarm.whenElapsed = alarm.expectedWhenElapsed;
+ alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed;
+ return true;
}
- return true;
+ return false;
}
final long oldWhenElapsed = alarm.whenElapsed;
final long oldMaxWhenElapsed = alarm.maxWhenElapsed;
@@ -1538,13 +1534,13 @@
final long lastElapsed = mLastAlarmDeliveredForPackage.getOrDefault(packageUser, 0L);
if (lastElapsed > 0) {
final long minElapsed = lastElapsed + getMinDelayForBucketLocked(standbyBucket);
- if (alarm.requestedWhenElapsed < minElapsed) {
+ if (alarm.expectedWhenElapsed < minElapsed) {
alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
} else {
// app is now eligible to run alarms at the originally requested window.
// Restore original requirements in case they were changed earlier.
- alarm.whenElapsed = alarm.requestedWhenElapsed;
- alarm.maxWhenElapsed = alarm.requestedMaxWhenElapsed;
+ alarm.whenElapsed = alarm.expectedWhenElapsed;
+ alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed;
}
}
return (oldWhenElapsed != alarm.whenElapsed || oldMaxWhenElapsed != alarm.maxWhenElapsed);
@@ -1728,7 +1724,7 @@
// This means we will allow these alarms to go off as normal even while idle, with no
// timing restrictions.
} else if (workSource == null && (callingUid < Process.FIRST_APPLICATION_UID
- || callingUid == mSystemUiUid
+ || UserHandle.isSameApp(callingUid, mSystemUiUid)
|| mForceAppStandbyTracker.isUidPowerSaveWhitelisted(callingUid))) {
flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
flags &= ~AlarmManager.FLAG_ALLOW_WHILE_IDLE;
@@ -3000,10 +2996,11 @@
// Whoops, it hasn't been long enough since the last ALLOW_WHILE_IDLE
// alarm went off for this app. Reschedule the alarm to be in the
// correct time period.
- alarm.whenElapsed = minTime;
+ alarm.expectedWhenElapsed = alarm.whenElapsed = minTime;
if (alarm.maxWhenElapsed < minTime) {
alarm.maxWhenElapsed = minTime;
}
+ alarm.expectedMaxWhenElapsed = alarm.maxWhenElapsed;
if (RECORD_DEVICE_IDLE_ALARMS) {
IdleDispatchEntry ent = new IdleDispatchEntry();
ent.uid = alarm.uid;
@@ -3053,7 +3050,7 @@
if (alarm.repeatInterval > 0) {
// this adjustment will be zero if we're late by
// less than one full repeat interval
- alarm.count += (nowELAPSED - alarm.requestedWhenElapsed) / alarm.repeatInterval;
+ alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval;
// Also schedule its next recurrence
final long delta = alarm.count * alarm.repeatInterval;
@@ -3128,8 +3125,9 @@
public long windowLength;
public long whenElapsed; // 'when' in the elapsed time base
public long maxWhenElapsed; // also in the elapsed time base
- public final long requestedWhenElapsed; // original expiry time requested by the app
- public final long requestedMaxWhenElapsed;
+ // Expected alarm expiry time before app standby deferring is applied.
+ public long expectedWhenElapsed;
+ public long expectedMaxWhenElapsed;
public long repeatInterval;
public PriorityClass priorityClass;
@@ -3143,10 +3141,10 @@
|| _type == AlarmManager.RTC_WAKEUP;
when = _when;
whenElapsed = _whenElapsed;
- requestedWhenElapsed = _whenElapsed;
+ expectedWhenElapsed = _whenElapsed;
windowLength = _windowLength;
maxWhenElapsed = _maxWhen;
- requestedMaxWhenElapsed = _maxWhen;
+ expectedMaxWhenElapsed = _maxWhen;
repeatInterval = _interval;
operation = _op;
listener = _rec;
@@ -3205,8 +3203,10 @@
final boolean isRtc = (type == RTC || type == RTC_WAKEUP);
pw.print(prefix); pw.print("tag="); pw.println(statsTag);
pw.print(prefix); pw.print("type="); pw.print(type);
- pw.print(" requestedWhenElapsed="); TimeUtils.formatDuration(
- requestedWhenElapsed, nowELAPSED, pw);
+ pw.print(" expectedWhenElapsed="); TimeUtils.formatDuration(
+ expectedWhenElapsed, nowELAPSED, pw);
+ pw.print(" expectedMaxWhenElapsed="); TimeUtils.formatDuration(
+ expectedMaxWhenElapsed, nowELAPSED, pw);
pw.print(" whenElapsed="); TimeUtils.formatDuration(whenElapsed,
nowELAPSED, pw);
pw.print(" when=");
@@ -3344,6 +3344,11 @@
}
}
+ private boolean isExemptFromAppStandby(Alarm a) {
+ return a.alarmClock != null || UserHandle.isCore(a.creatorUid)
+ || (a.flags & FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED) != 0;
+ }
+
private class AlarmThread extends Thread
{
public AlarmThread()
@@ -3462,7 +3467,7 @@
new ArraySet<>();
for (int i = 0; i < triggerList.size(); i++) {
final Alarm a = triggerList.get(i);
- if (!UserHandle.isCore(a.creatorUid)) {
+ if (!isExemptFromAppStandby(a)) {
triggerPackages.add(Pair.create(
a.sourcePackage, UserHandle.getUserId(a.creatorUid)));
}
@@ -4148,7 +4153,7 @@
mAllowWhileIdleDispatches.add(ent);
}
}
- if (!UserHandle.isCore(alarm.creatorUid)) {
+ if (!isExemptFromAppStandby(alarm)) {
final Pair<String, Integer> packageUser = Pair.create(alarm.sourcePackage,
UserHandle.getUserId(alarm.creatorUid));
mLastAlarmDeliveredForPackage.put(packageUser, nowELAPSED);
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 48b5a58..8d22eca 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -55,6 +55,7 @@
import android.view.InputDevice;
import android.media.AudioAttributes;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.DumpUtils;
@@ -111,6 +112,7 @@
private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators
private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
+ @GuardedBy("mLock")
private Vibration mCurrentVibration;
private int mCurVibUid = -1;
private boolean mLowPowerMode;
@@ -435,45 +437,45 @@
// If our current vibration is longer than the new vibration and is the same amplitude,
// then just let the current one finish.
- if (effect instanceof VibrationEffect.OneShot
- && mCurrentVibration != null
- && mCurrentVibration.effect instanceof VibrationEffect.OneShot) {
- VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect;
- VibrationEffect.OneShot currentOneShot =
- (VibrationEffect.OneShot) mCurrentVibration.effect;
- if (mCurrentVibration.hasTimeoutLongerThan(newOneShot.getDuration())
- && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
+ synchronized (mLock) {
+ if (effect instanceof VibrationEffect.OneShot
+ && mCurrentVibration != null
+ && mCurrentVibration.effect instanceof VibrationEffect.OneShot) {
+ VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect;
+ VibrationEffect.OneShot currentOneShot =
+ (VibrationEffect.OneShot) mCurrentVibration.effect;
+ if (mCurrentVibration.hasTimeoutLongerThan(newOneShot.getDuration())
+ && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring incoming vibration in favor of current vibration");
+ }
+ return;
+ }
+ }
+
+ // If the current vibration is repeating and the incoming one is non-repeating, then
+ // ignore the non-repeating vibration. This is so that we don't cancel vibrations that
+ // are meant to grab the attention of the user, like ringtones and alarms, in favor of
+ // one-shot vibrations that are likely quite short.
+ if (!isRepeatingVibration(effect)
+ && mCurrentVibration != null
+ && isRepeatingVibration(mCurrentVibration.effect)) {
if (DEBUG) {
- Slog.d(TAG, "Ignoring incoming vibration in favor of current vibration");
+ Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
}
return;
}
- }
- // If the current vibration is repeating and the incoming one is non-repeating, then ignore
- // the non-repeating vibration. This is so that we don't cancel vibrations that are meant
- // to grab the attention of the user, like ringtones and alarms, in favor of one-shot
- // vibrations that are likely quite short.
- if (!isRepeatingVibration(effect)
- && mCurrentVibration != null && isRepeatingVibration(mCurrentVibration.effect)) {
- if (DEBUG) {
- Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
- }
- return;
- }
-
- Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg);
- linkVibration(vib);
-
- long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
+ Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg);
+ linkVibration(vib);
+ long ident = Binder.clearCallingIdentity();
+ try {
doCancelVibrateLocked();
startVibrationLocked(vib);
addToPreviousVibrationsLocked(vib);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
- } finally {
- Binder.restoreCallingIdentity(ident);
}
}
@@ -516,6 +518,7 @@
}
};
+ @GuardedBy("mLock")
private void doCancelVibrateLocked() {
mH.removeCallbacks(mVibrationEndRunnable);
if (mThread != null) {
@@ -538,6 +541,7 @@
}
}
+ @GuardedBy("mLock")
private void startVibrationLocked(final Vibration vib) {
if (!isAllowedToVibrateLocked(vib)) {
return;
@@ -568,6 +572,7 @@
startVibrationInnerLocked(vib);
}
+ @GuardedBy("mLock")
private void startVibrationInnerLocked(Vibration vib) {
mCurrentVibration = vib;
if (vib.effect instanceof VibrationEffect.OneShot) {
@@ -701,6 +706,7 @@
return mode;
}
+ @GuardedBy("mLock")
private void reportFinishVibrationLocked() {
if (mCurrentVibration != null) {
try {
@@ -880,6 +886,7 @@
}
}
+ @GuardedBy("mLock")
private long doVibratorPrebakedEffectLocked(Vibration vib) {
final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect;
final boolean usingInputDeviceVibrators;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a6c4fc9..cee1ff9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1854,7 +1854,6 @@
static final int REPORT_MEM_USAGE_MSG = 33;
static final int IMMERSIVE_MODE_LOCK_MSG = 37;
static final int PERSIST_URI_GRANTS_MSG = 38;
- static final int REQUEST_ALL_PSS_MSG = 39;
static final int UPDATE_TIME_PREFERENCE_MSG = 41;
static final int ENTER_ANIMATION_COMPLETE_MSG = 44;
static final int FINISH_BOOTING_MSG = 45;
@@ -2320,12 +2319,6 @@
writeGrantedUriPermissions();
break;
}
- case REQUEST_ALL_PSS_MSG: {
- synchronized (ActivityManagerService.this) {
- requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
- }
- break;
- }
case UPDATE_TIME_PREFERENCE_MSG: {
// The user's time format preference might have changed.
// For convenience we re-use the Intent extra values.
@@ -2615,11 +2608,17 @@
procState = proc.pssProcState;
statType = proc.pssStatType;
lastPssTime = proc.lastPssTime;
+ long now = SystemClock.uptimeMillis();
if (proc.thread != null && procState == proc.setProcState
&& (lastPssTime+ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE)
- < SystemClock.uptimeMillis()) {
+ < now) {
pid = proc.pid;
} else {
+ ProcessList.abortNextPssTime(proc.procStateMemTracker);
+ if (DEBUG_PSS) Slog.d(TAG_PSS, "Skipped pss collection of " + pid +
+ ": still need " +
+ (lastPssTime+ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE-now) +
+ "ms until safe");
proc = null;
pid = 0;
}
@@ -3005,6 +3004,16 @@
Watchdog.getInstance().addMonitor(this);
Watchdog.getInstance().addThread(mHandler);
+
+ // bind background thread to little cores
+ // this is expected to fail inside of framework tests because apps can't touch cpusets directly
+ try {
+ Process.setThreadGroupAndCpuset(BackgroundThread.get().getThreadId(),
+ Process.THREAD_GROUP_BG_NONINTERACTIVE);
+ } catch (Exception e) {
+ Slog.w(TAG, "Setting background thread cpuset failed");
+ }
+
}
protected ActivityStackSupervisor createStackSupervisor() {
@@ -4075,9 +4084,9 @@
runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
}
- if (app.info.isAllowedToUseHiddenApi()) {
- // This app is allowed to use undocumented and private APIs. Set
- // up its runtime with the appropriate flag.
+ if (app.info.isAllowedToUseHiddenApi() || app.instr != null) {
+ // This app is allowed to use undocumented and private APIs or is
+ // being instrumented. Set up its runtime with the appropriate flag.
runtimeFlags |= Zygote.DISABLE_HIDDEN_API_CHECKS;
}
@@ -7209,7 +7218,7 @@
handleAppDiedLocked(app, willRestart, allowRestart);
if (willRestart) {
removeLruProcessLocked(app);
- addAppLocked(app.info, null, false, null /* ABI override */);
+ addAppLocked(app.info, null, false, null /* ABI override */, app.instr);
}
} else {
mRemovedProcesses.add(app);
@@ -12481,7 +12490,8 @@
.getPersistentApplications(STOCK_PM_FLAGS | matchFlags).getList();
for (ApplicationInfo app : apps) {
if (!"android".equals(app.packageName)) {
- addAppLocked(app, null, false, null /* ABI override */);
+ addAppLocked(app, null, false, null /* ABI override */,
+ null /* instrumentation */);
}
}
} catch (RemoteException ex) {
@@ -12651,6 +12661,9 @@
if (!mBooted && !mBooting
&& userId == UserHandle.USER_SYSTEM
&& (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
+ // The system process is initialized to SCHED_GROUP_DEFAULT in init.rc.
+ r.curSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+ r.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
r.persistent = true;
r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
@@ -12694,7 +12707,7 @@
}
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
- String abiOverride) {
+ String abiOverride, ActiveInstrumentation instrumentation) {
ProcessRecord app;
if (!isolated) {
app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
@@ -12723,6 +12736,9 @@
app.persistent = true;
app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
+
+ app.instr = instrumentation;
+
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
startProcessLocked(app, "added application",
@@ -14480,9 +14496,6 @@
mTestPssMode, isSleepingLocked(), now);
}
}
-
- mHandler.removeMessages(REQUEST_ALL_PSS_MSG);
- mHandler.sendEmptyMessageDelayed(REQUEST_ALL_PSS_MSG, 2*60*1000);
}
}
@@ -14789,8 +14802,6 @@
mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
BinderInternal.nSetBinderProxyCountEnabled(true);
- //STOPSHIP: Temporary BinderProxy Threshold for b/71353150
- BinderInternal.nSetBinderProxyCountWatermarks(1500, 1200);
BinderInternal.setBinderProxyCountCallback(
new BinderInternal.BinderProxyLimitListener() {
@Override
@@ -19529,6 +19540,7 @@
mProcessesToGc.remove(app);
mPendingPssProcesses.remove(app);
+ ProcessList.abortNextPssTime(app.procStateMemTracker);
// Dismiss any open dialogs.
if (app.crashDialog != null && !app.forceCrashReport) {
@@ -21531,8 +21543,7 @@
mUsageStatsService.reportEvent(ii.targetPackage, userId,
UsageEvents.Event.SYSTEM_INTERACTION);
}
- ProcessRecord app = addAppLocked(ai, defProcess, false, abiOverride);
- app.instr = activeInstr;
+ ProcessRecord app = addAppLocked(ai, defProcess, false, abiOverride, activeInstr);
activeInstr.mFinished = false;
activeInstr.mRunningProcesses.add(app);
if (!mActiveInstrumentation.contains(activeInstr)) {
@@ -23293,9 +23304,9 @@
/**
* Schedule PSS collection of a process.
*/
- void requestPssLocked(ProcessRecord proc, int procState) {
+ boolean requestPssLocked(ProcessRecord proc, int procState) {
if (mPendingPssProcesses.contains(proc)) {
- return;
+ return false;
}
if (mPendingPssProcesses.size() == 0) {
mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG);
@@ -23304,6 +23315,7 @@
proc.pssProcState = procState;
proc.pssStatType = ProcessStats.ADD_PSS_INTERNAL_SINGLE;
mPendingPssProcesses.add(proc);
+ return true;
}
/**
@@ -23320,6 +23332,9 @@
if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting pss of all procs! memLowered=" + memLowered);
mLastFullPssTime = now;
mFullPssPending = true;
+ for (int i = mPendingPssProcesses.size() - 1; i >= 0; i--) {
+ ProcessList.abortNextPssTime(mPendingPssProcesses.get(i).procStateMemTracker);;
+ }
mPendingPssProcesses.ensureCapacity(mLruProcesses.size());
mPendingPssProcesses.clear();
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
@@ -23328,7 +23343,9 @@
|| app.curProcState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
continue;
}
- if (memLowered || now > (app.lastStateTime+ProcessList.PSS_ALL_INTERVAL)) {
+ if (memLowered || (always && now >
+ app.lastStateTime+ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE)
+ || now > (app.lastStateTime+ProcessList.PSS_ALL_INTERVAL)) {
app.pssProcState = app.setProcState;
app.pssStatType = always ? ProcessStats.ADD_PSS_INTERNAL_ALL_POLL
: ProcessStats.ADD_PSS_INTERNAL_ALL_MEM;
@@ -23337,7 +23354,9 @@
mPendingPssProcesses.add(app);
}
}
- mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG);
+ if (!mBgHandler.hasMessages(COLLECT_PSS_BG_MSG)) {
+ mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG);
+ }
}
public void setTestPssMode(boolean enabled) {
@@ -23695,7 +23714,7 @@
// Experimental code to more aggressively collect pss while
// running test... the problem is that this tends to collect
// the data right when a process is transitioning between process
- // states, which well tend to give noisy data.
+ // states, which will tend to give noisy data.
long start = SystemClock.uptimeMillis();
long startTime = SystemClock.currentThreadTimeMillis();
long pss = Debug.getPss(app.pid, mTmpLong, null);
@@ -23718,9 +23737,10 @@
if (now > app.nextPssTime || (now > (app.lastPssTime+ProcessList.PSS_MAX_INTERVAL)
&& now > (app.lastStateTime+ProcessList.minTimeFromStateChange(
mTestPssMode)))) {
- requestPssLocked(app, app.setProcState);
- app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState,
- app.procStateMemTracker, mTestPssMode, isSleepingLocked(), now);
+ if (requestPssLocked(app, app.setProcState)) {
+ app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState,
+ app.procStateMemTracker, mTestPssMode, isSleepingLocked(), now);
+ }
} else if (false && DEBUG_PSS) Slog.d(TAG_PSS,
"Not requesting pss of " + app + ": next=" + (app.nextPssTime-now));
}
@@ -24922,7 +24942,7 @@
mRemovedProcesses.remove(i);
if (app.persistent) {
- addAppLocked(app.info, null, false, null /* ABI override */);
+ addAppLocked(app.info, null, false, null /* ABI override */, app.instr);
}
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index b061ba4..055b89b 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -790,7 +790,7 @@
// Instead, launch the ephemeral installer. Once the installer is finished, it
// starts either the intent we resolved here [on install error] or the ephemeral
// app [on install success].
- if (rInfo != null && rInfo.isInstantAppAvailable) {
+ if (rInfo != null && rInfo.auxiliaryInfo != null) {
intent = createLaunchIntent(rInfo.auxiliaryInfo, ephemeralIntent,
callingPackage, verificationBundle, resolvedType, userId);
resolvedType = null;
diff --git a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
index 9033b55..71fd71b 100644
--- a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
@@ -93,7 +93,7 @@
}
try {
systemPropertiesSet(key, value);
- } catch (IllegalArgumentException e) {
+ } catch (Exception e) {
Slog.e(TAG, "Unable to set property " + key + " value '" + value + "'", e);
}
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 08ee237..bf7aef9 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -501,19 +501,19 @@
private static final int PSS_FIRST_CACHED_INTERVAL = 20*1000;
// The amount of time until PSS when an important process stays in the same state.
- private static final int PSS_SAME_PERSISTENT_INTERVAL = 20*60*1000;
+ private static final int PSS_SAME_PERSISTENT_INTERVAL = 10*60*1000;
// The amount of time until PSS when the top process stays in the same state.
- private static final int PSS_SAME_TOP_INTERVAL = 5*60*1000;
+ private static final int PSS_SAME_TOP_INTERVAL = 1*60*1000;
// The amount of time until PSS when an important process stays in the same state.
- private static final int PSS_SAME_IMPORTANT_INTERVAL = 15*60*1000;
+ private static final int PSS_SAME_IMPORTANT_INTERVAL = 10*60*1000;
// The amount of time until PSS when a service process stays in the same state.
- private static final int PSS_SAME_SERVICE_INTERVAL = 20*60*1000;
+ private static final int PSS_SAME_SERVICE_INTERVAL = 5*60*1000;
// The amount of time until PSS when a cached process stays in the same state.
- private static final int PSS_SAME_CACHED_INTERVAL = 20*60*1000;
+ private static final int PSS_SAME_CACHED_INTERVAL = 10*60*1000;
// The amount of time until PSS when a persistent process first appears.
private static final int PSS_FIRST_ASLEEP_PERSISTENT_INTERVAL = 1*60*1000;
@@ -622,16 +622,17 @@
public static final class ProcStateMemTracker {
final int[] mHighestMem = new int[PROC_MEM_NUM];
+ final float[] mScalingFactor = new float[PROC_MEM_NUM];
int mTotalHighestMem = PROC_MEM_CACHED;
- float mCurFactor = 1.0f;
int mPendingMemState;
int mPendingHighestMemState;
- boolean mPendingSame;
+ float mPendingScalingFactor;
public ProcStateMemTracker() {
for (int i = PROC_MEM_PERSISTENT; i < PROC_MEM_NUM; i++) {
mHighestMem[i] = PROC_MEM_NUM;
+ mScalingFactor[i] = 1.0f;
}
mPendingMemState = -1;
}
@@ -639,16 +640,22 @@
public void dumpLine(PrintWriter pw) {
pw.print("best=");
pw.print(mTotalHighestMem);
- pw.print(" ");
- pw.print(mCurFactor);
- pw.print("x (");
+ pw.print(" (");
+ boolean needSep = false;
for (int i = 0; i < PROC_MEM_NUM; i++) {
- if (i != 0) {
- pw.print(", ");
+ if (mHighestMem[i] < PROC_MEM_NUM) {
+ if (needSep) {
+ pw.print(", ");
+ needSep = false;
+ }
+ pw.print(i);
+ pw.print("=");
+ pw.print(mHighestMem[i]);
+ pw.print(" ");
+ pw.print(mScalingFactor[i]);
+ pw.print("x");
+ needSep = true;
}
- pw.print(i);
- pw.print("=");
- pw.print(mHighestMem[i]);
}
pw.print(")");
if (mPendingMemState >= 0) {
@@ -656,8 +663,9 @@
pw.print(mPendingMemState);
pw.print(" highest=");
pw.print(mPendingHighestMemState);
- pw.print(" same=");
- pw.print(mPendingSame);
+ pw.print(" ");
+ pw.print(mPendingScalingFactor);
+ pw.print("x");
}
pw.println();
}
@@ -674,12 +682,8 @@
public static void commitNextPssTime(ProcStateMemTracker tracker) {
if (tracker.mPendingMemState >= 0) {
tracker.mHighestMem[tracker.mPendingMemState] = tracker.mPendingHighestMemState;
+ tracker.mScalingFactor[tracker.mPendingMemState] = tracker.mPendingScalingFactor;
tracker.mTotalHighestMem = tracker.mPendingHighestMemState;
- if (tracker.mPendingSame) {
- tracker.mCurFactor *= 1.5f;
- } else {
- tracker.mCurFactor = 1;
- }
tracker.mPendingMemState = -1;
}
}
@@ -691,6 +695,7 @@
public static long computeNextPssTime(int procState, ProcStateMemTracker tracker, boolean test,
boolean sleeping, long now) {
boolean first;
+ float scalingFactor;
final int memState = sProcStateToProcMem[procState];
if (tracker != null) {
final int highestMemState = memState < tracker.mTotalHighestMem
@@ -698,9 +703,15 @@
first = highestMemState < tracker.mHighestMem[memState];
tracker.mPendingMemState = memState;
tracker.mPendingHighestMemState = highestMemState;
- tracker.mPendingSame = !first;
+ if (first) {
+ tracker.mPendingScalingFactor = scalingFactor = 1.0f;
+ } else {
+ scalingFactor = tracker.mScalingFactor[memState];
+ tracker.mPendingScalingFactor = scalingFactor * 1.5f;
+ }
} else {
first = true;
+ scalingFactor = 1.0f;
}
final long[] table = test
? (first
@@ -709,8 +720,7 @@
: (first
? (sleeping ? sFirstAsleepPssTimes : sFirstAwakePssTimes)
: (sleeping ? sSameAsleepPssTimes : sSameAwakePssTimes));
- long delay = (long)(table[memState] * (tracker != null && !first
- ? tracker.mCurFactor : 1.0f));
+ long delay = (long)(table[memState] * scalingFactor);
if (delay > PSS_MAX_INTERVAL) {
delay = PSS_MAX_INTERVAL;
}
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index 8bf320e..edf565a 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -237,6 +237,7 @@
if (commit) {
mProcessStats.resetSafely();
updateFile();
+ mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
}
mLastWriteTime = SystemClock.uptimeMillis();
totalTime = SystemClock.uptimeMillis() - now;
@@ -784,12 +785,14 @@
} else if ("--reset".equals(arg)) {
synchronized (mAm) {
mProcessStats.resetSafely();
+ mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
pw.println("Process stats reset.");
quit = true;
}
} else if ("--clear".equals(arg)) {
synchronized (mAm) {
mProcessStats.resetSafely();
+ mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
ArrayList<String> files = getCommittedFiles(0, true, true);
if (files != null) {
for (int fi=0; fi<files.size(); fi++) {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index a6f049e..a17dd12 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1722,7 +1722,7 @@
final long totalBytes = getTotalBytes(
NetworkTemplate.buildTemplateMobileAll(state.subscriberId), start, end);
final long remainingBytes = limitBytes - totalBytes;
- final long remainingDays = Math.min(1, (end - currentTimeMillis())
+ final long remainingDays = Math.max(1, (end - currentTimeMillis())
/ TimeUnit.DAYS.toMillis(1));
if (remainingBytes > 0) {
quotaBytes = (remainingBytes / remainingDays) / 10;
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
index b0b07ea..671d7a6 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
@@ -363,7 +363,7 @@
indent = indent + " ";
pw.print(indent);
- pw.println("Battery Saver: Off On");
+ pw.println("Battery Saver: w/Off w/On");
dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr",
DozeState.NOT_DOZING, "NonDoze");
dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, " Intr",
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index b58b890..e869f58 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -237,8 +237,11 @@
final Rect minimizedHomeBounds =
mHomeAppToken != null && mHomeAppToken.inSplitScreenSecondaryWindowingMode()
? mMinimizedHomeBounds : null;
- mRunner.onAnimationStart_New(mController, appAnimations,
- mHomeAppToken.findMainWindow().mContentInsets, minimizedHomeBounds);
+ final Rect contentInsets =
+ mHomeAppToken != null && mHomeAppToken.findMainWindow() != null
+ ? mHomeAppToken.findMainWindow().mContentInsets : null;
+ mRunner.onAnimationStart_New(mController, appAnimations, contentInsets,
+ minimizedHomeBounds);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to start recents animation", e);
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 7540e26..5e003ff 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -24,6 +24,7 @@
"com_android_server_connectivity_Vpn.cpp",
"com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp",
"com_android_server_ConsumerIrService.cpp",
+ "com_android_server_devicepolicy_CryptoTestHelper.cpp",
"com_android_server_HardwarePropertiesManagerService.cpp",
"com_android_server_hdmi_HdmiCecController.cpp",
"com_android_server_input_InputApplicationHandle.cpp",
diff --git a/services/core/jni/com_android_server_devicepolicy_CryptoTestHelper.cpp b/services/core/jni/com_android_server_devicepolicy_CryptoTestHelper.cpp
new file mode 100644
index 0000000..b53ea92
--- /dev/null
+++ b/services/core/jni/com_android_server_devicepolicy_CryptoTestHelper.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#include "jni.h"
+#include "core_jni_helpers.h"
+
+#include <openssl/crypto.h>
+
+namespace {
+
+static jint runSelfTest(JNIEnv* env, jobject /* clazz */) {
+ return BORINGSSL_self_test();
+}
+
+static const JNINativeMethod methods[] = {
+ /* name, signature, funcPtr */
+ {"runSelfTest", "()I", (void*) runSelfTest}
+};
+
+} // anonymous namespace
+
+namespace android {
+
+int register_android_server_devicepolicy_CryptoTestHelper(JNIEnv *env) {
+ return jniRegisterNativeMethods(
+ env, "com/android/server/devicepolicy/CryptoTestHelper", methods, NELEM(methods));
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 07ddb05..bf2a637 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -42,6 +42,7 @@
int register_android_server_location_GnssLocationProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
int register_android_server_connectivity_tethering_OffloadHardwareInterface(JNIEnv*);
+int register_android_server_devicepolicy_CryptoTestHelper(JNIEnv*);
int register_android_server_hdmi_HdmiCecController(JNIEnv* env);
int register_android_server_tv_TvUinputBridge(JNIEnv* env);
int register_android_server_tv_TvInputHal(JNIEnv* env);
@@ -88,6 +89,7 @@
register_android_server_location_GnssLocationProvider(env);
register_android_server_connectivity_Vpn(env);
register_android_server_connectivity_tethering_OffloadHardwareInterface(env);
+ register_android_server_devicepolicy_CryptoTestHelper(env);
register_android_server_ConsumerIrService(env);
register_android_server_BatteryStatsService(env);
register_android_server_hdmi_HdmiCecController(env);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CryptoTestHelper.java b/services/devicepolicy/java/com/android/server/devicepolicy/CryptoTestHelper.java
new file mode 100644
index 0000000..a20758e
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/CryptoTestHelper.java
@@ -0,0 +1,30 @@
+/*
+ * 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.devicepolicy;
+
+import android.app.admin.SecurityLog;
+
+/**
+ * Helper to call native BoringSSL self test.
+ */
+public class CryptoTestHelper {
+ public static void runAndLogSelfTest() {
+ final int result = runSelfTest();
+ SecurityLog.writeEvent(SecurityLog.TAG_CRYPTO_SELF_TEST_COMPLETED, result);
+ }
+ private static native int runSelfTest();
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 953a79f..95e71ed 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2044,6 +2044,10 @@
public TransferOwnershipMetadataManager newTransferOwnershipMetadataManager() {
return new TransferOwnershipMetadataManager();
}
+
+ public void runCryptoSelfTest() {
+ CryptoTestHelper.runAndLogSelfTest();
+ }
}
/**
@@ -2296,6 +2300,7 @@
if (hasDeviceOwner && mInjector.securityLogGetLoggingEnabledProperty()) {
mSecurityLogMonitor.start();
+ mInjector.runCryptoSelfTest();
maybePauseDeviceWideLoggingLocked();
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index c361434..5ea113b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -724,7 +724,6 @@
MmsServiceBroker mmsService = null;
HardwarePropertiesManagerService hardwarePropertiesService = null;
- boolean disableRtt = SystemProperties.getBoolean("config.disable_rtt", false);
boolean disableSystemTextClassifier = SystemProperties.getBoolean(
"config.disable_systemtextclassifier", false);
boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
@@ -1105,18 +1104,12 @@
traceEnd();
}
- if (!disableRtt) {
- traceBeginAndSlog("StartWifiRtt");
- mSystemServiceManager.startService("com.android.server.wifi.RttService");
+ if (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_RTT)) {
+ traceBeginAndSlog("StartRttService");
+ mSystemServiceManager.startService(
+ "com.android.server.wifi.rtt.RttService");
traceEnd();
-
- if (context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_WIFI_RTT)) {
- traceBeginAndSlog("StartRttService");
- mSystemServiceManager.startService(
- "com.android.server.wifi.rtt.RttService");
- traceEnd();
- }
}
if (context.getPackageManager().hasSystemFeature(
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 00e27c9..ab0bfefb 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -447,5 +447,8 @@
return new TransferOwnershipMetadataManager(
new TransferOwnershipMetadataManagerTest.MockInjector());
}
+
+ @Override
+ public void runCryptoSelfTest() {}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 0abb48f..98483a8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -294,7 +294,7 @@
null /*disabledPkg*/,
null /*sharedUser*/,
UPDATED_CODE_PATH /*codePath*/,
- null /*resourcePath*/,
+ UPDATED_CODE_PATH /*resourcePath*/,
null /*legacyNativeLibraryPath*/,
"arm64-v8a" /*primaryCpuAbi*/,
"armeabi" /*secondaryCpuAbi*/,
@@ -328,7 +328,7 @@
null /*disabledPkg*/,
null /*sharedUser*/,
UPDATED_CODE_PATH /*codePath*/,
- null /*resourcePath*/,
+ UPDATED_CODE_PATH /*resourcePath*/,
null /*legacyNativeLibraryPath*/,
"arm64-v8a" /*primaryCpuAbi*/,
"armeabi" /*secondaryCpuAbi*/,
@@ -561,34 +561,6 @@
false /*notLaunched*/, false /*stopped*/, true /*installed*/);
}
- @Test
- public void testInsertPackageSetting() {
- final PackageSetting ps = createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/);
- final PackageParser.Package pkg = new PackageParser.Package(PACKAGE_NAME);
- pkg.applicationInfo.setCodePath(ps.codePathString);
- pkg.applicationInfo.setResourcePath(ps.resourcePathString);
- final Context context = InstrumentationRegistry.getContext();
- final Object lock = new Object();
- PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
- final Settings settings =
- new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
- pkg.usesStaticLibraries = new ArrayList<>(
- Arrays.asList("foo.bar1", "foo.bar2", "foo.bar3"));
- pkg.usesStaticLibrariesVersions = new long[] {2, 4, 6};
- settings.insertPackageSettingLPw(ps, pkg);
- assertEquals(pkg, ps.pkg);
- assertArrayEquals(pkg.usesStaticLibraries.toArray(new String[0]), ps.usesStaticLibraries);
- assertArrayEquals(pkg.usesStaticLibrariesVersions, ps.usesStaticLibrariesVersions);
-
- pkg.usesStaticLibraries = null;
- pkg.usesStaticLibrariesVersions = null;
- settings.insertPackageSettingLPw(ps, pkg);
- assertEquals(pkg, ps.pkg);
- assertNull("Actual: " + Arrays.toString(ps.usesStaticLibraries), ps.usesStaticLibraries);
- assertNull("Actual: " + Arrays.toString(ps.usesStaticLibrariesVersions),
- ps.usesStaticLibrariesVersions);
- }
-
private <T> void assertArrayEquals(T[] a, T[] b) {
assertTrue("Expected: " + Arrays.toString(a) + ", actual: " + Arrays.toString(b),
Arrays.equals(a, b));
diff --git a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
index f23bd62..3a1485e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.graphics.Color.BLUE;
import static android.graphics.Color.RED;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
@@ -27,7 +26,6 @@
import static android.view.Gravity.RIGHT;
import static android.view.Gravity.TOP;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
@@ -49,6 +47,7 @@
import android.os.Handler;
import android.platform.test.annotations.Presubmit;
import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.util.Pair;
@@ -66,18 +65,19 @@
import org.junit.runner.RunWith;
import java.util.ArrayList;
+import java.util.function.BooleanSupplier;
/**
* Tests for the {@link android.view.WindowManager.LayoutParams#PRIVATE_FLAG_IS_SCREEN_DECOR} flag.
*
* Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.wm.ScreenDecorWindowTests
+ * atest FrameworksServicesTests:com.android.server.wm.ScreenDecorWindowTests
*/
// TODO: Add test for FLAG_FULLSCREEN which hides the status bar and also other flags.
// TODO: Test non-Activity windows.
@SmallTest
-// TODO(b/68957554)
-//@Presubmit
+@Presubmit
+@FlakyTest(bugId = 68957554)
@RunWith(AndroidJUnit4.class)
public class ScreenDecorWindowTests {
@@ -123,40 +123,33 @@
public void testScreenSides() throws Exception {
// Decor on top
final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
- WindowInsets insets = getInsets(mTestActivity);
- assertGreaterOrEqual(insets.getSystemWindowInsetTop(), mDecorThickness);
+ assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness);
// Decor at the bottom
updateWindow(decorWindow, BOTTOM, MATCH_PARENT, mDecorThickness, 0, 0);
- insets = getInsets(mTestActivity);
- assertGreaterOrEqual(insets.getSystemWindowInsetBottom(), mDecorThickness);
+ assertInsetGreaterOrEqual(mTestActivity, BOTTOM, mDecorThickness);
// Decor to the left
updateWindow(decorWindow, LEFT, mDecorThickness, MATCH_PARENT, 0, 0);
- insets = getInsets(mTestActivity);
- assertGreaterOrEqual(insets.getSystemWindowInsetLeft(), mDecorThickness);
+ assertInsetGreaterOrEqual(mTestActivity, LEFT, mDecorThickness);
// Decor to the right
updateWindow(decorWindow, RIGHT, mDecorThickness, MATCH_PARENT, 0, 0);
- insets = getInsets(mTestActivity);
- assertGreaterOrEqual(insets.getSystemWindowInsetRight(), mDecorThickness);
+ assertInsetGreaterOrEqual(mTestActivity, RIGHT, mDecorThickness);
}
@Test
public void testMultipleDecors() throws Exception {
// Test 2 decor windows on-top.
createDecorWindow(TOP, MATCH_PARENT, mHalfDecorThickness);
- WindowInsets insets = getInsets(mTestActivity);
- assertGreaterOrEqual(insets.getSystemWindowInsetTop(), mHalfDecorThickness);
+ assertInsetGreaterOrEqual(mTestActivity, TOP, mHalfDecorThickness);
createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
- insets = getInsets(mTestActivity);
- assertGreaterOrEqual(insets.getSystemWindowInsetTop(), mDecorThickness);
+ assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness);
// And one at the bottom.
createDecorWindow(BOTTOM, MATCH_PARENT, mHalfDecorThickness);
- insets = getInsets(mTestActivity);
- assertGreaterOrEqual(insets.getSystemWindowInsetTop(), mDecorThickness);
- assertGreaterOrEqual(insets.getSystemWindowInsetBottom(), mHalfDecorThickness);
+ assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness);
+ assertInsetGreaterOrEqual(mTestActivity, BOTTOM, mHalfDecorThickness);
}
@Test
@@ -164,18 +157,15 @@
WindowInsets initialInsets = getInsets(mTestActivity);
final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
- WindowInsets insets = getInsets(mTestActivity);
- assertEquals(mDecorThickness, insets.getSystemWindowInsetTop());
+ assertTopInsetEquals(mTestActivity, mDecorThickness);
updateWindow(decorWindow, TOP, MATCH_PARENT, mDecorThickness,
0, PRIVATE_FLAG_IS_SCREEN_DECOR);
- insets = getInsets(mTestActivity);
- assertEquals(initialInsets.getSystemWindowInsetTop(), insets.getSystemWindowInsetTop());
+ assertTopInsetEquals(mTestActivity, initialInsets.getSystemWindowInsetTop());
updateWindow(decorWindow, TOP, MATCH_PARENT, mDecorThickness,
PRIVATE_FLAG_IS_SCREEN_DECOR, PRIVATE_FLAG_IS_SCREEN_DECOR);
- insets = getInsets(mTestActivity);
- assertEquals(mDecorThickness, insets.getSystemWindowInsetTop());
+ assertTopInsetEquals(mTestActivity, mDecorThickness);
}
@Test
@@ -183,17 +173,10 @@
WindowInsets initialInsets = getInsets(mTestActivity);
final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
- WindowInsets insets = getInsets(mTestActivity);
- assertGreaterOrEqual(insets.getSystemWindowInsetTop(), mDecorThickness);
+ assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness);
removeWindow(decorWindow);
- insets = getInsets(mTestActivity);
- assertEquals(initialInsets.getSystemWindowInsetTop(), insets.getSystemWindowInsetTop());
- }
-
- private View createAppWindow() {
- return createWindow("appWindow", TOP, MATCH_PARENT, MATCH_PARENT, BLUE,
- FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR, 0);
+ assertTopInsetEquals(mTestActivity, initialInsets.getSystemWindowInsetTop());
}
private View createDecorWindow(int gravity, int width, int height) {
@@ -249,10 +232,6 @@
waitForIdle();
}
- private WindowInsets getInsets(View v) {
- return new WindowInsets(v.getRootWindowInsets());
- }
-
private WindowInsets getInsets(Activity a) {
return new WindowInsets(a.getWindow().getDecorView().getRootWindowInsets());
}
@@ -269,11 +248,59 @@
lp.flags = (lp.flags & ~mask) | (flags & mask);
}
+ /**
+ * Asserts the top inset of {@param activity} is equal to {@param expected} waiting as needed.
+ */
+ private void assertTopInsetEquals(Activity activity, int expected) throws Exception {
+ waitFor(() -> getInsets(activity).getSystemWindowInsetTop() == expected);
+ assertEquals(expected, getInsets(activity).getSystemWindowInsetTop());
+ }
+
+ /**
+ * Asserts the inset at {@param side} of {@param activity} is equal to {@param expected}
+ * waiting as needed.
+ */
+ private void assertInsetGreaterOrEqual(Activity activity, int side, int expected)
+ throws Exception {
+ waitFor(() -> {
+ final WindowInsets insets = getInsets(activity);
+ switch (side) {
+ case TOP: return insets.getSystemWindowInsetTop() >= expected;
+ case BOTTOM: return insets.getSystemWindowInsetBottom() >= expected;
+ case LEFT: return insets.getSystemWindowInsetLeft() >= expected;
+ case RIGHT: return insets.getSystemWindowInsetRight() >= expected;
+ default: return true;
+ }
+ });
+
+ final WindowInsets insets = getInsets(activity);
+ switch (side) {
+ case TOP: assertGreaterOrEqual(insets.getSystemWindowInsetTop(), expected); break;
+ case BOTTOM: assertGreaterOrEqual(insets.getSystemWindowInsetBottom(), expected); break;
+ case LEFT: assertGreaterOrEqual(insets.getSystemWindowInsetLeft(), expected); break;
+ case RIGHT: assertGreaterOrEqual(insets.getSystemWindowInsetRight(), expected); break;
+ }
+ }
+
/** Asserts that the first entry is greater than or equal to the second entry. */
private void assertGreaterOrEqual(int first, int second) throws Exception {
Assert.assertTrue("Excepted " + first + " >= " + second, first >= second);
}
+ private void waitFor(BooleanSupplier waitCondition) {
+ int retriesLeft = 5;
+ do {
+ if (waitCondition.getAsBoolean()) {
+ break;
+ }
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ // Well I guess we are not waiting...
+ }
+ } while (retriesLeft-- > 0);
+ }
+
private void finishActivity(Activity a) {
if (a == null) {
return;
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 6799417..8c18518 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -352,8 +352,11 @@
*/
public static final int CAPABILITY_CAN_PULL_CALL = 0x00800000;
+ /** Call supports the deflect feature. */
+ public static final int CAPABILITY_SUPPORT_DEFLECT = 0x01000000;
+
//******************************************************************************************
- // Next CAPABILITY value: 0x01000000
+ // Next CAPABILITY value: 0x02000000
//******************************************************************************************
/**
@@ -528,6 +531,9 @@
if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
builder.append(" CAPABILITY_CAN_PULL_CALL");
}
+ if (can(capabilities, CAPABILITY_SUPPORT_DEFLECT)) {
+ builder.append(" CAPABILITY_SUPPORT_DEFLECT");
+ }
builder.append("]");
return builder.toString();
}
@@ -1235,6 +1241,15 @@
}
/**
+ * Instructs this {@link #STATE_RINGING} {@code Call} to deflect.
+ *
+ * @param address The address to which the call will be deflected.
+ */
+ public void deflect(Uri address) {
+ mInCallAdapter.deflectCall(mTelecomCallId, address);
+ }
+
+ /**
* Instructs this {@link #STATE_RINGING} {@code Call} to reject.
*
* @param rejectWithMessage Whether to reject with a text message.
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index e0b0bbf..24184e0 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -328,8 +328,11 @@
*/
public static final int CAPABILITY_CAN_PULL_CALL = 0x01000000;
+ /** Call supports the deflect feature. */
+ public static final int CAPABILITY_SUPPORT_DEFLECT = 0x02000000;
+
//**********************************************************************************************
- // Next CAPABILITY value: 0x02000000
+ // Next CAPABILITY value: 0x04000000
//**********************************************************************************************
/**
@@ -726,6 +729,9 @@
if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
builder.append(isLong ? " CAPABILITY_CAN_PULL_CALL" : " pull");
}
+ if (can(capabilities, CAPABILITY_SUPPORT_DEFLECT)) {
+ builder.append(isLong ? " CAPABILITY_SUPPORT_DEFLECT" : " sup_def");
+ }
builder.append("]");
return builder.toString();
@@ -2752,6 +2758,12 @@
/**
* Notifies this Connection, which is in {@link #STATE_RINGING}, of
+ * a request to deflect.
+ */
+ public void onDeflect(Uri address) {}
+
+ /**
+ * Notifies this Connection, which is in {@link #STATE_RINGING}, of
* a request to reject.
*/
public void onReject() {}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index c1040ad..211699e 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -124,6 +124,7 @@
private static final String SESSION_ABORT = "CS.ab";
private static final String SESSION_ANSWER = "CS.an";
private static final String SESSION_ANSWER_VIDEO = "CS.anV";
+ private static final String SESSION_DEFLECT = "CS.def";
private static final String SESSION_REJECT = "CS.r";
private static final String SESSION_REJECT_MESSAGE = "CS.rWM";
private static final String SESSION_SILENCE = "CS.s";
@@ -181,6 +182,7 @@
private static final int MSG_CONNECTION_SERVICE_FOCUS_GAINED = 31;
private static final int MSG_HANDOVER_FAILED = 32;
private static final int MSG_HANDOVER_COMPLETE = 33;
+ private static final int MSG_DEFLECT = 34;
private static Connection sNullConnection;
@@ -353,6 +355,20 @@
}
@Override
+ public void deflect(String callId, Uri address, Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_DEFLECT);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = address;
+ args.arg3 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_DEFLECT, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
public void reject(String callId, Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_REJECT);
try {
@@ -847,6 +863,17 @@
}
break;
}
+ case MSG_DEFLECT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession((Session) args.arg3, SESSION_HANDLER + SESSION_DEFLECT);
+ try {
+ deflect((String) args.arg1, (Uri) args.arg2);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
case MSG_REJECT: {
SomeArgs args = (SomeArgs) msg.obj;
Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_REJECT);
@@ -1605,6 +1632,11 @@
findConnectionForAction(callId, "answer").onAnswer();
}
+ private void deflect(String callId, Uri address) {
+ Log.d(this, "deflect %s", callId);
+ findConnectionForAction(callId, "deflect").onDeflect(address);
+ }
+
private void reject(String callId) {
Log.d(this, "reject %s", callId);
findConnectionForAction(callId, "reject").onReject();
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index 658685f..8678e33 100644
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.net.Uri;
import android.bluetooth.BluetoothDevice;
import android.os.Bundle;
import android.os.RemoteException;
@@ -61,6 +62,19 @@
}
/**
+ * Instructs Telecom to deflect the specified call.
+ *
+ * @param callId The identifier of the call to deflect.
+ * @param address The address to deflect.
+ */
+ public void deflectCall(String callId, Uri address) {
+ try {
+ mAdapter.deflectCall(callId, address);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Instructs Telecom to reject the specified call.
*
* @param callId The identifier of the call to reject.
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index 3a84f00..e35093c 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -16,6 +16,7 @@
package com.android.internal.telecom;
+import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.telecom.CallAudioState;
@@ -58,6 +59,8 @@
void answer(String callId, in Session.Info sessionInfo);
+ void deflect(String callId, in Uri address, in Session.Info sessionInfo);
+
void reject(String callId, in Session.Info sessionInfo);
void rejectWithMessage(String callId, String message, in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
index 87ccd3e..57df5c1 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -16,6 +16,7 @@
package com.android.internal.telecom;
+import android.net.Uri;
import android.os.Bundle;
import android.telecom.PhoneAccountHandle;
@@ -29,6 +30,8 @@
oneway interface IInCallAdapter {
void answerCall(String callId, int videoState);
+ void deflectCall(String callId, in Uri address);
+
void rejectCall(String callId, boolean rejectWithMessage, String textMessage);
void disconnectCall(String callId);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7eb691b..c8c898a 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1376,6 +1376,12 @@
*/
public static final String KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL = "allow_hold_in_ims_call";
+ /**
+ * Flag indicating whether the carrier supports call deflection for an incoming IMS call.
+ * @hide
+ */
+ public static final String KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL =
+ "carrier_allow_deflect_ims_call_bool";
/**
* Flag indicating whether the carrier always wants to play an "on-hold" tone when a call has
@@ -1844,6 +1850,7 @@
static {
sDefaults = new PersistableBundle();
sDefaults.putBoolean(KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL, true);
+ sDefaults.putBoolean(KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL, false);
sDefaults.putBoolean(KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL, false);
sDefaults.putBoolean(KEY_ADDITIONAL_CALL_SETTING_BOOL, true);
sDefaults.putBoolean(KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL, false);
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index 0b3cbad..0c17147 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -55,10 +55,11 @@
/** Card state restricted. */
public static final int CARD_STATE_INFO_RESTRICTED = 4;
- public final boolean isActive;
- public final boolean isEuicc;
- public final String cardId;
- public final @CardStateInfo int cardStateInfo;
+ private final boolean mIsActive;
+ private final boolean mIsEuicc;
+ private final String mCardId;
+ private final @CardStateInfo int mCardStateInfo;
+ private final int mLogicalSlotIdx;
public static final Creator<UiccSlotInfo> CREATOR = new Creator<UiccSlotInfo>() {
@Override
@@ -73,18 +74,20 @@
};
private UiccSlotInfo(Parcel in) {
- isActive = in.readByte() != 0;
- isEuicc = in.readByte() != 0;
- cardId = in.readString();
- cardStateInfo = in.readInt();
+ mIsActive = in.readByte() != 0;
+ mIsEuicc = in.readByte() != 0;
+ mCardId = in.readString();
+ mCardStateInfo = in.readInt();
+ mLogicalSlotIdx = in.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeByte((byte) (isActive ? 1 : 0));
- dest.writeByte((byte) (isEuicc ? 1 : 0));
- dest.writeString(cardId);
- dest.writeInt(cardStateInfo);
+ dest.writeByte((byte) (mIsActive ? 1 : 0));
+ dest.writeByte((byte) (mIsEuicc ? 1 : 0));
+ dest.writeString(mCardId);
+ dest.writeInt(mCardStateInfo);
+ dest.writeInt(mLogicalSlotIdx);
}
@Override
@@ -93,28 +96,33 @@
}
public UiccSlotInfo(boolean isActive, boolean isEuicc, String cardId,
- @CardStateInfo int cardStateInfo) {
- this.isActive = isActive;
- this.isEuicc = isEuicc;
- this.cardId = cardId;
- this.cardStateInfo = cardStateInfo;
+ @CardStateInfo int cardStateInfo, int logicalSlotIdx) {
+ this.mIsActive = isActive;
+ this.mIsEuicc = isEuicc;
+ this.mCardId = cardId;
+ this.mCardStateInfo = cardStateInfo;
+ this.mLogicalSlotIdx = logicalSlotIdx;
}
public boolean getIsActive() {
- return isActive;
+ return mIsActive;
}
public boolean getIsEuicc() {
- return isEuicc;
+ return mIsEuicc;
}
public String getCardId() {
- return cardId;
+ return mCardId;
}
@CardStateInfo
public int getCardStateInfo() {
- return cardStateInfo;
+ return mCardStateInfo;
+ }
+
+ public int getLogicalSlotIdx() {
+ return mLogicalSlotIdx;
}
@Override
@@ -127,32 +135,36 @@
}
UiccSlotInfo that = (UiccSlotInfo) obj;
- return (isActive == that.isActive)
- && (isEuicc == that.isEuicc)
- && (cardId == that.cardId)
- && (cardStateInfo == that.cardStateInfo);
+ return (mIsActive == that.mIsActive)
+ && (mIsEuicc == that.mIsEuicc)
+ && (mCardId == that.mCardId)
+ && (mCardStateInfo == that.mCardStateInfo)
+ && (mLogicalSlotIdx == that.mLogicalSlotIdx);
}
@Override
public int hashCode() {
int result = 1;
- result = 31 * result + (isActive ? 1 : 0);
- result = 31 * result + (isEuicc ? 1 : 0);
- result = 31 * result + Objects.hashCode(cardId);
- result = 31 * result + cardStateInfo;
+ result = 31 * result + (mIsActive ? 1 : 0);
+ result = 31 * result + (mIsEuicc ? 1 : 0);
+ result = 31 * result + Objects.hashCode(mCardId);
+ result = 31 * result + mCardStateInfo;
+ result = 31 * result + mLogicalSlotIdx;
return result;
}
@Override
public String toString() {
- return "UiccSlotInfo (isActive="
- + isActive
- + ", isEuicc="
- + isEuicc
- + ", cardId="
- + cardId
+ return "UiccSlotInfo (mIsActive="
+ + mIsActive
+ + ", mIsEuicc="
+ + mIsEuicc
+ + ", mCardId="
+ + mCardId
+ ", cardState="
- + cardStateInfo
+ + mCardStateInfo
+ + ", phoneId="
+ + mLogicalSlotIdx
+ ")";
}
}
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index c3d103f..a20d4f5 100644
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -754,6 +754,22 @@
}
/**
+ * Deflects an incoming call.
+ *
+ * @param number number to be deflected to
+ */
+ public void deflect(String number) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.deflect(number);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Rejects an incoming call or session update.
*
* @param reason reason code to reject an incoming call
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
index 00cb1e2..e5ed825 100644
--- a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
@@ -182,6 +182,15 @@
}
/**
+ * Deflects an incoming call.
+ *
+ * @param deflectNumber number to deflect the call
+ */
+ @Override
+ public void deflect(String deflectNumber) {
+ }
+
+ /**
* Rejects an incoming call or session update.
*
* @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}.
diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
index c6ca6fd..7b9fe2b 100644
--- a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
@@ -174,6 +174,11 @@
}
@Override
+ public void deflect(String deflectNumber) {
+ ImsCallSessionImplBase.this.deflect(deflectNumber);
+ }
+
+ @Override
public void reject(int reason) {
ImsCallSessionImplBase.this.reject(reason);
}
@@ -395,6 +400,14 @@
}
/**
+ * Deflects an incoming call.
+ *
+ * @param deflectNumber number to deflect the call
+ */
+ public void deflect(String deflectNumber) {
+ }
+
+ /**
* Rejects an incoming call or session update.
*
* @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}.
diff --git a/telephony/java/com/android/ims/internal/IImsCallSession.aidl b/telephony/java/com/android/ims/internal/IImsCallSession.aidl
index 203e6cf..15234e5 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSession.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSession.aidl
@@ -136,6 +136,13 @@
void accept(int callType, in ImsStreamMediaProfile profile);
/**
+ * Deflects an incoming call.
+ *
+ * @param deflectNumber number to deflect the call
+ */
+ void deflect(String deflectNumber);
+
+ /**
* Rejects an incoming call or session update.
*
* @param reason reason code to reject an incoming call
diff --git a/wifi/java/android/net/wifi/IRttManager.aidl b/wifi/java/android/net/wifi/IRttManager.aidl
deleted file mode 100644
index 3831809..0000000
--- a/wifi/java/android/net/wifi/IRttManager.aidl
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi;
-import android.os.Messenger;
-import android.net.wifi.RttManager;
-
-/**
- * {@hide}
- */
-interface IRttManager
-{
- Messenger getMessenger(in IBinder binder, out int[] key);
- RttManager.RttCapabilities getRttCapabilities();
-}
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index fe63aa1..3fb4fae 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -12,7 +12,6 @@
import android.net.wifi.rtt.RangingResult;
import android.net.wifi.rtt.RangingResultCallback;
import android.net.wifi.rtt.WifiRttManager;
-import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -838,8 +837,8 @@
}
dest.writeByte(result.LCR.id);
if (result.LCR.id != (byte) 0xFF) {
- dest.writeInt((byte) result.LCR.data.length);
- dest.writeByte(result.LCR.id);
+ dest.writeByte((byte) result.LCR.data.length);
+ dest.writeByteArray(result.LCR.data);
}
dest.writeByte(result.secure ? (byte) 1 : 0);
}
@@ -919,51 +918,6 @@
}
/**
- * A parcelable that contains rtt client information.
- *
- * @hide
- */
- public static class RttClient implements Parcelable {
- // Package name of RttClient.
- private final String mPackageName;
-
- public RttClient(String packageName) {
- mPackageName = packageName;
- }
-
- protected RttClient(Parcel in) {
- mPackageName = in.readString();
- }
-
- public static final Creator<RttManager.RttClient> CREATOR =
- new Creator<RttManager.RttClient>() {
- @Override
- public RttManager.RttClient createFromParcel(Parcel in) {
- return new RttManager.RttClient(in);
- }
-
- @Override
- public RttManager.RttClient[] newArray(int size) {
- return new RttManager.RttClient[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int i) {
- parcel.writeString(mPackageName);
- }
-
- public String getPackageName() {
- return mPackageName;
- }
- }
-
- /**
* Request to start an RTT ranging
*
* @param params -- RTT request Parameters
@@ -1202,7 +1156,6 @@
/** @hide */
public static final int CMD_OP_REG_BINDER = BASE + 9;
- private final Context mContext;
private final WifiRttManager mNewService;
private RttCapabilities mRttCapabilities;
@@ -1211,17 +1164,14 @@
* Applications will almost always want to use
* {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
* the standard {@link android.content.Context#WIFI_RTT_SERVICE Context.WIFI_RTT_SERVICE}.
- * @param context the application context
- * @param service the Binder interface
- * @param looper Looper for running the callbacks.
+ * @param service the new WifiRttManager service
*
* @hide
*/
- public RttManager(Context context, IRttManager service, Looper looper) {
- mContext = context;
- mNewService = (WifiRttManager) mContext.getSystemService(Context.WIFI_RTT_RANGING_SERVICE);
+ public RttManager(Context context, WifiRttManager service) {
+ mNewService = service;
- boolean rttSupported = mContext.getPackageManager().hasSystemFeature(
+ boolean rttSupported = context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WIFI_RTT);
mRttCapabilities = new RttCapabilities();
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index c46789c..8024bf0 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -273,27 +273,6 @@
public RadioChainInfo[] radioChainInfos;
/**
- * @hide
- * Update RSSI of the scan result
- * @param previousRssi
- * @param previousSeen
- * @param maxAge
- */
- public void averageRssi(int previousRssi, long previousSeen, int maxAge) {
-
- if (seen == 0) {
- seen = System.currentTimeMillis();
- }
- long age = seen - previousSeen;
-
- if (previousSeen > 0 && age > 0 && age < maxAge/2) {
- // Average the RSSI with previously seen instances of this scan result
- double alpha = 0.5 - (double) age / (double) maxAge;
- level = (int) ((double) level * (1 - alpha) + (double) previousRssi * alpha);
- }
- }
-
- /**
* Status indicating the scan result does not correspond to a user's saved configuration
* @hide
* @removed
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 7da2656..ddcf327 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -917,6 +917,9 @@
* Does not guarantee that the returned address is valid for use.
*/
public MacAddress getRandomizedMacAddress() {
+ if (mRandomizedMacAddress == null) {
+ mRandomizedMacAddress = MacAddress.ALL_ZEROS_ADDRESS;
+ }
return mRandomizedMacAddress;
}
@@ -1617,6 +1620,7 @@
creatorUid = -1;
shared = true;
dtimInterval = 0;
+ mRandomizedMacAddress = MacAddress.ALL_ZEROS_ADDRESS;
}
/**
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index e7377c1..8a3a7f5 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -176,6 +176,8 @@
@Test
public void testGetOrCreateRandomizedMacAddress_SavesAndReturnsSameAddress() {
WifiConfiguration config = new WifiConfiguration();
+ assertEquals(MacAddress.ALL_ZEROS_ADDRESS, config.getRandomizedMacAddress());
+
MacAddress firstMacAddress = config.getOrCreateRandomizedMacAddress();
MacAddress secondMacAddress = config.getOrCreateRandomizedMacAddress();
@@ -185,6 +187,8 @@
@Test
public void testSetRandomizedMacAddress_ChangesSavedAddress() {
WifiConfiguration config = new WifiConfiguration();
+ assertEquals(MacAddress.ALL_ZEROS_ADDRESS, config.getRandomizedMacAddress());
+
MacAddress macToChangeInto = MacAddress.createRandomUnicastAddress();
config.setRandomizedMacAddress(macToChangeInto);
MacAddress macAfterChange = config.getOrCreateRandomizedMacAddress();