diff options
323 files changed, 9696 insertions, 4069 deletions
diff --git a/Android.bp b/Android.bp index 2dcbc92974a9..69ee848cadad 100644 --- a/Android.bp +++ b/Android.bp @@ -256,6 +256,7 @@ java_library { "core/java/android/service/euicc/IGetEidCallback.aidl", "core/java/android/service/euicc/IGetEuiccInfoCallback.aidl", "core/java/android/service/euicc/IGetEuiccProfileInfoListCallback.aidl", + "core/java/android/service/euicc/IGetOtaStatusCallback.aidl", "core/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl", "core/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl", "core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl", @@ -721,11 +722,13 @@ gensrcs { ], srcs: [ + "core/proto/android/os/batterytype.proto", "core/proto/android/os/cpufreq.proto", "core/proto/android/os/cpuinfo.proto", "core/proto/android/os/kernelwake.proto", "core/proto/android/os/pagetypeinfo.proto", "core/proto/android/os/procrank.proto", + "core/proto/android/os/ps.proto", "core/proto/android/os/system_properties.proto", ], diff --git a/Android.mk b/Android.mk index a19f2d90d9bb..0e363d2491c8 100644 --- a/Android.mk +++ b/Android.mk @@ -252,12 +252,26 @@ aidl_files := \ system/netd/server/binder/android/net/UidRange.aidl \ frameworks/base/telephony/java/android/telephony/PcoData.aidl \ +aidl_parcelables := +define stubs-to-aidl-parcelables + gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/$1.aidl + aidl_parcelables += $$(gen) + $$(gen): $(call java-lib-header-files,$1) | $(HOST_OUT_EXECUTABLES)/sdkparcelables + @echo Extract SDK parcelables: $$@ + rm -f $$@ + $(HOST_OUT_EXECUTABLES)/sdkparcelables $$< $$@ +endef + +$(foreach stubs,android_stubs_current android_test_stubs_current android_system_stubs_current,\ + $(eval $(call stubs-to-aidl-parcelables,$(stubs)))) + gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/framework.aidl -$(gen): PRIVATE_SRC_FILES := $(aidl_files) -ALL_SDK_FILES += $(gen) -$(gen): $(aidl_files) | $(AIDL) - @echo Aidl Preprocess: $@ - $(hide) $(AIDL) --preprocess $@ $(PRIVATE_SRC_FILES) +.KATI_RESTAT: $(gen) +$(gen): $(aidl_parcelables) + @echo Combining SDK parcelables: $@ + rm -f $@.tmp + cat $^ | sort -u > $@.tmp + $(call commit-change-for-toc,$@) # the documentation # ============================================================ @@ -554,8 +568,6 @@ LOCAL_UNINSTALLABLE_MODULE := true include $(BUILD_DROIDDOC) -# $(gen), i.e. framework.aidl, is also needed while building against the current stub. -$(full_target): $(gen) $(INTERNAL_PLATFORM_API_FILE): $(full_target) $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE)) @@ -591,8 +603,6 @@ LOCAL_UNINSTALLABLE_MODULE := true include $(BUILD_DROIDDOC) -# $(gen), i.e. framework.aidl, is also needed while building against the current stub. -$(full_target): $(gen) $(INTERNAL_PLATFORM_SYSTEM_API_FILE): $(full_target) $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE)) @@ -629,8 +639,6 @@ LOCAL_UNINSTALLABLE_MODULE := true include $(BUILD_DROIDDOC) -# $(gen), i.e. framework.aidl, is also needed while building against the current stub. -$(full_target): $(gen) $(INTERNAL_PLATFORM_TEST_API_FILE): $(full_target) $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE)) @@ -660,9 +668,6 @@ LOCAL_UNINSTALLABLE_MODULE := true include $(BUILD_DROIDDOC) -# $(gen), i.e. framework.aidl, is also needed while building against the current stub. -$(full_target): $(gen) - # Run this for checkbuild checkbuild: doc-comment-check-docs # Check comment when you are updating the API diff --git a/api/current.txt b/api/current.txt index 104a7d967575..c57cf32992b1 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6338,7 +6338,7 @@ package android.app.admin { method public android.os.UserHandle createAndManageUser(android.content.ComponentName, java.lang.String, android.content.ComponentName, android.os.PersistableBundle, int); method public void enableSystemApp(android.content.ComponentName, java.lang.String); method public int enableSystemApp(android.content.ComponentName, android.content.Intent); - method public android.security.AttestedKeyPair generateKeyPair(android.content.ComponentName, java.lang.String, android.security.keystore.KeyGenParameterSpec); + method public android.security.AttestedKeyPair generateKeyPair(android.content.ComponentName, java.lang.String, android.security.keystore.KeyGenParameterSpec, int); method public java.lang.String[] getAccountTypesWithManagementDisabled(); method public java.util.List<android.content.ComponentName> getActiveAdmins(); method public java.util.Set<java.lang.String> getAffiliationIds(android.content.ComponentName); @@ -6572,6 +6572,10 @@ package android.app.admin { field public static final int FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY = 1; // 0x1 field public static final int FLAG_MANAGED_CAN_ACCESS_PARENT = 2; // 0x2 field public static final int FLAG_PARENT_CAN_ACCESS_MANAGED = 1; // 0x1 + field public static final int ID_TYPE_BASE_INFO = 1; // 0x1 + field public static final int ID_TYPE_IMEI = 4; // 0x4 + field public static final int ID_TYPE_MEID = 8; // 0x8 + field public static final int ID_TYPE_SERIAL = 2; // 0x2 field public static final int KEYGUARD_DISABLE_FEATURES_ALL = 2147483647; // 0x7fffffff field public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0; // 0x0 field public static final int KEYGUARD_DISABLE_FINGERPRINT = 32; // 0x20 @@ -6749,6 +6753,7 @@ package android.app.assist { method public java.lang.CharSequence getText(); method public int getTextBackgroundColor(); method public int getTextColor(); + method public java.lang.String getTextIdEntry(); method public int[] getTextLineBaselines(); method public int[] getTextLineCharOffsets(); method public int getTextSelectionEnd(); @@ -7042,6 +7047,7 @@ package android.app.slice { field public static final java.lang.String HINT_SUMMARY = "summary"; field public static final java.lang.String HINT_TITLE = "title"; field public static final java.lang.String SUBTYPE_COLOR = "color"; + field public static final java.lang.String SUBTYPE_CONTENT_DESCRIPTION = "content_description"; field public static final java.lang.String SUBTYPE_MESSAGE = "message"; field public static final java.lang.String SUBTYPE_PRIORITY = "priority"; field public static final java.lang.String SUBTYPE_SLIDER = "slider"; @@ -31571,6 +31577,7 @@ package android.os { method public final boolean postAtTime(java.lang.Runnable, long); method public final boolean postAtTime(java.lang.Runnable, java.lang.Object, long); method public final boolean postDelayed(java.lang.Runnable, long); + method public final boolean postDelayed(java.lang.Runnable, java.lang.Object, long); method public final void removeCallbacks(java.lang.Runnable); method public final void removeCallbacks(java.lang.Runnable, java.lang.Object); method public final void removeCallbacksAndMessages(java.lang.Object); @@ -37723,18 +37730,12 @@ package android.service.autofill { method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.widget.RemoteViews); } - public final class EditDistanceScorer implements android.os.Parcelable android.service.autofill.Scorer { - method public int describeContents(); - method public static android.service.autofill.EditDistanceScorer getInstance(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.service.autofill.EditDistanceScorer> CREATOR; - } - public final class FieldClassification { method public java.util.List<android.service.autofill.FieldClassification.Match> getMatches(); } public static final class FieldClassification.Match { + method public java.lang.String getAlgorithm(); method public java.lang.String getRemoteId(); method public float getScore(); } @@ -37886,9 +37887,6 @@ package android.service.autofill { field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR; } - public abstract interface Scorer { - } - public final class TextValueSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer { ctor public TextValueSanitizer(java.util.regex.Pattern, java.lang.String); method public int describeContents(); @@ -37901,6 +37899,7 @@ package android.service.autofill { public final class UserData implements android.os.Parcelable { method public int describeContents(); + method public java.lang.String getFieldClassificationAlgorithm(); method public static int getMaxFieldClassificationIdsSize(); method public static int getMaxUserDataSize(); method public static int getMaxValueLength(); @@ -37910,9 +37909,10 @@ package android.service.autofill { } public static final class UserData.Builder { - ctor public UserData.Builder(android.service.autofill.Scorer, java.lang.String, java.lang.String); + ctor public UserData.Builder(java.lang.String, java.lang.String); method public android.service.autofill.UserData.Builder add(java.lang.String, java.lang.String); method public android.service.autofill.UserData build(); + method public android.service.autofill.UserData.Builder setFieldClassificationAlgorithm(java.lang.String, android.os.Bundle); } public abstract interface Validator { @@ -47258,6 +47258,7 @@ package android.view { method public abstract void setSelected(boolean); method public abstract void setText(java.lang.CharSequence); method public abstract void setText(java.lang.CharSequence, int, int); + method public void setTextIdEntry(java.lang.String); method public abstract void setTextLines(int[], int[]); method public abstract void setTextStyle(float, int, int, int); method public abstract void setTransformation(android.graphics.Matrix); @@ -47777,6 +47778,7 @@ package android.view.accessibility { method public java.lang.CharSequence getPackageName(); method public android.view.accessibility.AccessibilityRecord getRecord(int); method public int getRecordCount(); + method public int getWindowChanges(); method public void initFromParcel(android.os.Parcel); method public static android.view.accessibility.AccessibilityEvent obtain(int); method public static android.view.accessibility.AccessibilityEvent obtain(android.view.accessibility.AccessibilityEvent); @@ -47821,6 +47823,17 @@ package android.view.accessibility { field public static final int TYPE_WINDOWS_CHANGED = 4194304; // 0x400000 field public static final int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800 field public static final int TYPE_WINDOW_STATE_CHANGED = 32; // 0x20 + field public static final int WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED = 128; // 0x80 + field public static final int WINDOWS_CHANGE_ACTIVE = 32; // 0x20 + field public static final int WINDOWS_CHANGE_ADDED = 1; // 0x1 + field public static final int WINDOWS_CHANGE_BOUNDS = 8; // 0x8 + field public static final int WINDOWS_CHANGE_CHILDREN = 512; // 0x200 + field public static final int WINDOWS_CHANGE_FOCUSED = 64; // 0x40 + field public static final int WINDOWS_CHANGE_LAYER = 16; // 0x10 + field public static final int WINDOWS_CHANGE_PARENT = 256; // 0x100 + field public static final int WINDOWS_CHANGE_PIP = 1024; // 0x400 + field public static final int WINDOWS_CHANGE_REMOVED = 2; // 0x2 + field public static final int WINDOWS_CHANGE_TITLE = 4; // 0x4 } public abstract interface AccessibilityEventSource { @@ -48532,6 +48545,8 @@ package android.view.autofill { method public void commit(); method public void disableAutofillServices(); method public android.content.ComponentName getAutofillServiceComponentName(); + method public java.util.List<java.lang.String> getAvailableFieldClassificationAlgorithms(); + method public java.lang.String getDefaultFieldClassificationAlgorithm(); method public android.service.autofill.UserData getUserData(); method public boolean hasEnabledAutofillServices(); method public boolean isAutofillSupported(); diff --git a/api/system-current.txt b/api/system-current.txt index 3e781670c784..596474c2f59f 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1340,9 +1340,30 @@ package android.hardware.hdmi { package android.hardware.location { + public class ContextHubClient implements java.io.Closeable { + method public void close(); + method public android.hardware.location.ContextHubInfo getAttachedHub(); + method public int sendMessageToNanoApp(android.hardware.location.NanoAppMessage); + } + + public class ContextHubClientCallback { + ctor public ContextHubClientCallback(); + method public void onHubReset(android.hardware.location.ContextHubClient); + method public void onMessageFromNanoApp(android.hardware.location.ContextHubClient, android.hardware.location.NanoAppMessage); + method public void onNanoAppAborted(android.hardware.location.ContextHubClient, long, int); + method public void onNanoAppDisabled(android.hardware.location.ContextHubClient, long); + method public void onNanoAppEnabled(android.hardware.location.ContextHubClient, long); + method public void onNanoAppLoaded(android.hardware.location.ContextHubClient, long); + method public void onNanoAppUnloaded(android.hardware.location.ContextHubClient, long); + } + public class ContextHubInfo implements android.os.Parcelable { ctor public ContextHubInfo(); method public int describeContents(); + method public byte getChreApiMajorVersion(); + method public byte getChreApiMinorVersion(); + method public short getChrePatchVersion(); + method public long getChrePlatformId(); method public int getId(); method public int getMaxPacketLengthBytes(); method public android.hardware.location.MemoryRegion[] getMemoryRegions(); @@ -1362,19 +1383,27 @@ package android.hardware.location { } public final class ContextHubManager { - method public int[] findNanoAppOnHub(int, android.hardware.location.NanoAppFilter); - method public int[] getContextHubHandles(); - method public android.hardware.location.ContextHubInfo getContextHubInfo(int); - method public android.hardware.location.NanoAppInstanceInfo getNanoAppInstanceInfo(int); - method public int loadNanoApp(int, android.hardware.location.NanoApp); - method public int registerCallback(android.hardware.location.ContextHubManager.Callback); - method public int registerCallback(android.hardware.location.ContextHubManager.Callback, android.os.Handler); - method public int sendMessage(int, int, android.hardware.location.ContextHubMessage); - method public int unloadNanoApp(int); - method public int unregisterCallback(android.hardware.location.ContextHubManager.Callback); - } - - public static abstract class ContextHubManager.Callback { + method public android.hardware.location.ContextHubClient createClient(android.hardware.location.ContextHubInfo, android.hardware.location.ContextHubClientCallback, java.util.concurrent.Executor); + method public android.hardware.location.ContextHubClient createClient(android.hardware.location.ContextHubInfo, android.hardware.location.ContextHubClientCallback); + method public android.hardware.location.ContextHubTransaction<java.lang.Void> disableNanoApp(android.hardware.location.ContextHubInfo, long); + method public android.hardware.location.ContextHubTransaction<java.lang.Void> enableNanoApp(android.hardware.location.ContextHubInfo, long); + method public deprecated int[] findNanoAppOnHub(int, android.hardware.location.NanoAppFilter); + method public deprecated int[] getContextHubHandles(); + method public deprecated android.hardware.location.ContextHubInfo getContextHubInfo(int); + method public java.util.List<android.hardware.location.ContextHubInfo> getContextHubs(); + method public deprecated android.hardware.location.NanoAppInstanceInfo getNanoAppInstanceInfo(int); + method public deprecated int loadNanoApp(int, android.hardware.location.NanoApp); + method public android.hardware.location.ContextHubTransaction<java.lang.Void> loadNanoApp(android.hardware.location.ContextHubInfo, android.hardware.location.NanoAppBinary); + method public android.hardware.location.ContextHubTransaction<java.util.List<android.hardware.location.NanoAppState>> queryNanoApps(android.hardware.location.ContextHubInfo); + method public deprecated int registerCallback(android.hardware.location.ContextHubManager.Callback); + method public deprecated int registerCallback(android.hardware.location.ContextHubManager.Callback, android.os.Handler); + method public deprecated int sendMessage(int, int, android.hardware.location.ContextHubMessage); + method public deprecated int unloadNanoApp(int); + method public android.hardware.location.ContextHubTransaction<java.lang.Void> unloadNanoApp(android.hardware.location.ContextHubInfo, long); + method public deprecated int unregisterCallback(android.hardware.location.ContextHubManager.Callback); + } + + public static abstract deprecated class ContextHubManager.Callback { ctor protected ContextHubManager.Callback(); method public abstract void onMessageReceipt(int, int, android.hardware.location.ContextHubMessage); } @@ -1392,6 +1421,37 @@ package android.hardware.location { field public static final android.os.Parcelable.Creator<android.hardware.location.ContextHubMessage> CREATOR; } + public class ContextHubTransaction<T> { + method public int getType(); + method public void setOnCompleteListener(android.hardware.location.ContextHubTransaction.OnCompleteListener<T>, java.util.concurrent.Executor); + method public void setOnCompleteListener(android.hardware.location.ContextHubTransaction.OnCompleteListener<T>); + method public static java.lang.String typeToString(int, boolean); + method public android.hardware.location.ContextHubTransaction.Response<T> waitForResponse(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException, java.util.concurrent.TimeoutException; + field public static final int RESULT_FAILED_AT_HUB = 5; // 0x5 + field public static final int RESULT_FAILED_BAD_PARAMS = 2; // 0x2 + field public static final int RESULT_FAILED_BUSY = 4; // 0x4 + field public static final int RESULT_FAILED_HAL_UNAVAILABLE = 8; // 0x8 + field public static final int RESULT_FAILED_SERVICE_INTERNAL_FAILURE = 7; // 0x7 + field public static final int RESULT_FAILED_TIMEOUT = 6; // 0x6 + field public static final int RESULT_FAILED_UNINITIALIZED = 3; // 0x3 + field public static final int RESULT_FAILED_UNKNOWN = 1; // 0x1 + field public static final int RESULT_SUCCESS = 0; // 0x0 + field public static final int TYPE_DISABLE_NANOAPP = 3; // 0x3 + field public static final int TYPE_ENABLE_NANOAPP = 2; // 0x2 + field public static final int TYPE_LOAD_NANOAPP = 0; // 0x0 + field public static final int TYPE_QUERY_NANOAPPS = 4; // 0x4 + field public static final int TYPE_UNLOAD_NANOAPP = 1; // 0x1 + } + + public static abstract interface ContextHubTransaction.OnCompleteListener<L> { + method public abstract void onComplete(android.hardware.location.ContextHubTransaction<L>, android.hardware.location.ContextHubTransaction.Response<L>); + } + + public static class ContextHubTransaction.Response<R> { + method public R getContents(); + method public int getResult(); + } + public final class GeofenceHardware { method public boolean addGeofence(int, int, android.hardware.location.GeofenceHardwareRequest, android.hardware.location.GeofenceHardwareCallback); method public int[] getMonitoringTypes(); @@ -1508,6 +1568,25 @@ package android.hardware.location { field public static final android.os.Parcelable.Creator<android.hardware.location.NanoApp> CREATOR; } + public final class NanoAppBinary implements android.os.Parcelable { + ctor public NanoAppBinary(byte[]); + method public int describeContents(); + method public byte[] getBinary(); + method public byte[] getBinaryNoHeader(); + method public int getFlags(); + method public int getHeaderVersion(); + method public long getHwHubType(); + method public long getNanoAppId(); + method public int getNanoAppVersion(); + method public byte getTargetChreApiMajorVersion(); + method public byte getTargetChreApiMinorVersion(); + method public boolean hasValidHeader(); + method public boolean isEncrypted(); + method public boolean isSigned(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.hardware.location.NanoAppBinary> CREATOR; + } + public class NanoAppFilter { ctor public NanoAppFilter(long, int, int, long); method public int describeContents(); @@ -1541,6 +1620,28 @@ package android.hardware.location { field public static final android.os.Parcelable.Creator<android.hardware.location.NanoAppInstanceInfo> CREATOR; } + public final class NanoAppMessage implements android.os.Parcelable { + method public static android.hardware.location.NanoAppMessage createMessageFromNanoApp(long, int, byte[], boolean); + method public static android.hardware.location.NanoAppMessage createMessageToNanoApp(long, int, byte[]); + method public int describeContents(); + method public byte[] getMessageBody(); + method public int getMessageType(); + method public long getNanoAppId(); + method public boolean isBroadcastMessage(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.hardware.location.NanoAppMessage> CREATOR; + } + + public final class NanoAppState implements android.os.Parcelable { + ctor public NanoAppState(long, int, boolean); + method public int describeContents(); + method public long getNanoAppId(); + method public long getNanoAppVersion(); + method public boolean isEnabled(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.hardware.location.NanoAppState> CREATOR; + } + } package android.hardware.radio { @@ -4368,10 +4469,10 @@ package android.util { } public final class StatsManager { - method public boolean addConfiguration(java.lang.String, byte[], java.lang.String, java.lang.String); - method public byte[] getData(java.lang.String); + method public boolean addConfiguration(long, byte[], java.lang.String, java.lang.String); + method public byte[] getData(long); method public byte[] getMetadata(); - method public boolean removeConfiguration(java.lang.String); + method public boolean removeConfiguration(long); } } diff --git a/api/test-current.txt b/api/test-current.txt index d56b0856c028..008832c09dcf 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -435,6 +435,10 @@ package android.provider { field public static final java.lang.String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS"; } + public static final class Settings.Global extends android.provider.Settings.NameValueTable { + field public static final java.lang.String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package"; + } + public static final class Settings.Secure extends android.provider.Settings.NameValueTable { field public static final java.lang.String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled"; field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification"; @@ -473,7 +477,8 @@ package android.service.autofill { method public void apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int) throws java.lang.Exception; } - public final class EditDistanceScorer extends android.service.autofill.InternalScorer implements android.os.Parcelable android.service.autofill.Scorer { + public final class EditDistanceScorer { + method public static android.service.autofill.EditDistanceScorer getInstance(); method public float getScore(android.view.autofill.AutofillValue, java.lang.String); } @@ -489,11 +494,6 @@ package android.service.autofill { ctor public InternalSanitizer(); } - public abstract class InternalScorer implements android.os.Parcelable android.service.autofill.Scorer { - ctor public InternalScorer(); - method public abstract float getScore(android.view.autofill.AutofillValue, java.lang.String); - } - public abstract class InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation { ctor public InternalTransformation(); } diff --git a/cmds/incident_helper/src/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp index 4bf956a9a03d..e23e80ae21e8 100644 --- a/cmds/incident_helper/src/ih_util.cpp +++ b/cmds/incident_helper/src/ih_util.cpp @@ -52,6 +52,12 @@ static inline std::string trimHeader(const std::string& s) { return toLowerStr(trimDefault(s)); } +static inline bool isNumber(const std::string& s) { + std::string::const_iterator it = s.begin(); + while (it != s.end() && std::isdigit(*it)) ++it; + return !s.empty() && it == s.end(); +} + // This is similiar to Split in android-base/file.h, but it won't add empty string static void split(const std::string& line, std::vector<std::string>& words, const trans_func& func, const std::string& delimiters) { @@ -86,24 +92,80 @@ record_t parseRecord(const std::string& line, const std::string& delimiters) { return record; } +bool getColumnIndices(std::vector<int>& indices, const char** headerNames, const std::string& line) { + indices.clear(); + + size_t lastIndex = 0; + int i = 0; + while (headerNames[i] != NULL) { + string s = headerNames[i]; + lastIndex = line.find(s, lastIndex); + if (lastIndex == string::npos) { + fprintf(stderr, "Bad Task Header: %s\n", line.c_str()); + return false; + } + lastIndex += s.length(); + indices.push_back(lastIndex); + i++; + } + + return true; +} + record_t parseRecordByColumns(const std::string& line, const std::vector<int>& indices, const std::string& delimiters) { record_t record; int lastIndex = 0; + int lastBeginning = 0; int lineSize = (int)line.size(); for (std::vector<int>::const_iterator it = indices.begin(); it != indices.end(); ++it) { int idx = *it; - if (lastIndex > idx || idx > lineSize) { - record.clear(); // The indices is wrong, return empty; + if (idx <= lastIndex) { + // We saved up until lastIndex last time, so we should start at + // lastIndex + 1 this time. + idx = lastIndex + 1; + } + if (idx > lineSize) { + if (lastIndex < idx && lastIndex < lineSize) { + // There's a little bit more for us to save, which we'll do + // outside of the loop. + break; + } + // If we're past the end of the line AND we've already saved everything up to the end. + fprintf(stderr, "index wrong: lastIndex: %d, idx: %d, lineSize: %d\n", lastIndex, idx, lineSize); + record.clear(); // The indices are wrong, return empty. return record; } while (idx < lineSize && delimiters.find(line[idx++]) == std::string::npos); record.push_back(trimDefault(line.substr(lastIndex, idx - lastIndex))); + lastBeginning = lastIndex; lastIndex = idx; } - record.push_back(trimDefault(line.substr(lastIndex, lineSize - lastIndex))); + if (lineSize - lastIndex > 0) { + int beginning = lastIndex; + if (record.size() == indices.size()) { + // We've already encountered all of the columns...put whatever is + // left in the last column. + record.pop_back(); + beginning = lastBeginning; + } + record.push_back(trimDefault(line.substr(beginning, lineSize - beginning))); + } return record; } +void printRecord(const record_t& record) { + fprintf(stderr, "Record: { "); + if (record.size() == 0) { + fprintf(stderr, "}\n"); + return; + } + for(size_t i = 0; i < record.size(); ++i) { + if(i != 0) fprintf(stderr, "\", "); + fprintf(stderr, "\"%s", record[i].c_str()); + } + fprintf(stderr, "\" }\n"); +} + bool stripPrefix(std::string* line, const char* key, bool endAtDelimiter) { const auto head = line->find_first_not_of(DEFAULT_WHITESPACE); if (head == std::string::npos) return false; @@ -210,7 +272,10 @@ Table::~Table() void Table::addEnumTypeMap(const char* field, const char* enumNames[], const int enumValues[], const int enumSize) { - if (mFields.find(field) == mFields.end()) return; + if (mFields.find(field) == mFields.end()) { + fprintf(stderr, "Field '%s' not found", string(field).c_str()); + return; + } map<std::string, int> enu; for (int i = 0; i < enumSize; i++) { @@ -268,6 +333,8 @@ Table::insertField(ProtoOutputStream* proto, const std::string& name, const std: } } else if (mEnumValuesByName.find(value) != mEnumValuesByName.end()) { proto->write(found, mEnumValuesByName[value]); + } else if (isNumber(value)) { + proto->write(found, toInt(value)); } else { return false; } diff --git a/cmds/incident_helper/src/ih_util.h b/cmds/incident_helper/src/ih_util.h index 58ef29044048..b063b2fe0bba 100644 --- a/cmds/incident_helper/src/ih_util.h +++ b/cmds/incident_helper/src/ih_util.h @@ -56,12 +56,23 @@ header_t parseHeader(const std::string& line, const std::string& delimiters = DE record_t parseRecord(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE); /** + * Gets the list of end indices of each word in the line and places it in the given vector, + * clearing out the vector beforehand. These indices can be used with parseRecordByColumns. + * Will return false if there was a problem getting the indices. headerNames + * must be NULL terminated. + */ +bool getColumnIndices(std::vector<int>& indices, const char* headerNames[], const std::string& line); + +/** * When a text-format table aligns by its vertical position, it is not possible to split them by purely delimiters. * This function allows to parse record by its header's column position' indices, must in ascending order. * At the same time, it still looks at the char at index, if it doesn't belong to delimiters, moves forward to find the delimiters. */ record_t parseRecordByColumns(const std::string& line, const std::vector<int>& indices, const std::string& delimiters = DEFAULT_WHITESPACE); +/** Prints record_t to stderr */ +void printRecord(const record_t& record); + /** * When the line starts/ends with the given key, the function returns true * as well as the line argument is changed to the rest trimmed part of the original. diff --git a/cmds/incident_helper/src/main.cpp b/cmds/incident_helper/src/main.cpp index c8a0883d493c..8c6cd78d3bf2 100644 --- a/cmds/incident_helper/src/main.cpp +++ b/cmds/incident_helper/src/main.cpp @@ -16,11 +16,13 @@ #define LOG_TAG "incident_helper" +#include "parsers/BatteryTypeParser.h" #include "parsers/CpuFreqParser.h" #include "parsers/CpuInfoParser.h" #include "parsers/KernelWakesParser.h" #include "parsers/PageTypeInfoParser.h" #include "parsers/ProcrankParser.h" +#include "parsers/PsParser.h" #include "parsers/SystemPropertiesParser.h" #include <android-base/file.h> @@ -63,6 +65,10 @@ static TextParserBase* selectParser(int section) { return new CpuInfoParser(); case 2004: return new CpuFreqParser(); + case 2005: + return new PsParser(); + case 2006: + return new BatteryTypeParser(); default: return NULL; } diff --git a/cmds/incident_helper/src/parsers/BatteryTypeParser.cpp b/cmds/incident_helper/src/parsers/BatteryTypeParser.cpp new file mode 100644 index 000000000000..ced6cf807e0d --- /dev/null +++ b/cmds/incident_helper/src/parsers/BatteryTypeParser.cpp @@ -0,0 +1,60 @@ +/* + * 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. + */ +#define LOG_TAG "incident_helper" + +#include <android/util/ProtoOutputStream.h> + +#include "frameworks/base/core/proto/android/os/batterytype.proto.h" +#include "ih_util.h" +#include "BatteryTypeParser.h" + +using namespace android::os; + +status_t +BatteryTypeParser::Parse(const int in, const int out) const +{ + Reader reader(in); + string line; + bool readLine = false; + + ProtoOutputStream proto; + + // parse line by line + while (reader.readLine(&line)) { + if (line.empty()) continue; + + if (readLine) { + fprintf(stderr, "Multiple lines in file. Unsure what to do.\n"); + break; + } + + proto.write(BatteryTypeProto::TYPE, line); + + readLine = true; + } + + if (!reader.ok(&line)) { + fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); + return -1; + } + + if (!proto.flush(out)) { + fprintf(stderr, "[%s]Error writing proto back\n", this->name.string()); + return -1; + } + fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size()); + return NO_ERROR; +} diff --git a/cmds/incident_helper/src/parsers/BatteryTypeParser.h b/cmds/incident_helper/src/parsers/BatteryTypeParser.h new file mode 100644 index 000000000000..ac0c098965d3 --- /dev/null +++ b/cmds/incident_helper/src/parsers/BatteryTypeParser.h @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#ifndef BATTERY_TYPE_PARSER_H +#define BATTERY_TYPE_PARSER_H + +#include "TextParserBase.h" + +using namespace android; + +/** + * Battery type parser, parses text in file + * /sys/class/power_supply/bms/battery_type. + */ +class BatteryTypeParser : public TextParserBase { +public: + BatteryTypeParser() : TextParserBase(String8("BatteryTypeParser")) {}; + ~BatteryTypeParser() {}; + + virtual status_t Parse(const int in, const int out) const; +}; + +#endif // BATTERY_TYPE_PARSER_H diff --git a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp index 3faca00c1b88..d73de54d8c5d 100644 --- a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp +++ b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp @@ -49,6 +49,7 @@ CpuInfoParser::Parse(const int in, const int out) const vector<int> columnIndices; // task table can't be split by purely delimiter, needs column positions. record_t record; int nline = 0; + int diff = 0; bool nextToSwap = false; bool nextToUsage = false; @@ -107,18 +108,10 @@ CpuInfoParser::Parse(const int in, const int out) const header = parseHeader(line, "[ %]"); nextToUsage = false; - // NAME is not in the list since the last split index is default to the end of line. - const char* headerNames[11] = { "PID", "TID", "USER", "PR", "NI", "CPU", "S", "VIRT", "RES", "PCY", "CMD" }; - size_t lastIndex = 0; - for (int i = 0; i < 11; i++) { - string s = headerNames[i]; - lastIndex = line.find(s, lastIndex); - if (lastIndex == string::npos) { - fprintf(stderr, "Bad Task Header: %s\n", line.c_str()); - return -1; - } - lastIndex += s.length(); - columnIndices.push_back(lastIndex); + // NAME is not in the list since we need to modify the end of the CMD index. + const char* headerNames[] = { "PID", "TID", "USER", "PR", "NI", "CPU", "S", "VIRT", "RES", "PCY", "CMD", NULL }; + if (!getColumnIndices(columnIndices, headerNames, line)) { + return -1; } // Need to remove the end index of CMD and use the start index of NAME because CMD values contain spaces. // for example: ... CMD NAME @@ -128,12 +121,20 @@ CpuInfoParser::Parse(const int in, const int out) const int endCMD = columnIndices.back(); columnIndices.pop_back(); columnIndices.push_back(line.find("NAME", endCMD) - 1); + // Add NAME index to complete the column list. + columnIndices.push_back(columnIndices.back() + 4); continue; } record = parseRecordByColumns(line, columnIndices); - if (record.size() != header.size()) { - fprintf(stderr, "[%s]Line %d has missing fields:\n%s\n", this->name.string(), nline, line.c_str()); + diff = record.size() - header.size(); + if (diff < 0) { + fprintf(stderr, "[%s]Line %d has %d missing fields\n%s\n", this->name.string(), nline, -diff, line.c_str()); + printRecord(record); + continue; + } else if (diff > 0) { + fprintf(stderr, "[%s]Line %d has %d extra fields\n%s\n", this->name.string(), nline, diff, line.c_str()); + printRecord(record); continue; } diff --git a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp index ada4a5d0ffe2..cae51abbe57f 100644 --- a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp +++ b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp @@ -47,10 +47,14 @@ KernelWakesParser::Parse(const int in, const int out) const // parse for each record, the line delimiter is \t only! record = parseRecord(line, TAB_DELIMITER); - if (record.size() != header.size()) { + if (record.size() < header.size()) { // TODO: log this to incident report! fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, line.c_str()); continue; + } else if (record.size() > header.size()) { + // TODO: log this to incident report! + fprintf(stderr, "[%s]Line %d has extra fields\n%s\n", this->name.string(), nline, line.c_str()); + continue; } long long token = proto.start(KernelWakeSources::WAKEUP_SOURCES); diff --git a/cmds/incident_helper/src/parsers/PsParser.cpp b/cmds/incident_helper/src/parsers/PsParser.cpp new file mode 100644 index 000000000000..e9014cacfa0b --- /dev/null +++ b/cmds/incident_helper/src/parsers/PsParser.cpp @@ -0,0 +1,95 @@ +/* + * 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. + */ +#define LOG_TAG "incident_helper" + +#include <android/util/ProtoOutputStream.h> + +#include "frameworks/base/core/proto/android/os/ps.proto.h" +#include "ih_util.h" +#include "PsParser.h" + +using namespace android::os; + +status_t PsParser::Parse(const int in, const int out) const { + Reader reader(in); + string line; + header_t header; // the header of /d/wakeup_sources + vector<int> columnIndices; // task table can't be split by purely delimiter, needs column positions. + record_t record; // retain each record + int nline = 0; + int diff = 0; + + ProtoOutputStream proto; + Table table(PsDumpProto::Process::_FIELD_NAMES, PsDumpProto::Process::_FIELD_IDS, PsDumpProto::Process::_FIELD_COUNT); + const char* pcyNames[] = { "fg", "bg", "ta" }; + const int pcyValues[] = {PsDumpProto::Process::POLICY_FG, PsDumpProto::Process::POLICY_BG, PsDumpProto::Process::POLICY_TA}; + table.addEnumTypeMap("pcy", pcyNames, pcyValues, 3); + const char* sNames[] = { "D", "R", "S", "T", "t", "X", "Z" }; + const int sValues[] = {PsDumpProto::Process::STATE_D, PsDumpProto::Process::STATE_R, PsDumpProto::Process::STATE_S, PsDumpProto::Process::STATE_T, PsDumpProto::Process::STATE_TRACING, PsDumpProto::Process::STATE_X, PsDumpProto::Process::STATE_Z}; + table.addEnumTypeMap("s", sNames, sValues, 7); + + // Parse line by line + while (reader.readLine(&line)) { + if (line.empty()) continue; + + if (nline++ == 0) { + header = parseHeader(line, DEFAULT_WHITESPACE); + + const char* headerNames[] = { "LABEL", "USER", "PID", "TID", "PPID", "VSZ", "RSS", "WCHAN", "ADDR", "S", "PRI", "NI", "RTPRIO", "SCH", "PCY", "TIME", "CMD", NULL }; + if (!getColumnIndices(columnIndices, headerNames, line)) { + return -1; + } + + continue; + } + + record = parseRecordByColumns(line, columnIndices); + + diff = record.size() - header.size(); + if (diff < 0) { + // TODO: log this to incident report! + fprintf(stderr, "[%s]Line %d has %d missing fields\n%s\n", this->name.string(), nline, -diff, line.c_str()); + printRecord(record); + continue; + } else if (diff > 0) { + // TODO: log this to incident report! + fprintf(stderr, "[%s]Line %d has %d extra fields\n%s\n", this->name.string(), nline, diff, line.c_str()); + printRecord(record); + continue; + } + + long long token = proto.start(PsDumpProto::PROCESSES); + for (int i=0; i<(int)record.size(); i++) { + if (!table.insertField(&proto, header[i], record[i])) { + fprintf(stderr, "[%s]Line %d has bad value %s of %s\n", + this->name.string(), nline, header[i].c_str(), record[i].c_str()); + } + } + proto.end(token); + } + + if (!reader.ok(&line)) { + fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); + return -1; + } + + if (!proto.flush(out)) { + fprintf(stderr, "[%s]Error writing proto back\n", this->name.string()); + return -1; + } + fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size()); + return NO_ERROR; +} diff --git a/core/java/android/service/autofill/Scorer.java b/cmds/incident_helper/src/parsers/PsParser.h index c4018558b3ed..9488e40e88fe 100644 --- a/core/java/android/service/autofill/Scorer.java +++ b/cmds/incident_helper/src/parsers/PsParser.h @@ -13,16 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.service.autofill; + +#ifndef PS_PARSER_H +#define PS_PARSER_H + +#include "TextParserBase.h" /** - * Helper class used to calculate a score. - * - * <p>Typically used to calculate the - * <a href="AutofillService.html#FieldClassification">field classification</a> score between an - * actual {@link android.view.autofill.AutofillValue} filled by the user and the expected value - * predicted by an autofill service. + * PS parser, parses output of 'ps' command to protobuf. */ -public interface Scorer { +class PsParser : public TextParserBase { +public: + PsParser() : TextParserBase(String8("Ps")) {}; + ~PsParser() {}; + + virtual status_t Parse(const int in, const int out) const; +}; -} +#endif // PS_PARSER_H diff --git a/cmds/incident_helper/testdata/batterytype.txt b/cmds/incident_helper/testdata/batterytype.txt new file mode 100644 index 000000000000..c763d36ad811 --- /dev/null +++ b/cmds/incident_helper/testdata/batterytype.txt @@ -0,0 +1 @@ +random_battery_type_string diff --git a/cmds/incident_helper/testdata/ps.txt b/cmds/incident_helper/testdata/ps.txt new file mode 100644 index 000000000000..72dafc2c4378 --- /dev/null +++ b/cmds/incident_helper/testdata/ps.txt @@ -0,0 +1,9 @@ +LABEL USER PID TID PPID VSZ RSS WCHAN ADDR S PRI NI RTPRIO SCH PCY TIME CMD +u:r:init:s0 root 1 1 0 15816 2636 SyS_epoll_wait 0 S 19 0 - 0 fg 00:00:01 init +u:r:kernel:s0 root 2 2 0 0 0 kthreadd 0 S 19 0 - 0 fg 00:00:00 kthreadd +u:r:surfaceflinger:s0 system 499 534 1 73940 22024 futex_wait_queue_me 0 S 42 -9 2 1 fg 00:00:00 EventThread +u:r:hal_gnss_default:s0 gps 670 2004 1 43064 7272 poll_schedule_timeout 0 S 19 0 - 0 fg 00:00:00 Loc_hal_worker +u:r:platform_app:s0:c512,c768 u0_a48 1660 1976 806 4468612 138328 binder_thread_read 0 S 35 -16 - 0 ta 00:00:00 HwBinder:1660_1 +u:r:perfd:s0 root 1939 1946 1 18132 2088 __skb_recv_datagram 7b9782fd14 S 19 0 - 0 00:00:00 perfd +u:r:perfd:s0 root 1939 1955 1 18132 2088 do_sigtimedwait 7b9782ff6c S 19 0 - 0 00:00:00 POSIX timer 0 +u:r:shell:s0 shell 2645 2645 802 11664 2972 0 7f67a2f8b4 R 19 0 - 0 fg 00:00:00 ps diff --git a/cmds/incident_helper/tests/BatteryTypeParser_test.cpp b/cmds/incident_helper/tests/BatteryTypeParser_test.cpp new file mode 100644 index 000000000000..7fbe22df4b0a --- /dev/null +++ b/cmds/incident_helper/tests/BatteryTypeParser_test.cpp @@ -0,0 +1,66 @@ +/* + * 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. + */ + +#include "BatteryTypeParser.h" + +#include "frameworks/base/core/proto/android/os/batterytype.pb.h" + +#include <android-base/file.h> +#include <android-base/test_utils.h> +#include <gmock/gmock.h> +#include <google/protobuf/message_lite.h> +#include <gtest/gtest.h> +#include <string.h> +#include <fcntl.h> + +using namespace android::base; +using namespace android::os; +using namespace std; +using ::testing::StrEq; +using ::testing::Test; +using ::testing::internal::CaptureStderr; +using ::testing::internal::CaptureStdout; +using ::testing::internal::GetCapturedStderr; +using ::testing::internal::GetCapturedStdout; + +class BatteryTypeParserTest : public Test { +public: + virtual void SetUp() override { + ASSERT_TRUE(tf.fd != -1); + } + +protected: + TemporaryFile tf; + + const string kTestPath = GetExecutableDirectory(); + const string kTestDataPath = kTestPath + "/testdata/"; +}; + +TEST_F(BatteryTypeParserTest, Success) { + const string testFile = kTestDataPath + "batterytype.txt"; + BatteryTypeParser parser; + BatteryTypeProto expected; + + expected.set_type("random_battery_type_string"); + + int fd = open(testFile.c_str(), O_RDONLY); + ASSERT_TRUE(fd != -1); + + CaptureStdout(); + ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO)); + EXPECT_EQ(GetCapturedStdout(), expected.SerializeAsString()); + close(fd); +} diff --git a/cmds/incident_helper/tests/PsParser_test.cpp b/cmds/incident_helper/tests/PsParser_test.cpp new file mode 100644 index 000000000000..1f03a7f3a332 --- /dev/null +++ b/cmds/incident_helper/tests/PsParser_test.cpp @@ -0,0 +1,300 @@ +/* + * 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. + */ + +#include "PsParser.h" + +#include "frameworks/base/core/proto/android/os/ps.pb.h" + +#include <android-base/file.h> +#include <android-base/test_utils.h> +#include <gmock/gmock.h> +#include <google/protobuf/message_lite.h> +#include <gtest/gtest.h> +#include <string.h> +#include <fcntl.h> + +using namespace android::base; +using namespace android::os; +using namespace std; +using ::testing::StrEq; +using ::testing::Test; +using ::testing::internal::CaptureStderr; +using ::testing::internal::CaptureStdout; +using ::testing::internal::GetCapturedStderr; +using ::testing::internal::GetCapturedStdout; + +class PsParserTest : public Test { +public: + virtual void SetUp() override { + ASSERT_TRUE(tf.fd != -1); + } + +protected: + TemporaryFile tf; + + const string kTestPath = GetExecutableDirectory(); + const string kTestDataPath = kTestPath + "/testdata/"; +}; + +TEST_F(PsParserTest, Normal) { + const string testFile = kTestDataPath + "ps.txt"; + PsParser parser; + PsDumpProto expected; + PsDumpProto got; + + PsDumpProto::Process* record1 = expected.add_processes(); + record1->set_label("u:r:init:s0"); + record1->set_user("root"); + record1->set_pid(1); + record1->set_tid(1); + record1->set_ppid(0); + record1->set_vsz(15816); + record1->set_rss(2636); + record1->set_wchan("SyS_epoll_wait"); + record1->set_addr("0"); + record1->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S); + record1->set_pri(19); + record1->set_ni(0); + record1->set_rtprio("-"); + record1->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL); + record1->set_pcy(PsDumpProto::Process::POLICY_FG); + record1->set_time("00:00:01"); + record1->set_cmd("init"); + + PsDumpProto::Process* record2 = expected.add_processes(); + record2->set_label("u:r:kernel:s0"); + record2->set_user("root"); + record2->set_pid(2); + record2->set_tid(2); + record2->set_ppid(0); + record2->set_vsz(0); + record2->set_rss(0); + record2->set_wchan("kthreadd"); + record2->set_addr("0"); + record2->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S); + record2->set_pri(19); + record2->set_ni(0); + record2->set_rtprio("-"); + record2->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL); + record2->set_pcy(PsDumpProto::Process::POLICY_FG); + record2->set_time("00:00:00"); + record2->set_cmd("kthreadd"); + + PsDumpProto::Process* record3 = expected.add_processes(); + record3->set_label("u:r:surfaceflinger:s0"); + record3->set_user("system"); + record3->set_pid(499); + record3->set_tid(534); + record3->set_ppid(1); + record3->set_vsz(73940); + record3->set_rss(22024); + record3->set_wchan("futex_wait_queue_me"); + record3->set_addr("0"); + record3->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S); + record3->set_pri(42); + record3->set_ni(-9); + record3->set_rtprio("2"); + record3->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_FIFO); + record3->set_pcy(PsDumpProto::Process::POLICY_FG); + record3->set_time("00:00:00"); + record3->set_cmd("EventThread"); + + PsDumpProto::Process* record4 = expected.add_processes(); + record4->set_label("u:r:hal_gnss_default:s0"); + record4->set_user("gps"); + record4->set_pid(670); + record4->set_tid(2004); + record4->set_ppid(1); + record4->set_vsz(43064); + record4->set_rss(7272); + record4->set_wchan("poll_schedule_timeout"); + record4->set_addr("0"); + record4->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S); + record4->set_pri(19); + record4->set_ni(0); + record4->set_rtprio("-"); + record4->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL); + record4->set_pcy(PsDumpProto::Process::POLICY_FG); + record4->set_time("00:00:00"); + record4->set_cmd("Loc_hal_worker"); + + PsDumpProto::Process* record5 = expected.add_processes(); + record5->set_label("u:r:platform_app:s0:c512,c768"); + record5->set_user("u0_a48"); + record5->set_pid(1660); + record5->set_tid(1976); + record5->set_ppid(806); + record5->set_vsz(4468612); + record5->set_rss(138328); + record5->set_wchan("binder_thread_read"); + record5->set_addr("0"); + record5->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S); + record5->set_pri(35); + record5->set_ni(-16); + record5->set_rtprio("-"); + record5->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL); + record5->set_pcy(PsDumpProto::Process::POLICY_TA); + record5->set_time("00:00:00"); + record5->set_cmd("HwBinder:1660_1"); + + PsDumpProto::Process* record6 = expected.add_processes(); + record6->set_label("u:r:perfd:s0"); + record6->set_user("root"); + record6->set_pid(1939); + record6->set_tid(1946); + record6->set_ppid(1); + record6->set_vsz(18132); + record6->set_rss(2088); + record6->set_wchan("__skb_recv_datagram"); + record6->set_addr("7b9782fd14"); + record6->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S); + record6->set_pri(19); + record6->set_ni(0); + record6->set_rtprio("-"); + record6->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL); + record6->set_pcy(PsDumpProto::Process::POLICY_UNKNOWN); + record6->set_time("00:00:00"); + record6->set_cmd("perfd"); + + PsDumpProto::Process* record7 = expected.add_processes(); + record7->set_label("u:r:perfd:s0"); + record7->set_user("root"); + record7->set_pid(1939); + record7->set_tid(1955); + record7->set_ppid(1); + record7->set_vsz(18132); + record7->set_rss(2088); + record7->set_wchan("do_sigtimedwait"); + record7->set_addr("7b9782ff6c"); + record7->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S); + record7->set_pri(19); + record7->set_ni(0); + record7->set_rtprio("-"); + record7->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL); + record7->set_pcy(PsDumpProto::Process::POLICY_UNKNOWN); + record7->set_time("00:00:00"); + record7->set_cmd("POSIX timer 0"); + + PsDumpProto::Process* record8 = expected.add_processes(); + record8->set_label("u:r:shell:s0"); + record8->set_user("shell"); + record8->set_pid(2645); + record8->set_tid(2645); + record8->set_ppid(802); + record8->set_vsz(11664); + record8->set_rss(2972); + record8->set_wchan("0"); + record8->set_addr("7f67a2f8b4"); + record8->set_s(PsDumpProto_Process_ProcessStateCode_STATE_R); + record8->set_pri(19); + record8->set_ni(0); + record8->set_rtprio("-"); + record8->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL); + record8->set_pcy(PsDumpProto::Process::POLICY_FG); + record8->set_time("00:00:00"); + record8->set_cmd("ps"); + + int fd = open(testFile.c_str(), O_RDONLY); + ASSERT_TRUE(fd != -1); + + CaptureStdout(); + ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO)); + got.ParseFromString(GetCapturedStdout()); + bool matches = true; + + if (got.processes_size() != expected.processes_size()) { + fprintf(stderr, "Got %d processes, want %d\n", got.processes_size(), expected.processes_size()); + matches = false; + } else { + int n = got.processes_size(); + for (int i = 0; i < n; i++) { + PsDumpProto::Process g = got.processes(i); + PsDumpProto::Process e = expected.processes(i); + + if (g.label() != e.label()) { + fprintf(stderr, "prcs[%d]: Invalid label. Got %s, want %s\n", i, g.label().c_str(), e.label().c_str()); + matches = false; + } + if (g.user() != e.user()) { + fprintf(stderr, "prcs[%d]: Invalid user. Got %s, want %s\n", i, g.user().c_str(), e.user().c_str()); + matches = false; + } + if (g.pid() != e.pid()) { + fprintf(stderr, "prcs[%d]: Invalid pid. Got %d, want %d\n", i, g.pid(), e.pid()); + matches = false; + } + if (g.tid() != e.tid()) { + fprintf(stderr, "prcs[%d]: Invalid tid. Got %d, want %d\n", i, g.tid(), e.tid()); + matches = false; + } + if (g.ppid() != e.ppid()) { + fprintf(stderr, "prcs[%d]: Invalid ppid. Got %d, want %d\n", i, g.ppid(), e.ppid()); + matches = false; + } + if (g.vsz() != e.vsz()) { + fprintf(stderr, "prcs[%d]: Invalid vsz. Got %d, want %d\n", i, g.vsz(), e.vsz()); + matches = false; + } + if (g.rss() != e.rss()) { + fprintf(stderr, "prcs[%d]: Invalid rss. Got %d, want %d\n", i, g.rss(), e.rss()); + matches = false; + } + if (g.wchan() != e.wchan()) { + fprintf(stderr, "prcs[%d]: Invalid wchan. Got %s, want %s\n", i, g.wchan().c_str(), e.wchan().c_str()); + matches = false; + } + if (g.addr() != e.addr()) { + fprintf(stderr, "prcs[%d]: Invalid addr. Got %s, want %s\n", i, g.addr().c_str(), e.addr().c_str()); + matches = false; + } + if (g.s() != e.s()) { + fprintf(stderr, "prcs[%d]: Invalid s. Got %u, want %u\n", i, g.s(), e.s()); + matches = false; + } + if (g.pri() != e.pri()) { + fprintf(stderr, "prcs[%d]: Invalid pri. Got %d, want %d\n", i, g.pri(), e.pri()); + matches = false; + } + if (g.ni() != e.ni()) { + fprintf(stderr, "prcs[%d]: Invalid ni. Got %d, want %d\n", i, g.ni(), e.ni()); + matches = false; + } + if (g.rtprio() != e.rtprio()) { + fprintf(stderr, "prcs[%d]: Invalid rtprio. Got %s, want %s\n", i, g.rtprio().c_str(), e.rtprio().c_str()); + matches = false; + } + if (g.sch() != e.sch()) { + fprintf(stderr, "prcs[%d]: Invalid sch. Got %u, want %u\n", i, g.sch(), e.sch()); + matches = false; + } + if (g.pcy() != e.pcy()) { + fprintf(stderr, "prcs[%d]: Invalid pcy. Got %u, want %u\n", i, g.pcy(), e.pcy()); + matches = false; + } + if (g.time() != e.time()) { + fprintf(stderr, "prcs[%d]: Invalid time. Got %s, want %s\n", i, g.time().c_str(), e.time().c_str()); + matches = false; + } + if (g.cmd() != e.cmd()) { + fprintf(stderr, "prcs[%d]: Invalid cmd. Got %s, want %s\n", i, g.cmd().c_str(), e.cmd().c_str()); + matches = false; + } + } + } + + EXPECT_TRUE(matches); + close(fd); +} diff --git a/cmds/incident_helper/tests/ih_util_test.cpp b/cmds/incident_helper/tests/ih_util_test.cpp index 5740b330d949..7b8cf52c8bee 100644 --- a/cmds/incident_helper/tests/ih_util_test.cpp +++ b/cmds/incident_helper/tests/ih_util_test.cpp @@ -71,11 +71,29 @@ TEST(IhUtilTest, ParseRecordByColumns) { EXPECT_EQ(expected, result); result = parseRecordByColumns("abc \t2345 6789 ", indices); - expected = { "abc", "2345", "6789" }; + expected = { "abc", "2345 6789" }; EXPECT_EQ(expected, result); - result = parseRecordByColumns("abc \t23456789 bob", indices); - expected = { "abc", "23456789", "bob" }; + std::string extraColumn1 = "abc \t23456789 bob"; + std::string emptyMidColm = "abc \t bob"; + std::string longFirstClm = "abcdefgt\t6789 bob"; + std::string lngFrstEmpty = "abcdefgt\t bob"; + + result = parseRecordByColumns(extraColumn1, indices); + expected = { "abc", "23456789 bob" }; + EXPECT_EQ(expected, result); + + // 2nd column should be treated as an empty entry. + result = parseRecordByColumns(emptyMidColm, indices); + expected = { "abc", "bob" }; + EXPECT_EQ(expected, result); + + result = parseRecordByColumns(longFirstClm, indices); + expected = { "abcdefgt", "6789 bob" }; + EXPECT_EQ(expected, result); + + result = parseRecordByColumns(lngFrstEmpty, indices); + expected = { "abcdefgt", "bob" }; EXPECT_EQ(expected, result); } diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk index fb8ef6338d90..11d3e4911761 100644 --- a/cmds/incidentd/Android.mk +++ b/cmds/incidentd/Android.mk @@ -72,9 +72,7 @@ LOCAL_GENERATED_SOURCES += $(GEN) gen_src_dir:= GEN:= -ifeq ($(BUILD_WITH_INCIDENTD_RC), true) LOCAL_INIT_RC := incidentd.rc -endif include $(BUILD_EXECUTABLE) diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp index 1bf795bb6557..22053ef3c53a 100644 --- a/cmds/incidentd/src/Section.cpp +++ b/cmds/incidentd/src/Section.cpp @@ -521,7 +521,7 @@ CommandSection::Execute(ReportRequestSet* requests) const ALOGW("CommandSection '%s' failed to set up stdout: %s", this->name.string(), strerror(errno)); _exit(EXIT_FAILURE); } - execv(this->mCommand[0], (char *const *) this->mCommand); + execvp(this->mCommand[0], (char *const *) this->mCommand); int err = errno; // record command error code ALOGW("CommandSection '%s' failed in executing command: %s", this->name.string(), strerror(errno)); _exit(err); // exit with command error code diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp index 6da12438926c..288ebe9dbbe9 100644 --- a/cmds/statsd/src/HashableDimensionKey.cpp +++ b/cmds/statsd/src/HashableDimensionKey.cpp @@ -21,8 +21,8 @@ namespace android { namespace os { namespace statsd { -android::hash_t hashDimensionsValue(const DimensionsValue& value) { - android::hash_t hash = 0; +android::hash_t hashDimensionsValue(int64_t seed, const DimensionsValue& value) { + android::hash_t hash = seed; hash = android::JenkinsHashMix(hash, android::hash_type(value.field())); hash = android::JenkinsHashMix(hash, android::hash_type((int)value.value_case())); @@ -63,6 +63,10 @@ android::hash_t hashDimensionsValue(const DimensionsValue& value) { return JenkinsHashWhiten(hash); } +android::hash_t hashDimensionsValue(const DimensionsValue& value) { + return hashDimensionsValue(0, value); +} + using std::string; diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h index 3a4ffce1aadd..85c317f8cf1f 100644 --- a/cmds/statsd/src/HashableDimensionKey.h +++ b/cmds/statsd/src/HashableDimensionKey.h @@ -53,6 +53,7 @@ private: DimensionsValue mDimensionsValue; }; +android::hash_t hashDimensionsValue(int64_t seed, const DimensionsValue& value); android::hash_t hashDimensionsValue(const DimensionsValue& value); } // namespace statsd diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 13f332ed34c3..991badcdddac 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -54,7 +54,7 @@ const int FIELD_ID_CONFIG_KEY = 1; const int FIELD_ID_REPORTS = 2; // for ConfigKey const int FIELD_ID_UID = 1; -const int FIELD_ID_NAME = 2; +const int FIELD_ID_ID = 2; // for ConfigMetricsReport const int FIELD_ID_METRICS = 1; const int FIELD_ID_UID_MAP = 2; @@ -127,7 +127,7 @@ void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig if (newMetricsManager->isConfigValid()) { mUidMap->OnConfigUpdated(key); newMetricsManager->setAnomalyMonitor(mAnomalyMonitor); - if (config.log_source().package().size() > 0) { + if (newMetricsManager->shouldAddUidMapListener()) { // We have to add listener after the MetricsManager is constructed because it's // not safe to create wp or sp from this pointer inside its constructor. mUidMap->addListener(newMetricsManager.get()); @@ -150,14 +150,15 @@ size_t StatsLogProcessor::GetMetricsSize(const ConfigKey& key) const { return it->second->byteSize(); } -void StatsLogProcessor::onDumpReport(const ConfigKey& key, const uint64_t& dumpTimeStampNs, ConfigMetricsReportList* report) { +void StatsLogProcessor::onDumpReport(const ConfigKey& key, const uint64_t& dumpTimeStampNs, + ConfigMetricsReportList* report) { auto it = mMetricsManagers.find(key); if (it == mMetricsManagers.end()) { ALOGW("Config source %s does not exist", key.ToString().c_str()); return; } report->mutable_config_key()->set_uid(key.GetUid()); - report->mutable_config_key()->set_name(key.GetName()); + report->mutable_config_key()->set_id(key.GetId()); ConfigMetricsReport* configMetricsReport = report->add_reports(); it->second->onDumpReport(dumpTimeStampNs, configMetricsReport); // TODO: dump uid mapping. @@ -181,7 +182,7 @@ void StatsLogProcessor::onDumpReport(const ConfigKey& key, vector<uint8_t>* outD // Start of ConfigKey. long long configKeyToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY); proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID, key.GetUid()); - proto.write(FIELD_TYPE_STRING | FIELD_ID_NAME, key.GetName()); + proto.write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)key.GetId()); proto.end(configKeyToken); // End of ConfigKey. @@ -278,8 +279,8 @@ void StatsLogProcessor::WriteDataToDisk() { vector<uint8_t> data; onDumpReport(key, &data); // TODO: Add a guardrail to prevent accumulation of file on disk. - string file_name = StringPrintf("%s/%d-%s-%ld", STATS_DATA_DIR, key.GetUid(), - key.GetName().c_str(), time(nullptr)); + string file_name = StringPrintf("%s/%d-%lld-%ld", STATS_DATA_DIR, key.GetUid(), + (long long)key.GetId(), time(nullptr)); StorageManager::writeFile(file_name.c_str(), &data[0], data.size()); } } diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index e8b0bd284570..45f1ea1bb183 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -335,7 +335,7 @@ status_t StatsService::cmd_trigger_broadcast(FILE* out, Vector<String8>& args) { print_cmd_help(out); return UNKNOWN_ERROR; } - auto receiver = mConfigManager->GetConfigReceiver(ConfigKey(uid, name)); + auto receiver = mConfigManager->GetConfigReceiver(ConfigKey(uid, StrToInt64(name))); sp<IStatsCompanionService> sc = getStatsCompanionService(); if (sc != nullptr) { sc->sendBroadcast(String16(receiver.first.c_str()), String16(receiver.second.c_str())); @@ -404,13 +404,13 @@ status_t StatsService::cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8 } // Add / update the config. - mConfigManager->UpdateConfig(ConfigKey(uid, name), config); + mConfigManager->UpdateConfig(ConfigKey(uid, StrToInt64(name)), config); } else { if (argCount == 2) { cmd_remove_all_configs(out); } else { // Remove the config. - mConfigManager->RemoveConfig(ConfigKey(uid, name)); + mConfigManager->RemoveConfig(ConfigKey(uid, StrToInt64(name))); } } @@ -459,7 +459,7 @@ status_t StatsService::cmd_dump_report(FILE* out, FILE* err, const Vector<String } if (good) { vector<uint8_t> data; - mProcessor->onDumpReport(ConfigKey(uid, name), &data); + mProcessor->onDumpReport(ConfigKey(uid, StrToInt64(name)), &data); // TODO: print the returned StatsLogReport to file instead of printing to logcat. if (proto) { for (size_t i = 0; i < data.size(); i ++) { @@ -699,12 +699,11 @@ void StatsService::OnLogEvent(const LogEvent& event) { mProcessor->OnLogEvent(event); } -Status StatsService::getData(const String16& key, vector<uint8_t>* output) { +Status StatsService::getData(int64_t key, vector<uint8_t>* output) { IPCThreadState* ipc = IPCThreadState::self(); VLOG("StatsService::getData with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid()); if (checkCallingPermission(String16(kPermissionDump))) { - string keyStr = string(String8(key).string()); - ConfigKey configKey(ipc->getCallingUid(), keyStr); + ConfigKey configKey(ipc->getCallingUid(), key); mProcessor->onDumpReport(configKey, output); return Status::ok(); } else { @@ -724,14 +723,13 @@ Status StatsService::getMetadata(vector<uint8_t>* output) { } } -Status StatsService::addConfiguration(const String16& key, +Status StatsService::addConfiguration(int64_t key, const vector <uint8_t>& config, const String16& package, const String16& cls, bool* success) { IPCThreadState* ipc = IPCThreadState::self(); if (checkCallingPermission(String16(kPermissionDump))) { - string keyString = string(String8(key).string()); - ConfigKey configKey(ipc->getCallingUid(), keyString); + ConfigKey configKey(ipc->getCallingUid(), key); StatsdConfig cfg; if (!cfg.ParseFromArray(&config[0], config.size())) { *success = false; @@ -748,11 +746,10 @@ Status StatsService::addConfiguration(const String16& key, } } -Status StatsService::removeConfiguration(const String16& key, bool* success) { +Status StatsService::removeConfiguration(int64_t key, bool* success) { IPCThreadState* ipc = IPCThreadState::self(); if (checkCallingPermission(String16(kPermissionDump))) { - string keyStr = string(String8(key).string()); - mConfigManager->RemoveConfig(ConfigKey(ipc->getCallingUid(), keyStr)); + mConfigManager->RemoveConfig(ConfigKey(ipc->getCallingUid(), key)); *success = true; return Status::ok(); } else { diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 08fcdac27539..c0424f39a1fd 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -77,25 +77,27 @@ public: /** * Binder call for clients to request data for this configuration key. */ - virtual Status getData(const String16& key, vector<uint8_t>* output) override; + virtual Status getData(int64_t key, vector<uint8_t>* output) override; + /** * Binder call for clients to get metadata across all configs in statsd. */ virtual Status getMetadata(vector<uint8_t>* output) override; + /** * Binder call to let clients send a configuration and indicate they're interested when they * should requestData for this configuration. */ - virtual Status addConfiguration(const String16& key, const vector <uint8_t>& config, - const String16& package, const String16& cls, bool* success) + virtual Status addConfiguration(int64_t key, const vector <uint8_t>& config, + const String16& package, const String16& cls, bool* success) override; /** * Binder call to allow clients to remove the specified configuration. */ - virtual Status removeConfiguration(const String16& key, bool* success) override; + virtual Status removeConfiguration(int64_t key, bool* success) override; // TODO: public for testing since statsd doesn't run when system starts. Change to private // later. diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp index f8a941362648..05c68e1fa471 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp @@ -34,11 +34,11 @@ namespace statsd { AnomalyTracker::AnomalyTracker(const Alert& alert, const ConfigKey& configKey) : mAlert(alert), mConfigKey(configKey), - mNumOfPastBuckets(mAlert.number_of_buckets() - 1) { + mNumOfPastBuckets(mAlert.num_buckets() - 1) { VLOG("AnomalyTracker() called"); - if (mAlert.number_of_buckets() <= 0) { + if (mAlert.num_buckets() <= 0) { ALOGE("Cannot create AnomalyTracker with %lld buckets", - (long long)mAlert.number_of_buckets()); + (long long)mAlert.num_buckets()); return; } if (!mAlert.has_trigger_if_sum_gt()) { @@ -205,23 +205,21 @@ void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs) { // TODO: If we had access to the bucket_size_millis, consider calling resetStorage() // if (mAlert.refractory_period_secs() > mNumOfPastBuckets * bucketSizeNs) { resetStorage(); } - if (mAlert.has_incidentd_details()) { - if (mAlert.has_name()) { - ALOGI("An anomaly (%s) has occurred! Informing incidentd.", - mAlert.name().c_str()); + if (!mSubscriptions.empty()) { + if (mAlert.has_id()) { + ALOGI("An anomaly (%llu) has occurred! Informing subscribers.",mAlert.id()); + informSubscribers(); } else { - // TODO: Can construct a name based on the criteria (and/or relay the criteria). - ALOGI("An anomaly (nameless) has occurred! Informing incidentd."); + ALOGI("An anomaly (with no id) has occurred! Not informing any subscribers."); } - informIncidentd(); } else { - ALOGI("An anomaly has occurred! (But informing incidentd not requested.)"); + ALOGI("An anomaly has occurred! (But no subscriber for that alert.)"); } - StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.name()); + StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id()); android::util::stats_write(android::util::ANOMALY_DETECTED, mConfigKey.GetUid(), - mConfigKey.GetName().c_str(), mAlert.name().c_str()); + mConfigKey.GetId(), mAlert.id()); } void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestampNs, @@ -246,27 +244,46 @@ bool AnomalyTracker::isInRefractoryPeriod(const uint64_t& timestampNs) const { timestampNs - mLastAnomalyTimestampNs <= mAlert.refractory_period_secs() * NS_PER_SEC; } -void AnomalyTracker::informIncidentd() { - VLOG("informIncidentd called."); - if (!mAlert.has_incidentd_details()) { - ALOGE("Attempted to call incidentd without any incidentd_details."); - return; - } - sp<IIncidentManager> service = interface_cast<IIncidentManager>( - defaultServiceManager()->getService(android::String16("incident"))); - if (service == NULL) { - ALOGW("Couldn't get the incident service."); +void AnomalyTracker::informSubscribers() { + VLOG("informSubscribers called."); + if (mSubscriptions.empty()) { + ALOGE("Attempt to call with no subscribers."); return; } - IncidentReportArgs incidentReport; - const Alert::IncidentdDetails& details = mAlert.incidentd_details(); - for (int i = 0; i < details.section_size(); i++) { - incidentReport.addSection(details.section(i)); + std::set<int> incidentdSections; + for (const Subscription& subscription : mSubscriptions) { + switch (subscription.subscriber_information_case()) { + case Subscription::SubscriberInformationCase::kIncidentdDetails: + for (int i = 0; i < subscription.incidentd_details().section_size(); i++) { + incidentdSections.insert(subscription.incidentd_details().section(i)); + } + break; + case Subscription::SubscriberInformationCase::kPerfettoDetails: + ALOGW("Perfetto reports not implemented."); + break; + default: + break; + } + } + if (!incidentdSections.empty()) { + sp<IIncidentManager> service = interface_cast<IIncidentManager>( + defaultServiceManager()->getService(android::String16("incident"))); + if (service != NULL) { + IncidentReportArgs incidentReport; + for (const auto section : incidentdSections) { + incidentReport.addSection(section); + } + int64_t alertId = mAlert.id(); + std::vector<uint8_t> header; + uint8_t* src = static_cast<uint8_t*>(static_cast<void*>(&alertId)); + header.insert(header.end(), src, src + sizeof(int64_t)); + incidentReport.addHeader(header); + service->reportIncident(incidentReport); + } else { + ALOGW("Couldn't get the incident service."); + } } - // TODO: Pass in mAlert.name() into the addHeader? - - service->reportIncident(incidentReport); } } // namespace statsd diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h index 48f02036517f..2d5ab867da00 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.h +++ b/cmds/statsd/src/anomaly/AnomalyTracker.h @@ -40,6 +40,11 @@ public: virtual ~AnomalyTracker(); + // Add subscriptions that depend on this alert. + void addSubscription(const Subscription& subscription) { + mSubscriptions.push_back(subscription); + } + // Adds a bucket. // Bucket index starts from 0. void addPastBucket(std::shared_ptr<DimToValMap> bucketValues, const int64_t& bucketNum); @@ -97,6 +102,9 @@ protected: // statsd_config.proto Alert message that defines this tracker. const Alert mAlert; + // The subscriptions that depend on this alert. + std::vector<Subscription> mSubscriptions; + // A reference to the Alert's config key. const ConfigKey& mConfigKey; @@ -104,7 +112,7 @@ protected: // for the anomaly detection (since the current bucket is not in the past). int mNumOfPastBuckets; - // The exisiting bucket list. + // The existing bucket list. std::vector<shared_ptr<DimToValMap>> mPastBuckets; // Sum over all existing buckets cached in mPastBuckets. @@ -133,8 +141,8 @@ protected: // Resets all bucket data. For use when all the data gets stale. virtual void resetStorage(); - // Informs the incident service that an anomaly has occurred. - void informIncidentd(); + // Informs the subscribers that an anomaly has occurred. + void informSubscribers(); FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets); FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets); diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 1ee86f05ca29..221a55438f73 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -845,11 +845,11 @@ message AnomalyDetected { // Uid that owns the config whose anomaly detection alert fired. optional int32 config_uid = 1; - // Name of the config whose anomaly detection alert fired. - optional string config_name = 2; + // Id of the config whose anomaly detection alert fired. + optional int64 config_id = 2; - // Name of the alert (i.e. name of the anomaly that was detected). - optional string alert_name = 3; + // Id of the alert (i.e. name of the anomaly that was detected). + optional int64 alert_id = 3; } /** diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp index 52b83d875caa..afa26f6da08a 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp +++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp @@ -30,20 +30,20 @@ using std::unique_ptr; using std::unordered_map; using std::vector; -CombinationConditionTracker::CombinationConditionTracker(const string& name, const int index) - : ConditionTracker(name, index) { - VLOG("creating CombinationConditionTracker %s", mName.c_str()); +CombinationConditionTracker::CombinationConditionTracker(const int64_t& id, const int index) + : ConditionTracker(id, index) { + VLOG("creating CombinationConditionTracker %lld", (long long)mConditionId); } CombinationConditionTracker::~CombinationConditionTracker() { - VLOG("~CombinationConditionTracker() %s", mName.c_str()); + VLOG("~CombinationConditionTracker() %lld", (long long)mConditionId); } bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConfig, const vector<sp<ConditionTracker>>& allConditionTrackers, - const unordered_map<string, int>& conditionNameIndexMap, + const unordered_map<int64_t, int>& conditionIdIndexMap, vector<bool>& stack) { - VLOG("Combination predicate init() %s", mName.c_str()); + VLOG("Combination predicate init() %lld", (long long)mConditionId); if (mInitialized) { return true; } @@ -62,11 +62,11 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf return false; } - for (string child : combinationCondition.predicate()) { - auto it = conditionNameIndexMap.find(child); + for (auto child : combinationCondition.predicate()) { + auto it = conditionIdIndexMap.find(child); - if (it == conditionNameIndexMap.end()) { - ALOGW("Predicate %s not found in the config", child.c_str()); + if (it == conditionIdIndexMap.end()) { + ALOGW("Predicate %lld not found in the config", (long long)child); return false; } @@ -79,13 +79,13 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf } bool initChildSucceeded = childTracker->init(allConditionConfig, allConditionTrackers, - conditionNameIndexMap, stack); + conditionIdIndexMap, stack); if (!initChildSucceeded) { - ALOGW("Child initialization failed %s ", child.c_str()); + ALOGW("Child initialization failed %lld ", (long long)child); return false; } else { - ALOGW("Child initialization success %s ", child.c_str()); + ALOGW("Child initialization success %lld ", (long long)child); } mChildren.push_back(childIndex); @@ -154,8 +154,8 @@ void CombinationConditionTracker::evaluateCondition( } } nonSlicedConditionCache[mIndex] = ConditionState::kUnknown; - ALOGD("CombinationPredicate %s sliced may changed? %d", mName.c_str(), - conditionChangedCache[mIndex] == true); + ALOGD("CombinationPredicate %lld sliced may changed? %d", (long long)mConditionId, + conditionChangedCache[mIndex] == true); } } diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h index 894283323c78..dfd3837f31f4 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.h +++ b/cmds/statsd/src/condition/CombinationConditionTracker.h @@ -26,13 +26,13 @@ namespace statsd { class CombinationConditionTracker : public virtual ConditionTracker { public: - CombinationConditionTracker(const std::string& name, const int index); + CombinationConditionTracker(const int64_t& id, const int index); ~CombinationConditionTracker(); bool init(const std::vector<Predicate>& allConditionConfig, const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<std::string, int>& conditionNameIndexMap, + const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack) override; void evaluateCondition(const LogEvent& event, diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h index 1154b6fed177..773860f429b1 100644 --- a/cmds/statsd/src/condition/ConditionTracker.h +++ b/cmds/statsd/src/condition/ConditionTracker.h @@ -32,8 +32,8 @@ namespace statsd { class ConditionTracker : public virtual RefBase { public: - ConditionTracker(const std::string& name, const int index) - : mName(name), + ConditionTracker(const int64_t& id, const int index) + : mConditionId(id), mIndex(index), mInitialized(false), mTrackerIndex(), @@ -42,7 +42,7 @@ public: virtual ~ConditionTracker(){}; - inline const string& getName() { return mName; } + inline const int64_t& getId() { return mConditionId; } // Initialize this ConditionTracker. This initialization is done recursively (DFS). It can also // be done in the constructor, but we do it separately because (1) easy to return a bool to @@ -50,11 +50,11 @@ public: // allConditionConfig: the list of all Predicate config from statsd_config. // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also // need to call init() on children conditions) - // conditionNameIndexMap: the mapping from condition name to its index. + // conditionIdIndexMap: the mapping from condition id to its index. // stack: a bit map to keep track which nodes have been visited on the stack in the recursion. virtual bool init(const std::vector<Predicate>& allConditionConfig, const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<std::string, int>& conditionNameIndexMap, + const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack) = 0; // evaluate current condition given the new event. @@ -99,9 +99,7 @@ public: } protected: - // We don't really need the string name, but having a name here makes log messages - // easy to debug. - const std::string mName; + const int64_t mConditionId; // the index of this condition in the manager's condition list. const int mIndex; diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp index 1803cbb6f500..25257213a5d0 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp +++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp @@ -33,17 +33,17 @@ using std::unordered_map; using std::vector; SimpleConditionTracker::SimpleConditionTracker( - const ConfigKey& key, const string& name, const int index, + const ConfigKey& key, const int64_t& id, const int index, const SimplePredicate& simplePredicate, - const unordered_map<string, int>& trackerNameIndexMap) - : ConditionTracker(name, index), mConfigKey(key) { - VLOG("creating SimpleConditionTracker %s", mName.c_str()); + const unordered_map<int64_t, int>& trackerNameIndexMap) + : ConditionTracker(id, index), mConfigKey(key) { + VLOG("creating SimpleConditionTracker %lld", (long long)mConditionId); mCountNesting = simplePredicate.count_nesting(); if (simplePredicate.has_start()) { auto pair = trackerNameIndexMap.find(simplePredicate.start()); if (pair == trackerNameIndexMap.end()) { - ALOGW("Start matcher %s not found in the config", simplePredicate.start().c_str()); + ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start()); return; } mStartLogMatcherIndex = pair->second; @@ -55,7 +55,7 @@ SimpleConditionTracker::SimpleConditionTracker( if (simplePredicate.has_stop()) { auto pair = trackerNameIndexMap.find(simplePredicate.stop()); if (pair == trackerNameIndexMap.end()) { - ALOGW("Stop matcher %s not found in the config", simplePredicate.stop().c_str()); + ALOGW("Stop matcher %lld not found in the config", (long long)simplePredicate.stop()); return; } mStopLogMatcherIndex = pair->second; @@ -67,7 +67,7 @@ SimpleConditionTracker::SimpleConditionTracker( if (simplePredicate.has_stop_all()) { auto pair = trackerNameIndexMap.find(simplePredicate.stop_all()); if (pair == trackerNameIndexMap.end()) { - ALOGW("Stop all matcher %s not found in the config", simplePredicate.stop().c_str()); + ALOGW("Stop all matcher %lld found in the config", (long long)simplePredicate.stop_all()); return; } mStopAllLogMatcherIndex = pair->second; @@ -99,15 +99,15 @@ SimpleConditionTracker::~SimpleConditionTracker() { bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig, const vector<sp<ConditionTracker>>& allConditionTrackers, - const unordered_map<string, int>& conditionNameIndexMap, + const unordered_map<int64_t, int>& conditionIdIndexMap, vector<bool>& stack) { // SimpleConditionTracker does not have dependency on other conditions, thus we just return // if the initialization was successful. return mInitialized; } -void print(map<HashableDimensionKey, int>& conditions, const string& name) { - VLOG("%s DUMP:", name.c_str()); +void print(map<HashableDimensionKey, int>& conditions, const int64_t& id) { + VLOG("%lld DUMP:", (long long)id); for (const auto& pair : conditions) { VLOG("\t%s : %d", pair.first.c_str(), pair.second); } @@ -135,10 +135,11 @@ bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) { // 1. Report the tuple count if the tuple count > soft limit if (mSlicedConditionState.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { size_t newTupleCount = mSlicedConditionState.size() + 1; - StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mName, newTupleCount); + StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mConditionId, newTupleCount); // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("Predicate %s dropping data for dimension key %s", mName.c_str(), newKey.c_str()); + ALOGE("Predicate %lld dropping data for dimension key %s", + (long long)mConditionId, newKey.c_str()); return true; } } @@ -222,13 +223,13 @@ void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& ou // dump all dimensions for debugging if (DEBUG) { - print(mSlicedConditionState, mName); + print(mSlicedConditionState, mConditionId); } conditionChangedCache[mIndex] = changed; conditionCache[mIndex] = newCondition; - VLOG("SimplePredicate %s nonSlicedChange? %d", mName.c_str(), + VLOG("SimplePredicate %lld nonSlicedChange? %d", (long long)mConditionId, conditionChangedCache[mIndex] == true); } @@ -239,7 +240,8 @@ void SimpleConditionTracker::evaluateCondition(const LogEvent& event, vector<bool>& conditionChangedCache) { if (conditionCache[mIndex] != ConditionState::kNotEvaluated) { // it has been evaluated. - VLOG("Yes, already evaluated, %s %d", mName.c_str(), conditionCache[mIndex]); + VLOG("Yes, already evaluated, %lld %d", + (long long)mConditionId, conditionCache[mIndex]); return; } @@ -320,11 +322,11 @@ void SimpleConditionTracker::isConditionMet( const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions, vector<ConditionState>& conditionCache) const { - const auto pair = conditionParameters.find(mName); + const auto pair = conditionParameters.find(mConditionId); if (pair == conditionParameters.end() && mOutputDimensions.child_size() > 0) { - ALOGE("Predicate %s output has dimension, but it's not specified in the query!", - mName.c_str()); + ALOGE("Predicate %lld output has dimension, but it's not specified in the query!", + (long long)mConditionId); conditionCache[mIndex] = mInitialValue; return; } @@ -343,7 +345,7 @@ void SimpleConditionTracker::isConditionMet( } } conditionCache[mIndex] = conditionState; - VLOG("Predicate %s return %d", mName.c_str(), conditionCache[mIndex]); + VLOG("Predicate %lld return %d", (long long)mConditionId, conditionCache[mIndex]); } } // namespace statsd diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h index 50486358db66..815b445a8c5b 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.h +++ b/cmds/statsd/src/condition/SimpleConditionTracker.h @@ -29,15 +29,15 @@ namespace statsd { class SimpleConditionTracker : public virtual ConditionTracker { public: - SimpleConditionTracker(const ConfigKey& key, const std::string& name, const int index, + SimpleConditionTracker(const ConfigKey& key, const int64_t& id, const int index, const SimplePredicate& simplePredicate, - const std::unordered_map<std::string, int>& trackerNameIndexMap); + const std::unordered_map<int64_t, int>& trackerNameIndexMap); ~SimpleConditionTracker(); bool init(const std::vector<Predicate>& allConditionConfig, const std::vector<sp<ConditionTracker>>& allConditionTrackers, - const std::unordered_map<std::string, int>& conditionNameIndexMap, + const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack) override; void evaluateCondition(const LogEvent& event, diff --git a/cmds/statsd/src/config/ConfigKey.cpp b/cmds/statsd/src/config/ConfigKey.cpp index a365dc0b9189..d791f8632f3c 100644 --- a/cmds/statsd/src/config/ConfigKey.cpp +++ b/cmds/statsd/src/config/ConfigKey.cpp @@ -27,10 +27,10 @@ using std::ostringstream; ConfigKey::ConfigKey() { } -ConfigKey::ConfigKey(const ConfigKey& that) : mName(that.mName), mUid(that.mUid) { +ConfigKey::ConfigKey(const ConfigKey& that) : mId(that.mId), mUid(that.mUid) { } -ConfigKey::ConfigKey(int uid, const string& name) : mName(name), mUid(uid) { +ConfigKey::ConfigKey(int uid, const int64_t& id) : mId(id), mUid(uid) { } ConfigKey::~ConfigKey() { @@ -38,10 +38,21 @@ ConfigKey::~ConfigKey() { string ConfigKey::ToString() const { ostringstream out; - out << '(' << mUid << ',' << mName << ')'; + out << '(' << mUid << ',' << mId << ')'; return out.str(); } + +int64_t StrToInt64(const string& str) { + char* endp; + int64_t value; + value = strtoll(str.c_str(), &endp, 0); + if (endp == str.c_str() || *endp != '\0') { + value = 0; + } + return value; +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/config/ConfigKey.h b/cmds/statsd/src/config/ConfigKey.h index 3489c43c8052..3ad0eed3f2b9 100644 --- a/cmds/statsd/src/config/ConfigKey.h +++ b/cmds/statsd/src/config/ConfigKey.h @@ -37,14 +37,14 @@ class ConfigKey { public: ConfigKey(); explicit ConfigKey(const ConfigKey& that); - ConfigKey(int uid, const string& name); + ConfigKey(int uid, const int64_t& id); ~ConfigKey(); inline int GetUid() const { return mUid; } - inline const string& GetName() const { - return mName; + inline const int64_t& GetId() const { + return mId; } inline bool operator<(const ConfigKey& that) const { @@ -54,17 +54,17 @@ public: if (mUid > that.mUid) { return false; } - return mName < that.mName; + return mId < that.mId; }; inline bool operator==(const ConfigKey& that) const { - return mUid == that.mUid && mName == that.mName; + return mUid == that.mUid && mId == that.mId; }; string ToString() const; private: - string mName; + int64_t mId; int mUid; }; @@ -72,6 +72,8 @@ inline ostream& operator<<(ostream& os, const ConfigKey& config) { return os << config.ToString(); } +int64_t StrToInt64(const string& str); + } // namespace statsd } // namespace os } // namespace android @@ -87,7 +89,7 @@ using android::os::statsd::ConfigKey; template <> struct hash<ConfigKey> { std::size_t operator()(const ConfigKey& key) const { - return (7 * key.GetUid()) ^ ((hash<string>()(key.GetName()))); + return (7 * key.GetUid()) ^ ((hash<long long>()(key.GetId()))); } }; diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp index addc1111a93d..c41301aecb98 100644 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ b/cmds/statsd/src/config/ConfigManager.cpp @@ -103,7 +103,7 @@ void ConfigManager::RemoveConfig(const ConfigKey& key) { } void ConfigManager::remove_saved_configs(const ConfigKey& key) { - string prefix = StringPrintf("%d-%s", key.GetUid(), key.GetName().c_str()); + string prefix = StringPrintf("%d-%lld", key.GetUid(), (long long)key.GetId()); StorageManager::deletePrefixedFiles(STATS_SERVICE_DIR, prefix.c_str()); } @@ -173,7 +173,7 @@ void ConfigManager::Dump(FILE* out) { fprintf(out, "CONFIGURATIONS (%d)\n", (int)mConfigs.size()); fprintf(out, " uid name\n"); for (const auto& key : mConfigs) { - fprintf(out, " %6d %s\n", key.GetUid(), key.GetName().c_str()); + fprintf(out, " %6d %lld\n", key.GetUid(), (long long)key.GetId()); auto receiverIt = mConfigReceivers.find(key); if (receiverIt != mConfigReceivers.end()) { fprintf(out, " -> received by %s, %s\n", receiverIt->second.first.c_str(), @@ -189,8 +189,8 @@ void ConfigManager::update_saved_configs(const ConfigKey& key, const StatsdConfi remove_saved_configs(key); // Then we save the latest config. - string file_name = StringPrintf("%s/%d-%s-%ld", STATS_SERVICE_DIR, key.GetUid(), - key.GetName().c_str(), time(nullptr)); + string file_name = StringPrintf("%s/%d-%lld-%ld", STATS_SERVICE_DIR, key.GetUid(), + (long long)key.GetId(), time(nullptr)); const int numBytes = config.ByteSize(); vector<uint8_t> buffer(numBytes); config.SerializeToArray(&buffer[0], numBytes); @@ -200,7 +200,7 @@ void ConfigManager::update_saved_configs(const ConfigKey& key, const StatsdConfi StatsdConfig build_fake_config() { // HACK: Hard code a test metric for counting screen on events... StatsdConfig config; - config.set_name("CONFIG_12345"); + config.set_id(12345); int WAKE_LOCK_TAG_ID = 1111; // put a fake id here to make testing easier. int WAKE_LOCK_UID_KEY_ID = 1; @@ -232,14 +232,14 @@ StatsdConfig build_fake_config() { // Count Screen ON events. CountMetric* metric = config.add_count_metric(); - metric->set_name("METRIC_1"); - metric->set_what("SCREEN_TURNED_ON"); + metric->set_id(1); // METRIC_1 + metric->set_what(102); // "SCREEN_TURNED_ON" metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); // Anomaly threshold for screen-on count. // TODO(b/70627390): Uncomment once the bug is fixed. /*Alert* alert = config.add_alert(); - alert->set_name("ALERT_1"); + alert->set_id("ALERT_1"); alert->set_metric_name("METRIC_1"); alert->set_number_of_buckets(6); alert->set_trigger_if_sum_gt(10); @@ -248,16 +248,16 @@ StatsdConfig build_fake_config() { details->add_section(12); details->add_section(13);*/ - AllowedLogSource* logSource = config.mutable_log_source(); - logSource->add_uid(1000); - logSource->add_uid(0); - logSource->add_package("com.android.statsd.dogfood"); - logSource->add_package("com.android.bluetooth"); + config.add_allowed_log_source("AID_ROOT"); + config.add_allowed_log_source("AID_SYSTEM"); + config.add_allowed_log_source("AID_BLUETOOTH"); + config.add_allowed_log_source("com.android.statsd.dogfood"); + config.add_allowed_log_source("com.android.systemui"); // Count process state changes, slice by uid. metric = config.add_count_metric(); - metric->set_name("METRIC_2"); - metric->set_what("PROCESS_STATE_CHANGE"); + metric->set_id(2); // "METRIC_2" + metric->set_what(104); metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); FieldMatcher* dimensions = metric->mutable_dimensions(); dimensions->set_field(UID_PROCESS_STATE_TAG_ID); @@ -267,7 +267,7 @@ StatsdConfig build_fake_config() { // TODO(b/70627390): Uncomment once the bug is fixed. /* alert = config.add_alert(); - alert->set_name("ALERT_2"); + alert->set_id("ALERT_2"); alert->set_metric_name("METRIC_2"); alert->set_number_of_buckets(4); alert->set_trigger_if_sum_gt(30); @@ -278,28 +278,28 @@ StatsdConfig build_fake_config() { // Count process state changes, slice by uid, while SCREEN_IS_OFF metric = config.add_count_metric(); - metric->set_name("METRIC_3"); - metric->set_what("PROCESS_STATE_CHANGE"); + metric->set_id(3); + metric->set_what(104); metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); dimensions = metric->mutable_dimensions(); dimensions->set_field(UID_PROCESS_STATE_TAG_ID); dimensions->add_child()->set_field(UID_PROCESS_STATE_UID_KEY); - metric->set_condition("SCREEN_IS_OFF"); + metric->set_condition(202); // Count wake lock, slice by uid, while SCREEN_IS_ON and app in background metric = config.add_count_metric(); - metric->set_name("METRIC_4"); - metric->set_what("APP_GET_WL"); + metric->set_id(4); + metric->set_what(107); metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); dimensions = metric->mutable_dimensions(); dimensions->set_field(WAKE_LOCK_TAG_ID); dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); - metric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON"); + metric->set_condition(204); MetricConditionLink* link = metric->add_links(); - link->set_condition("APP_IS_BACKGROUND"); + link->set_condition(203); link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID); link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID); @@ -307,16 +307,16 @@ StatsdConfig build_fake_config() { // Duration of an app holding any wl, while screen on and app in background, slice by uid DurationMetric* durationMetric = config.add_duration_metric(); - durationMetric->set_name("METRIC_5"); + durationMetric->set_id(5); durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM); dimensions = durationMetric->mutable_dimensions(); dimensions->set_field(WAKE_LOCK_TAG_ID); dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); - durationMetric->set_what("WL_HELD_PER_APP_PER_NAME"); - durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON"); + durationMetric->set_what(205); + durationMetric->set_condition(204); link = durationMetric->add_links(); - link->set_condition("APP_IS_BACKGROUND"); + link->set_condition(203); link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID); link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID); @@ -324,16 +324,16 @@ StatsdConfig build_fake_config() { // max Duration of an app holding any wl, while screen on and app in background, slice by uid durationMetric = config.add_duration_metric(); - durationMetric->set_name("METRIC_6"); + durationMetric->set_id(6); durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); durationMetric->set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE); dimensions = durationMetric->mutable_dimensions(); dimensions->set_field(WAKE_LOCK_TAG_ID); dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); - durationMetric->set_what("WL_HELD_PER_APP_PER_NAME"); - durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON"); + durationMetric->set_what(205); + durationMetric->set_condition(204); link = durationMetric->add_links(); - link->set_condition("APP_IS_BACKGROUND"); + link->set_condition(203); link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID); link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID); @@ -341,13 +341,13 @@ StatsdConfig build_fake_config() { // Duration of an app holding any wl, while screen on and app in background durationMetric = config.add_duration_metric(); - durationMetric->set_name("METRIC_7"); + durationMetric->set_id(7); durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); durationMetric->set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE); - durationMetric->set_what("WL_HELD_PER_APP_PER_NAME"); - durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON"); + durationMetric->set_what(205); + durationMetric->set_condition(204); link = durationMetric->add_links(); - link->set_condition("APP_IS_BACKGROUND"); + link->set_condition(203); link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID); link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID); @@ -356,17 +356,17 @@ StatsdConfig build_fake_config() { // Duration of screen on time. durationMetric = config.add_duration_metric(); - durationMetric->set_name("METRIC_8"); + durationMetric->set_id(8); durationMetric->mutable_bucket()->set_bucket_size_millis(10 * 1000L); durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM); - durationMetric->set_what("SCREEN_IS_ON"); + durationMetric->set_what(201); // Anomaly threshold for background count. // TODO(b/70627390): Uncomment once the bug is fixed. /* alert = config.add_alert(); - alert->set_name("ALERT_8"); - alert->set_metric_name("METRIC_8"); + alert->set_id(308); + alert->set_metric_id(8); alert->set_number_of_buckets(4); alert->set_trigger_if_sum_gt(2000000000); // 2 seconds alert->set_refractory_period_secs(120); @@ -375,10 +375,11 @@ StatsdConfig build_fake_config() { // Value metric to count KERNEL_WAKELOCK when screen turned on ValueMetric* valueMetric = config.add_value_metric(); - valueMetric->set_name("METRIC_6"); - valueMetric->set_what("KERNEL_WAKELOCK"); - valueMetric->set_value_field(KERNEL_WAKELOCK_COUNT_KEY); - valueMetric->set_condition("SCREEN_IS_ON"); + valueMetric->set_id(11); + valueMetric->set_what(109); + valueMetric->mutable_value_field()->set_field(KERNEL_WAKELOCK_TAG_ID); + valueMetric->mutable_value_field()->add_child()->set_field(KERNEL_WAKELOCK_COUNT_KEY); + valueMetric->set_condition(201); dimensions = valueMetric->mutable_dimensions(); dimensions->set_field(KERNEL_WAKELOCK_TAG_ID); dimensions->add_child()->set_field(KERNEL_WAKELOCK_NAME_KEY); @@ -387,13 +388,13 @@ StatsdConfig build_fake_config() { // Add an EventMetric to log process state change events. EventMetric* eventMetric = config.add_event_metric(); - eventMetric->set_name("METRIC_9"); - eventMetric->set_what("SCREEN_TURNED_ON"); + eventMetric->set_id(9); + eventMetric->set_what(102); // "SCREEN_TURNED_ON" // Add an GaugeMetric. GaugeMetric* gaugeMetric = config.add_gauge_metric(); - gaugeMetric->set_name("METRIC_10"); - gaugeMetric->set_what("DEVICE_TEMPERATURE"); + gaugeMetric->set_id(10); + gaugeMetric->set_what(101); auto gaugeFieldMatcher = gaugeMetric->mutable_gauge_fields_filter()->mutable_fields(); gaugeFieldMatcher->set_field(DEVICE_TEMPERATURE_TAG_ID); gaugeFieldMatcher->add_child()->set_field(DEVICE_TEMPERATURE_KEY); @@ -401,12 +402,12 @@ StatsdConfig build_fake_config() { // Event matchers. AtomMatcher* temperatureAtomMatcher = config.add_atom_matcher(); - temperatureAtomMatcher->set_name("DEVICE_TEMPERATURE"); + temperatureAtomMatcher->set_id(101); // "DEVICE_TEMPERATURE" temperatureAtomMatcher->mutable_simple_atom_matcher()->set_atom_id( DEVICE_TEMPERATURE_TAG_ID); AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_name("SCREEN_TURNED_ON"); + eventMatcher->set_id(102); // "SCREEN_TURNED_ON" SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); simpleAtomMatcher->set_atom_id(SCREEN_EVENT_TAG_ID); FieldValueMatcher* fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher(); @@ -414,7 +415,7 @@ StatsdConfig build_fake_config() { fieldValueMatcher->set_eq_int(SCREEN_EVENT_ON_VALUE); eventMatcher = config.add_atom_matcher(); - eventMatcher->set_name("SCREEN_TURNED_OFF"); + eventMatcher->set_id(103); // "SCREEN_TURNED_OFF" simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); simpleAtomMatcher->set_atom_id(SCREEN_EVENT_TAG_ID); fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher(); @@ -422,12 +423,12 @@ StatsdConfig build_fake_config() { fieldValueMatcher->set_eq_int(SCREEN_EVENT_OFF_VALUE); eventMatcher = config.add_atom_matcher(); - eventMatcher->set_name("PROCESS_STATE_CHANGE"); + eventMatcher->set_id(104); // "PROCESS_STATE_CHANGE" simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); simpleAtomMatcher->set_atom_id(UID_PROCESS_STATE_TAG_ID); eventMatcher = config.add_atom_matcher(); - eventMatcher->set_name("APP_GOES_BACKGROUND"); + eventMatcher->set_id(105); // "APP_GOES_BACKGROUND" simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); simpleAtomMatcher->set_atom_id(APP_USAGE_TAG_ID); fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher(); @@ -435,7 +436,7 @@ StatsdConfig build_fake_config() { fieldValueMatcher->set_eq_int(APP_USAGE_BACKGROUND); eventMatcher = config.add_atom_matcher(); - eventMatcher->set_name("APP_GOES_FOREGROUND"); + eventMatcher->set_id(106); // "APP_GOES_FOREGROUND" simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); simpleAtomMatcher->set_atom_id(APP_USAGE_TAG_ID); fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher(); @@ -443,7 +444,7 @@ StatsdConfig build_fake_config() { fieldValueMatcher->set_eq_int(APP_USAGE_FOREGROUND); eventMatcher = config.add_atom_matcher(); - eventMatcher->set_name("APP_GET_WL"); + eventMatcher->set_id(107); // "APP_GET_WL" simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); simpleAtomMatcher->set_atom_id(WAKE_LOCK_TAG_ID); fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher(); @@ -451,7 +452,7 @@ StatsdConfig build_fake_config() { fieldValueMatcher->set_eq_int(WAKE_LOCK_ACQUIRE_VALUE); eventMatcher = config.add_atom_matcher(); - eventMatcher->set_name("APP_RELEASE_WL"); + eventMatcher->set_id(108); //"APP_RELEASE_WL" simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); simpleAtomMatcher->set_atom_id(WAKE_LOCK_TAG_ID); fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher(); @@ -460,47 +461,47 @@ StatsdConfig build_fake_config() { // pulled events eventMatcher = config.add_atom_matcher(); - eventMatcher->set_name("KERNEL_WAKELOCK"); + eventMatcher->set_id(109); // "KERNEL_WAKELOCK" simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); simpleAtomMatcher->set_atom_id(KERNEL_WAKELOCK_TAG_ID); // Predicates............. Predicate* predicate = config.add_predicate(); - predicate->set_name("SCREEN_IS_ON"); + predicate->set_id(201); // "SCREEN_IS_ON" SimplePredicate* simplePredicate = predicate->mutable_simple_predicate(); - simplePredicate->set_start("SCREEN_TURNED_ON"); - simplePredicate->set_stop("SCREEN_TURNED_OFF"); + simplePredicate->set_start(102); // "SCREEN_TURNED_ON" + simplePredicate->set_stop(103); simplePredicate->set_count_nesting(false); predicate = config.add_predicate(); - predicate->set_name("SCREEN_IS_OFF"); + predicate->set_id(202); // "SCREEN_IS_OFF" simplePredicate = predicate->mutable_simple_predicate(); - simplePredicate->set_start("SCREEN_TURNED_OFF"); - simplePredicate->set_stop("SCREEN_TURNED_ON"); + simplePredicate->set_start(103); + simplePredicate->set_stop(102); // "SCREEN_TURNED_ON" simplePredicate->set_count_nesting(false); predicate = config.add_predicate(); - predicate->set_name("APP_IS_BACKGROUND"); + predicate->set_id(203); // "APP_IS_BACKGROUND" simplePredicate = predicate->mutable_simple_predicate(); - simplePredicate->set_start("APP_GOES_BACKGROUND"); - simplePredicate->set_stop("APP_GOES_FOREGROUND"); + simplePredicate->set_start(105); + simplePredicate->set_stop(106); FieldMatcher* predicate_dimension1 = simplePredicate->mutable_dimensions(); predicate_dimension1->set_field(APP_USAGE_TAG_ID); predicate_dimension1->add_child()->set_field(APP_USAGE_UID_KEY_ID); simplePredicate->set_count_nesting(false); predicate = config.add_predicate(); - predicate->set_name("APP_IS_BACKGROUND_AND_SCREEN_ON"); + predicate->set_id(204); // "APP_IS_BACKGROUND_AND_SCREEN_ON" Predicate_Combination* combination_predicate = predicate->mutable_combination(); combination_predicate->set_operation(LogicalOperation::AND); - combination_predicate->add_predicate("APP_IS_BACKGROUND"); - combination_predicate->add_predicate("SCREEN_IS_ON"); + combination_predicate->add_predicate(203); + combination_predicate->add_predicate(201); predicate = config.add_predicate(); - predicate->set_name("WL_HELD_PER_APP_PER_NAME"); + predicate->set_id(205); // "WL_HELD_PER_APP_PER_NAME" simplePredicate = predicate->mutable_simple_predicate(); - simplePredicate->set_start("APP_GET_WL"); - simplePredicate->set_stop("APP_RELEASE_WL"); + simplePredicate->set_start(107); + simplePredicate->set_stop(108); FieldMatcher* predicate_dimension = simplePredicate->mutable_dimensions(); predicate_dimension1->set_field(WAKE_LOCK_TAG_ID); predicate_dimension->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); @@ -508,10 +509,10 @@ StatsdConfig build_fake_config() { simplePredicate->set_count_nesting(true); predicate = config.add_predicate(); - predicate->set_name("WL_HELD_PER_APP"); + predicate->set_id(206); // "WL_HELD_PER_APP" simplePredicate = predicate->mutable_simple_predicate(); - simplePredicate->set_start("APP_GET_WL"); - simplePredicate->set_stop("APP_RELEASE_WL"); + simplePredicate->set_start(107); + simplePredicate->set_stop(108); simplePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE); predicate_dimension = simplePredicate->mutable_dimensions(); predicate_dimension->set_field(WAKE_LOCK_TAG_ID); diff --git a/cmds/statsd/src/dimension.cpp b/cmds/statsd/src/dimension.cpp index 886a33bf540f..8c0ae978fcb8 100644 --- a/cmds/statsd/src/dimension.cpp +++ b/cmds/statsd/src/dimension.cpp @@ -351,6 +351,23 @@ bool IsSubDimension(const DimensionsValue& dimension, const DimensionsValue& sub } } +long getLongFromDimenValue(const DimensionsValue& dimensionValue) { + switch (dimensionValue.value_case()) { + case DimensionsValue::ValueCase::kValueInt: + return dimensionValue.value_int(); + case DimensionsValue::ValueCase::kValueLong: + return dimensionValue.value_long(); + case DimensionsValue::ValueCase::kValueBool: + return dimensionValue.value_bool() ? 1 : 0; + case DimensionsValue::ValueCase::kValueFloat: + return (int64_t)dimensionValue.value_float(); + case DimensionsValue::ValueCase::kValueTuple: + case DimensionsValue::ValueCase::kValueStr: + case DimensionsValue::ValueCase::VALUE_NOT_SET: + return 0; + } +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/dimension.h b/cmds/statsd/src/dimension.h index c866958fdaae..5bb64a9cf5ee 100644 --- a/cmds/statsd/src/dimension.h +++ b/cmds/statsd/src/dimension.h @@ -56,6 +56,8 @@ void DimensionsValueToString(const DimensionsValue& value, std::string *flattene bool IsSubDimension(const DimensionsValue& dimension, const DimensionsValue& sub); +// Helper function to get long value from the DimensionsValue proto. +long getLongFromDimenValue(const DimensionsValue& dimensionValue); } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index bf277f0a383d..33927aa9b44c 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -80,7 +80,7 @@ void StatsdStats::noteConfigReceived(const ConfigKey& key, int metricsCount, int StatsdStatsReport_ConfigStats configStats; configStats.set_uid(key.GetUid()); - configStats.set_name(key.GetName()); + configStats.set_id(key.GetId()); configStats.set_creation_time_sec(nowTimeSec); configStats.set_metric_count(metricsCount); configStats.set_condition_count(conditionsCount); @@ -196,34 +196,34 @@ void StatsdStats::setCurrentUidMapMemory(int bytes) { mUidMapStats.set_bytes_used(bytes); } -void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const string& name, int size) { +void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const int64_t& id, int size) { lock_guard<std::mutex> lock(mLock); // if name doesn't exist before, it will create the key with count 0. auto& conditionSizeMap = mConditionStats[key]; - if (size > conditionSizeMap[name]) { - conditionSizeMap[name] = size; + if (size > conditionSizeMap[id]) { + conditionSizeMap[id] = size; } } -void StatsdStats::noteMetricDimensionSize(const ConfigKey& key, const string& name, int size) { +void StatsdStats::noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size) { lock_guard<std::mutex> lock(mLock); // if name doesn't exist before, it will create the key with count 0. auto& metricsDimensionMap = mMetricsStats[key]; - if (size > metricsDimensionMap[name]) { - metricsDimensionMap[name] = size; + if (size > metricsDimensionMap[id]) { + metricsDimensionMap[id] = size; } } -void StatsdStats::noteMatcherMatched(const ConfigKey& key, const string& name) { +void StatsdStats::noteMatcherMatched(const ConfigKey& key, const int64_t& id) { lock_guard<std::mutex> lock(mLock); auto& matcherStats = mMatcherStats[key]; - matcherStats[name]++; + matcherStats[id]++; } -void StatsdStats::noteAnomalyDeclared(const ConfigKey& key, const string& name) { +void StatsdStats::noteAnomalyDeclared(const ConfigKey& key, const int64_t& id) { lock_guard<std::mutex> lock(mLock); auto& alertStats = mAlertStats[key]; - alertStats[name]++; + alertStats[id]++; } void StatsdStats::noteRegisteredAnomalyAlarmChanged() { @@ -279,9 +279,10 @@ void StatsdStats::addSubStatsToConfigLocked(const ConfigKey& key, const auto& matcherStats = mMatcherStats[key]; for (const auto& stats : matcherStats) { auto output = configStats.add_matcher_stats(); - output->set_name(stats.first); + output->set_id(stats.first); output->set_matched_times(stats.second); - VLOG("matcher %s matched %d times", stats.first.c_str(), stats.second); + VLOG("matcher %lld matched %d times", + (long long)stats.first, stats.second); } } // Add condition stats @@ -289,9 +290,10 @@ void StatsdStats::addSubStatsToConfigLocked(const ConfigKey& key, const auto& conditionStats = mConditionStats[key]; for (const auto& stats : conditionStats) { auto output = configStats.add_condition_stats(); - output->set_name(stats.first); + output->set_id(stats.first); output->set_max_tuple_counts(stats.second); - VLOG("condition %s max output tuple size %d", stats.first.c_str(), stats.second); + VLOG("condition %lld max output tuple size %d", + (long long)stats.first, stats.second); } } // Add metrics stats @@ -299,9 +301,10 @@ void StatsdStats::addSubStatsToConfigLocked(const ConfigKey& key, const auto& conditionStats = mMetricsStats[key]; for (const auto& stats : conditionStats) { auto output = configStats.add_metric_stats(); - output->set_name(stats.first); + output->set_id(stats.first); output->set_max_tuple_counts(stats.second); - VLOG("metrics %s max output tuple size %d", stats.first.c_str(), stats.second); + VLOG("metrics %lld max output tuple size %d", + (long long)stats.first, stats.second); } } // Add anomaly detection alert stats @@ -309,9 +312,9 @@ void StatsdStats::addSubStatsToConfigLocked(const ConfigKey& key, const auto& alertStats = mAlertStats[key]; for (const auto& stats : alertStats) { auto output = configStats.add_alert_stats(); - output->set_name(stats.first); + output->set_id(stats.first); output->set_alerted_times(stats.second); - VLOG("alert %s declared %d times", stats.first.c_str(), stats.second); + VLOG("alert %lld declared %d times", (long long)stats.first, stats.second); } } } @@ -343,9 +346,9 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { // in production. if (DEBUG) { VLOG("*****ICEBOX*****"); - VLOG("Config {%d-%s}: creation=%d, deletion=%d, #metric=%d, #condition=%d, " + VLOG("Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, " "#matcher=%d, #alert=%d, #valid=%d", - configStats.uid(), configStats.name().c_str(), configStats.creation_time_sec(), + configStats.uid(), (long long)configStats.id(), configStats.creation_time_sec(), configStats.deletion_time_sec(), configStats.metric_count(), configStats.condition_count(), configStats.matcher_count(), configStats.alert_count(), configStats.is_valid()); @@ -364,9 +367,9 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { auto& configStats = pair.second; if (DEBUG) { VLOG("********Active Configs***********"); - VLOG("Config {%d-%s}: creation=%d, deletion=%d, #metric=%d, #condition=%d, " + VLOG("Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, " "#matcher=%d, #alert=%d, #valid=%d", - configStats.uid(), configStats.name().c_str(), configStats.creation_time_sec(), + configStats.uid(), (long long)configStats.id(), configStats.creation_time_sec(), configStats.deletion_time_sec(), configStats.metric_count(), configStats.condition_count(), configStats.matcher_count(), configStats.alert_count(), configStats.is_valid()); diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index cb868e1fd8c6..45aa192318a4 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -99,10 +99,10 @@ public: * count > kDimensionKeySizeSoftLimit. * * [key]: The config key that this condition belongs to. - * [name]: The name of the condition. + * [id]: The id of the condition. * [size]: The output tuple size. */ - void noteConditionDimensionSize(const ConfigKey& key, const std::string& name, int size); + void noteConditionDimensionSize(const ConfigKey& key, const int64_t& id, int size); /** * Report the size of output tuple of a metric. @@ -111,26 +111,26 @@ public: * count > kDimensionKeySizeSoftLimit. * * [key]: The config key that this metric belongs to. - * [name]: The name of the metric. + * [id]: The id of the metric. * [size]: The output tuple size. */ - void noteMetricDimensionSize(const ConfigKey& key, const std::string& name, int size); + void noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size); /** * Report a matcher has been matched. * * [key]: The config key that this matcher belongs to. - * [name]: The name of the matcher. + * [id]: The id of the matcher. */ - void noteMatcherMatched(const ConfigKey& key, const std::string& name); + void noteMatcherMatched(const ConfigKey& key, const int64_t& id); /** * Report that an anomaly detection alert has been declared. * * [key]: The config key that this alert belongs to. - * [name]: The name of the alert. + * [id]: The id of the alert. */ - void noteAnomalyDeclared(const ConfigKey& key, const std::string& name); + void noteAnomalyDeclared(const ConfigKey& key, const int64_t& id); /** * Report an atom event has been logged. @@ -187,12 +187,12 @@ private: // Stores the number of output tuple of condition trackers when it's bigger than // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1, // it means some data has been dropped. - std::map<const ConfigKey, std::map<const std::string, int>> mConditionStats; + std::map<const ConfigKey, std::map<const int64_t, int>> mConditionStats; // Stores the number of output tuple of metric producers when it's bigger than // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1, // it means some data has been dropped. - std::map<const ConfigKey, std::map<const std::string, int>> mMetricsStats; + std::map<const ConfigKey, std::map<const int64_t, int>> mMetricsStats; // Stores the number of times a pushed atom is logged. // The size of the vector is the largest pushed atom id in atoms.proto + 1. Atoms @@ -206,10 +206,10 @@ private: // Stores the number of times an anomaly detection alert has been declared // (per config, per alert name). - std::map<const ConfigKey, std::map<const std::string, int>> mAlertStats; + std::map<const ConfigKey, std::map<const int64_t, int>> mAlertStats; // Stores how many times a matcher have been matched. - std::map<const ConfigKey, std::map<const std::string, int>> mMatcherStats; + std::map<const ConfigKey, std::map<const int64_t, int>> mMatcherStats; void noteConfigRemovedInternalLocked(const ConfigKey& key); diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp index bd5e3cbce07c..15c067ee936d 100644 --- a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp +++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp @@ -29,8 +29,8 @@ using std::unique_ptr; using std::unordered_map; using std::vector; -CombinationLogMatchingTracker::CombinationLogMatchingTracker(const string& name, const int index) - : LogMatchingTracker(name, index) { +CombinationLogMatchingTracker::CombinationLogMatchingTracker(const int64_t& id, const int index) + : LogMatchingTracker(id, index) { } CombinationLogMatchingTracker::~CombinationLogMatchingTracker() { @@ -38,7 +38,7 @@ CombinationLogMatchingTracker::~CombinationLogMatchingTracker() { bool CombinationLogMatchingTracker::init(const vector<AtomMatcher>& allLogMatchers, const vector<sp<LogMatchingTracker>>& allTrackers, - const unordered_map<string, int>& matcherMap, + const unordered_map<int64_t, int>& matcherMap, vector<bool>& stack) { if (mInitialized) { return true; @@ -60,10 +60,10 @@ bool CombinationLogMatchingTracker::init(const vector<AtomMatcher>& allLogMatche return false; } - for (const string& child : matcher.matcher()) { + for (const auto& child : matcher.matcher()) { auto pair = matcherMap.find(child); if (pair == matcherMap.end()) { - ALOGW("Matcher %s not found in the config", child.c_str()); + ALOGW("Matcher %lld not found in the config", (long long)child); return false; } @@ -76,7 +76,7 @@ bool CombinationLogMatchingTracker::init(const vector<AtomMatcher>& allLogMatche } if (!allTrackers[childIndex]->init(allLogMatchers, allTrackers, matcherMap, stack)) { - ALOGW("child matcher init failed %s", child.c_str()); + ALOGW("child matcher init failed %lld", (long long)child); return false; } diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h index 81f6e8052ebc..2a3f08da7b96 100644 --- a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h +++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h @@ -31,11 +31,11 @@ namespace statsd { // Represents a AtomMatcher_Combination in the StatsdConfig. class CombinationLogMatchingTracker : public virtual LogMatchingTracker { public: - CombinationLogMatchingTracker(const std::string& name, const int index); + CombinationLogMatchingTracker(const int64_t& id, const int index); bool init(const std::vector<AtomMatcher>& allLogMatchers, const std::vector<sp<LogMatchingTracker>>& allTrackers, - const std::unordered_map<std::string, int>& matcherMap, + const std::unordered_map<int64_t, int>& matcherMap, std::vector<bool>& stack); ~CombinationLogMatchingTracker(); diff --git a/cmds/statsd/src/matchers/LogMatchingTracker.h b/cmds/statsd/src/matchers/LogMatchingTracker.h index 567944fce8a6..4f30a047e256 100644 --- a/cmds/statsd/src/matchers/LogMatchingTracker.h +++ b/cmds/statsd/src/matchers/LogMatchingTracker.h @@ -33,8 +33,8 @@ namespace statsd { class LogMatchingTracker : public virtual RefBase { public: - LogMatchingTracker(const std::string& name, const int index) - : mName(name), mIndex(index), mInitialized(false){}; + LogMatchingTracker(const int64_t& id, const int index) + : mId(id), mIndex(index), mInitialized(false){}; virtual ~LogMatchingTracker(){}; @@ -48,7 +48,7 @@ public: // circle dependency. virtual bool init(const std::vector<AtomMatcher>& allLogMatchers, const std::vector<sp<LogMatchingTracker>>& allTrackers, - const std::unordered_map<std::string, int>& matcherMap, + const std::unordered_map<int64_t, int>& matcherMap, std::vector<bool>& stack) = 0; // Called when a log event comes. @@ -69,13 +69,13 @@ public: return mAtomIds; } - const std::string& getName() const { - return mName; + const int64_t& getId() const { + return mId; } protected: // Name of this matching. We don't really need the name, but it makes log message easy to debug. - const std::string mName; + const int64_t mId; // Index of this LogMatchingTracker in MetricsManager's container. const int mIndex; diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp index 91ef0344da91..31b3db524e80 100644 --- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp +++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp @@ -29,10 +29,10 @@ using std::unordered_map; using std::vector; -SimpleLogMatchingTracker::SimpleLogMatchingTracker(const string& name, const int index, +SimpleLogMatchingTracker::SimpleLogMatchingTracker(const int64_t& id, const int index, const SimpleAtomMatcher& matcher, const UidMap& uidMap) - : LogMatchingTracker(name, index), mMatcher(matcher), mUidMap(uidMap) { + : LogMatchingTracker(id, index), mMatcher(matcher), mUidMap(uidMap) { if (!matcher.has_atom_id()) { mInitialized = false; } else { @@ -46,7 +46,7 @@ SimpleLogMatchingTracker::~SimpleLogMatchingTracker() { bool SimpleLogMatchingTracker::init(const vector<AtomMatcher>& allLogMatchers, const vector<sp<LogMatchingTracker>>& allTrackers, - const unordered_map<string, int>& matcherMap, + const unordered_map<int64_t, int>& matcherMap, vector<bool>& stack) { // no need to do anything. return mInitialized; @@ -56,7 +56,7 @@ void SimpleLogMatchingTracker::onLogEvent(const LogEvent& event, const vector<sp<LogMatchingTracker>>& allTrackers, vector<MatchingState>& matcherResults) { if (matcherResults[mIndex] != MatchingState::kNotComputed) { - VLOG("Matcher %s already evaluated ", mName.c_str()); + VLOG("Matcher %lld already evaluated ", (long long)mId); return; } @@ -67,7 +67,7 @@ void SimpleLogMatchingTracker::onLogEvent(const LogEvent& event, bool matched = matchesSimple(mUidMap, mMatcher, event); matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched; - VLOG("Stats SimpleLogMatcher %s matched? %d", mName.c_str(), matched); + VLOG("Stats SimpleLogMatcher %lld matched? %d", (long long)mId, matched); } } // namespace statsd diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h index 68518f8a224d..28b339caa466 100644 --- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h +++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h @@ -32,7 +32,7 @@ namespace statsd { class SimpleLogMatchingTracker : public virtual LogMatchingTracker { public: - SimpleLogMatchingTracker(const std::string& name, const int index, + SimpleLogMatchingTracker(const int64_t& id, const int index, const SimpleAtomMatcher& matcher, const UidMap& uidMap); @@ -40,7 +40,7 @@ public: bool init(const std::vector<AtomMatcher>& allLogMatchers, const std::vector<sp<LogMatchingTracker>>& allTrackers, - const std::unordered_map<std::string, int>& matcherMap, + const std::unordered_map<int64_t, int>& matcherMap, std::vector<bool>& stack) override; void onLogEvent(const LogEvent& event, diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index 5bf3cffd8bab..a24364df0fb2 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -43,7 +43,7 @@ namespace os { namespace statsd { // for StatsLogReport -const int FIELD_ID_NAME = 1; +const int FIELD_ID_ID = 1; const int FIELD_ID_START_REPORT_NANOS = 2; const int FIELD_ID_END_REPORT_NANOS = 3; const int FIELD_ID_COUNT_METRICS = 5; @@ -61,7 +61,7 @@ CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric const int conditionIndex, const sp<ConditionWizard>& wizard, const uint64_t startTimeNs) - : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard) { + : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard) { // TODO: evaluate initial conditions. and set mConditionMet. if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) { mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000; @@ -78,7 +78,7 @@ CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric mConditionSliced = true; } - VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(), + VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), (long long)mBucketSizeNs, (long long)mStartTimeNs); } @@ -87,12 +87,12 @@ CountMetricProducer::~CountMetricProducer() { } void CountMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) { - VLOG("Metric %s onSlicedConditionMayChange", mName.c_str()); + VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId); } void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) { flushIfNeededLocked(dumpTimeNs); - report->set_metric_name(mName); + report->set_metric_id(mMetricId); report->set_start_report_nanos(mStartTimeNs); auto count_metrics = report->mutable_count_metrics(); @@ -112,11 +112,11 @@ void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { flushIfNeededLocked(dumpTimeNs); - protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs); long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS); - VLOG("metric %s dump report now...", mName.c_str()); + VLOG("metric %lld dump report now...",(long long)mMetricId); for (const auto& counter : mPastBuckets) { const HashableDimensionKey& hashableKey = counter.first; @@ -158,7 +158,7 @@ void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, void CountMetricProducer::onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) { - VLOG("Metric %s onConditionChanged", mName.c_str()); + VLOG("Metric %lld onConditionChanged", (long long)mMetricId); mCondition = conditionMet; } @@ -170,11 +170,11 @@ bool CountMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) // 1. Report the tuple count if the tuple count > soft limit if (mCurrentSlicedCounter->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { size_t newTupleCount = mCurrentSlicedCounter->size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName, newTupleCount); + StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount); // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("CountMetric %s dropping data for dimension key %s", mName.c_str(), - newKey.c_str()); + ALOGE("CountMetric %lld dropping data for dimension key %s", + (long long)mMetricId, newKey.c_str()); return true; } } @@ -215,7 +215,7 @@ void CountMetricProducer::onMatchedLogEventInternalLocked( mCurrentSlicedCounter->find(eventKey)->second); } - VLOG("metric %s %s->%lld", mName.c_str(), eventKey.c_str(), + VLOG("metric %lld %s->%lld", (long long)mMetricId, eventKey.c_str(), (long long)(*mCurrentSlicedCounter)[eventKey]); } @@ -234,8 +234,8 @@ void CountMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) { info.mCount = counter.second; auto& bucketList = mPastBuckets[counter.first]; bucketList.push_back(info); - VLOG("metric %s, dump key value: %s -> %lld", mName.c_str(), counter.first.c_str(), - (long long)counter.second); + VLOG("metric %lld, dump key value: %s -> %lld", + (long long)mMetricId, counter.first.c_str(), (long long)counter.second); } for (auto& tracker : mAnomalyTrackers) { @@ -247,7 +247,7 @@ void CountMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) { uint64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs; mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs; mCurrentBucketNum += numBucketsForward; - VLOG("metric %s: new bucket start time: %lld", mName.c_str(), + VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId, (long long)mCurrentBucketStartTimeNs); } diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 30b105c71500..0117b6d7faa5 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -42,7 +42,7 @@ namespace os { namespace statsd { // for StatsLogReport -const int FIELD_ID_NAME = 1; +const int FIELD_ID_ID = 1; const int FIELD_ID_START_REPORT_NANOS = 2; const int FIELD_ID_END_REPORT_NANOS = 3; const int FIELD_ID_DURATION_METRICS = 6; @@ -63,7 +63,7 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat const sp<ConditionWizard>& wizard, const FieldMatcher& internalDimensions, const uint64_t startTimeNs) - : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard), + : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard), mAggregationType(metric.aggregation_type()), mStartIndex(startIndex), mStopIndex(stopIndex), @@ -88,7 +88,7 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat mConditionSliced = true; } - VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(), + VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), (long long)mBucketSizeNs, (long long)mStartTimeNs); } @@ -98,9 +98,9 @@ DurationMetricProducer::~DurationMetricProducer() { sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker(const Alert &alert) { std::lock_guard<std::mutex> lock(mMutex); - if (alert.trigger_if_sum_gt() > alert.number_of_buckets() * mBucketSizeNs) { - ALOGW("invalid alert: threshold (%lld) > possible recordable value (%d x %lld)", - alert.trigger_if_sum_gt(), alert.number_of_buckets(), + if (alert.trigger_if_sum_gt() > alert.num_buckets() * mBucketSizeNs) { + ALOGW("invalid alert: threshold (%f) > possible recordable value (%d x %lld)", + alert.trigger_if_sum_gt(), alert.num_buckets(), (long long)mBucketSizeNs); return nullptr; } @@ -116,17 +116,17 @@ unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker( switch (mAggregationType) { case DurationMetric_AggregationType_SUM: return make_unique<OringDurationTracker>( - mConfigKey, mName, eventKey, mWizard, mConditionTrackerIndex, mNested, + mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested, mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers); case DurationMetric_AggregationType_MAX_SPARSE: return make_unique<MaxDurationTracker>( - mConfigKey, mName, eventKey, mWizard, mConditionTrackerIndex, mNested, + mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested, mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers); } } void DurationMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) { - VLOG("Metric %s onSlicedConditionMayChange", mName.c_str()); + VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId); flushIfNeededLocked(eventTime); // Now for each of the on-going event, check if the condition has changed for them. for (auto& pair : mCurrentSlicedDuration) { @@ -136,7 +136,7 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eve void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) { - VLOG("Metric %s onConditionChanged", mName.c_str()); + VLOG("Metric %lld onConditionChanged", (long long)mMetricId); mCondition = conditionMet; flushIfNeededLocked(eventTime); // TODO: need to populate the condition change time from the event which triggers the condition @@ -148,7 +148,7 @@ void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet, void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) { flushIfNeededLocked(dumpTimeNs); - report->set_metric_name(mName); + report->set_metric_id(mMetricId); report->set_start_report_nanos(mStartTimeNs); auto duration_metrics = report->mutable_duration_metrics(); @@ -168,11 +168,11 @@ void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { flushIfNeededLocked(dumpTimeNs); - protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs); long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS); - VLOG("metric %s dump report now...", mName.c_str()); + VLOG("metric %lld dump report now...", (long long)mMetricId); for (const auto& pair : mPastBuckets) { const HashableDimensionKey& hashableKey = pair.first; @@ -237,11 +237,11 @@ bool DurationMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newK // 1. Report the tuple count if the tuple count > soft limit if (mCurrentSlicedDuration.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { size_t newTupleCount = mCurrentSlicedDuration.size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName, newTupleCount); + StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount); // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("DurationMetric %s dropping data for dimension key %s", mName.c_str(), - newKey.c_str()); + ALOGE("DurationMetric %lld dropping data for dimension key %s", + (long long)mMetricId, newKey.c_str()); return true; } } diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index c8138d3a7d9a..821d8ea48aef 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -41,7 +41,7 @@ namespace os { namespace statsd { // for StatsLogReport -const int FIELD_ID_NAME = 1; +const int FIELD_ID_ID = 1; const int FIELD_ID_START_REPORT_NANOS = 2; const int FIELD_ID_END_REPORT_NANOS = 3; const int FIELD_ID_EVENT_METRICS = 4; @@ -55,7 +55,7 @@ EventMetricProducer::EventMetricProducer(const ConfigKey& key, const EventMetric const int conditionIndex, const sp<ConditionWizard>& wizard, const uint64_t startTimeNs) - : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard) { + : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard) { if (metric.links().size() > 0) { mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), metric.links().end()); @@ -64,7 +64,7 @@ EventMetricProducer::EventMetricProducer(const ConfigKey& key, const EventMetric startNewProtoOutputStreamLocked(); - VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(), + VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), (long long)mBucketSizeNs, (long long)mStartTimeNs); } @@ -102,12 +102,13 @@ void EventMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLog void EventMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { - protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs); size_t bufferSize = mProto->size(); - VLOG("metric %s dump report now... proto size: %zu ", mName.c_str(), bufferSize); + VLOG("metric %lld dump report now... proto size: %zu ", + (long long)mMetricId, bufferSize); std::unique_ptr<std::vector<uint8_t>> buffer = serializeProtoLocked(*mProto); protoOutput->write(FIELD_TYPE_MESSAGE | FIELD_ID_EVENT_METRICS, @@ -119,7 +120,7 @@ void EventMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, void EventMetricProducer::onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) { - VLOG("Metric %s onConditionChanged", mName.c_str()); + VLOG("Metric %lld onConditionChanged", (long long)mMetricId); mCondition = conditionMet; } diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index 51fd9fcb335b..eaf1de238e8d 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -44,7 +44,7 @@ namespace os { namespace statsd { // for StatsLogReport -const int FIELD_ID_NAME = 1; +const int FIELD_ID_ID = 1; const int FIELD_ID_START_REPORT_NANOS = 2; const int FIELD_ID_END_REPORT_NANOS = 3; const int FIELD_ID_GAUGE_METRICS = 8; @@ -63,7 +63,7 @@ GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric const sp<ConditionWizard>& wizard, const int atomTagId, const int pullTagId, const uint64_t startTimeNs, shared_ptr<StatsPullerManager> statsPullerManager) - : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard), + : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard), mStatsPullerManager(statsPullerManager), mPullTagId(pullTagId), mAtomTagId(atomTagId) { @@ -92,7 +92,7 @@ GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric metric.bucket().bucket_size_millis()); } - VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(), + VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), (long long)mBucketSizeNs, (long long)mStartTimeNs); } @@ -118,11 +118,11 @@ void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLog void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { - VLOG("gauge metric %s dump report now...", mName.c_str()); + VLOG("gauge metric %lld report now...", (long long)mMetricId); flushIfNeededLocked(dumpTimeNs); - protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs); long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS); @@ -166,7 +166,7 @@ void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) { - VLOG("Metric %s onConditionChanged", mName.c_str()); + VLOG("Metric %lld onConditionChanged", (long long)mMetricId); flushIfNeededLocked(eventTime); mCondition = conditionMet; @@ -194,7 +194,7 @@ void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet, } void GaugeMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) { - VLOG("Metric %s onSlicedConditionMayChange", mName.c_str()); + VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId); } std::shared_ptr<FieldValueMap> GaugeMetricProducer::getGaugeFields(const LogEvent& event) { @@ -223,11 +223,11 @@ bool GaugeMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) // 1. Report the tuple count if the tuple count > soft limit if (mCurrentSlicedBucket->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { size_t newTupleCount = mCurrentSlicedBucket->size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName, newTupleCount); + StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount); // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("GaugeMetric %s dropping data for dimension key %s", mName.c_str(), - newKey.c_str()); + ALOGE("GaugeMetric %lld dropping data for dimension key %s", + (long long)mMetricId, newKey.c_str()); return true; } } @@ -314,7 +314,8 @@ void GaugeMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) { info.mGaugeFields = slice.second; auto& bucketList = mPastBuckets[slice.first]; bucketList.push_back(info); - VLOG("gauge metric %s, dump key value: %s", mName.c_str(), slice.first.c_str()); + VLOG("gauge metric %lld, dump key value: %s", + (long long)mMetricId, slice.first.c_str()); } // Reset counters @@ -331,7 +332,7 @@ void GaugeMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) { int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs; mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs; mCurrentBucketNum += numBucketsForward; - VLOG("metric %s: new bucket start time: %lld", mName.c_str(), + VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId, (long long)mCurrentBucketStartTimeNs); } diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index 4ed8289ab133..d620a7ebee00 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -29,13 +29,13 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo } bool condition; - map<string, std::vector<HashableDimensionKey>> conditionKeys; + ConditionKey conditionKey; if (mConditionSliced) { for (const auto& link : mConditionLinks) { - conditionKeys.insert(std::make_pair(link.condition(), - getDimensionKeysForCondition(event, link))); + conditionKey.insert(std::make_pair(link.condition(), + getDimensionKeysForCondition(event, link))); } - if (mWizard->query(mConditionTrackerIndex, conditionKeys) != ConditionState::kTrue) { + if (mWizard->query(mConditionTrackerIndex, conditionKey) != ConditionState::kTrue) { condition = false; } else { condition = true; @@ -48,11 +48,11 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo vector<DimensionsValue> dimensionValues = getDimensionKeys(event, mDimensions); for (const DimensionsValue& dimensionValue : dimensionValues) { onMatchedLogEventInternalLocked( - matcherIndex, HashableDimensionKey(dimensionValue), conditionKeys, condition, event); + matcherIndex, HashableDimensionKey(dimensionValue), conditionKey, condition, event); } } else { onMatchedLogEventInternalLocked( - matcherIndex, DEFAULT_DIMENSION_KEY, conditionKeys, condition, event); + matcherIndex, DEFAULT_DIMENSION_KEY, conditionKey, condition, event); } } diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index fe1a53bef735..3779c4487d23 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -39,9 +39,9 @@ namespace statsd { // be a no-op. class MetricProducer : public virtual PackageInfoListener { public: - MetricProducer(const std::string& name, const ConfigKey& key, const int64_t startTimeNs, + MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t startTimeNs, const int conditionIndex, const sp<ConditionWizard>& wizard) - : mName(name), + : mMetricId(metricId), mConfigKey(key), mStartTimeNs(startTimeNs), mCurrentBucketStartTimeNs(startTimeNs), @@ -117,8 +117,8 @@ public: return mBucketSizeNs; } - inline const string& getName() { - return mName; + inline const int64_t& getMetricId() { + return mMetricId; } protected: @@ -129,7 +129,7 @@ protected: virtual void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) = 0; virtual size_t byteSizeLocked() const = 0; - const std::string mName; + const int64_t mMetricId; const ConfigKey mConfigKey; diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 9749e0f4af4f..f92951703030 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -51,9 +51,9 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, mConfigValid = initStatsdConfig(key, config, *uidMap, timeBaseSec, mTagIds, mAllAtomMatchers, mAllConditionTrackers, mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap, - mTrackerToMetricMap, mTrackerToConditionMap); + mTrackerToMetricMap, mTrackerToConditionMap, mNoReportMetricIds); - if (!config.has_log_source()) { + if (config.allowed_log_source_size() == 0) { // TODO(b/70794411): uncomment the following line and remove the hard coded log source // after all configs have the log source added. // mConfigValid = false; @@ -63,10 +63,14 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, mAllowedUid.push_back(0); mAllowedLogSources.insert(mAllowedUid.begin(), mAllowedUid.end()); } else { - mAllowedUid.insert(mAllowedUid.begin(), config.log_source().uid().begin(), - config.log_source().uid().end()); - mAllowedPkg.insert(mAllowedPkg.begin(), config.log_source().package().begin(), - config.log_source().package().end()); + for (const auto& source : config.allowed_log_source()) { + auto it = UidMap::sAidToUidMapping.find(source); + if (it != UidMap::sAidToUidMapping.end()) { + mAllowedUid.push_back(it->second); + } else { + mAllowedPkg.push_back(source); + } + } if (mAllowedUid.size() + mAllowedPkg.size() > StatsdStats::kMaxLogSourceCount) { ALOGE("Too many log sources. This is likely to be an error in the config."); @@ -143,8 +147,10 @@ void MetricsManager::onUidMapReceived() { } void MetricsManager::onDumpReport(const uint64_t& dumpTimeStampNs, ConfigMetricsReport* report) { - for (auto& producer : mAllMetricProducers) { - producer->onDumpReport(dumpTimeStampNs, report->add_metrics()); + for (const auto& producer : mAllMetricProducers) { + if (mNoReportMetricIds.find(producer->getMetricId()) == mNoReportMetricIds.end()) { + producer->onDumpReport(dumpTimeStampNs, report->add_metrics()); + } } } @@ -152,11 +158,13 @@ void MetricsManager::onDumpReport(ProtoOutputStream* protoOutput) { VLOG("=========================Metric Reports Start=========================="); uint64_t dumpTimeStampNs = time(nullptr) * NS_PER_SEC; // one StatsLogReport per MetricProduer - for (auto& metric : mAllMetricProducers) { - long long token = - protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS); - metric->onDumpReport(dumpTimeStampNs, protoOutput); - protoOutput->end(token); + for (const auto& producer : mAllMetricProducers) { + if (mNoReportMetricIds.find(producer->getMetricId()) == mNoReportMetricIds.end()) { + long long token = + protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS); + producer->onDumpReport(dumpTimeStampNs, protoOutput); + protoOutput->end(token); + } } VLOG("=========================Metric Reports End=========================="); } @@ -173,6 +181,22 @@ void MetricsManager::onLogEvent(const LogEvent& event) { VLOG("log source %d not on the whitelist", event.GetUid()); return; } + } else { // Check that app hook fields are valid. + // TODO: Find a way to make these checks easier to maintain if the app hooks get changed. + + // Label is 2nd from last field and must be from [0, 15]. + status_t err = NO_ERROR; + long label = event.GetLong(event.size()-1, &err); + if (err != NO_ERROR || label < 0 || label > 15) { + VLOG("App hook does not have valid label %ld", label); + return; + } + // The state must be from 0,3. This part of code must be manually updated. + long apphookState = event.GetLong(event.size(), &err); + if (err != NO_ERROR || apphookState < 0 || apphookState > 3) { + VLOG("App hook does not have valid state %ld", apphookState); + return; + } } int tagId = event.GetTagId(); @@ -240,7 +264,7 @@ void MetricsManager::onLogEvent(const LogEvent& event) { for (size_t i = 0; i < mAllAtomMatchers.size(); i++) { if (matcherCache[i] == MatchingState::kMatched) { StatsdStats::getInstance().noteMatcherMatched(mConfigKey, - mAllAtomMatchers[i]->getName()); + mAllAtomMatchers[i]->getId()); auto pair = mTrackerToMetricMap.find(i); if (pair != mTrackerToMetricMap.end()) { auto& metricList = pair->second; diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 51c4c90fe2ac..a03047f3aff2 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -57,6 +57,10 @@ public: void onUidMapReceived() override; + bool shouldAddUidMapListener() const { + return !mAllowedPkg.empty(); + } + // Config source owner can call onDumpReport() to get all the metrics collected. virtual void onDumpReport(android::util::ProtoOutputStream* protoOutput); virtual void onDumpReport(const uint64_t& dumpTimeStampNs, ConfigMetricsReport* report); @@ -131,6 +135,9 @@ private: void initLogSourceWhiteList(); + // The metrics that don't need to be uploaded or even reported. + std::set<int64_t> mNoReportMetricIds; + FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions); FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks); }; diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index b58dc689fed4..5f7d76140397 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -17,6 +17,7 @@ #define DEBUG false // STOPSHIP if true #include "Log.h" +#include "dimension.h" #include "ValueMetricProducer.h" #include "guardrail/StatsdStats.h" #include "stats_log_util.h" @@ -46,7 +47,7 @@ namespace os { namespace statsd { // for StatsLogReport -const int FIELD_ID_NAME = 1; +const int FIELD_ID_ID = 1; const int FIELD_ID_START_REPORT_NANOS = 2; const int FIELD_ID_END_REPORT_NANOS = 3; const int FIELD_ID_VALUE_METRICS = 7; @@ -68,7 +69,7 @@ ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric const sp<ConditionWizard>& wizard, const int pullTagId, const uint64_t startTimeNs, shared_ptr<StatsPullerManager> statsPullerManager) - : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard), + : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard), mValueField(metric.value_field()), mStatsPullerManager(statsPullerManager), mPullTagId(pullTagId) { @@ -92,8 +93,8 @@ ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric mStatsPullerManager->RegisterReceiver(mPullTagId, this, metric.bucket().bucket_size_millis()); } - VLOG("value metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(), - (long long)mBucketSizeNs, (long long)mStartTimeNs); + VLOG("value metric %lld created. bucket size %lld start_time: %lld", + (long long)metric.id(), (long long)mBucketSizeNs, (long long)mStartTimeNs); } // for testing @@ -113,12 +114,12 @@ ValueMetricProducer::~ValueMetricProducer() { } void ValueMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) { - VLOG("Metric %s onSlicedConditionMayChange", mName.c_str()); + VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId); } void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) { flushIfNeededLocked(dumpTimeNs); - report->set_metric_name(mName); + report->set_metric_id(mMetricId); report->set_start_report_nanos(mStartTimeNs); auto value_metrics = report->mutable_value_metrics(); for (const auto& pair : mPastBuckets) { @@ -135,9 +136,9 @@ void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLog void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { - VLOG("metric %s dump report now...", mName.c_str()); + VLOG("metric %lld dump report now...", (long long)mMetricId); flushIfNeededLocked(dumpTimeNs); - protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs); long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS); @@ -171,7 +172,7 @@ void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, protoOutput->end(protoToken); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs); - VLOG("metric %s dump report now...", mName.c_str()); + VLOG("metric %lld dump report now...", (long long)mMetricId); mPastBuckets.clear(); mStartTimeNs = mCurrentBucketStartTimeNs; // TODO: Clear mDimensionKeyMap once the report is dumped. @@ -218,7 +219,8 @@ void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven // For scheduled pulled data, the effective event time is snap to the nearest // bucket boundary to make bucket finalize. uint64_t realEventTime = allData.at(0)->GetTimestampNs(); - uint64_t eventTime = mStartTimeNs + ((realEventTime - mStartTimeNs)/mBucketSizeNs) * mBucketSizeNs; + uint64_t eventTime = mStartTimeNs + + ((realEventTime - mStartTimeNs)/mBucketSizeNs) * mBucketSizeNs; mCondition = false; for (const auto& data : allData) { @@ -242,11 +244,11 @@ bool ValueMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) } if (mCurrentSlicedBucket.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { size_t newTupleCount = mCurrentSlicedBucket.size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName, newTupleCount); + StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount); // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("ValueMetric %s dropping data for dimension key %s", mName.c_str(), - newKey.c_str()); + ALOGE("ValueMetric %lld dropping data for dimension key %s", + (long long)mMetricId, newKey.c_str()); return true; } } @@ -272,7 +274,11 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked( } Interval& interval = mCurrentSlicedBucket[eventKey]; - long value = get_value(event); + std::shared_ptr<FieldValueMap> valueFieldMap = getValueFields(event); + if (valueFieldMap->empty() || valueFieldMap->size() > 1) { + return; + } + const long value = getLongFromDimenValue(valueFieldMap->begin()->second); if (mPullTagId != -1) { // for pulled events if (mCondition == true) { @@ -296,15 +302,11 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked( } } -long ValueMetricProducer::get_value(const LogEvent& event) { - status_t err = NO_ERROR; - long val = event.GetLong(mValueField, &err); - if (err == NO_ERROR) { - return val; - } else { - VLOG("Can't find value in message. %s", event.ToString().c_str()); - return 0; - } +std::shared_ptr<FieldValueMap> ValueMetricProducer::getValueFields(const LogEvent& event) { + std::shared_ptr<FieldValueMap> valueFields = + std::make_shared<FieldValueMap>(event.getFieldValueMap()); + filterFields(mValueField, valueFields.get()); + return valueFields; } void ValueMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) { @@ -346,7 +348,7 @@ void ValueMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) { if (numBucketsForward > 1) { VLOG("Skipping forward %lld buckets", (long long)numBucketsForward); } - VLOG("metric %s: new bucket start time: %lld", mName.c_str(), + VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId, (long long)mCurrentBucketStartTimeNs); } diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index 6f3b1d1c67d7..3e7032d8cf2d 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -70,7 +70,7 @@ private: // Util function to flush the old packet. void flushIfNeededLocked(const uint64_t& eventTime); - const int32_t mValueField; + const FieldMatcher mValueField; std::shared_ptr<StatsPullerManager> mStatsPullerManager; @@ -103,7 +103,7 @@ private: // TODO: Add a lock to mPastBuckets. std::unordered_map<HashableDimensionKey, std::vector<ValueBucket>> mPastBuckets; - long get_value(const LogEvent& event); + std::shared_ptr<FieldValueMap> getValueFields(const LogEvent& event); // Util function to check whether the specified dimension hits the guardrail. bool hitGuardRailLocked(const HashableDimensionKey& newKey); diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h index 9192d12fa982..842581ed1a9f 100644 --- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h @@ -60,12 +60,12 @@ struct DurationBucket { class DurationTracker { public: - DurationTracker(const ConfigKey& key, const string& name, const HashableDimensionKey& eventKey, + DurationTracker(const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers) : mConfigKey(key), - mName(name), + mTrackerId(id), mEventKey(eventKey), mWizard(wizard), mConditionTrackerIndex(conditionIndex), @@ -145,7 +145,7 @@ protected: // A reference to the DurationMetricProducer's config key. const ConfigKey& mConfigKey; - const std::string mName; + const int64_t mTrackerId; HashableDimensionKey mEventKey; diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp index d8a8e238fa5c..94f98ada7014 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp @@ -24,12 +24,12 @@ namespace android { namespace os { namespace statsd { -MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const string& name, +MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, const vector<sp<DurationAnomalyTracker>>& anomalyTrackers) - : DurationTracker(key, name, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, + : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs, anomalyTrackers) { } @@ -42,12 +42,13 @@ bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { // 1. Report the tuple count if the tuple count > soft limit if (mInfos.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { size_t newTupleCount = mInfos.size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName + mEventKey.toString(), - newTupleCount); + StatsdStats::getInstance().noteMetricDimensionSize( + mConfigKey, hashDimensionsValue(mTrackerId, mEventKey.getDimensionsValue()), + newTupleCount); // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("MaxDurTracker %s dropping data for dimension key %s", mName.c_str(), - newKey.c_str()); + ALOGE("MaxDurTracker %lld dropping data for dimension key %s", + (long long)mTrackerId, newKey.c_str()); return true; } } diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h index 76f486e0c713..68c48cb10a4d 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h @@ -28,7 +28,7 @@ namespace statsd { // they stop or bucket expires. class MaxDurationTracker : public DurationTracker { public: - MaxDurationTracker(const ConfigKey& key, const string& name, + MaxDurationTracker(const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp index c347d5ca0f8b..c77d0b70507b 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp @@ -25,11 +25,11 @@ namespace statsd { using std::pair; OringDurationTracker::OringDurationTracker( - const ConfigKey& key, const string& name, const HashableDimensionKey& eventKey, + const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, const vector<sp<DurationAnomalyTracker>>& anomalyTrackers) - : DurationTracker(key, name, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, + : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs, anomalyTrackers), mStarted(), mPaused() { @@ -44,12 +44,13 @@ bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { } if (mConditionKeyMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { size_t newTupleCount = mConditionKeyMap.size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName + mEventKey.toString(), - newTupleCount); + StatsdStats::getInstance().noteMetricDimensionSize( + mConfigKey, hashDimensionsValue(mTrackerId, mEventKey.getDimensionsValue()), + newTupleCount); // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("OringDurTracker %s dropping data for dimension key %s", mName.c_str(), - newKey.c_str()); + ALOGE("OringDurTracker %lld dropping data for dimension key %s", + (long long)mTrackerId, newKey.c_str()); return true; } } diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h index dcf04dbcc8af..7fe649c436e2 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h @@ -27,7 +27,7 @@ namespace statsd { // Tracks the "Or'd" duration -- if 2 durations are overlapping, they won't be double counted. class OringDurationTracker : public DurationTracker { public: - OringDurationTracker(const ConfigKey& key, const string& name, + OringDurationTracker(const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 97dbf7a4f7a1..89d54c7bc7ee 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -38,21 +38,21 @@ namespace android { namespace os { namespace statsd { -bool handleMetricWithLogTrackers(const string what, const int metricIndex, +bool handleMetricWithLogTrackers(const int64_t what, const int metricIndex, const bool usedForDimension, const vector<sp<LogMatchingTracker>>& allAtomMatchers, - const unordered_map<string, int>& logTrackerMap, + const unordered_map<int64_t, int>& logTrackerMap, unordered_map<int, std::vector<int>>& trackerToMetricMap, int& logTrackerIndex) { auto logTrackerIt = logTrackerMap.find(what); if (logTrackerIt == logTrackerMap.end()) { - ALOGW("cannot find the AtomMatcher \"%s\" in config", what.c_str()); + ALOGW("cannot find the AtomMatcher \"%lld\" in config", (long long)what); return false; } if (usedForDimension && allAtomMatchers[logTrackerIt->second]->getAtomIds().size() > 1) { - ALOGE("AtomMatcher \"%s\" has more than one tag ids. When a metric has dimension, " + ALOGE("AtomMatcher \"%lld\" has more than one tag ids. When a metric has dimension, " "the \"what\" can only about one atom type.", - what.c_str()); + (long long)what); return false; } logTrackerIndex = logTrackerIt->second; @@ -62,22 +62,22 @@ bool handleMetricWithLogTrackers(const string what, const int metricIndex, } bool handleMetricWithConditions( - const string condition, const int metricIndex, - const unordered_map<string, int>& conditionTrackerMap, + const int64_t condition, const int metricIndex, + const unordered_map<int64_t, int>& conditionTrackerMap, const ::google::protobuf::RepeatedPtrField<::android::os::statsd::MetricConditionLink>& links, vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex, unordered_map<int, std::vector<int>>& conditionToMetricMap) { auto condition_it = conditionTrackerMap.find(condition); if (condition_it == conditionTrackerMap.end()) { - ALOGW("cannot find Predicate \"%s\" in the config", condition.c_str()); + ALOGW("cannot find Predicate \"%lld\" in the config", (long long)condition); return false; } for (const auto& link : links) { auto it = conditionTrackerMap.find(link.condition()); if (it == conditionTrackerMap.end()) { - ALOGW("cannot find Predicate \"%s\" in the config", link.condition().c_str()); + ALOGW("cannot find Predicate \"%lld\" in the config", (long long)link.condition()); return false; } allConditionTrackers[condition_it->second]->setSliced(true); @@ -93,7 +93,7 @@ bool handleMetricWithConditions( } bool initLogTrackers(const StatsdConfig& config, const UidMap& uidMap, - unordered_map<string, int>& logTrackerMap, + unordered_map<int64_t, int>& logTrackerMap, vector<sp<LogMatchingTracker>>& allAtomMatchers, set<int>& allTagIds) { vector<AtomMatcher> matcherConfigs; const int atomMatcherCount = config.atom_matcher_size(); @@ -107,22 +107,22 @@ bool initLogTrackers(const StatsdConfig& config, const UidMap& uidMap, switch (logMatcher.contents_case()) { case AtomMatcher::ContentsCase::kSimpleAtomMatcher: allAtomMatchers.push_back(new SimpleLogMatchingTracker( - logMatcher.name(), index, logMatcher.simple_atom_matcher(), uidMap)); + logMatcher.id(), index, logMatcher.simple_atom_matcher(), uidMap)); break; case AtomMatcher::ContentsCase::kCombination: allAtomMatchers.push_back( - new CombinationLogMatchingTracker(logMatcher.name(), index)); + new CombinationLogMatchingTracker(logMatcher.id(), index)); break; default: - ALOGE("Matcher \"%s\" malformed", logMatcher.name().c_str()); + ALOGE("Matcher \"%lld\" malformed", (long long)logMatcher.id()); return false; // continue; } - if (logTrackerMap.find(logMatcher.name()) != logTrackerMap.end()) { + if (logTrackerMap.find(logMatcher.id()) != logTrackerMap.end()) { ALOGE("Duplicate AtomMatcher found!"); return false; } - logTrackerMap[logMatcher.name()] = index; + logTrackerMap[logMatcher.id()] = index; matcherConfigs.push_back(logMatcher); } @@ -139,8 +139,8 @@ bool initLogTrackers(const StatsdConfig& config, const UidMap& uidMap, } bool initConditions(const ConfigKey& key, const StatsdConfig& config, - const unordered_map<string, int>& logTrackerMap, - unordered_map<string, int>& conditionTrackerMap, + const unordered_map<int64_t, int>& logTrackerMap, + unordered_map<int64_t, int>& conditionTrackerMap, vector<sp<ConditionTracker>>& allConditionTrackers, unordered_map<int, std::vector<int>>& trackerToConditionMap) { vector<Predicate> conditionConfigs; @@ -154,23 +154,23 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, switch (condition.contents_case()) { case Predicate::ContentsCase::kSimplePredicate: { allConditionTrackers.push_back(new SimpleConditionTracker( - key, condition.name(), index, condition.simple_predicate(), logTrackerMap)); + key, condition.id(), index, condition.simple_predicate(), logTrackerMap)); break; } case Predicate::ContentsCase::kCombination: { allConditionTrackers.push_back( - new CombinationConditionTracker(condition.name(), index)); + new CombinationConditionTracker(condition.id(), index)); break; } default: - ALOGE("Predicate \"%s\" malformed", condition.name().c_str()); + ALOGE("Predicate \"%lld\" malformed", (long long)condition.id()); return false; } - if (conditionTrackerMap.find(condition.name()) != conditionTrackerMap.end()) { + if (conditionTrackerMap.find(condition.id()) != conditionTrackerMap.end()) { ALOGE("Duplicate Predicate found!"); return false; } - conditionTrackerMap[condition.name()] = index; + conditionTrackerMap[condition.id()] = index; conditionConfigs.push_back(condition); } @@ -190,14 +190,15 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, } bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec, - const unordered_map<string, int>& logTrackerMap, - const unordered_map<string, int>& conditionTrackerMap, + const unordered_map<int64_t, int>& logTrackerMap, + const unordered_map<int64_t, int>& conditionTrackerMap, const vector<sp<LogMatchingTracker>>& allAtomMatchers, vector<sp<ConditionTracker>>& allConditionTrackers, vector<sp<MetricProducer>>& allMetricProducers, unordered_map<int, std::vector<int>>& conditionToMetricMap, unordered_map<int, std::vector<int>>& trackerToMetricMap, - unordered_map<string, int>& metricMap) { + unordered_map<int64_t, int>& metricMap, + std::set<int64_t> &noReportMetricIds) { sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers); const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() + config.event_metric_size() + config.value_metric_size(); @@ -219,12 +220,12 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti for (int i = 0; i < config.count_metric_size(); i++) { const CountMetric& metric = config.count_metric(i); if (!metric.has_what()) { - ALOGW("cannot find \"what\" in CountMetric \"%s\"", metric.name().c_str()); + ALOGW("cannot find \"what\" in CountMetric \"%lld\"", (long long)metric.id()); return false; } int metricIndex = allMetricProducers.size(); - metricMap.insert({metric.name(), metricIndex}); + metricMap.insert({metric.id(), metricIndex}); int trackerIndex; if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.has_dimensions(), allAtomMatchers, logTrackerMap, trackerToMetricMap, @@ -256,7 +257,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti for (int i = 0; i < config.duration_metric_size(); i++) { int metricIndex = allMetricProducers.size(); const DurationMetric& metric = config.duration_metric(i); - metricMap.insert({metric.name(), metricIndex}); + metricMap.insert({metric.id(), metricIndex}); auto what_it = conditionTrackerMap.find(metric.what()); if (what_it == conditionTrackerMap.end()) { @@ -327,8 +328,8 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti for (int i = 0; i < config.event_metric_size(); i++) { int metricIndex = allMetricProducers.size(); const EventMetric& metric = config.event_metric(i); - metricMap.insert({metric.name(), metricIndex}); - if (!metric.has_name() || !metric.has_what()) { + metricMap.insert({metric.id(), metricIndex}); + if (!metric.has_id() || !metric.has_what()) { ALOGW("cannot find the metric name or what in config"); return false; } @@ -363,12 +364,12 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti for (int i = 0; i < config.value_metric_size(); i++) { const ValueMetric& metric = config.value_metric(i); if (!metric.has_what()) { - ALOGW("cannot find \"what\" in ValueMetric \"%s\"", metric.name().c_str()); + ALOGW("cannot find \"what\" in ValueMetric \"%lld\"", (long long)metric.id()); return false; } int metricIndex = allMetricProducers.size(); - metricMap.insert({metric.name(), metricIndex}); + metricMap.insert({metric.id(), metricIndex}); int trackerIndex; if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.has_dimensions(), allAtomMatchers, logTrackerMap, trackerToMetricMap, @@ -408,25 +409,25 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti for (int i = 0; i < config.gauge_metric_size(); i++) { const GaugeMetric& metric = config.gauge_metric(i); if (!metric.has_what()) { - ALOGW("cannot find \"what\" in GaugeMetric \"%s\"", metric.name().c_str()); + ALOGW("cannot find \"what\" in GaugeMetric \"%lld\"", (long long)metric.id()); return false; } if ((!metric.gauge_fields_filter().has_include_all() || (metric.gauge_fields_filter().include_all() == false)) && !hasLeafNode(metric.gauge_fields_filter().fields())) { - ALOGW("Incorrect field filter setting in GaugeMetric %s", metric.name().c_str()); + ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id()); return false; } if ((metric.gauge_fields_filter().has_include_all() && metric.gauge_fields_filter().include_all() == true) && hasLeafNode(metric.gauge_fields_filter().fields())) { - ALOGW("Incorrect field filter setting in GaugeMetric %s", metric.name().c_str()); + ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id()); return false; } int metricIndex = allMetricProducers.size(); - metricMap.insert({metric.name(), metricIndex}); + metricMap.insert({metric.id(), metricIndex}); int trackerIndex; if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.has_dimensions(), allAtomMatchers, logTrackerMap, trackerToMetricMap, @@ -461,49 +462,77 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti key, metric, conditionIndex, wizard, pullTagId, atomTagId, startTimeNs); allMetricProducers.push_back(gaugeProducer); } + for (int i = 0; i < config.no_report_metric_size(); ++i) { + const auto no_report_metric = config.no_report_metric(i); + if (metricMap.find(no_report_metric) == metricMap.end()) { + ALOGW("no_report_metric %lld not exist", no_report_metric); + return false; + } + noReportMetricIds.insert(no_report_metric); + } return true; } bool initAlerts(const StatsdConfig& config, - const unordered_map<string, int>& metricProducerMap, + const unordered_map<int64_t, int>& metricProducerMap, vector<sp<MetricProducer>>& allMetricProducers, vector<sp<AnomalyTracker>>& allAnomalyTrackers) { + unordered_map<int64_t, int> anomalyTrackerMap; for (int i = 0; i < config.alert_size(); i++) { const Alert& alert = config.alert(i); - const auto& itr = metricProducerMap.find(alert.metric_name()); + const auto& itr = metricProducerMap.find(alert.metric_id()); if (itr == metricProducerMap.end()) { - ALOGW("alert \"%s\" has unknown metric name: \"%s\"", alert.name().c_str(), - alert.metric_name().c_str()); + ALOGW("alert \"%lld\" has unknown metric id: \"%lld\"", (long long)alert.id(), + (long long)alert.metric_id()); return false; } - if (alert.trigger_if_sum_gt() < 0 || alert.number_of_buckets() <= 0) { - ALOGW("invalid alert: threshold=%lld num_buckets= %d", - alert.trigger_if_sum_gt(), alert.number_of_buckets()); + if (alert.trigger_if_sum_gt() < 0 || alert.num_buckets() <= 0) { + ALOGW("invalid alert: threshold=%f num_buckets= %d", + alert.trigger_if_sum_gt(), alert.num_buckets()); return false; } const int metricIndex = itr->second; sp<MetricProducer> metric = allMetricProducers[metricIndex]; sp<AnomalyTracker> anomalyTracker = metric->addAnomalyTracker(alert); if (anomalyTracker != nullptr) { + anomalyTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size())); allAnomalyTrackers.push_back(anomalyTracker); } } + for (int i = 0; i < config.subscription_size(); ++i) { + const Subscription& subscription = config.subscription(i); + if (subscription.subscriber_information_case() == + Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) { + ALOGW("subscription \"%lld\" has no subscriber info.\"", + (long long)subscription.id()); + return false; + } + const auto& itr = anomalyTrackerMap.find(subscription.rule_id()); + if (itr == anomalyTrackerMap.end()) { + ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"", + (long long)subscription.id(), (long long)subscription.rule_id()); + return false; + } + const int anomalyTrackerIndex = itr->second; + allAnomalyTrackers[anomalyTrackerIndex]->addSubscription(subscription); + } return true; } bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, - const UidMap& uidMap, - const long timeBaseSec, set<int>& allTagIds, + const UidMap& uidMap, + const long timeBaseSec, set<int>& allTagIds, vector<sp<LogMatchingTracker>>& allAtomMatchers, vector<sp<ConditionTracker>>& allConditionTrackers, vector<sp<MetricProducer>>& allMetricProducers, vector<sp<AnomalyTracker>>& allAnomalyTrackers, unordered_map<int, std::vector<int>>& conditionToMetricMap, unordered_map<int, std::vector<int>>& trackerToMetricMap, - unordered_map<int, std::vector<int>>& trackerToConditionMap) { - unordered_map<string, int> logTrackerMap; - unordered_map<string, int> conditionTrackerMap; - unordered_map<string, int> metricProducerMap; + unordered_map<int, std::vector<int>>& trackerToConditionMap, + std::set<int64_t> &noReportMetricIds) { + unordered_map<int64_t, int> logTrackerMap; + unordered_map<int64_t, int> conditionTrackerMap; + unordered_map<int64_t, int> metricProducerMap; if (!initLogTrackers(config, uidMap, logTrackerMap, allAtomMatchers, allTagIds)) { ALOGE("initLogMatchingTrackers failed"); @@ -519,7 +548,7 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, if (!initMetrics(key, config, timeBaseSec, logTrackerMap, conditionTrackerMap, allAtomMatchers, allConditionTrackers, allMetricProducers, conditionToMetricMap, - trackerToMetricMap, metricProducerMap)) { + trackerToMetricMap, metricProducerMap, noReportMetricIds)) { ALOGE("initMetricProducers failed"); return false; } diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h index 9ad5176ee6fd..4f19ada5b022 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.h +++ b/cmds/statsd/src/metrics/metrics_manager_util.h @@ -43,8 +43,8 @@ namespace statsd { // [allAtomMatchers]: should store the sp to all the LogMatchingTracker // [allTagIds]: contains the set of all interesting tag ids to this config. bool initLogTrackers(const StatsdConfig& config, - const UidMap& uidMap, - std::unordered_map<std::string, int>& logTrackerMap, + const UidMap& uidMap, + std::unordered_map<int64_t, int>& logTrackerMap, std::vector<sp<LogMatchingTracker>>& allAtomMatchers, std::set<int>& allTagIds); @@ -59,8 +59,8 @@ bool initLogTrackers(const StatsdConfig& config, // [trackerToConditionMap]: contain the mapping from index of // log tracker to condition trackers that use the log tracker bool initConditions(const ConfigKey& key, const StatsdConfig& config, - const std::unordered_map<std::string, int>& logTrackerMap, - std::unordered_map<std::string, int>& conditionTrackerMap, + const std::unordered_map<int64_t, int>& logTrackerMap, + std::unordered_map<int64_t, int>& conditionTrackerMap, std::vector<sp<ConditionTracker>>& allConditionTrackers, std::unordered_map<int, std::vector<int>>& trackerToConditionMap, std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks); @@ -79,28 +79,29 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, // [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index. bool initMetrics( const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec, - const std::unordered_map<std::string, int>& logTrackerMap, - const std::unordered_map<std::string, int>& conditionTrackerMap, + const std::unordered_map<int64_t, int>& logTrackerMap, + const std::unordered_map<int64_t, int>& conditionTrackerMap, const std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks, const vector<sp<LogMatchingTracker>>& allAtomMatchers, vector<sp<ConditionTracker>>& allConditionTrackers, std::vector<sp<MetricProducer>>& allMetricProducers, std::unordered_map<int, std::vector<int>>& conditionToMetricMap, - std::unordered_map<int, std::vector<int>>& trackerToMetricMap); + std::unordered_map<int, std::vector<int>>& trackerToMetricMap, + std::set<int64_t> &noReportMetricIds); // Initialize MetricsManager from StatsdConfig. // Parameters are the members of MetricsManager. See MetricsManager for declaration. bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, - - const UidMap& uidMap, - const long timeBaseSec, std::set<int>& allTagIds, + const UidMap& uidMap, + const long timeBaseSec, std::set<int>& allTagIds, std::vector<sp<LogMatchingTracker>>& allAtomMatchers, std::vector<sp<ConditionTracker>>& allConditionTrackers, std::vector<sp<MetricProducer>>& allMetricProducers, vector<sp<AnomalyTracker>>& allAnomalyTrackers, std::unordered_map<int, std::vector<int>>& conditionToMetricMap, std::unordered_map<int, std::vector<int>>& trackerToMetricMap, - std::unordered_map<int, std::vector<int>>& trackerToConditionMap); + std::unordered_map<int, std::vector<int>>& trackerToConditionMap, + std::set<int64_t> &noReportMetricIds); } // namespace statsd } // namespace os diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp index 416b87b36c54..517d21d6dcce 100644 --- a/cmds/statsd/src/packages/UidMap.cpp +++ b/cmds/statsd/src/packages/UidMap.cpp @@ -402,6 +402,79 @@ set<int32_t> UidMap::getAppUid(const string& package) const { return results; } +// Note not all the following AIDs are used as uids. Some are used only for gids. +// It's ok to leave them in the map, but we won't ever see them in the log's uid field. +// App's uid starts from 10000, and will not overlap with the following AIDs. +const std::map<string, uint32_t> UidMap::sAidToUidMapping = {{"AID_ROOT", 0}, + {"AID_SYSTEM", 1000}, + {"AID_RADIO", 1001}, + {"AID_BLUETOOTH", 1002}, + {"AID_GRAPHICS", 1003}, + {"AID_INPUT", 1004}, + {"AID_AUDIO", 1005}, + {"AID_CAMERA", 1006}, + {"AID_LOG", 1007}, + {"AID_COMPASS", 1008}, + {"AID_MOUNT", 1009}, + {"AID_WIFI", 1010}, + {"AID_ADB", 1011}, + {"AID_INSTALL", 1012}, + {"AID_MEDIA", 1013}, + {"AID_DHCP", 1014}, + {"AID_SDCARD_RW", 1015}, + {"AID_VPN", 1016}, + {"AID_KEYSTORE", 1017}, + {"AID_USB", 1018}, + {"AID_DRM", 1019}, + {"AID_MDNSR", 1020}, + {"AID_GPS", 1021}, + // {"AID_UNUSED1", 1022}, + {"AID_MEDIA_RW", 1023}, + {"AID_MTP", 1024}, + // {"AID_UNUSED2", 1025}, + {"AID_DRMRPC", 1026}, + {"AID_NFC", 1027}, + {"AID_SDCARD_R", 1028}, + {"AID_CLAT", 1029}, + {"AID_LOOP_RADIO", 1030}, + {"AID_MEDIA_DRM", 1031}, + {"AID_PACKAGE_INFO", 1032}, + {"AID_SDCARD_PICS", 1033}, + {"AID_SDCARD_AV", 1034}, + {"AID_SDCARD_ALL", 1035}, + {"AID_LOGD", 1036}, + {"AID_SHARED_RELRO", 1037}, + {"AID_DBUS", 1038}, + {"AID_TLSDATE", 1039}, + {"AID_MEDIA_EX", 1040}, + {"AID_AUDIOSERVER", 1041}, + {"AID_METRICS_COLL", 1042}, + {"AID_METRICSD", 1043}, + {"AID_WEBSERV", 1044}, + {"AID_DEBUGGERD", 1045}, + {"AID_MEDIA_CODEC", 1046}, + {"AID_CAMERASERVER", 1047}, + {"AID_FIREWALL", 1048}, + {"AID_TRUNKS", 1049}, + {"AID_NVRAM", 1050}, + {"AID_DNS", 1051}, + {"AID_DNS_TETHER", 1052}, + {"AID_WEBVIEW_ZYGOTE", 1053}, + {"AID_VEHICLE_NETWORK", 1054}, + {"AID_MEDIA_AUDIO", 1055}, + {"AID_MEDIA_VIDEO", 1056}, + {"AID_MEDIA_IMAGE", 1057}, + {"AID_TOMBSTONED", 1058}, + {"AID_MEDIA_OBB", 1059}, + {"AID_ESE", 1060}, + {"AID_OTA_UPDATE", 1061}, + {"AID_AUTOMOTIVE_EVS", 1062}, + {"AID_LOWPAN", 1063}, + {"AID_HSM", 1064}, + {"AID_SHELL", 2000}, + {"AID_CACHE", 2001}, + {"AID_DIAG", 2002}}; + } // namespace statsd } // namespace os } // namespace android
\ No newline at end of file diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h index 02dea54f1b50..07e13e02498c 100644 --- a/cmds/statsd/src/packages/UidMap.h +++ b/cmds/statsd/src/packages/UidMap.h @@ -51,7 +51,7 @@ class UidMap : public virtual android::RefBase { public: UidMap(); ~UidMap(); - + static const std::map<std::string, uint32_t> sAidToUidMapping; /* * All three inputs must be the same size, and the jth element in each array refers to the same * tuple, ie. uid[j] corresponds to packageName[j] with versionCode[j]. diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index f5282eaaecd4..0b369bb2fdbc 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -137,7 +137,7 @@ message UidMapping { } message StatsLogReport { - optional string metric_name = 1; + optional int64 metric_id = 1; optional int64 start_report_nanos = 2; @@ -178,7 +178,7 @@ message ConfigMetricsReport { message ConfigMetricsReportList { message ConfigKey { optional int32 uid = 1; - optional string name = 2; + optional int64 id = 2; } optional ConfigKey config_key = 1; @@ -191,28 +191,28 @@ message StatsdStatsReport { optional int32 stats_end_time_sec = 2; message MatcherStats { - optional string name = 1; + optional int64 id = 1; optional int32 matched_times = 2; } message ConditionStats { - optional string name = 1; + optional int64 id = 1; optional int32 max_tuple_counts = 2; } message MetricStats { - optional string name = 1; + optional int64 id = 1; optional int32 max_tuple_counts = 2; } message AlertStats { - optional string name = 1; + optional int64 id = 1; optional int32 alerted_times = 2; } message ConfigStats { optional int32 uid = 1; - optional string name = 2; + optional int64 id = 2; optional int32 creation_time_sec = 3; optional int32 deletion_time_sec = 4; optional int32 metric_count = 5; diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h index e46b8ebe0021..160b1f40243f 100644 --- a/cmds/statsd/src/stats_util.h +++ b/cmds/statsd/src/stats_util.h @@ -32,7 +32,7 @@ const HashableDimensionKey DEFAULT_DIMENSION_KEY = HashableDimensionKey(); // Minimum bucket size in seconds const long kMinBucketSizeSec = 5 * 60; -typedef std::map<std::string, std::vector<HashableDimensionKey>> ConditionKey; +typedef std::map<int64_t, std::vector<HashableDimensionKey>> ConditionKey; typedef std::unordered_map<HashableDimensionKey, int64_t> DimToValMap; diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index 1ed1e05ba272..1e0a437f30c2 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -84,12 +84,12 @@ message SimpleAtomMatcher { } message AtomMatcher { - optional string name = 1; + optional int64 id = 1; message Combination { optional LogicalOperation operation = 1; - repeated string matcher = 2; + repeated int64 matcher = 2; } oneof contents { SimpleAtomMatcher simple_atom_matcher = 2; @@ -98,13 +98,13 @@ message AtomMatcher { } message SimplePredicate { - optional string start = 1; + optional int64 start = 1; - optional string stop = 2; + optional int64 stop = 2; optional bool count_nesting = 3 [default = true]; - optional string stop_all = 4; + optional int64 stop_all = 4; enum InitialValue { UNKNOWN = 0; @@ -116,12 +116,12 @@ message SimplePredicate { } message Predicate { - optional string name = 1; + optional int64 id = 1; message Combination { optional LogicalOperation operation = 1; - repeated string predicate = 2; + repeated int64 predicate = 2; } oneof contents { @@ -135,7 +135,7 @@ message Bucket { } message MetricConditionLink { - optional string condition = 1; + optional int64 condition = 1; optional FieldMatcher dimensions_in_what = 2; @@ -148,21 +148,21 @@ message FieldFilter { } message EventMetric { - optional string name = 1; + optional int64 id = 1; - optional string what = 2; + optional int64 what = 2; - optional string condition = 3; + optional int64 condition = 3; repeated MetricConditionLink links = 4; } message CountMetric { - optional string name = 1; + optional int64 id = 1; - optional string what = 2; + optional int64 what = 2; - optional string condition = 3; + optional int64 condition = 3; optional FieldMatcher dimensions = 4; @@ -172,11 +172,11 @@ message CountMetric { } message DurationMetric { - optional string name = 1; + optional int64 id = 1; - optional string what = 2; + optional int64 what = 2; - optional string condition = 3; + optional int64 condition = 3; repeated MetricConditionLink links = 4; @@ -193,13 +193,13 @@ message DurationMetric { } message GaugeMetric { - optional string name = 1; + optional int64 id = 1; - optional string what = 2; + optional int64 what = 2; optional FieldFilter gauge_fields_filter = 3; - optional string condition = 4; + optional int64 condition = 4; optional FieldMatcher dimensions = 5; @@ -209,13 +209,13 @@ message GaugeMetric { } message ValueMetric { - optional string name = 1; + optional int64 id = 1; - optional string what = 2; + optional int64 what = 2; - optional int32 value_field = 3; + optional FieldMatcher value_field = 3; - optional string condition = 4; + optional int64 condition = 4; optional FieldMatcher dimensions = 5; @@ -228,29 +228,51 @@ message ValueMetric { } message Alert { - optional string name = 1; + optional int64 id = 1; - optional string metric_name = 2; + optional int64 metric_id = 2; - message IncidentdDetails { - repeated int32 section = 1; - } - optional IncidentdDetails incidentd_details = 3; + optional int32 num_buckets = 3; + + optional int32 refractory_period_secs = 4; - optional int32 number_of_buckets = 4; + optional double trigger_if_sum_gt = 5; +} - optional int32 refractory_period_secs = 5; +message Alarm { + optional int64 id = 1; + optional int64 offset_millis = 2; + optional int64 period_millis = 3; +} - optional int64 trigger_if_sum_gt = 6; +message IncidentdDetails { + repeated int32 section = 1; } -message AllowedLogSource { - repeated int32 uid = 1; - repeated string package = 2; +message PerfettoDetails { + optional int32 perfetto_stuff = 1; +} + +message Subscription { + optional int64 id = 1; + + enum RuleType { + RULE_TYPE_UNSPECIFIED = 0; + ALARM = 1; + ALERT = 2; + } + optional RuleType rule_type = 2; + + optional int64 rule_id = 3; + + oneof subscriber_information { + IncidentdDetails incidentd_details = 4; + PerfettoDetails perfetto_details = 5; + } } message StatsdConfig { - optional string name = 1; + optional int64 id = 1; repeated EventMetric event_metric = 2; @@ -268,5 +290,11 @@ message StatsdConfig { repeated Alert alert = 9; - optional AllowedLogSource log_source = 10; + repeated Alarm alarm = 10; + + repeated Subscription subscription = 11; + + repeated string allowed_log_source = 12; + + repeated int64 no_report_metric = 13; } diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp index 9919abf7532a..c542db2312ea 100644 --- a/cmds/statsd/src/storage/StorageManager.cpp +++ b/cmds/statsd/src/storage/StorageManager.cpp @@ -124,7 +124,7 @@ void StorageManager::sendBroadcast(const char* path, } if (index < 2) continue; - sendBroadcast(ConfigKey(uid, configName)); + sendBroadcast(ConfigKey(uid, StrToInt64(configName))); } } @@ -198,6 +198,7 @@ void StorageManager::readConfigFromDisk(map<ConfigKey, StatsdConfig>& configsMap index++; } if (index < 2) continue; + string file_name = StringPrintf("%s/%s", STATS_SERVICE_DIR, name); VLOG("full file %s", file_name.c_str()); int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC); @@ -206,7 +207,7 @@ void StorageManager::readConfigFromDisk(map<ConfigKey, StatsdConfig>& configsMap if (android::base::ReadFdToString(fd, &content)) { StatsdConfig config; if (config.ParseFromString(content)) { - configsMap[ConfigKey(uid, configName)] = config; + configsMap[ConfigKey(uid, StrToInt64(configName))] = config; VLOG("map key uid=%d|name=%s", uid, name); } } diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp index 3d923e246e71..3eac5d213c22 100644 --- a/cmds/statsd/tests/ConfigManager_test.cpp +++ b/cmds/statsd/tests/ConfigManager_test.cpp @@ -14,6 +14,7 @@ #include "src/config/ConfigManager.h" #include "src/metrics/MetricsManager.h" +#include "statsd_test_util.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -31,7 +32,7 @@ namespace os { namespace statsd { static ostream& operator<<(ostream& os, const StatsdConfig& config) { - return os << "StatsdConfig{name=" << config.name().c_str() << "}"; + return os << "StatsdConfig{id=" << config.id() << "}"; } } // namespace statsd @@ -50,19 +51,21 @@ public: /** * Validate that the ConfigKey is the one we wanted. */ -MATCHER_P2(ConfigKeyEq, uid, name, "") { - return arg.GetUid() == uid && arg.GetName() == name; +MATCHER_P2(ConfigKeyEq, uid, id, "") { + return arg.GetUid() == uid && (long long)arg.GetId() == (long long)id; } /** * Validate that the StatsdConfig is the one we wanted. */ -MATCHER_P(StatsdConfigEq, name, "") { - return arg.name() == name; +MATCHER_P(StatsdConfigEq, id, 0) { + return (long long)arg.id() == (long long)id; } +const int64_t testConfigId = 12345; + TEST(ConfigManagerTest, TestFakeConfig) { - auto metricsManager = std::make_unique<MetricsManager>(ConfigKey(0, "test"), + auto metricsManager = std::make_unique<MetricsManager>(ConfigKey(0, testConfigId), build_fake_config(), 1000, new UidMap()); EXPECT_TRUE(metricsManager->isConfigValid()); } @@ -77,13 +80,13 @@ TEST(ConfigManagerTest, TestAddUpdateRemove) { manager->AddListener(listener); StatsdConfig config91; - config91.set_name("91"); + config91.set_id(91); StatsdConfig config92; - config92.set_name("92"); + config92.set_id(92); StatsdConfig config93; - config93.set_name("93"); + config93.set_id(93); StatsdConfig config94; - config94.set_name("94"); + config94.set_id(94); { InSequence s; @@ -91,42 +94,46 @@ TEST(ConfigManagerTest, TestAddUpdateRemove) { manager->Startup(); // Add another one - EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "zzz"), StatsdConfigEq("91"))) + EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, StringToId("zzz")), + StatsdConfigEq(91))) .RetiresOnSaturation(); - manager->UpdateConfig(ConfigKey(1, "zzz"), config91); + manager->UpdateConfig(ConfigKey(1, StringToId("zzz")), config91); // Update It - EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "zzz"), StatsdConfigEq("92"))) + EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, StringToId("zzz")), + StatsdConfigEq(92))) .RetiresOnSaturation(); - manager->UpdateConfig(ConfigKey(1, "zzz"), config92); + manager->UpdateConfig(ConfigKey(1, StringToId("zzz")), config92); // Add one with the same uid but a different name - EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "yyy"), StatsdConfigEq("93"))) + EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, StringToId("yyy")), + StatsdConfigEq(93))) .RetiresOnSaturation(); - manager->UpdateConfig(ConfigKey(1, "yyy"), config93); + manager->UpdateConfig(ConfigKey(1, StringToId("yyy")), config93); // Add one with the same name but a different uid - EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(2, "zzz"), StatsdConfigEq("94"))) + EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(2, StringToId("zzz")), + StatsdConfigEq(94))) .RetiresOnSaturation(); - manager->UpdateConfig(ConfigKey(2, "zzz"), config94); + manager->UpdateConfig(ConfigKey(2, StringToId("zzz")), config94); // Remove (1,yyy) - EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(1, "yyy"))) + EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(1, StringToId("yyy")))) .RetiresOnSaturation(); - manager->RemoveConfig(ConfigKey(1, "yyy")); + manager->RemoveConfig(ConfigKey(1, StringToId("yyy"))); // Remove (2,zzz) - EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, "zzz"))) + EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("zzz")))) .RetiresOnSaturation(); - manager->RemoveConfig(ConfigKey(2, "zzz")); + manager->RemoveConfig(ConfigKey(2, StringToId("zzz"))); // Remove (1,zzz) - EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(1, "zzz"))) + EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(1, StringToId("zzz")))) .RetiresOnSaturation(); - manager->RemoveConfig(ConfigKey(1, "zzz")); + manager->RemoveConfig(ConfigKey(1, StringToId("zzz"))); // Remove (2,zzz) again and we shouldn't get the callback - manager->RemoveConfig(ConfigKey(2, "zzz")); + manager->RemoveConfig(ConfigKey(2, StringToId("zzz"))); } } @@ -142,16 +149,16 @@ TEST(ConfigManagerTest, TestRemoveUid) { StatsdConfig config; EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, _)).Times(5); - EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, "xxx"))); - EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, "yyy"))); - EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, "zzz"))); + EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("xxx")))); + EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("yyy")))); + EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("zzz")))); manager->Startup(); - manager->UpdateConfig(ConfigKey(1, "aaa"), config); - manager->UpdateConfig(ConfigKey(2, "xxx"), config); - manager->UpdateConfig(ConfigKey(2, "yyy"), config); - manager->UpdateConfig(ConfigKey(2, "zzz"), config); - manager->UpdateConfig(ConfigKey(3, "bbb"), config); + manager->UpdateConfig(ConfigKey(1, StringToId("aaa")), config); + manager->UpdateConfig(ConfigKey(2, StringToId("xxx")), config); + manager->UpdateConfig(ConfigKey(2, StringToId("yyy")), config); + manager->UpdateConfig(ConfigKey(2, StringToId("zzz")), config); + manager->UpdateConfig(ConfigKey(3, StringToId("bbb")), config); manager->RemoveConfigs(2); } diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp index 6cd31dd100c8..cb212a76a1dd 100644 --- a/cmds/statsd/tests/MetricsManager_test.cpp +++ b/cmds/statsd/tests/MetricsManager_test.cpp @@ -21,6 +21,7 @@ #include "src/metrics/MetricProducer.h" #include "src/metrics/ValueMetricProducer.h" #include "src/metrics/metrics_manager_util.h" +#include "statsd_test_util.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" @@ -40,16 +41,16 @@ using android::os::statsd::Predicate; // TODO: ADD MORE TEST CASES. -const ConfigKey kConfigKey(0, "test"); +const ConfigKey kConfigKey(0, 12345); const long timeBaseSec = 1000; StatsdConfig buildGoodConfig() { StatsdConfig config; - config.set_name("12345"); + config.set_id(12345); AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_name("SCREEN_IS_ON"); + eventMatcher->set_id(StringToId("SCREEN_IS_ON")); SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); @@ -59,7 +60,7 @@ StatsdConfig buildGoodConfig() { 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); eventMatcher = config.add_atom_matcher(); - eventMatcher->set_name("SCREEN_IS_OFF"); + eventMatcher->set_id(StringToId("SCREEN_IS_OFF")); simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); @@ -69,24 +70,26 @@ StatsdConfig buildGoodConfig() { 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/); eventMatcher = config.add_atom_matcher(); - eventMatcher->set_name("SCREEN_ON_OR_OFF"); + eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF")); AtomMatcher_Combination* combination = eventMatcher->mutable_combination(); combination->set_operation(LogicalOperation::OR); - combination->add_matcher("SCREEN_IS_ON"); - combination->add_matcher("SCREEN_IS_OFF"); + combination->add_matcher(StringToId("SCREEN_IS_ON")); + combination->add_matcher(StringToId("SCREEN_IS_OFF")); CountMetric* metric = config.add_count_metric(); - metric->set_name("3"); - metric->set_what("SCREEN_IS_ON"); + metric->set_id(3); + metric->set_what(StringToId("SCREEN_IS_ON")); metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); metric->mutable_dimensions()->set_field(2 /*SCREEN_STATE_CHANGE*/); metric->mutable_dimensions()->add_child()->set_field(1); + config.add_no_report_metric(3); + auto alert = config.add_alert(); - alert->set_name("3"); - alert->set_metric_name("3"); - alert->set_number_of_buckets(10); + alert->set_id(3); + alert->set_metric_id(3); + alert->set_num_buckets(10); alert->set_refractory_period_secs(100); alert->set_trigger_if_sum_gt(100); return config; @@ -94,10 +97,10 @@ StatsdConfig buildGoodConfig() { StatsdConfig buildCircleMatchers() { StatsdConfig config; - config.set_name("12345"); + config.set_id(12345); AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_name("SCREEN_IS_ON"); + eventMatcher->set_id(StringToId("SCREEN_IS_ON")); SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); @@ -107,35 +110,35 @@ StatsdConfig buildCircleMatchers() { 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); eventMatcher = config.add_atom_matcher(); - eventMatcher->set_name("SCREEN_ON_OR_OFF"); + eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF")); AtomMatcher_Combination* combination = eventMatcher->mutable_combination(); combination->set_operation(LogicalOperation::OR); - combination->add_matcher("SCREEN_IS_ON"); + combination->add_matcher(StringToId("SCREEN_IS_ON")); // Circle dependency - combination->add_matcher("SCREEN_ON_OR_OFF"); + combination->add_matcher(StringToId("SCREEN_ON_OR_OFF")); return config; } StatsdConfig buildAlertWithUnknownMetric() { StatsdConfig config; - config.set_name("12345"); + config.set_id(12345); AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_name("SCREEN_IS_ON"); + eventMatcher->set_id(StringToId("SCREEN_IS_ON")); CountMetric* metric = config.add_count_metric(); - metric->set_name("3"); - metric->set_what("SCREEN_IS_ON"); + metric->set_id(3); + metric->set_what(StringToId("SCREEN_IS_ON")); metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); metric->mutable_dimensions()->set_field(2 /*SCREEN_STATE_CHANGE*/); metric->mutable_dimensions()->add_child()->set_field(1); auto alert = config.add_alert(); - alert->set_name("3"); - alert->set_metric_name("2"); - alert->set_number_of_buckets(10); + alert->set_id(3); + alert->set_metric_id(2); + alert->set_num_buckets(10); alert->set_refractory_period_secs(100); alert->set_trigger_if_sum_gt(100); return config; @@ -143,10 +146,10 @@ StatsdConfig buildAlertWithUnknownMetric() { StatsdConfig buildMissingMatchers() { StatsdConfig config; - config.set_name("12345"); + config.set_id(12345); AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_name("SCREEN_IS_ON"); + eventMatcher->set_id(StringToId("SCREEN_IS_ON")); SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); @@ -156,29 +159,29 @@ StatsdConfig buildMissingMatchers() { 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); eventMatcher = config.add_atom_matcher(); - eventMatcher->set_name("SCREEN_ON_OR_OFF"); + eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF")); AtomMatcher_Combination* combination = eventMatcher->mutable_combination(); combination->set_operation(LogicalOperation::OR); - combination->add_matcher("SCREEN_IS_ON"); + combination->add_matcher(StringToId("SCREEN_IS_ON")); // undefined matcher - combination->add_matcher("ABC"); + combination->add_matcher(StringToId("ABC")); return config; } StatsdConfig buildMissingPredicate() { StatsdConfig config; - config.set_name("12345"); + config.set_id(12345); CountMetric* metric = config.add_count_metric(); - metric->set_name("3"); - metric->set_what("SCREEN_EVENT"); + metric->set_id(3); + metric->set_what(StringToId("SCREEN_EVENT")); metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); - metric->set_condition("SOME_CONDITION"); + metric->set_condition(StringToId("SOME_CONDITION")); AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_name("SCREEN_EVENT"); + eventMatcher->set_id(StringToId("SCREEN_EVENT")); SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); simpleAtomMatcher->set_atom_id(2); @@ -188,38 +191,38 @@ StatsdConfig buildMissingPredicate() { StatsdConfig buildDimensionMetricsWithMultiTags() { StatsdConfig config; - config.set_name("12345"); + config.set_id(12345); AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_name("BATTERY_VERY_LOW"); + eventMatcher->set_id(StringToId("BATTERY_VERY_LOW")); SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); simpleAtomMatcher->set_atom_id(2); eventMatcher = config.add_atom_matcher(); - eventMatcher->set_name("BATTERY_VERY_VERY_LOW"); + eventMatcher->set_id(StringToId("BATTERY_VERY_VERY_LOW")); simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); simpleAtomMatcher->set_atom_id(3); eventMatcher = config.add_atom_matcher(); - eventMatcher->set_name("BATTERY_LOW"); + eventMatcher->set_id(StringToId("BATTERY_LOW")); AtomMatcher_Combination* combination = eventMatcher->mutable_combination(); combination->set_operation(LogicalOperation::OR); - combination->add_matcher("BATTERY_VERY_LOW"); - combination->add_matcher("BATTERY_VERY_VERY_LOW"); + combination->add_matcher(StringToId("BATTERY_VERY_LOW")); + combination->add_matcher(StringToId("BATTERY_VERY_VERY_LOW")); // Count process state changes, slice by uid, while SCREEN_IS_OFF CountMetric* metric = config.add_count_metric(); - metric->set_name("3"); - metric->set_what("BATTERY_LOW"); + metric->set_id(3); + metric->set_what(StringToId("BATTERY_LOW")); metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); // This case is interesting. We want to dimension across two atoms. metric->mutable_dimensions()->add_child()->set_field(1); auto alert = config.add_alert(); - alert->set_name("3"); - alert->set_metric_name("3"); - alert->set_number_of_buckets(10); + alert->set_id(103); + alert->set_metric_id(3); + alert->set_num_buckets(10); alert->set_refractory_period_secs(100); alert->set_trigger_if_sum_gt(100); return config; @@ -227,10 +230,10 @@ StatsdConfig buildDimensionMetricsWithMultiTags() { StatsdConfig buildCirclePredicates() { StatsdConfig config; - config.set_name("12345"); + config.set_id(12345); AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_name("SCREEN_IS_ON"); + eventMatcher->set_id(StringToId("SCREEN_IS_ON")); SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); @@ -240,7 +243,7 @@ StatsdConfig buildCirclePredicates() { 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); eventMatcher = config.add_atom_matcher(); - eventMatcher->set_name("SCREEN_IS_OFF"); + eventMatcher->set_id(StringToId("SCREEN_IS_OFF")); simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); @@ -250,18 +253,18 @@ StatsdConfig buildCirclePredicates() { 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/); auto condition = config.add_predicate(); - condition->set_name("SCREEN_IS_ON"); + condition->set_id(StringToId("SCREEN_IS_ON")); SimplePredicate* simplePredicate = condition->mutable_simple_predicate(); - simplePredicate->set_start("SCREEN_IS_ON"); - simplePredicate->set_stop("SCREEN_IS_OFF"); + simplePredicate->set_start(StringToId("SCREEN_IS_ON")); + simplePredicate->set_stop(StringToId("SCREEN_IS_OFF")); condition = config.add_predicate(); - condition->set_name("SCREEN_IS_EITHER_ON_OFF"); + condition->set_id(StringToId("SCREEN_IS_EITHER_ON_OFF")); Predicate_Combination* combination = condition->mutable_combination(); combination->set_operation(LogicalOperation::OR); - combination->add_predicate("SCREEN_IS_ON"); - combination->add_predicate("SCREEN_IS_EITHER_ON_OFF"); + combination->add_predicate(StringToId("SCREEN_IS_ON")); + combination->add_predicate(StringToId("SCREEN_IS_EITHER_ON_OFF")); return config; } @@ -277,12 +280,15 @@ TEST(MetricsManagerTest, TestGoodConfig) { unordered_map<int, std::vector<int>> conditionToMetricMap; unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; + std::set<int64_t> noReportMetricIds; EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, - conditionToMetricMap, trackerToMetricMap, trackerToConditionMap)); + conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, + noReportMetricIds)); EXPECT_EQ(1u, allMetricProducers.size()); EXPECT_EQ(1u, allAnomalyTrackers.size()); + EXPECT_EQ(1u, noReportMetricIds.size()); } TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { @@ -296,10 +302,12 @@ TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { unordered_map<int, std::vector<int>> conditionToMetricMap; unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; + std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, - conditionToMetricMap, trackerToMetricMap, trackerToConditionMap)); + conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, + noReportMetricIds)); } TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { @@ -313,10 +321,12 @@ TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { unordered_map<int, std::vector<int>> conditionToMetricMap; unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; + std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, - conditionToMetricMap, trackerToMetricMap, trackerToConditionMap)); + conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, + noReportMetricIds)); } TEST(MetricsManagerTest, TestMissingMatchers) { @@ -330,9 +340,11 @@ TEST(MetricsManagerTest, TestMissingMatchers) { unordered_map<int, std::vector<int>> conditionToMetricMap; unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; + std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, - conditionToMetricMap, trackerToMetricMap, trackerToConditionMap)); + conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, + noReportMetricIds)); } TEST(MetricsManagerTest, TestMissingPredicate) { @@ -346,9 +358,11 @@ TEST(MetricsManagerTest, TestMissingPredicate) { unordered_map<int, std::vector<int>> conditionToMetricMap; unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; + std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, - conditionToMetricMap, trackerToMetricMap, trackerToConditionMap)); + conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, + noReportMetricIds)); } TEST(MetricsManagerTest, TestCirclePredicateDependency) { @@ -362,10 +376,12 @@ TEST(MetricsManagerTest, TestCirclePredicateDependency) { unordered_map<int, std::vector<int>> conditionToMetricMap; unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; + std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, - conditionToMetricMap, trackerToMetricMap, trackerToConditionMap)); + conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, + noReportMetricIds)); } TEST(MetricsManagerTest, testAlertWithUnknownMetric) { @@ -379,10 +395,12 @@ TEST(MetricsManagerTest, testAlertWithUnknownMetric) { unordered_map<int, std::vector<int>> conditionToMetricMap; unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; + std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, - conditionToMetricMap, trackerToMetricMap, trackerToConditionMap)); + conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, + noReportMetricIds)); } #else diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index a27bed552f9e..5d053e25003d 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -41,7 +41,7 @@ using android::util::ProtoOutputStream; */ class MockMetricsManager : public MetricsManager { public: - MockMetricsManager() : MetricsManager(ConfigKey(1, "key"), StatsdConfig(), 1000, new UidMap()) { + MockMetricsManager() : MetricsManager(ConfigKey(1, 12345), StatsdConfig(), 1000, new UidMap()) { } MOCK_METHOD0(byteSize, size_t()); @@ -56,7 +56,7 @@ TEST(StatsLogProcessorTest, TestRateLimitByteSize) { MockMetricsManager mockMetricsManager; - ConfigKey key(100, "key"); + ConfigKey key(100, 12345); // Expect only the first flush to trigger a check for byte size since the last two are // rate-limited. EXPECT_CALL(mockMetricsManager, byteSize()).Times(1); @@ -74,7 +74,7 @@ TEST(StatsLogProcessorTest, TestRateLimitBroadcast) { MockMetricsManager mockMetricsManager; - ConfigKey key(100, "key"); + ConfigKey key(100, 12345); EXPECT_CALL(mockMetricsManager, byteSize()) .Times(2) .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * .95))); @@ -98,7 +98,7 @@ TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) { MockMetricsManager mockMetricsManager; - ConfigKey key(100, "key"); + ConfigKey key(100, 12345); EXPECT_CALL(mockMetricsManager, byteSize()) .Times(1) .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * 1.2))); diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp index 8a394f75a0d0..945af2746eae 100644 --- a/cmds/statsd/tests/UidMap_test.cpp +++ b/cmds/statsd/tests/UidMap_test.cpp @@ -18,6 +18,7 @@ #include "guardrail/StatsdStats.h" #include "logd/LogEvent.h" #include "statslog.h" +#include "statsd_test_util.h" #include <gtest/gtest.h> @@ -156,8 +157,8 @@ TEST(UidMapTest, TestUpdateApp) { TEST(UidMapTest, TestClearingOutput) { UidMap m; - ConfigKey config1(1, "config1"); - ConfigKey config2(1, "config2"); + ConfigKey config1(1, StringToId("config1")); + ConfigKey config2(1, StringToId("config2")); m.OnConfigUpdated(config1); @@ -211,7 +212,7 @@ TEST(UidMapTest, TestClearingOutput) { TEST(UidMapTest, TestMemoryComputed) { UidMap m; - ConfigKey config1(1, "config1"); + ConfigKey config1(1, StringToId("config1")); m.OnConfigUpdated(config1); size_t startBytes = m.mBytesUsed; @@ -241,7 +242,7 @@ TEST(UidMapTest, TestMemoryGuardrail) { UidMap m; string buf; - ConfigKey config1(1, "config1"); + ConfigKey config1(1, StringToId("config1")); m.OnConfigUpdated(config1); size_t startBytes = m.mBytesUsed; diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp index da872add6c0d..5842bc889f93 100644 --- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp +++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp @@ -31,7 +31,7 @@ namespace android { namespace os { namespace statsd { -const ConfigKey kConfigKey(0, "test"); +const ConfigKey kConfigKey(0, 12345); HashableDimensionKey getMockDimensionKey(int key, string value) { DimensionsValue dimensionsValue; @@ -57,7 +57,7 @@ std::shared_ptr<DimToValMap> MockBucket( TEST(AnomalyTrackerTest, TestConsecutiveBuckets) { const int64_t bucketSizeNs = 30 * NS_PER_SEC; Alert alert; - alert.set_number_of_buckets(3); + alert.set_num_buckets(3); alert.set_refractory_period_secs(2 * bucketSizeNs / NS_PER_SEC); alert.set_trigger_if_sum_gt(2); @@ -177,7 +177,7 @@ TEST(AnomalyTrackerTest, TestConsecutiveBuckets) { TEST(AnomalyTrackerTest, TestSparseBuckets) { const int64_t bucketSizeNs = 30 * NS_PER_SEC; Alert alert; - alert.set_number_of_buckets(3); + alert.set_num_buckets(3); alert.set_refractory_period_secs(2 * bucketSizeNs / NS_PER_SEC); alert.set_trigger_if_sum_gt(2); diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp index 705804fef560..819f2bebf327 100644 --- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp +++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp @@ -11,7 +11,9 @@ // 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 "src/condition/SimpleConditionTracker.h" +#include "tests/statsd_test_util.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -29,7 +31,7 @@ namespace android { namespace os { namespace statsd { -const ConfigKey kConfigKey(0, "test"); +const ConfigKey kConfigKey(0, 12345); const int ATTRIBUTION_NODE_FIELD_ID = 1; const int ATTRIBUTION_UID_FIELD_ID = 1; @@ -38,9 +40,9 @@ const int TAG_ID = 1; SimplePredicate getWakeLockHeldCondition(bool countNesting, bool defaultFalse, bool outputSlicedUid, Position position) { SimplePredicate simplePredicate; - simplePredicate.set_start("WAKE_LOCK_ACQUIRE"); - simplePredicate.set_stop("WAKE_LOCK_RELEASE"); - simplePredicate.set_stop_all("RELEASE_ALL"); + simplePredicate.set_start(StringToId("WAKE_LOCK_ACQUIRE")); + simplePredicate.set_stop(StringToId("WAKE_LOCK_RELEASE")); + simplePredicate.set_stop_all(StringToId("RELEASE_ALL")); if (outputSlicedUid) { simplePredicate.mutable_dimensions()->set_field(TAG_ID); simplePredicate.mutable_dimensions()->add_child()->set_field(ATTRIBUTION_NODE_FIELD_ID); @@ -73,10 +75,10 @@ void makeWakeLockEvent( event->init(); } -std::map<string, std::vector<HashableDimensionKey>> getWakeLockQueryKey( +std::map<int64_t, std::vector<HashableDimensionKey>> getWakeLockQueryKey( const Position position, const std::vector<int> &uids, const string& conditionName) { - std::map<string, std::vector<HashableDimensionKey>> outputKeyMap; + std::map<int64_t, std::vector<HashableDimensionKey>> outputKeyMap; std::vector<int> uid_indexes; switch(position) { case Position::FIRST: @@ -96,28 +98,29 @@ std::map<string, std::vector<HashableDimensionKey>> getWakeLockQueryKey( for (const int idx : uid_indexes) { DimensionsValue dimensionsValue; dimensionsValue.set_field(TAG_ID); - dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field(ATTRIBUTION_NODE_FIELD_ID); + dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field( + ATTRIBUTION_NODE_FIELD_ID); dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0) ->mutable_value_tuple()->add_dimensions_value()->set_field(ATTRIBUTION_NODE_FIELD_ID); dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0) ->mutable_value_tuple()->mutable_dimensions_value(0)->set_value_int(uids[idx]); - outputKeyMap[conditionName].push_back(HashableDimensionKey(dimensionsValue)); + outputKeyMap[StringToId(conditionName)].push_back(HashableDimensionKey(dimensionsValue)); } return outputKeyMap; } TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) { SimplePredicate simplePredicate; - simplePredicate.set_start("SCREEN_TURNED_ON"); - simplePredicate.set_stop("SCREEN_TURNED_OFF"); + simplePredicate.set_start(StringToId("SCREEN_TURNED_ON")); + simplePredicate.set_stop(StringToId("SCREEN_TURNED_OFF")); simplePredicate.set_count_nesting(false); simplePredicate.set_initial_value(SimplePredicate_InitialValue_UNKNOWN); - unordered_map<string, int> trackerNameIndexMap; - trackerNameIndexMap["SCREEN_TURNED_ON"] = 0; - trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1; + unordered_map<int64_t, int> trackerNameIndexMap; + trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0; + trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1; - SimpleConditionTracker conditionTracker(kConfigKey, "SCREEN_IS_ON", 0 /*tracker index*/, + SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), 0 /*tracker index*/, simplePredicate, trackerNameIndexMap); LogEvent event(1 /*tagId*/, 0 /*timestamp*/); @@ -191,15 +194,15 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) { TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) { SimplePredicate simplePredicate; - simplePredicate.set_start("SCREEN_TURNED_ON"); - simplePredicate.set_stop("SCREEN_TURNED_OFF"); + simplePredicate.set_start(StringToId("SCREEN_TURNED_ON")); + simplePredicate.set_stop(StringToId("SCREEN_TURNED_OFF")); simplePredicate.set_count_nesting(true); - unordered_map<string, int> trackerNameIndexMap; - trackerNameIndexMap["SCREEN_TURNED_ON"] = 0; - trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1; + unordered_map<int64_t, int> trackerNameIndexMap; + trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0; + trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1; - SimpleConditionTracker conditionTracker(kConfigKey, "SCREEN_IS_ON", + SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), 0 /*condition tracker index*/, simplePredicate, trackerNameIndexMap); @@ -267,12 +270,12 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { position); string conditionName = "WL_HELD_BY_UID2"; - unordered_map<string, int> trackerNameIndexMap; - trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0; - trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1; - trackerNameIndexMap["RELEASE_ALL"] = 2; + unordered_map<int64_t, int> trackerNameIndexMap; + trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0; + trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1; + trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2; - SimpleConditionTracker conditionTracker(kConfigKey, conditionName, + SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), 0 /*condition tracker index*/, simplePredicate, trackerNameIndexMap); std::vector<int> uids = {111, 222, 333}; @@ -371,12 +374,12 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { Position::ANY /* position */); string conditionName = "WL_HELD"; - unordered_map<string, int> trackerNameIndexMap; - trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0; - trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1; - trackerNameIndexMap["RELEASE_ALL"] = 2; + unordered_map<int64_t, int> trackerNameIndexMap; + trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0; + trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1; + trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2; - SimpleConditionTracker conditionTracker(kConfigKey, conditionName, + SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), 0 /*condition tracker index*/, simplePredicate, trackerNameIndexMap); @@ -461,12 +464,12 @@ TEST(SimpleConditionTrackerTest, TestStopAll) { position); string conditionName = "WL_HELD_BY_UID3"; - unordered_map<string, int> trackerNameIndexMap; - trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0; - trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1; - trackerNameIndexMap["RELEASE_ALL"] = 2; + unordered_map<int64_t, int> trackerNameIndexMap; + trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0; + trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1; + trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2; - SimpleConditionTracker conditionTracker(kConfigKey, conditionName, + SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), 0 /*condition tracker index*/, simplePredicate, trackerNameIndexMap); diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp index c747016161d3..b56b8176cceb 100644 --- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp @@ -55,16 +55,16 @@ StatsdConfig CreateStatsdConfig() { *config.add_predicate() = isInBackgroundPredicate; auto combinationPredicate = config.add_predicate(); - combinationPredicate->set_name("combinationPredicate"); + combinationPredicate->set_id(StringToId("combinationPredicate")); combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND); addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate); addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate); addPredicateToPredicateCombination(isInBackgroundPredicate, combinationPredicate); auto countMetric = config.add_count_metric(); - countMetric->set_name("AppCrashes"); - countMetric->set_what(appCrashMatcher.name()); - countMetric->set_condition(combinationPredicate->name()); + countMetric->set_id(StringToId("AppCrashes")); + countMetric->set_what(appCrashMatcher.id()); + countMetric->set_condition(combinationPredicate->id()); // The metric is dimensioning by uid only. *countMetric->mutable_dimensions() = CreateDimensions(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1}); @@ -72,7 +72,7 @@ StatsdConfig CreateStatsdConfig() { // Links between crash atom and condition of app is in syncing. auto links = countMetric->add_links(); - links->set_condition(isSyncingPredicate.name()); + links->set_condition(isSyncingPredicate.id()); auto dimensionWhat = links->mutable_dimensions_in_what(); dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); dimensionWhat->add_child()->set_field(1); // uid field. @@ -82,7 +82,7 @@ StatsdConfig CreateStatsdConfig() { // Links between crash atom and condition of app is in background. links = countMetric->add_links(); - links->set_condition(isInBackgroundPredicate.name()); + links->set_condition(isInBackgroundPredicate.id()); dimensionWhat = links->mutable_dimensions_in_what(); dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); dimensionWhat->add_child()->set_field(1); // uid field. diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp index 8d7b2d511eaa..ecdb002c1863 100644 --- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp @@ -43,9 +43,9 @@ StatsdConfig CreateStatsdConfig(DurationMetric::AggregationType aggregationType) *config.add_predicate() = holdingWakelockPredicate; auto durationMetric = config.add_duration_metric(); - durationMetric->set_name("WakelockDuration"); - durationMetric->set_what(holdingWakelockPredicate.name()); - durationMetric->set_condition(screenIsOffPredicate.name()); + durationMetric->set_id(StringToId("WakelockDuration")); + durationMetric->set_what(holdingWakelockPredicate.id()); + durationMetric->set_condition(screenIsOffPredicate.id()); durationMetric->set_aggregation_type(aggregationType); // The metric is dimensioning by first attribution node and only by uid. *durationMetric->mutable_dimensions() = diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp index 765804475eef..a1343002405b 100644 --- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp +++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp @@ -14,6 +14,7 @@ #include "src/guardrail/StatsdStats.h" #include "statslog.h" +#include "tests/statsd_test_util.h" #include <gtest/gtest.h> #include <vector> @@ -28,8 +29,7 @@ using std::vector; TEST(StatsdStatsTest, TestValidConfigAdd) { StatsdStats stats; - string name = "StatsdTest"; - ConfigKey key(0, name); + ConfigKey key(0, 12345); const int metricsCount = 10; const int conditionsCount = 20; const int matchersCount = 30; @@ -45,7 +45,7 @@ TEST(StatsdStatsTest, TestValidConfigAdd) { EXPECT_EQ(1, report.config_stats_size()); const auto& configReport = report.config_stats(0); EXPECT_EQ(0, configReport.uid()); - EXPECT_EQ(name, configReport.name()); + EXPECT_EQ(12345, configReport.id()); EXPECT_EQ(metricsCount, configReport.metric_count()); EXPECT_EQ(conditionsCount, configReport.condition_count()); EXPECT_EQ(matchersCount, configReport.matcher_count()); @@ -56,8 +56,7 @@ TEST(StatsdStatsTest, TestValidConfigAdd) { TEST(StatsdStatsTest, TestInvalidConfigAdd) { StatsdStats stats; - string name = "StatsdTest"; - ConfigKey key(0, name); + ConfigKey key(0, 12345); const int metricsCount = 10; const int conditionsCount = 20; const int matchersCount = 30; @@ -78,8 +77,7 @@ TEST(StatsdStatsTest, TestInvalidConfigAdd) { TEST(StatsdStatsTest, TestConfigRemove) { StatsdStats stats; - string name = "StatsdTest"; - ConfigKey key(0, name); + ConfigKey key(0, 12345); const int metricsCount = 10; const int conditionsCount = 20; const int matchersCount = 30; @@ -105,22 +103,22 @@ TEST(StatsdStatsTest, TestConfigRemove) { TEST(StatsdStatsTest, TestSubStats) { StatsdStats stats; - ConfigKey key(0, "test"); + ConfigKey key(0, 12345); stats.noteConfigReceived(key, 2, 3, 4, 5, true); - stats.noteMatcherMatched(key, "matcher1"); - stats.noteMatcherMatched(key, "matcher1"); - stats.noteMatcherMatched(key, "matcher2"); + stats.noteMatcherMatched(key, StringToId("matcher1")); + stats.noteMatcherMatched(key, StringToId("matcher1")); + stats.noteMatcherMatched(key, StringToId("matcher2")); - stats.noteConditionDimensionSize(key, "condition1", 250); - stats.noteConditionDimensionSize(key, "condition1", 240); + stats.noteConditionDimensionSize(key, StringToId("condition1"), 250); + stats.noteConditionDimensionSize(key, StringToId("condition1"), 240); - stats.noteMetricDimensionSize(key, "metric1", 201); - stats.noteMetricDimensionSize(key, "metric1", 202); + stats.noteMetricDimensionSize(key, StringToId("metric1"), 201); + stats.noteMetricDimensionSize(key, StringToId("metric1"), 202); - stats.noteAnomalyDeclared(key, "alert1"); - stats.noteAnomalyDeclared(key, "alert1"); - stats.noteAnomalyDeclared(key, "alert2"); + stats.noteAnomalyDeclared(key, StringToId("alert1")); + stats.noteAnomalyDeclared(key, StringToId("alert1")); + stats.noteAnomalyDeclared(key, StringToId("alert2")); // broadcast-> 2 stats.noteBroadcastSent(key); @@ -147,39 +145,39 @@ TEST(StatsdStatsTest, TestSubStats) { EXPECT_EQ(2, configReport.matcher_stats_size()); // matcher1 is the first in the list - if (!configReport.matcher_stats(0).name().compare("matcher1")) { + if (configReport.matcher_stats(0).id() == StringToId("matcher1")) { EXPECT_EQ(2, configReport.matcher_stats(0).matched_times()); EXPECT_EQ(1, configReport.matcher_stats(1).matched_times()); - EXPECT_EQ("matcher2", configReport.matcher_stats(1).name()); + EXPECT_EQ(StringToId("matcher2"), configReport.matcher_stats(1).id()); } else { // matcher1 is the second in the list. EXPECT_EQ(1, configReport.matcher_stats(0).matched_times()); - EXPECT_EQ("matcher2", configReport.matcher_stats(0).name()); + EXPECT_EQ(StringToId("matcher2"), configReport.matcher_stats(0).id()); EXPECT_EQ(2, configReport.matcher_stats(1).matched_times()); - EXPECT_EQ("matcher1", configReport.matcher_stats(1).name()); + EXPECT_EQ(StringToId("matcher1"), configReport.matcher_stats(1).id()); } EXPECT_EQ(2, configReport.alert_stats_size()); - bool alert1first = !configReport.alert_stats(0).name().compare("alert1"); - EXPECT_EQ("alert1", configReport.alert_stats(alert1first ? 0 : 1).name()); + bool alert1first = configReport.alert_stats(0).id() == StringToId("alert1"); + EXPECT_EQ(StringToId("alert1"), configReport.alert_stats(alert1first ? 0 : 1).id()); EXPECT_EQ(2, configReport.alert_stats(alert1first ? 0 : 1).alerted_times()); - EXPECT_EQ("alert2", configReport.alert_stats(alert1first ? 1 : 0).name()); + EXPECT_EQ(StringToId("alert2"), configReport.alert_stats(alert1first ? 1 : 0).id()); EXPECT_EQ(1, configReport.alert_stats(alert1first ? 1 : 0).alerted_times()); EXPECT_EQ(1, configReport.condition_stats_size()); - EXPECT_EQ("condition1", configReport.condition_stats(0).name()); + EXPECT_EQ(StringToId("condition1"), configReport.condition_stats(0).id()); EXPECT_EQ(250, configReport.condition_stats(0).max_tuple_counts()); EXPECT_EQ(1, configReport.metric_stats_size()); - EXPECT_EQ("metric1", configReport.metric_stats(0).name()); + EXPECT_EQ(StringToId("metric1"), configReport.metric_stats(0).id()); EXPECT_EQ(202, configReport.metric_stats(0).max_tuple_counts()); // after resetting the stats, some new events come - stats.noteMatcherMatched(key, "matcher99"); - stats.noteConditionDimensionSize(key, "condition99", 300); - stats.noteMetricDimensionSize(key, "metric99", 270); - stats.noteAnomalyDeclared(key, "alert99"); + stats.noteMatcherMatched(key, StringToId("matcher99")); + stats.noteConditionDimensionSize(key, StringToId("condition99"), 300); + stats.noteMetricDimensionSize(key, StringToId("metric99tion99"), 270); + stats.noteAnomalyDeclared(key, StringToId("alert99")); // now the config stats should only contain the stats about the new event. stats.dumpStats(&output, false); @@ -188,19 +186,19 @@ TEST(StatsdStatsTest, TestSubStats) { EXPECT_EQ(1, report.config_stats_size()); const auto& configReport2 = report.config_stats(0); EXPECT_EQ(1, configReport2.matcher_stats_size()); - EXPECT_EQ("matcher99", configReport2.matcher_stats(0).name()); + EXPECT_EQ(StringToId("matcher99"), configReport2.matcher_stats(0).id()); EXPECT_EQ(1, configReport2.matcher_stats(0).matched_times()); EXPECT_EQ(1, configReport2.condition_stats_size()); - EXPECT_EQ("condition99", configReport2.condition_stats(0).name()); + EXPECT_EQ(StringToId("condition99"), configReport2.condition_stats(0).id()); EXPECT_EQ(300, configReport2.condition_stats(0).max_tuple_counts()); EXPECT_EQ(1, configReport2.metric_stats_size()); - EXPECT_EQ("metric99", configReport2.metric_stats(0).name()); + EXPECT_EQ(StringToId("metric99tion99"), configReport2.metric_stats(0).id()); EXPECT_EQ(270, configReport2.metric_stats(0).max_tuple_counts()); EXPECT_EQ(1, configReport2.alert_stats_size()); - EXPECT_EQ("alert99", configReport2.alert_stats(0).name()); + EXPECT_EQ(StringToId("alert99"), configReport2.alert_stats(0).id()); EXPECT_EQ(1, configReport2.alert_stats(0).alerted_times()); } @@ -260,7 +258,7 @@ TEST(StatsdStatsTest, TestTimestampThreshold) { for (int i = 0; i < StatsdStats::kMaxTimestampCount; i++) { timestamps.push_back(i); } - ConfigKey key(0, "test"); + ConfigKey key(0, 12345); stats.noteConfigReceived(key, 2, 3, 4, 5, true); for (int i = 0; i < StatsdStats::kMaxTimestampCount; i++) { diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp index 6e114a628fef..4cb242aa3bcc 100644 --- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp @@ -15,6 +15,7 @@ #include "src/metrics/CountMetricProducer.h" #include "src/dimension.h" #include "metrics_test_helper.h" +#include "tests/statsd_test_util.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -33,7 +34,7 @@ namespace android { namespace os { namespace statsd { -const ConfigKey kConfigKey(0, "test"); +const ConfigKey kConfigKey(0, 12345); TEST(CountMetricProducerTest, TestNonDimensionalEvents) { int64_t bucketStartTimeNs = 10000000000; @@ -43,7 +44,7 @@ TEST(CountMetricProducerTest, TestNonDimensionalEvents) { int tagId = 1; CountMetric metric; - metric.set_name("1"); + metric.set_id(1); metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000); LogEvent event1(tagId, bucketStartTimeNs + 1); @@ -100,9 +101,9 @@ TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) { int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; CountMetric metric; - metric.set_name("1"); + metric.set_id(1); metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000); - metric.set_condition("SCREEN_ON"); + metric.set_condition(StringToId("SCREEN_ON")); LogEvent event1(1, bucketStartTimeNs + 1); LogEvent event2(1, bucketStartTimeNs + 10); @@ -142,11 +143,11 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { int conditionTagId = 2; CountMetric metric; - metric.set_name("1"); + metric.set_id(1); metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000); - metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON"); + metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON")); MetricConditionLink* link = metric.add_links(); - link->set_condition("APP_IN_BACKGROUND_PER_UID"); + link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID")); *link->mutable_dimensions_in_what() = buildSimpleAtomFieldMatcher(tagId, 1); *link->mutable_dimensions_in_condition() = buildSimpleAtomFieldMatcher(conditionTagId, 2); @@ -154,13 +155,15 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { event1.write("111"); // uid event1.init(); ConditionKey key1; - key1["APP_IN_BACKGROUND_PER_UID"] = {getMockedDimensionKey(conditionTagId, 2, "111")}; + key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = + {getMockedDimensionKey(conditionTagId, 2, "111")}; LogEvent event2(tagId, bucketStartTimeNs + 10); event2.write("222"); // uid event2.init(); ConditionKey key2; - key2["APP_IN_BACKGROUND_PER_UID"] = {getMockedDimensionKey(conditionTagId, 2, "222")}; + key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = + {getMockedDimensionKey(conditionTagId, 2, "222")}; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse)); @@ -189,10 +192,10 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { TEST(CountMetricProducerTest, TestAnomalyDetection) { Alert alert; - alert.set_name("alert"); - alert.set_metric_name("1"); + alert.set_id(11); + alert.set_metric_id(1); alert.set_trigger_if_sum_gt(2); - alert.set_number_of_buckets(2); + alert.set_num_buckets(2); alert.set_refractory_period_secs(1); int64_t bucketStartTimeNs = 10000000000; @@ -201,7 +204,7 @@ TEST(CountMetricProducerTest, TestAnomalyDetection) { int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; CountMetric metric; - metric.set_name("1"); + metric.set_id(1); metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp index 8ee94c75356f..a4213dec7c44 100644 --- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp @@ -36,7 +36,7 @@ namespace android { namespace os { namespace statsd { -const ConfigKey kConfigKey(0, "test"); +const ConfigKey kConfigKey(0, 12345); TEST(DurationMetricTrackerTest, TestNoCondition) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); @@ -44,7 +44,7 @@ TEST(DurationMetricTrackerTest, TestNoCondition) { uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; DurationMetric metric; - metric.set_name("1"); + metric.set_id(1); metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000); metric.set_aggregation_type(DurationMetric_AggregationType_SUM); @@ -79,7 +79,7 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; DurationMetric metric; - metric.set_name("1"); + metric.set_id(1); metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000); metric.set_aggregation_type(DurationMetric_AggregationType_SUM); diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp index 0ba1c2f522d9..7171de939c62 100644 --- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp @@ -15,6 +15,7 @@ #include "src/metrics/EventMetricProducer.h" #include "src/dimension.h" #include "metrics_test_helper.h" +#include "tests/statsd_test_util.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -33,7 +34,7 @@ namespace android { namespace os { namespace statsd { -const ConfigKey kConfigKey(0, "test"); +const ConfigKey kConfigKey(0, 12345); TEST(EventMetricProducerTest, TestNoCondition) { uint64_t bucketStartTimeNs = 10000000000; @@ -41,7 +42,7 @@ TEST(EventMetricProducerTest, TestNoCondition) { uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; EventMetric metric; - metric.set_name("1"); + metric.set_id(1); LogEvent event1(1 /*tag id*/, bucketStartTimeNs + 1); LogEvent event2(1 /*tag id*/, bucketStartTimeNs + 2); @@ -64,8 +65,8 @@ TEST(EventMetricProducerTest, TestEventsWithNonSlicedCondition) { uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; EventMetric metric; - metric.set_name("1"); - metric.set_condition("SCREEN_ON"); + metric.set_id(1); + metric.set_condition(StringToId("SCREEN_ON")); LogEvent event1(1, bucketStartTimeNs + 1); LogEvent event2(1, bucketStartTimeNs + 10); @@ -93,10 +94,10 @@ TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) { int conditionTagId = 2; EventMetric metric; - metric.set_name("1"); - metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON"); + metric.set_id(1); + metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON")); MetricConditionLink* link = metric.add_links(); - link->set_condition("APP_IN_BACKGROUND_PER_UID"); + link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID")); *link->mutable_dimensions_in_what() = buildSimpleAtomFieldMatcher(tagId, 1); *link->mutable_dimensions_in_condition() = buildSimpleAtomFieldMatcher(conditionTagId, 2); @@ -104,13 +105,13 @@ TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) { EXPECT_TRUE(event1.write("111")); event1.init(); ConditionKey key1; - key1["APP_IN_BACKGROUND_PER_UID"] = {getMockedDimensionKey(conditionTagId, 2, "111")}; + key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = {getMockedDimensionKey(conditionTagId, 2, "111")}; LogEvent event2(tagId, bucketStartTimeNs + 10); EXPECT_TRUE(event2.write("222")); event2.init(); ConditionKey key2; - key2["APP_IN_BACKGROUND_PER_UID"] = {getMockedDimensionKey(conditionTagId, 2, "222")}; + key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {getMockedDimensionKey(conditionTagId, 2, "222")}; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse)); diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp index 359851fbb217..749cf26e4630 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -15,6 +15,7 @@ #include "src/metrics/GaugeMetricProducer.h" #include "logd/LogEvent.h" #include "metrics_test_helper.h" +#include "tests/statsd_test_util.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -34,9 +35,9 @@ namespace android { namespace os { namespace statsd { -const ConfigKey kConfigKey(0, "test"); +const ConfigKey kConfigKey(0, 12345); const int tagId = 1; -const string metricName = "test_metric"; +const int64_t metricId = 123; const int64_t bucketStartTimeNs = 10000000000; const int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL; const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; @@ -45,7 +46,7 @@ const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs; TEST(GaugeMetricProducerTest, TestNoCondition) { GaugeMetric metric; - metric.set_name(metricName); + metric.set_id(metricId); metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000); metric.mutable_gauge_fields_filter()->set_include_all(false); auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); @@ -119,12 +120,12 @@ TEST(GaugeMetricProducerTest, TestNoCondition) { TEST(GaugeMetricProducerTest, TestWithCondition) { GaugeMetric metric; - metric.set_name(metricName); + metric.set_id(metricId); metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000); auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); gaugeFieldMatcher->set_field(tagId); gaugeFieldMatcher->add_child()->set_field(2); - metric.set_condition("SCREEN_ON"); + metric.set_condition(StringToId("SCREEN_ON")); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); @@ -186,7 +187,7 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) { EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); GaugeMetric metric; - metric.set_name(metricName); + metric.set_id(metricId); metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000); auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); gaugeFieldMatcher->set_field(tagId); @@ -195,10 +196,10 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) { tagId, tagId, bucketStartTimeNs, pullerManager); Alert alert; - alert.set_name("alert"); - alert.set_metric_name(metricName); + alert.set_id(101); + alert.set_metric_id(metricId); alert.set_trigger_if_sum_gt(25); - alert.set_number_of_buckets(2); + alert.set_num_buckets(2); sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert); int tagId = 1; diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp index 7843ca085d5b..704a46691328 100644 --- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp @@ -13,8 +13,9 @@ // limitations under the License. #include "src/metrics/duration_helper/MaxDurationTracker.h" -#include "metrics_test_helper.h" #include "src/condition/ConditionWizard.h" +#include "metrics_test_helper.h" +#include "tests/statsd_test_util.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -36,7 +37,7 @@ namespace android { namespace os { namespace statsd { -const ConfigKey kConfigKey(0, "test"); +const ConfigKey kConfigKey(0, 12345); const int TagId = 1; @@ -50,12 +51,13 @@ TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) { unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; ConditionKey conditionKey1; - conditionKey1["condition"] = conditionKey; + conditionKey1[StringToId("condition")] = conditionKey; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, false, bucketStartTimeNs, + int64_t metricId = 1; + MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {}); tracker.noteStart(key1, true, bucketStartTimeNs, conditionKey1); @@ -79,12 +81,13 @@ TEST(MaxDurationTrackerTest, TestStopAll) { unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; ConditionKey conditionKey1; - conditionKey1["condition"] = conditionKey; + conditionKey1[StringToId("condition")] = conditionKey; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, false, bucketStartTimeNs, + int64_t metricId = 1; + MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {}); tracker.noteStart(key1, true, bucketStartTimeNs + 1, conditionKey1); @@ -110,12 +113,13 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) { unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; ConditionKey conditionKey1; - conditionKey1["condition"] = conditionKey; + conditionKey1[StringToId("condition")] = conditionKey; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, false, bucketStartTimeNs, + int64_t metricId = 1; + MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {}); // The event starts. @@ -141,12 +145,13 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) { unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; ConditionKey conditionKey1; - conditionKey1["condition"] = conditionKey; + conditionKey1[StringToId("condition")] = conditionKey; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, true, bucketStartTimeNs, + int64_t metricId = 1; + MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs, bucketSizeNs, {}); // 2 starts @@ -177,7 +182,7 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { ConditionKey conditionKey1; HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 2, "maps"); - conditionKey1["APP_BACKGROUND"] = conditionKey; + conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey; EXPECT_CALL(*wizard, query(_, conditionKey1)) // #4 .WillOnce(Return(ConditionState::kFalse)); @@ -189,7 +194,8 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; int64_t durationTimeNs = 2 * 1000; - MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false, bucketStartTimeNs, + int64_t metricId = 1; + MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {}); EXPECT_TRUE(tracker.mAnomalyTrackers.empty()); @@ -206,23 +212,24 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { } TEST(MaxDurationTrackerTest, TestAnomalyDetection) { + int64_t metricId = 1; Alert alert; - alert.set_name("alert"); - alert.set_metric_name("metric"); + alert.set_id(101); + alert.set_metric_id(metricId); alert.set_trigger_if_sum_gt(32 * NS_PER_SEC); - alert.set_number_of_buckets(2); + alert.set_num_buckets(2); alert.set_refractory_period_secs(1); unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey conditionKey1; - conditionKey1["APP_BACKGROUND"] = conditionKey; + conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey; uint64_t bucketStartTimeNs = 10 * NS_PER_SEC; uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; uint64_t bucketSizeNs = 30 * NS_PER_SEC; sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); - MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, true, bucketStartTimeNs, + MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs, bucketSizeNs, {anomalyTracker}); tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1); diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp index 550b059e4d69..36cdaae01b4f 100644 --- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp @@ -13,8 +13,9 @@ // limitations under the License. #include "src/metrics/duration_helper/OringDurationTracker.h" -#include "metrics_test_helper.h" #include "src/condition/ConditionWizard.h" +#include "metrics_test_helper.h" +#include "tests/statsd_test_util.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -34,8 +35,9 @@ namespace android { namespace os { namespace statsd { -const ConfigKey kConfigKey(0, "test"); +const ConfigKey kConfigKey(0, 12345); const int TagId = 1; +const int64_t metricId = 123; const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "event"); const std::vector<HashableDimensionKey> kConditionKey1 = {getMockedDimensionKey(TagId, 1, "maps")}; @@ -46,7 +48,7 @@ TEST(OringDurationTrackerTest, TestDurationOverlap) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey key1; - key1["APP_BACKGROUND"] = kConditionKey1; + key1[StringToId("APP_BACKGROUND")] = kConditionKey1; unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; @@ -55,7 +57,7 @@ TEST(OringDurationTrackerTest, TestDurationOverlap) { uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false, + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); @@ -75,7 +77,7 @@ TEST(OringDurationTrackerTest, TestDurationNested) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey key1; - key1["APP_BACKGROUND"] = kConditionKey1; + key1[StringToId("APP_BACKGROUND")] = kConditionKey1; unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; @@ -83,7 +85,7 @@ TEST(OringDurationTrackerTest, TestDurationNested) { uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs, + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); @@ -102,7 +104,7 @@ TEST(OringDurationTrackerTest, TestStopAll) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey key1; - key1["APP_BACKGROUND"] = kConditionKey1; + key1[StringToId("APP_BACKGROUND")] = kConditionKey1; unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; @@ -110,7 +112,7 @@ TEST(OringDurationTrackerTest, TestStopAll) { uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs, + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); @@ -128,7 +130,7 @@ TEST(OringDurationTrackerTest, TestCrossBucketBoundary) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey key1; - key1["APP_BACKGROUND"] = kConditionKey1; + key1[StringToId("APP_BACKGROUND")] = kConditionKey1; unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; @@ -137,7 +139,7 @@ TEST(OringDurationTrackerTest, TestCrossBucketBoundary) { uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs, + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); @@ -163,7 +165,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey key1; - key1["APP_BACKGROUND"] = kConditionKey1; + key1[StringToId("APP_BACKGROUND")] = kConditionKey1; EXPECT_CALL(*wizard, query(_, key1)) // #4 .WillOnce(Return(ConditionState::kFalse)); @@ -175,7 +177,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) { uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false, + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); @@ -194,7 +196,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange2) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey key1; - key1["APP_BACKGROUND"] = kConditionKey1; + key1[StringToId("APP_BACKGROUND")] = kConditionKey1; EXPECT_CALL(*wizard, query(_, key1)) .Times(2) @@ -208,7 +210,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange2) { uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false, + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); @@ -229,7 +231,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey key1; - key1["APP_BACKGROUND"] = kConditionKey1; + key1[StringToId("APP_BACKGROUND")] = kConditionKey1; EXPECT_CALL(*wizard, query(_, key1)) // #4 .WillOnce(Return(ConditionState::kFalse)); @@ -240,7 +242,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs, + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); @@ -260,22 +262,22 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { Alert alert; - alert.set_name("alert"); - alert.set_metric_name("1"); + alert.set_id(101); + alert.set_metric_id(1); alert.set_trigger_if_sum_gt(40 * NS_PER_SEC); - alert.set_number_of_buckets(2); + alert.set_num_buckets(2); alert.set_refractory_period_secs(1); unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey key1; - key1["APP_BACKGROUND"] = kConditionKey1; + key1[StringToId("APP_BACKGROUND")] = kConditionKey1; uint64_t bucketStartTimeNs = 10 * NS_PER_SEC; uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; uint64_t bucketSizeNs = 30 * NS_PER_SEC; sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); - OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs, + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {anomalyTracker}); // Nothing in the past bucket. @@ -322,22 +324,22 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { TEST(OringDurationTrackerTest, TestAnomalyDetection) { Alert alert; - alert.set_name("alert"); - alert.set_metric_name("1"); + alert.set_id(101); + alert.set_metric_id(1); alert.set_trigger_if_sum_gt(40 * NS_PER_SEC); - alert.set_number_of_buckets(2); + alert.set_num_buckets(2); alert.set_refractory_period_secs(1); unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey key1; - key1["APP_BACKGROUND"] = kConditionKey1; + key1[StringToId("APP_BACKGROUND")] = kConditionKey1; uint64_t bucketStartTimeNs = 10 * NS_PER_SEC; uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; uint64_t bucketSizeNs = 30 * NS_PER_SEC; sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); - OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true /*nesting*/, + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/, bucketStartTimeNs, bucketSizeNs, {anomalyTracker}); tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, key1); diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index 12bc834be540..15acca4bd2a1 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -14,6 +14,7 @@ #include "src/metrics/ValueMetricProducer.h" #include "metrics_test_helper.h" +#include "tests/statsd_test_util.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -34,9 +35,9 @@ namespace android { namespace os { namespace statsd { -const ConfigKey kConfigKey(0, "test"); +const ConfigKey kConfigKey(0, 12345); const int tagId = 1; -const string metricName = "test_metric"; +const int64_t metricId = 123; const int64_t bucketStartTimeNs = 10000000000; const int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL; const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; @@ -48,9 +49,10 @@ const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs; */ TEST(ValueMetricProducerTest, TestNonDimensionalEvents) { ValueMetric metric; - metric.set_name(metricName); + metric.set_id(metricId); metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000); - metric.set_value_field(2); + metric.mutable_value_field()->set_field(tagId); + metric.mutable_value_field()->add_child()->set_field(2); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); // TODO: pending refactor of StatsPullerManager @@ -124,10 +126,11 @@ TEST(ValueMetricProducerTest, TestNonDimensionalEvents) { */ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { ValueMetric metric; - metric.set_name(metricName); + metric.set_id(metricId); metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000); - metric.set_value_field(2); - metric.set_condition("SCREEN_ON"); + metric.mutable_value_field()->set_field(tagId); + metric.mutable_value_field()->add_child()->set_field(2); + metric.set_condition(StringToId("SCREEN_ON")); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); shared_ptr<MockStatsPullerManager> pullerManager = @@ -200,9 +203,10 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { ValueMetric metric; - metric.set_name(metricName); + metric.set_id(metricId); metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000); - metric.set_value_field(2); + metric.mutable_value_field()->set_field(tagId); + metric.mutable_value_field()->add_child()->set_field(2); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); shared_ptr<MockStatsPullerManager> pullerManager = @@ -240,16 +244,17 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { TEST(ValueMetricProducerTest, TestAnomalyDetection) { Alert alert; - alert.set_name("alert"); - alert.set_metric_name(metricName); + alert.set_id(101); + alert.set_metric_id(metricId); alert.set_trigger_if_sum_gt(130); - alert.set_number_of_buckets(2); + alert.set_num_buckets(2); alert.set_refractory_period_secs(3); ValueMetric metric; - metric.set_name(metricName); + metric.set_id(metricId); metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000); - metric.set_value_field(2); + metric.mutable_value_field()->set_field(tagId); + metric.mutable_value_field()->add_child()->set_field(2); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index 39e366fae3f8..939dc1f7c66c 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -22,7 +22,7 @@ namespace statsd { AtomMatcher CreateWakelockStateChangedAtomMatcher(const string& name, WakelockStateChanged::State state) { AtomMatcher atom_matcher; - atom_matcher.set_name(name); + atom_matcher.set_id(StringToId(name)); auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); simple_atom_matcher->set_atom_id(android::util::WAKELOCK_STATE_CHANGED); auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); @@ -42,7 +42,7 @@ AtomMatcher CreateReleaseWakelockAtomMatcher() { AtomMatcher CreateScreenStateChangedAtomMatcher( const string& name, ScreenStateChanged::State state) { AtomMatcher atom_matcher; - atom_matcher.set_name(name); + atom_matcher.set_id(StringToId(name)); auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); simple_atom_matcher->set_atom_id(android::util::SCREEN_STATE_CHANGED); auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); @@ -62,7 +62,7 @@ AtomMatcher CreateScreenTurnedOffAtomMatcher() { AtomMatcher CreateSyncStateChangedAtomMatcher( const string& name, SyncStateChanged::State state) { AtomMatcher atom_matcher; - atom_matcher.set_name(name); + atom_matcher.set_id(StringToId(name)); auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); simple_atom_matcher->set_atom_id(android::util::SYNC_STATE_CHANGED); auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); @@ -82,7 +82,7 @@ AtomMatcher CreateSyncEndAtomMatcher() { AtomMatcher CreateActivityForegroundStateChangedAtomMatcher( const string& name, ActivityForegroundStateChanged::Activity activity) { AtomMatcher atom_matcher; - atom_matcher.set_name(name); + atom_matcher.set_id(StringToId(name)); auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); simple_atom_matcher->set_atom_id(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED); auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); @@ -104,7 +104,7 @@ AtomMatcher CreateMoveToForegroundAtomMatcher() { AtomMatcher CreateProcessLifeCycleStateChangedAtomMatcher( const string& name, ProcessLifeCycleStateChanged::Event event) { AtomMatcher atom_matcher; - atom_matcher.set_name(name); + atom_matcher.set_id(StringToId(name)); auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); simple_atom_matcher->set_atom_id(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); @@ -121,47 +121,47 @@ AtomMatcher CreateProcessCrashAtomMatcher() { Predicate CreateScreenIsOnPredicate() { Predicate predicate; - predicate.set_name("ScreenIsOn"); - predicate.mutable_simple_predicate()->set_start("ScreenTurnedOn"); - predicate.mutable_simple_predicate()->set_stop("ScreenTurnedOff"); + predicate.set_id(StringToId("ScreenIsOn")); + predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOn")); + predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOff")); return predicate; } Predicate CreateScreenIsOffPredicate() { Predicate predicate; - predicate.set_name("ScreenIsOff"); - predicate.mutable_simple_predicate()->set_start("ScreenTurnedOff"); - predicate.mutable_simple_predicate()->set_stop("ScreenTurnedOn"); + predicate.set_id(StringToId("ScreenIsOff")); + predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOff")); + predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOn")); return predicate; } Predicate CreateHoldingWakelockPredicate() { Predicate predicate; - predicate.set_name("HoldingWakelock"); - predicate.mutable_simple_predicate()->set_start("AcquireWakelock"); - predicate.mutable_simple_predicate()->set_stop("ReleaseWakelock"); + predicate.set_id(StringToId("HoldingWakelock")); + predicate.mutable_simple_predicate()->set_start(StringToId("AcquireWakelock")); + predicate.mutable_simple_predicate()->set_stop(StringToId("ReleaseWakelock")); return predicate; } Predicate CreateIsSyncingPredicate() { Predicate predicate; - predicate.set_name("IsSyncing"); - predicate.mutable_simple_predicate()->set_start("SyncStart"); - predicate.mutable_simple_predicate()->set_stop("SyncEnd"); + predicate.set_id(StringToId("IsSyncing")); + predicate.mutable_simple_predicate()->set_start(StringToId("SyncStart")); + predicate.mutable_simple_predicate()->set_stop(StringToId("SyncEnd")); return predicate; } Predicate CreateIsInBackgroundPredicate() { Predicate predicate; - predicate.set_name("IsInBackground"); - predicate.mutable_simple_predicate()->set_start("MoveToBackground"); - predicate.mutable_simple_predicate()->set_stop("MoveToForeground"); + predicate.set_id(StringToId("IsInBackground")); + predicate.mutable_simple_predicate()->set_start(StringToId("MoveToBackground")); + predicate.mutable_simple_predicate()->set_stop(StringToId("MoveToForeground")); return predicate; } void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combinationPredicate) { - combinationPredicate->mutable_combination()->add_predicate(predicate.name()); + combinationPredicate->mutable_combination()->add_predicate(predicate.id()); } FieldMatcher CreateAttributionUidDimensions(const int atomId, @@ -316,6 +316,9 @@ void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events) { }); } +int64_t StringToId(const string& str) { + return static_cast<int64_t>(std::hash<std::string>()(str)); +} } // namespace statsd } // namespace os } // namespace android
\ No newline at end of file diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h index 282e1b2c70a0..5e19da032e07 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -121,6 +121,8 @@ sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const Stat // Util function to sort the log events by timestamp. void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events); +int64_t StringToId(const string& str); + } // namespace statsd } // namespace os } // namespace android
\ No newline at end of file diff --git a/cmds/statsd/tools/dogfood/Android.mk b/cmds/statsd/tools/dogfood/Android.mk index 7bd15d707bbc..a4c080063284 100644 --- a/cmds/statsd/tools/dogfood/Android.mk +++ b/cmds/statsd/tools/dogfood/Android.mk @@ -20,7 +20,8 @@ LOCAL_PACKAGE_NAME := StatsdDogfood LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_SRC_FILES += ../../src/stats_log.proto \ - ../../src/atoms.proto + ../../src/atoms.proto \ + ../../src/statsd_config.proto LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/../../src/ LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res diff --git a/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config b/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config Binary files differindex 03299929f84b..c1c391483855 100644 --- a/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config +++ b/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java index 9294681f5baa..93dba71716a5 100644 --- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java +++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java @@ -26,7 +26,7 @@ public class DisplayProtoUtils { sb.append("ConfigKey: "); if (reports.hasConfigKey()) { com.android.os.StatsLog.ConfigMetricsReportList.ConfigKey key = reports.getConfigKey(); - sb.append("\tuid: ").append(key.getUid()).append(" name: ").append(key.getName()) + sb.append("\tuid: ").append(key.getUid()).append(" name: ").append(key.getId()) .append("\n"); } @@ -34,7 +34,7 @@ public class DisplayProtoUtils { sb.append("StatsLogReport size: ").append(report.getMetricsCount()).append("\n"); for (StatsLog.StatsLogReport log : report.getMetricsList()) { sb.append("\n\n"); - sb.append("metric id: ").append(log.getMetricName()).append("\n"); + sb.append("metric id: ").append(log.getMetricId()).append("\n"); sb.append("start time:").append(getDateStr(log.getStartReportNanos())).append("\n"); sb.append("end time:").append(getDateStr(log.getEndReportNanos())).append("\n"); diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java index 137fd4d01fda..4f9032ff01b4 100644 --- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java +++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java @@ -34,6 +34,7 @@ import static com.android.statsd.dogfood.DisplayProtoUtils.displayLogReport; public class MainActivity extends Activity { private final static String TAG = "StatsdDogfood"; + private final static long CONFIG_ID = 987654321; final int[] mUids = {11111111, 2222222}; StatsManager mStatsManager; @@ -163,7 +164,7 @@ public class MainActivity extends Activity { return; } if (mStatsManager != null) { - byte[] data = mStatsManager.getData("fake"); + byte[] data = mStatsManager.getData(CONFIG_ID); if (data != null) { displayData(data); } else { @@ -186,7 +187,7 @@ public class MainActivity extends Activity { byte[] config = new byte[inputStream.available()]; inputStream.read(config); if (mStatsManager != null) { - if (mStatsManager.addConfiguration("fake", + if (mStatsManager.addConfiguration(CONFIG_ID, config, getPackageName(), MainActivity.this.getClass().getName())) { Toast.makeText( MainActivity.this, "Config pushed", Toast.LENGTH_LONG).show(); diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java index f516477abf9f..4bd284448446 100644 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java +++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java @@ -41,7 +41,7 @@ import java.util.List; * Creates StatsdConfig protos for loadtesting. */ public class ConfigFactory { - public static final String CONFIG_NAME = "LOADTEST"; + public static final long CONFIG_ID = 123456789; private static final String TAG = "loadtest.ConfigFactory"; @@ -86,7 +86,7 @@ public class ConfigFactory { boolean includeDuration, boolean includeEvent, boolean includeValue, boolean includeGauge) { StatsdConfig.Builder config = StatsdConfig.newBuilder() - .setName(CONFIG_NAME); + .setId(CONFIG_ID); if (placebo) { replication = 0; // Config will be empty, aside from a name. } @@ -160,7 +160,7 @@ public class ConfigFactory { */ private void addEventMetric(EventMetric template, int suffix, StatsdConfig.Builder config) { EventMetric.Builder metric = template.toBuilder() - .setName(template.getName() + suffix) + .setId(template.getId() + suffix) .setWhat(template.getWhat() + suffix); if (template.hasCondition()) { metric.setCondition(template.getCondition() + suffix); @@ -186,7 +186,7 @@ public class ConfigFactory { private void addCountMetric(CountMetric template, int suffix, long bucketMillis, StatsdConfig.Builder config) { CountMetric.Builder metric = template.toBuilder() - .setName(template.getName() + suffix) + .setId(template.getId() + suffix) .setWhat(template.getWhat() + suffix); if (template.hasCondition()) { metric.setCondition(template.getCondition() + suffix); @@ -207,7 +207,7 @@ public class ConfigFactory { private void addDurationMetric(DurationMetric template, int suffix, long bucketMillis, StatsdConfig.Builder config) { DurationMetric.Builder metric = template.toBuilder() - .setName(template.getName() + suffix) + .setId(template.getId() + suffix) .setWhat(template.getWhat() + suffix); if (template.hasCondition()) { metric.setCondition(template.getCondition() + suffix); @@ -228,7 +228,7 @@ public class ConfigFactory { private void addGaugeMetric(GaugeMetric template, int suffix, long bucketMillis, StatsdConfig.Builder config) { GaugeMetric.Builder metric = template.toBuilder() - .setName(template.getName() + suffix) + .setId(template.getId() + suffix) .setWhat(template.getWhat() + suffix); if (template.hasCondition()) { metric.setCondition(template.getCondition() + suffix); @@ -249,7 +249,7 @@ public class ConfigFactory { private void addValueMetric(ValueMetric template, int suffix, long bucketMillis, StatsdConfig.Builder config) { ValueMetric.Builder metric = template.toBuilder() - .setName(template.getName() + suffix) + .setId(template.getId() + suffix) .setWhat(template.getWhat() + suffix); if (template.hasCondition()) { metric.setCondition(template.getCondition() + suffix); @@ -269,11 +269,11 @@ public class ConfigFactory { */ private void addPredicate(Predicate template, int suffix, StatsdConfig.Builder config) { Predicate.Builder predicate = template.toBuilder() - .setName(template.getName() + suffix); + .setId(template.getId() + suffix); if (template.hasCombination()) { Predicate.Combination.Builder cb = template.getCombination().toBuilder() .clearPredicate(); - for (String child : template.getCombination().getPredicateList()) { + for (long child : template.getCombination().getPredicateList()) { cb.addPredicate(child + suffix); } predicate.setCombination(cb.build()); @@ -296,11 +296,11 @@ public class ConfigFactory { */ private void addMatcher(AtomMatcher template, int suffix, StatsdConfig.Builder config) { AtomMatcher.Builder matcher = template.toBuilder() - .setName(template.getName() + suffix); + .setId(template.getId() + suffix); if (template.hasCombination()) { AtomMatcher.Combination.Builder cb = template.getCombination().toBuilder() .clearMatcher(); - for (String child : template.getCombination().getMatcherList()) { + for (long child : template.getCombination().getMatcherList()) { cb.addMatcher(child + suffix); } matcher.setCombination(cb); diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java index ba43d57db8af..19087d86c4a6 100644 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java +++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java @@ -26,7 +26,7 @@ public class DisplayProtoUtils { sb.append("ConfigKey: "); if (reports.hasConfigKey()) { com.android.os.StatsLog.ConfigMetricsReportList.ConfigKey key = reports.getConfigKey(); - sb.append("\tuid: ").append(key.getUid()).append(" name: ").append(key.getName()) + sb.append("\tuid: ").append(key.getUid()).append(" id: ").append(key.getId()) .append("\n"); } @@ -34,7 +34,7 @@ public class DisplayProtoUtils { sb.append("StatsLogReport size: ").append(report.getMetricsCount()).append("\n"); for (StatsLog.StatsLogReport log : report.getMetricsList()) { sb.append("\n\n"); - sb.append("metric id: ").append(log.getMetricName()).append("\n"); + sb.append("metric id: ").append(log.getMetricId()).append("\n"); sb.append("start time:").append(getDateStr(log.getStartReportNanos())).append("\n"); sb.append("end time:").append(getDateStr(log.getEndReportNanos())).append("\n"); diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java index 83f4b7bed558..86da16c82e9b 100644 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java +++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java @@ -294,7 +294,7 @@ public class LoadtestActivity extends Activity { return null; } if (mStatsManager != null) { - byte[] data = mStatsManager.getData(ConfigFactory.CONFIG_NAME); + byte[] data = mStatsManager.getData(ConfigFactory.CONFIG_ID); if (data != null) { ConfigMetricsReportList reports = null; try { @@ -453,7 +453,7 @@ public class LoadtestActivity extends Activity { // TODO: Clear all configs instead of specific ones. if (mStatsManager != null) { if (mStarted) { - if (!mStatsManager.removeConfiguration(ConfigFactory.CONFIG_NAME)) { + if (!mStatsManager.removeConfiguration(ConfigFactory.CONFIG_ID)) { Log.d(TAG, "Removed loadtest statsd configs."); } else { Log.d(TAG, "Failed to remove loadtest configs."); @@ -464,7 +464,7 @@ public class LoadtestActivity extends Activity { private boolean setConfig(byte[] config) { if (mStatsManager != null) { - if (mStatsManager.addConfiguration(ConfigFactory.CONFIG_NAME, + if (mStatsManager.addConfiguration(ConfigFactory.CONFIG_ID, config, getPackageName(), LoadtestActivity.this.getClass().getName())) { Log.d(TAG, "Config pushed to statsd"); return true; diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ValidationRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ValidationRecorder.java index 4b614aa19492..d122654ec8a1 100644 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ValidationRecorder.java +++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ValidationRecorder.java @@ -59,18 +59,8 @@ public class ValidationRecorder extends PerfDataRecorder { Log.d(TAG, "GOT DATA"); for (ConfigMetricsReport report : reports) { for (StatsLogReport logReport : report.getMetricsList()) { - if (!logReport.hasMetricName()) { + if (!logReport.hasMetricId()) { Log.e(TAG, "Metric missing name."); - continue; - } - String metricName = logReport.getMetricName(); - if (metricName.startsWith("EVENT_BATTERY_LEVEL_CHANGES_WHILE_SCREEN_IS_ON_")) { - validateEventBatteryLevelChangesWhileScreenIsOn(logReport); - continue; - } - if (metricName.startsWith("EVENT_BATTERY_LEVEL_CHANGES_")) { - validateEventBatteryLevelChanges(logReport); - continue; } } } @@ -78,7 +68,7 @@ public class ValidationRecorder extends PerfDataRecorder { } private void validateEventBatteryLevelChanges(StatsLogReport logReport) { - Log.d(TAG, "Validating " + logReport.getMetricName()); + Log.d(TAG, "Validating " + logReport.getMetricId()); if (logReport.hasEventMetrics()) { Log.d(TAG, "Num events captured: " + logReport.getEventMetrics().getDataCount()); for (EventMetricData data : logReport.getEventMetrics().getDataList()) { @@ -90,6 +80,6 @@ public class ValidationRecorder extends PerfDataRecorder { } private void validateEventBatteryLevelChangesWhileScreenIsOn(StatsLogReport logReport) { - Log.d(TAG, "Validating " + logReport.getMetricName()); + Log.d(TAG, "Validating " + logReport.getMetricId()); } } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 1adae7a84fcc..847f91bdc59a 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -60,6 +60,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; +import android.os.WorkSource; import android.text.TextUtils; import android.util.ArrayMap; import android.util.DisplayMetrics; @@ -3911,10 +3912,10 @@ public class ActivityManager { /** * @hide */ - public static void noteWakeupAlarm(PendingIntent ps, int sourceUid, String sourcePkg, - String tag) { + public static void noteWakeupAlarm(PendingIntent ps, WorkSource workSource, int sourceUid, + String sourcePkg, String tag) { try { - getService().noteWakeupAlarm((ps != null) ? ps.getTarget() : null, + getService().noteWakeupAlarm((ps != null) ? ps.getTarget() : null, workSource, sourceUid, sourcePkg, tag); } catch (RemoteException ex) { } @@ -3923,19 +3924,24 @@ public class ActivityManager { /** * @hide */ - public static void noteAlarmStart(PendingIntent ps, int sourceUid, String tag) { + public static void noteAlarmStart(PendingIntent ps, WorkSource workSource, int sourceUid, + String tag) { try { - getService().noteAlarmStart((ps != null) ? ps.getTarget() : null, sourceUid, tag); + getService().noteAlarmStart((ps != null) ? ps.getTarget() : null, workSource, + sourceUid, tag); } catch (RemoteException ex) { } } + /** * @hide */ - public static void noteAlarmFinish(PendingIntent ps, int sourceUid, String tag) { + public static void noteAlarmFinish(PendingIntent ps, WorkSource workSource, int sourceUid, + String tag) { try { - getService().noteAlarmFinish((ps != null) ? ps.getTarget() : null, sourceUid, tag); + getService().noteAlarmFinish((ps != null) ? ps.getTarget() : null, workSource, + sourceUid, tag); } catch (RemoteException ex) { } } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index c09403c29943..4c558f374f91 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -75,20 +75,20 @@ public abstract class ActivityManagerNative { */ static public void noteWakeupAlarm(PendingIntent ps, int sourceUid, String sourcePkg, String tag) { - ActivityManager.noteWakeupAlarm(ps, sourceUid, sourcePkg, tag); + ActivityManager.noteWakeupAlarm(ps, null, sourceUid, sourcePkg, tag); } /** * @deprecated use ActivityManager.noteAlarmStart instead. */ static public void noteAlarmStart(PendingIntent ps, int sourceUid, String tag) { - ActivityManager.noteAlarmStart(ps, sourceUid, tag); + ActivityManager.noteAlarmStart(ps, null, sourceUid, tag); } /** * @deprecated use ActivityManager.noteAlarmFinish instead. */ static public void noteAlarmFinish(PendingIntent ps, int sourceUid, String tag) { - ActivityManager.noteAlarmFinish(ps, sourceUid, tag); + ActivityManager.noteAlarmFinish(ps, null, sourceUid, tag); } } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index aaa6bf0333a2..de346f315016 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -1768,9 +1768,7 @@ public final class ActivityThread extends ClientTransactionHandler { (String[]) ((SomeArgs) msg.obj).arg2); break; case EXECUTE_TRANSACTION: - final ClientTransaction transaction = (ClientTransaction) msg.obj; - mTransactionExecutor.execute(transaction); - transaction.recycle(); + mTransactionExecutor.execute(((ClientTransaction) msg.obj)); break; } Object obj = msg.obj; diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 1278f75abce0..a9e633ff392d 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -63,6 +63,7 @@ import android.os.IProgressListener; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.StrictMode; +import android.os.WorkSource; import android.service.voice.IVoiceInteractionSession; import com.android.internal.app.IVoiceInteractor; import com.android.internal.os.IResultReceiver; @@ -198,7 +199,7 @@ interface IActivityManager { void enterSafeMode(); boolean startNextMatchingActivity(in IBinder callingActivity, in Intent intent, in Bundle options); - void noteWakeupAlarm(in IIntentSender sender, int sourceUid, + void noteWakeupAlarm(in IIntentSender sender, in WorkSource workSource, int sourceUid, in String sourcePkg, in String tag); void removeContentProvider(in IBinder connection, boolean stable); void setRequestedOrientation(in IBinder token, int requestedOrientation); @@ -468,8 +469,8 @@ interface IActivityManager { void dumpHeapFinished(in String path); void setVoiceKeepAwake(in IVoiceInteractionSession session, boolean keepAwake); void updateLockTaskPackages(int userId, in String[] packages); - void noteAlarmStart(in IIntentSender sender, int sourceUid, in String tag); - void noteAlarmFinish(in IIntentSender sender, int sourceUid, in String tag); + void noteAlarmStart(in IIntentSender sender, in WorkSource workSource, int sourceUid, in String tag); + void noteAlarmFinish(in IIntentSender sender, in WorkSource workSource, int sourceUid, in String tag); int getPackageProcessState(in String packageName, in String callingPackage); oneway void showLockTaskEscapeMessage(in IBinder token); void updateDeviceOwner(in String packageName); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 7e80ac7b8d94..0b747416bbbf 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1668,6 +1668,46 @@ public class DevicePolicyManager { public static final String ACTION_DEVICE_ADMIN_SERVICE = "android.app.action.DEVICE_ADMIN_SERVICE"; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = {"ID_TYPE_"}, value = { + ID_TYPE_BASE_INFO, + ID_TYPE_SERIAL, + ID_TYPE_IMEI, + ID_TYPE_MEID + }) + public @interface AttestationIdType {} + + /** + * Specifies that the device should attest its manufacturer details. For use with + * {@link #generateKeyPair}. + * + * @see #generateKeyPair + */ + public static final int ID_TYPE_BASE_INFO = 1; + + /** + * Specifies that the device should attest its serial number. For use with + * {@link #generateKeyPair}. + * + * @see #generateKeyPair + */ + public static final int ID_TYPE_SERIAL = 2; + + /** + * Specifies that the device should attest its IMEI. For use with {@link #generateKeyPair}. + * + * @see #generateKeyPair + */ + public static final int ID_TYPE_IMEI = 4; + + /** + * Specifies that the device should attest its MEID. For use with {@link #generateKeyPair}. + * + * @see #generateKeyPair + */ + public static final int ID_TYPE_MEID = 8; + /** * Return true if the given administrator component is currently active (enabled) in the system. * @@ -4106,22 +4146,46 @@ public class DevicePolicyManager { * @param algorithm The key generation algorithm, see {@link java.security.KeyPairGenerator}. * @param keySpec Specification of the key to generate, see * {@link java.security.KeyPairGenerator}. + * @param idAttestationFlags A bitmask of all the identifiers that should be included in the + * attestation record ({@code ID_TYPE_BASE_INFO}, {@code ID_TYPE_SERIAL}, + * {@code ID_TYPE_IMEI} and {@code ID_TYPE_MEID}), or {@code 0} if no device + * identification is required in the attestation record. + * Device owner, profile owner and their delegated certificate installer can use + * {@link #ID_TYPE_BASE_INFO} to request inclusion of the general device information + * including manufacturer, model, brand, device and product in the attestation record. + * Only device owner and their delegated certificate installer can use + * {@link #ID_TYPE_SERIAL}, {@link #ID_TYPE_IMEI} and {@link #ID_TYPE_MEID} to request + * unique device identifiers to be attested. + * <p> + * If any of {@link #ID_TYPE_SERIAL}, {@link #ID_TYPE_IMEI} and {@link #ID_TYPE_MEID} + * is set, it is implicitly assumed that {@link #ID_TYPE_BASE_INFO} is also set. + * <p> + * If any flag is specified, then an attestation challenge must be included in the + * {@code keySpec}. * @return A non-null {@code AttestedKeyPair} if the key generation succeeded, null otherwise. * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile - * owner. - * @throws IllegalArgumentException if the alias in {@code keySpec} is empty, or if the + * owner. If Device ID attestation is requested (using {@link #ID_TYPE_SERIAL}, + * {@link #ID_TYPE_IMEI} or {@link #ID_TYPE_MEID}), the caller must be the Device Owner + * or the Certificate Installer delegate. + * @throws IllegalArgumentException if the alias in {@code keySpec} is empty, if the * algorithm specification in {@code keySpec} is not {@code RSAKeyGenParameterSpec} - * or {@code ECGenParameterSpec}. + * or {@code ECGenParameterSpec}, or if Device ID attestation was requested but the + * {@code keySpec} does not contain an attestation challenge. + * @see KeyGenParameterSpec.Builder#setAttestationChallenge(byte[]) */ public AttestedKeyPair generateKeyPair(@Nullable ComponentName admin, - @NonNull String algorithm, @NonNull KeyGenParameterSpec keySpec) { + @NonNull String algorithm, @NonNull KeyGenParameterSpec keySpec, + @AttestationIdType int idAttestationFlags) { throwIfParentInstance("generateKeyPair"); try { final ParcelableKeyGenParameterSpec parcelableSpec = new ParcelableKeyGenParameterSpec(keySpec); KeymasterCertificateChain attestationChain = new KeymasterCertificateChain(); + + // Translate ID attestation flags to values used by AttestationUtils final boolean success = mService.generateKeyPair( - admin, mContext.getPackageName(), algorithm, parcelableSpec, attestationChain); + admin, mContext.getPackageName(), algorithm, parcelableSpec, + idAttestationFlags, attestationChain); if (!success) { Log.e(TAG, "Error generating key via DevicePolicyManagerService."); return null; diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 7cf19eeb881d..5916a629f2e6 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -173,7 +173,7 @@ interface IDevicePolicyManager { boolean removeKeyPair(in ComponentName who, in String callerPackage, String alias); boolean generateKeyPair(in ComponentName who, in String callerPackage, in String algorithm, in ParcelableKeyGenParameterSpec keySpec, - out KeymasterCertificateChain attestationChain); + in int idAttestationFlags, out KeymasterCertificateChain attestationChain); boolean setKeyPairCertificate(in ComponentName who, in String callerPackage, in String alias, in byte[] certBuffer, in byte[] certChainBuffer, boolean isUserSelectable); void choosePrivateKeyAlias(int uid, in Uri uri, in String alias, IBinder aliasCallback); diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 7b549cd59666..87f227129586 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -32,6 +32,8 @@ import android.view.WindowManagerGlobal; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; +import com.android.internal.util.Preconditions; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -624,6 +626,7 @@ public class AssistStructure implements Parcelable { int mMinEms = -1; int mMaxEms = -1; int mMaxLength = -1; + @Nullable String mTextIdEntry; // POJO used to override some autofill-related values when the node is parcelized. // Not written to parcel. @@ -701,7 +704,7 @@ public class AssistStructure implements Parcelable { final int flags = mFlags; if ((flags&FLAGS_HAS_ID) != 0) { mId = in.readInt(); - if (mId != 0) { + if (mId != View.NO_ID) { mIdEntry = preader.readString(); if (mIdEntry != null) { mIdType = preader.readString(); @@ -724,6 +727,7 @@ public class AssistStructure implements Parcelable { mMinEms = in.readInt(); mMaxEms = in.readInt(); mMaxLength = in.readInt(); + mTextIdEntry = preader.readString(); } if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) { mX = in.readInt(); @@ -857,7 +861,7 @@ public class AssistStructure implements Parcelable { out.writeInt(writtenFlags); if ((flags&FLAGS_HAS_ID) != 0) { out.writeInt(mId); - if (mId != 0) { + if (mId != View.NO_ID) { pwriter.writeString(mIdEntry); if (mIdEntry != null) { pwriter.writeString(mIdType); @@ -890,6 +894,7 @@ public class AssistStructure implements Parcelable { out.writeInt(mMinEms); out.writeInt(mMaxEms); out.writeInt(mMaxLength); + pwriter.writeString(mTextIdEntry); } if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) { out.writeInt(mX); @@ -1430,6 +1435,17 @@ public class AssistStructure implements Parcelable { } /** + * Gets the identifier used to set the text associated with this view. + * + * <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes, + * not for assist purposes. + */ + @Nullable + public String getTextIdEntry() { + return mTextIdEntry; + } + + /** * Return additional hint text associated with the node; this is typically used with * a node that takes user input, describing to the user what the input means. */ @@ -1684,6 +1700,11 @@ public class AssistStructure implements Parcelable { } @Override + public void setTextIdEntry(@NonNull String entryName) { + mNode.mTextIdEntry = Preconditions.checkNotNull(entryName); + } + + @Override public void setHint(CharSequence hint) { getNodeText().mHint = hint != null ? hint.toString() : null; } @@ -2082,6 +2103,7 @@ public class AssistStructure implements Parcelable { Log.i(TAG, prefix + " Text color fg: #" + Integer.toHexString(node.getTextColor()) + ", bg: #" + Integer.toHexString(node.getTextBackgroundColor())); Log.i(TAG, prefix + " Input type: " + node.getInputType()); + Log.i(TAG, prefix + " Resource id: " + node.getTextIdEntry()); } String webDomain = node.getWebDomain(); if (webDomain != null) { diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java index 3c96f06945c5..764ceede5d20 100644 --- a/core/java/android/app/servertransaction/ClientTransaction.java +++ b/core/java/android/app/servertransaction/ClientTransaction.java @@ -54,11 +54,6 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem { /** Target client activity. Might be null if the entire transaction is targeting an app. */ private IBinder mActivityToken; - /** Get the target client of the transaction. */ - public IApplicationThread getClient() { - return mClient; - } - /** * Add a message to the end of the sequence of callbacks. * @param activityCallback A single message that can contain a lifecycle request/callback. diff --git a/core/java/android/app/servertransaction/ObjectPool.java b/core/java/android/app/servertransaction/ObjectPool.java index 2fec30a0dde7..98121253f486 100644 --- a/core/java/android/app/servertransaction/ObjectPool.java +++ b/core/java/android/app/servertransaction/ObjectPool.java @@ -16,8 +16,8 @@ package android.app.servertransaction; -import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedList; import java.util.Map; /** @@ -27,7 +27,7 @@ import java.util.Map; class ObjectPool { private static final Object sPoolSync = new Object(); - private static final Map<Class, ArrayList<? extends ObjectPoolItem>> sPoolMap = + private static final Map<Class, LinkedList<? extends ObjectPoolItem>> sPoolMap = new HashMap<>(); private static final int MAX_POOL_SIZE = 50; @@ -40,9 +40,9 @@ class ObjectPool { public static <T extends ObjectPoolItem> T obtain(Class<T> itemClass) { synchronized (sPoolSync) { @SuppressWarnings("unchecked") - final ArrayList<T> itemPool = (ArrayList<T>) sPoolMap.get(itemClass); + LinkedList<T> itemPool = (LinkedList<T>) sPoolMap.get(itemClass); if (itemPool != null && !itemPool.isEmpty()) { - return itemPool.remove(itemPool.size() - 1); + return itemPool.poll(); } return null; } @@ -56,20 +56,16 @@ class ObjectPool { public static <T extends ObjectPoolItem> void recycle(T item) { synchronized (sPoolSync) { @SuppressWarnings("unchecked") - ArrayList<T> itemPool = (ArrayList<T>) sPoolMap.get(item.getClass()); + LinkedList<T> itemPool = (LinkedList<T>) sPoolMap.get(item.getClass()); if (itemPool == null) { - itemPool = new ArrayList<>(); + itemPool = new LinkedList<>(); sPoolMap.put(item.getClass(), itemPool); } - // Check if the item is already in the pool - final int size = itemPool.size(); - for (int i = 0; i < size; i++) { - if (itemPool.get(i) == item) { - throw new IllegalStateException("Trying to recycle already recycled item"); - } + if (itemPool.contains(item)) { + throw new IllegalStateException("Trying to recycle already recycled item"); } - if (size < MAX_POOL_SIZE) { + if (itemPool.size() < MAX_POOL_SIZE) { itemPool.add(item); } } diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java index 5c7f67411a83..b8fb2e34d083 100644 --- a/core/java/android/app/slice/Slice.java +++ b/core/java/android/app/slice/Slice.java @@ -184,6 +184,10 @@ public final class Slice implements Parcelable { * Subtype to tag an item representing priority. */ public static final String SUBTYPE_PRIORITY = "priority"; + /** + * Subtype to tag an item to use as a content description. + */ + public static final String SUBTYPE_CONTENT_DESCRIPTION = "content_description"; private final SliceItem[] mItems; private final @SliceHint String[] mHints; diff --git a/core/java/android/content/pm/PackageList.java b/core/java/android/content/pm/PackageList.java new file mode 100644 index 000000000000..cfd99abc6283 --- /dev/null +++ b/core/java/android/content/pm/PackageList.java @@ -0,0 +1,74 @@ +/* + * 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 android.content.pm; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.pm.PackageManagerInternal.PackageListObserver; + +import com.android.server.LocalServices; + +import java.util.List; + +/** + * All of the package name installed on the system. + * <p>A self observable list that automatically removes the listener when it goes out of scope. + * + * @hide Only for use within the system server. + */ +public class PackageList implements PackageListObserver, AutoCloseable { + private final PackageListObserver mWrappedObserver; + private final List<String> mPackageNames; + + /** + * Create a new object. + * <p>Ownership of the given {@link List} transfers to this object and should not + * be modified by the caller. + */ + public PackageList(@NonNull List<String> packageNames, @Nullable PackageListObserver observer) { + mPackageNames = packageNames; + mWrappedObserver = observer; + } + + @Override + public void onPackageAdded(String packageName) { + if (mWrappedObserver != null) { + mWrappedObserver.onPackageAdded(packageName); + } + } + + @Override + public void onPackageRemoved(String packageName) { + if (mWrappedObserver != null) { + mWrappedObserver.onPackageRemoved(packageName); + } + } + + @Override + public void close() throws Exception { + LocalServices.getService(PackageManagerInternal.class).removePackageListObserver(this); + } + + /** + * Returns the names of packages installed on the system. + * <p>The list is a copy-in-time and the actual set of installed packages may differ. Real + * time updates to the package list are sent via the {@link PackageListObserver} callback. + */ + public @NonNull List<String> getPackageNames() { + return mPackageNames; + } +} diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 713cd109ef87..2c45b8d8b30e 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -53,6 +53,14 @@ public abstract class PackageManagerInternal { @Retention(RetentionPolicy.SOURCE) public @interface KnownPackage {} + /** Observer called whenever the list of packages changes */ + public interface PackageListObserver { + /** A package was added to the system. */ + void onPackageAdded(@NonNull String packageName); + /** A package was removed from the system. */ + void onPackageRemoved(@NonNull String packageName); + } + /** * Provider for package names. */ @@ -111,6 +119,12 @@ public abstract class PackageManagerInternal { public abstract void setSimCallManagerPackagesProvider(PackagesProvider provider); /** + * Sets the Use Open Wifi packages provider. + * @param provider The packages provider. + */ + public abstract void setUseOpenWifiAppPackagesProvider(PackagesProvider provider); + + /** * Sets the sync adapter packages provider. * @param provider The provider. */ @@ -139,6 +153,14 @@ public abstract class PackageManagerInternal { int userId); /** + * Requests granting of the default permissions to the current default Use Open Wifi app. + * @param packageName The default use open wifi package name. + * @param userId The user for which to grant the permissions. + */ + public abstract void grantDefaultPermissionsToDefaultUseOpenWifiApp(String packageName, + int userId); + + /** * Sets a list of apps to keep in PM's internal data structures and as APKs even if no user has * currently installed it. The apps are not preloaded. * @param packageList List of package names to keep cached. @@ -435,6 +457,35 @@ public abstract class PackageManagerInternal { public abstract @Nullable PackageParser.Package getPackage(@NonNull String packageName); /** + * Returns a list without a change observer. + * + * {@see #getPackageList(PackageListObserver)} + */ + public @NonNull PackageList getPackageList() { + return getPackageList(null); + } + + /** + * Returns the list of packages installed at the time of the method call. + * <p>The given observer is notified when the list of installed packages + * changes [eg. a package was installed or uninstalled]. It will not be + * notified if a package is updated. + * <p>The package list will not be updated automatically as packages are + * installed / uninstalled. Any changes must be handled within the observer. + */ + public abstract @NonNull PackageList getPackageList(@Nullable PackageListObserver observer); + + /** + * Removes the observer. + * <p>Generally not needed. {@link #getPackageList(PackageListObserver)} will automatically + * remove the observer. + * <p>Does nothing if the observer isn't currently registered. + * <p>Observers are notified asynchronously and it's possible for an observer to be + * invoked after its been removed. + */ + public abstract void removePackageListObserver(@NonNull PackageListObserver observer); + + /** * Returns a package object for the disabled system package name. */ public abstract @Nullable PackageParser.Package getDisabledPackage(@NonNull String packageName); diff --git a/core/java/android/hardware/display/BrightnessChangeEvent.java b/core/java/android/hardware/display/BrightnessChangeEvent.java index 3003607e5f72..0a08353cbe4c 100644 --- a/core/java/android/hardware/display/BrightnessChangeEvent.java +++ b/core/java/android/hardware/display/BrightnessChangeEvent.java @@ -28,7 +28,7 @@ import android.os.Parcelable; */ public final class BrightnessChangeEvent implements Parcelable { /** Brightness in nits */ - public int brightness; + public float brightness; /** Timestamp of the change {@see System.currentTimeMillis()} */ public long timeStamp; @@ -58,7 +58,7 @@ public final class BrightnessChangeEvent implements Parcelable { public int colorTemperature; /** Brightness level before slider adjustment */ - public int lastBrightness; + public float lastBrightness; public BrightnessChangeEvent() { } @@ -78,7 +78,7 @@ public final class BrightnessChangeEvent implements Parcelable { } private BrightnessChangeEvent(Parcel source) { - brightness = source.readInt(); + brightness = source.readFloat(); timeStamp = source.readLong(); packageName = source.readString(); userId = source.readInt(); @@ -87,7 +87,7 @@ public final class BrightnessChangeEvent implements Parcelable { batteryLevel = source.readFloat(); nightMode = source.readBoolean(); colorTemperature = source.readInt(); - lastBrightness = source.readInt(); + lastBrightness = source.readFloat(); } public static final Creator<BrightnessChangeEvent> CREATOR = @@ -107,7 +107,7 @@ public final class BrightnessChangeEvent implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(brightness); + dest.writeFloat(brightness); dest.writeLong(timeStamp); dest.writeString(packageName); dest.writeInt(userId); @@ -116,6 +116,6 @@ public final class BrightnessChangeEvent implements Parcelable { dest.writeFloat(batteryLevel); dest.writeBoolean(nightMode); dest.writeInt(colorTemperature); - dest.writeInt(lastBrightness); + dest.writeFloat(lastBrightness); } } diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 7de667dcaa2b..97e9b9c2e2f4 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -628,13 +628,6 @@ public final class DisplayManager { } /** - * @hide STOPSHIP - remove when adaptive brightness accepts curves. - */ - public void setBrightness(int brightness) { - mGlobal.setBrightness(brightness); - } - - /** * Sets the global display brightness configuration. * * @hide diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index bf4cc1d826a9..cbb5a7de7db8 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -476,18 +476,6 @@ public final class DisplayManagerGlobal { } /** - * Set brightness but don't add a BrightnessChangeEvent - * STOPSHIP remove when adaptive brightness accepts curves. - */ - public void setBrightness(int brightness) { - try { - mDm.setBrightness(brightness); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } - } - - /** * Sets the global brightness configuration for a given user. * * @hide diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index cd551bd42e0f..3f6dd2e757ed 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -222,6 +222,11 @@ public abstract class DisplayManagerInternal { // set by the user as opposed to being programmatically controlled by apps. public boolean brightnessSetByUser; + // Set to true if screenBrightness or screenAutoBrightnessAdjustment are being set + // temporarily. This is typically set while the user has their finger on the brightness + // control, before they've selected the final brightness value. + public boolean brightnessIsTemporary; + // If true, enables automatic brightness control. public boolean useAutoBrightness; @@ -280,6 +285,7 @@ public abstract class DisplayManagerInternal { screenAutoBrightnessAdjustment = other.screenAutoBrightnessAdjustment; screenLowPowerBrightnessFactor = other.screenLowPowerBrightnessFactor; brightnessSetByUser = other.brightnessSetByUser; + brightnessIsTemporary = other.brightnessIsTemporary; useAutoBrightness = other.useAutoBrightness; blockScreenOn = other.blockScreenOn; lowPowerMode = other.lowPowerMode; @@ -303,6 +309,7 @@ public abstract class DisplayManagerInternal { && screenLowPowerBrightnessFactor == other.screenLowPowerBrightnessFactor && brightnessSetByUser == other.brightnessSetByUser + && brightnessIsTemporary == other.brightnessIsTemporary && useAutoBrightness == other.useAutoBrightness && blockScreenOn == other.blockScreenOn && lowPowerMode == other.lowPowerMode @@ -324,6 +331,7 @@ public abstract class DisplayManagerInternal { + ", screenAutoBrightnessAdjustment=" + screenAutoBrightnessAdjustment + ", screenLowPowerBrightnessFactor=" + screenLowPowerBrightnessFactor + ", brightnessSetByUser=" + brightnessSetByUser + + ", brightnessIsTemporary=" + brightnessIsTemporary + ", useAutoBrightness=" + useAutoBrightness + ", blockScreenOn=" + blockScreenOn + ", lowPowerMode=" + lowPowerMode diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 8afae6ec9010..61c42e1ab491 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -87,10 +87,6 @@ interface IDisplayManager { // Requires BRIGHTNESS_SLIDER_USAGE permission. ParceledListSlice getBrightnessEvents(String callingPackage); - // STOPSHIP remove when adaptive brightness code is updated to accept curves. - // Requires BRIGHTNESS_SLIDER_USAGE permission. - void setBrightness(int brightness); - // Sets the global brightness configuration for a given user. Requires // CONFIGURE_DISPLAY_BRIGHTNESS, and INTERACT_ACROSS_USER if the user being configured is not // the same as the calling user. diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java index 52527ed67ae4..0a21083a0262 100644 --- a/core/java/android/hardware/location/ContextHubClient.java +++ b/core/java/android/hardware/location/ContextHubClient.java @@ -15,9 +15,13 @@ */ package android.hardware.location; +import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.os.RemoteException; +import com.android.internal.util.Preconditions; + import dalvik.system.CloseGuard; import java.io.Closeable; @@ -31,16 +35,12 @@ import java.util.concurrent.atomic.AtomicBoolean; * * @hide */ +@SystemApi public class ContextHubClient implements Closeable { /* * The proxy to the client interface at the service. */ - private final IContextHubClient mClientProxy; - - /* - * The callback interface associated with this client. - */ - private final IContextHubClientCallback mCallbackInterface; + private IContextHubClient mClientProxy = null; /* * The Context Hub that this client is attached to. @@ -51,20 +51,33 @@ public class ContextHubClient implements Closeable { private final AtomicBoolean mIsClosed = new AtomicBoolean(false); - /* package */ ContextHubClient( - IContextHubClient clientProxy, IContextHubClientCallback callback, - ContextHubInfo hubInfo) { - mClientProxy = clientProxy; - mCallbackInterface = callback; + /* package */ ContextHubClient(ContextHubInfo hubInfo) { mAttachedHub = hubInfo; mCloseGuard.open("close"); } /** + * Sets the proxy interface of the client at the service. This method should always be called + * by the ContextHubManager after the client is registered at the service, and should only be + * called once. + * + * @param clientProxy the proxy of the client at the service + */ + /* package */ void setClientProxy(IContextHubClient clientProxy) { + Preconditions.checkNotNull(clientProxy, "IContextHubClient cannot be null"); + if (mClientProxy != null) { + throw new IllegalStateException("Cannot change client proxy multiple times"); + } + + mClientProxy = clientProxy; + } + + /** * Returns the hub that this client is attached to. * * @return the ContextHubInfo of the attached hub */ + @NonNull public ContextHubInfo getAttachedHub() { return mAttachedHub; } @@ -96,12 +109,16 @@ public class ContextHubClient implements Closeable { * * @return the result of sending the message defined as in ContextHubTransaction.Result * + * @throws NullPointerException if NanoAppMessage is null + * * @see NanoAppMessage * @see ContextHubTransaction.Result */ @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) @ContextHubTransaction.Result - public int sendMessageToNanoApp(NanoAppMessage message) { + public int sendMessageToNanoApp(@NonNull NanoAppMessage message) { + Preconditions.checkNotNull(message, "NanoAppMessage cannot be null"); + try { return mClientProxy.sendMessageToNanoApp(message); } catch (RemoteException e) { diff --git a/core/java/android/hardware/location/ContextHubClientCallback.java b/core/java/android/hardware/location/ContextHubClientCallback.java index ab19d547025d..cc2fe65dcb7e 100644 --- a/core/java/android/hardware/location/ContextHubClientCallback.java +++ b/core/java/android/hardware/location/ContextHubClientCallback.java @@ -15,15 +15,20 @@ */ package android.hardware.location; +import android.annotation.SystemApi; + +import java.util.concurrent.Executor; + /** * A class for {@link android.hardware.location.ContextHubClient ContextHubClient} to * receive messages and life-cycle events from nanoapps in the Context Hub at which the client is * attached to. * - * This callback is registered through the - * {@link android.hardware.location.ContextHubManager#createClient() creation} of - * {@link android.hardware.location.ContextHubClient ContextHubClient}. Callbacks are - * invoked in the following ways: + * This callback is registered through the {@link + * android.hardware.location.ContextHubManager#createClient( + * ContextHubInfo, ContextHubClientCallback, Executor) creation} of + * {@link android.hardware.location.ContextHubClient ContextHubClient}. Callbacks are invoked in + * the following ways: * 1) Messages from nanoapps delivered through onMessageFromNanoApp may either be broadcasted * or targeted to a specific client. * 2) Nanoapp or Context Hub events (the remaining callbacks) are broadcasted to all clients, and @@ -31,6 +36,7 @@ package android.hardware.location; * * @hide */ +@SystemApi public class ContextHubClientCallback { /** * Callback invoked when receiving a message from a nanoapp. @@ -38,48 +44,56 @@ public class ContextHubClientCallback { * The message contents of this callback may either be broadcasted or targeted to the * client receiving the invocation. * + * @param client the client that is associated with this callback * @param message the message sent by the nanoapp */ - public void onMessageFromNanoApp(NanoAppMessage message) {} + public void onMessageFromNanoApp(ContextHubClient client, NanoAppMessage message) {} /** * Callback invoked when the attached Context Hub has reset. + * + * @param client the client that is associated with this callback */ - public void onHubReset() {} + public void onHubReset(ContextHubClient client) {} /** * Callback invoked when a nanoapp aborts at the attached Context Hub. * + * @param client the client that is associated with this callback * @param nanoAppId the ID of the nanoapp that had aborted * @param abortCode the reason for nanoapp's abort, specific to each nanoapp */ - public void onNanoAppAborted(long nanoAppId, int abortCode) {} + public void onNanoAppAborted(ContextHubClient client, long nanoAppId, int abortCode) {} /** * Callback invoked when a nanoapp is loaded at the attached Context Hub. * + * @param client the client that is associated with this callback * @param nanoAppId the ID of the nanoapp that had been loaded */ - public void onNanoAppLoaded(long nanoAppId) {} + public void onNanoAppLoaded(ContextHubClient client, long nanoAppId) {} /** * Callback invoked when a nanoapp is unloaded from the attached Context Hub. * + * @param client the client that is associated with this callback * @param nanoAppId the ID of the nanoapp that had been unloaded */ - public void onNanoAppUnloaded(long nanoAppId) {} + public void onNanoAppUnloaded(ContextHubClient client, long nanoAppId) {} /** * Callback invoked when a nanoapp is enabled at the attached Context Hub. * + * @param client the client that is associated with this callback * @param nanoAppId the ID of the nanoapp that had been enabled */ - public void onNanoAppEnabled(long nanoAppId) {} + public void onNanoAppEnabled(ContextHubClient client, long nanoAppId) {} /** * Callback invoked when a nanoapp is disabled at the attached Context Hub. * + * @param client the client that is associated with this callback * @param nanoAppId the ID of the nanoapp that had been disabled */ - public void onNanoAppDisabled(long nanoAppId) {} + public void onNanoAppDisabled(ContextHubClient client, long nanoAppId) {} } diff --git a/core/java/android/hardware/location/ContextHubInfo.java b/core/java/android/hardware/location/ContextHubInfo.java index c2b280016f68..36123e3d4229 100644 --- a/core/java/android/hardware/location/ContextHubInfo.java +++ b/core/java/android/hardware/location/ContextHubInfo.java @@ -221,9 +221,6 @@ public class ContextHubInfo implements Parcelable { /** * @return the CHRE platform ID as defined in chre/version.h - * - * TODO(b/67734082): Expose as public API - * @hide */ public long getChrePlatformId() { return mChrePlatformId; @@ -231,9 +228,6 @@ public class ContextHubInfo implements Parcelable { /** * @return the CHRE API's major version as defined in chre/version.h - * - * TODO(b/67734082): Expose as public API - * @hide */ public byte getChreApiMajorVersion() { return mChreApiMajorVersion; @@ -241,9 +235,6 @@ public class ContextHubInfo implements Parcelable { /** * @return the CHRE API's minor version as defined in chre/version.h - * - * TODO(b/67734082): Expose as public API - * @hide */ public byte getChreApiMinorVersion() { return mChreApiMinorVersion; @@ -251,9 +242,6 @@ public class ContextHubInfo implements Parcelable { /** * @return the CHRE patch version as defined in chre/version.h - * - * TODO(b/67734082): Expose as public API - * @hide */ public short getChrePatchVersion() { return mChrePatchVersion; diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java index 4cea0acd3809..de13c8132a65 100644 --- a/core/java/android/hardware/location/ContextHubManager.java +++ b/core/java/android/hardware/location/ContextHubManager.java @@ -17,6 +17,7 @@ package android.hardware.location; import android.annotation.CallbackExecutor; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; @@ -30,6 +31,8 @@ import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; import android.util.Log; +import com.android.internal.util.Preconditions; + import java.util.List; import java.util.concurrent.Executor; @@ -59,7 +62,11 @@ public final class ContextHubManager { /** * An interface to receive asynchronous communication from the context hub. + * + * @deprecated Use the more refined {@link android.hardware.location.ContextHubClientCallback} + * instead for notification callbacks. */ + @Deprecated public abstract static class Callback { protected Callback() {} @@ -75,7 +82,7 @@ public final class ContextHubManager { public abstract void onMessageReceipt( int hubHandle, int nanoAppHandle, - ContextHubMessage message); + @NonNull ContextHubMessage message); } /** @@ -98,8 +105,13 @@ public final class ContextHubManager { /** * Get a handle to all the context hubs in the system + * * @return array of context hub handles + * + * @deprecated Use {@link #getContextHubs()} instead. The use of handles are deprecated in the + * new APIs. */ + @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public int[] getContextHubHandles() { try { @@ -116,7 +128,11 @@ public final class ContextHubManager { * @return ContextHubInfo Information about the requested context hub. * * @see ContextHubInfo + * + * @deprecated Use {@link #getContextHubs()} instead. The use of handles are deprecated in the + * new APIs. */ + @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public ContextHubInfo getContextHubInfo(int hubHandle) { try { @@ -144,9 +160,12 @@ public final class ContextHubManager { * -1 otherwise * * @see NanoApp + * + * @deprecated Use {@link #loadNanoApp(ContextHubInfo, NanoAppBinary)} instead. */ + @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) - public int loadNanoApp(int hubHandle, NanoApp app) { + public int loadNanoApp(int hubHandle, @NonNull NanoApp app) { try { return mService.loadNanoApp(hubHandle, app); } catch (RemoteException e) { @@ -168,7 +187,10 @@ public final class ContextHubManager { * * @return 0 if the command for unloading was sent to the context hub; * -1 otherwise + * + * @deprecated Use {@link #unloadNanoApp(ContextHubInfo, long)} instead. */ + @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public int unloadNanoApp(int nanoAppHandle) { try { @@ -199,13 +221,18 @@ public final class ContextHubManager { * TODO(b/30943489): Have the returned NanoAppInstanceInfo contain the * correct information. * - * @param nanoAppHandle handle of the nanoAppInstance - * @return NanoAppInstanceInfo Information about the nano app instance. + * @param nanoAppHandle handle of the nanoapp instance + * @return NanoAppInstanceInfo the NanoAppInstanceInfo of the nanoapp, or null if the nanoapp + * does not exist * * @see NanoAppInstanceInfo + * + * @deprecated Use {@link #queryNanoApps(ContextHubInfo)} instead to explicitly query the hub + * for loaded nanoapps. */ + @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) - public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) { + @Nullable public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) { try { return mService.getNanoAppInstanceInfo(nanoAppHandle); } catch (RemoteException e) { @@ -222,9 +249,13 @@ public final class ContextHubManager { * @see NanoAppFilter * * @return int[] Array of handles to any found nano apps + * + * @deprecated Use {@link #queryNanoApps(ContextHubInfo)} instead to explicitly query the hub + * for loaded nanoapps. */ + @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) - public int[] findNanoAppOnHub(int hubHandle, NanoAppFilter filter) { + @NonNull public int[] findNanoAppOnHub(int hubHandle, @NonNull NanoAppFilter filter) { try { return mService.findNanoAppOnHub(hubHandle, filter); } catch (RemoteException e) { @@ -250,9 +281,16 @@ public final class ContextHubManager { * @see ContextHubMessage * * @return int 0 on success, -1 otherwise + * + * @deprecated Use {@link android.hardware.location.ContextHubClient#sendMessageToNanoApp( + * NanoAppMessage)} instead, after creating a + * {@link android.hardware.location.ContextHubClient} with + * {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)} + * or {@link #createClient(ContextHubInfo, ContextHubClientCallback)}. */ + @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) - public int sendMessage(int hubHandle, int nanoAppHandle, ContextHubMessage message) { + public int sendMessage(int hubHandle, int nanoAppHandle, @NonNull ContextHubMessage message) { try { return mService.sendMessage(hubHandle, nanoAppHandle, message); } catch (RemoteException e) { @@ -266,11 +304,9 @@ public final class ContextHubManager { * @return the list of ContextHubInfo objects * * @see ContextHubInfo - * - * @hide */ @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) - public List<ContextHubInfo> getContextHubs() { + @NonNull public List<ContextHubInfo> getContextHubs() { try { return mService.getContextHubs(); } catch (RemoteException e) { @@ -342,13 +378,16 @@ public final class ContextHubManager { * * @return the ContextHubTransaction of the request * - * @see NanoAppBinary + * @throws NullPointerException if hubInfo or NanoAppBinary is null * - * @hide + * @see NanoAppBinary */ @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) - public ContextHubTransaction<Void> loadNanoApp( - ContextHubInfo hubInfo, NanoAppBinary appBinary) { + @NonNull public ContextHubTransaction<Void> loadNanoApp( + @NonNull ContextHubInfo hubInfo, @NonNull NanoAppBinary appBinary) { + Preconditions.checkNotNull(hubInfo, "ContextHubInfo cannot be null"); + Preconditions.checkNotNull(appBinary, "NanoAppBinary cannot be null"); + ContextHubTransaction<Void> transaction = new ContextHubTransaction<>(ContextHubTransaction.TYPE_LOAD_NANOAPP); IContextHubTransactionCallback callback = createTransactionCallback(transaction); @@ -370,10 +409,13 @@ public final class ContextHubManager { * * @return the ContextHubTransaction of the request * - * @hide + * @throws NullPointerException if hubInfo is null */ @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) - public ContextHubTransaction<Void> unloadNanoApp(ContextHubInfo hubInfo, long nanoAppId) { + @NonNull public ContextHubTransaction<Void> unloadNanoApp( + @NonNull ContextHubInfo hubInfo, long nanoAppId) { + Preconditions.checkNotNull(hubInfo, "ContextHubInfo cannot be null"); + ContextHubTransaction<Void> transaction = new ContextHubTransaction<>(ContextHubTransaction.TYPE_UNLOAD_NANOAPP); IContextHubTransactionCallback callback = createTransactionCallback(transaction); @@ -395,10 +437,13 @@ public final class ContextHubManager { * * @return the ContextHubTransaction of the request * - * @hide + * @throws NullPointerException if hubInfo is null */ @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) - public ContextHubTransaction<Void> enableNanoApp(ContextHubInfo hubInfo, long nanoAppId) { + @NonNull public ContextHubTransaction<Void> enableNanoApp( + @NonNull ContextHubInfo hubInfo, long nanoAppId) { + Preconditions.checkNotNull(hubInfo, "ContextHubInfo cannot be null"); + ContextHubTransaction<Void> transaction = new ContextHubTransaction<>(ContextHubTransaction.TYPE_ENABLE_NANOAPP); IContextHubTransactionCallback callback = createTransactionCallback(transaction); @@ -420,10 +465,13 @@ public final class ContextHubManager { * * @return the ContextHubTransaction of the request * - * @hide + * @throws NullPointerException if hubInfo is null */ @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) - public ContextHubTransaction<Void> disableNanoApp(ContextHubInfo hubInfo, long nanoAppId) { + @NonNull public ContextHubTransaction<Void> disableNanoApp( + @NonNull ContextHubInfo hubInfo, long nanoAppId) { + Preconditions.checkNotNull(hubInfo, "ContextHubInfo cannot be null"); + ContextHubTransaction<Void> transaction = new ContextHubTransaction<>(ContextHubTransaction.TYPE_DISABLE_NANOAPP); IContextHubTransactionCallback callback = createTransactionCallback(transaction); @@ -444,10 +492,13 @@ public final class ContextHubManager { * * @return the ContextHubTransaction of the request * - * @hide + * @throws NullPointerException if hubInfo is null */ @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) - public ContextHubTransaction<List<NanoAppState>> queryNanoApps(ContextHubInfo hubInfo) { + @NonNull public ContextHubTransaction<List<NanoAppState>> queryNanoApps( + @NonNull ContextHubInfo hubInfo) { + Preconditions.checkNotNull(hubInfo, "ContextHubInfo cannot be null"); + ContextHubTransaction<List<NanoAppState>> transaction = new ContextHubTransaction<>(ContextHubTransaction.TYPE_QUERY_NANOAPPS); IContextHubTransactionCallback callback = createQueryCallback(transaction); @@ -469,9 +520,14 @@ public final class ContextHubManager { * @see Callback * * @return int 0 on success, -1 otherwise + * + * @deprecated Use {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)} + * or {@link #createClient(ContextHubInfo, ContextHubClientCallback)} instead to + * register a {@link android.hardware.location.ContextHubClientCallback}. */ + @Deprecated @SuppressLint("Doclava125") - public int registerCallback(Callback callback) { + public int registerCallback(@NonNull Callback callback) { return registerCallback(callback, null); } @@ -498,7 +554,12 @@ public final class ContextHubManager { * @see Callback * * @return int 0 on success, -1 otherwise + * + * @deprecated Use {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)} + * or {@link #createClient(ContextHubInfo, ContextHubClientCallback)} instead to + * register a {@link android.hardware.location.ContextHubClientCallback}. */ + @Deprecated @SuppressLint("Doclava125") public int registerCallback(Callback callback, Handler handler) { synchronized(this) { @@ -515,47 +576,48 @@ public final class ContextHubManager { /** * Creates an interface to the ContextHubClient to send down to the service. * + * @param client the ContextHubClient object associated with this callback * @param callback the callback to invoke at the client process * @param executor the executor to invoke callbacks for this client * * @return the callback interface */ private IContextHubClientCallback createClientCallback( - ContextHubClientCallback callback, Executor executor) { + ContextHubClient client, ContextHubClientCallback callback, Executor executor) { return new IContextHubClientCallback.Stub() { @Override public void onMessageFromNanoApp(NanoAppMessage message) { - executor.execute(() -> callback.onMessageFromNanoApp(message)); + executor.execute(() -> callback.onMessageFromNanoApp(client, message)); } @Override public void onHubReset() { - executor.execute(() -> callback.onHubReset()); + executor.execute(() -> callback.onHubReset(client)); } @Override public void onNanoAppAborted(long nanoAppId, int abortCode) { - executor.execute(() -> callback.onNanoAppAborted(nanoAppId, abortCode)); + executor.execute(() -> callback.onNanoAppAborted(client, nanoAppId, abortCode)); } @Override public void onNanoAppLoaded(long nanoAppId) { - executor.execute(() -> callback.onNanoAppLoaded(nanoAppId)); + executor.execute(() -> callback.onNanoAppLoaded(client, nanoAppId)); } @Override public void onNanoAppUnloaded(long nanoAppId) { - executor.execute(() -> callback.onNanoAppUnloaded(nanoAppId)); + executor.execute(() -> callback.onNanoAppUnloaded(client, nanoAppId)); } @Override public void onNanoAppEnabled(long nanoAppId) { - executor.execute(() -> callback.onNanoAppEnabled(nanoAppId)); + executor.execute(() -> callback.onNanoAppEnabled(client, nanoAppId)); } @Override public void onNanoAppDisabled(long nanoAppId) { - executor.execute(() -> callback.onNanoAppDisabled(nanoAppId)); + executor.execute(() -> callback.onNanoAppDisabled(client, nanoAppId)); } }; } @@ -574,31 +636,30 @@ public final class ContextHubManager { * * @throws IllegalArgumentException if hubInfo does not represent a valid hub * @throws IllegalStateException if there were too many registered clients at the service - * @throws NullPointerException if callback or hubInfo is null + * @throws NullPointerException if callback, hubInfo, or executor is null * - * @hide * @see ContextHubClientCallback */ @NonNull public ContextHubClient createClient( @NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback, @NonNull @CallbackExecutor Executor executor) { - if (callback == null) { - throw new NullPointerException("Callback cannot be null"); - } - if (hubInfo == null) { - throw new NullPointerException("Hub info cannot be null"); - } + Preconditions.checkNotNull(callback, "Callback cannot be null"); + Preconditions.checkNotNull(hubInfo, "ContextHubInfo cannot be null"); + Preconditions.checkNotNull(executor, "Executor cannot be null"); - IContextHubClientCallback clientInterface = createClientCallback(callback, executor); + ContextHubClient client = new ContextHubClient(hubInfo); + IContextHubClientCallback clientInterface = createClientCallback( + client, callback, executor); - IContextHubClient client; + IContextHubClient clientProxy; try { - client = mService.createClient(clientInterface, hubInfo.getId()); + clientProxy = mService.createClient(clientInterface, hubInfo.getId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } - return new ContextHubClient(client, clientInterface, hubInfo); + client.setClientProxy(clientProxy); + return client; } /** @@ -612,7 +673,7 @@ public final class ContextHubManager { * @throws IllegalArgumentException if hubInfo does not represent a valid hub * @throws IllegalStateException if there were too many registered clients at the service * @throws NullPointerException if callback or hubInfo is null - * @hide + * * @see ContextHubClientCallback */ @NonNull public ContextHubClient createClient( @@ -628,9 +689,13 @@ public final class ContextHubManager { * @param callback method to deregister * * @return int 0 on success, -1 otherwise + * + * @deprecated Use {@link android.hardware.location.ContextHubClient#close()} to unregister + * a {@link android.hardware.location.ContextHubClientCallback}. */ @SuppressLint("Doclava125") - public int unregisterCallback(Callback callback) { + @Deprecated + public int unregisterCallback(@NonNull Callback callback) { synchronized(this) { if (callback != mCallback) { Log.w(TAG, "Cannot recognize callback!"); @@ -679,8 +744,6 @@ public final class ContextHubManager { synchronized (this) { mLocalCallback.onMessageReceipt(hubId, nanoAppId, message); } - } else { - Log.d(TAG, "Context hub manager client callback is NULL"); } } }; @@ -694,7 +757,7 @@ public final class ContextHubManager { try { mService.registerCallback(mClientCallback); } catch (RemoteException e) { - Log.w(TAG, "Could not register callback:" + e); + throw e.rethrowFromSystemServer(); } } } diff --git a/core/java/android/hardware/location/ContextHubTransaction.java b/core/java/android/hardware/location/ContextHubTransaction.java index a1b743da785b..bc7efef55bcf 100644 --- a/core/java/android/hardware/location/ContextHubTransaction.java +++ b/core/java/android/hardware/location/ContextHubTransaction.java @@ -18,9 +18,12 @@ package android.hardware.location; import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Handler; import android.os.HandlerExecutor; +import com.android.internal.util.Preconditions; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.concurrent.CountDownLatch; @@ -35,17 +38,19 @@ import java.util.concurrent.TimeoutException; * through the ContextHubManager APIs. The caller can either retrieve the result * synchronously through a blocking call ({@link #waitForResponse(long, TimeUnit)}) or * asynchronously through a user-defined listener - * ({@link #setOnCompleteListener(Listener, Executor)} )}). + * ({@link #setOnCompleteListener(OnCompleteListener, Executor)} )}). * * @param <T> the type of the contents in the transaction response * * @hide */ +@SystemApi public class ContextHubTransaction<T> { private static final String TAG = "ContextHubTransaction"; /** * Constants describing the type of a transaction through the Context Hub Service. + * {@hide} */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "TYPE_" }, value = { @@ -65,6 +70,7 @@ public class ContextHubTransaction<T> { /** * Constants describing the result of a transaction or request through the Context Hub Service. + * {@hide} */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "RESULT_" }, value = { @@ -72,7 +78,7 @@ public class ContextHubTransaction<T> { RESULT_FAILED_UNKNOWN, RESULT_FAILED_BAD_PARAMS, RESULT_FAILED_UNINITIALIZED, - RESULT_FAILED_PENDING, + RESULT_FAILED_BUSY, RESULT_FAILED_AT_HUB, RESULT_FAILED_TIMEOUT, RESULT_FAILED_SERVICE_INTERNAL_FAILURE, @@ -95,7 +101,7 @@ public class ContextHubTransaction<T> { /** * Failure mode when there are too many transactions pending. */ - public static final int RESULT_FAILED_PENDING = 4; + public static final int RESULT_FAILED_BUSY = 4; /** * Failure mode when the request went through, but failed asynchronously at the hub. */ @@ -151,7 +157,7 @@ public class ContextHubTransaction<T> { * @param <L> the type of the contents in the transaction response */ @FunctionalInterface - public interface Listener<L> { + public interface OnCompleteListener<L> { /** * The listener function to invoke when the transaction completes. * @@ -181,7 +187,7 @@ public class ContextHubTransaction<T> { /* * The listener to be invoked when the transaction completes. */ - private ContextHubTransaction.Listener<T> mListener = null; + private ContextHubTransaction.OnCompleteListener<T> mListener = null; /* * Synchronization latch used to block on response. @@ -272,8 +278,8 @@ public class ContextHubTransaction<T> { * A transaction can be invalidated if the process owning the transaction is no longer active * and the reference to this object is lost. * - * This method or {@link #setOnCompleteListener(ContextHubTransaction.Listener)} can only be - * invoked once, or an IllegalStateException will be thrown. + * This method or {@link #setOnCompleteListener(ContextHubTransaction.OnCompleteListener)} can + * only be invoked once, or an IllegalStateException will be thrown. * * @param listener the listener to be invoked upon completion * @param executor the executor to invoke the callback @@ -282,15 +288,11 @@ public class ContextHubTransaction<T> { * @throws NullPointerException if the callback or handler is null */ public void setOnCompleteListener( - @NonNull ContextHubTransaction.Listener<T> listener, + @NonNull ContextHubTransaction.OnCompleteListener<T> listener, @NonNull @CallbackExecutor Executor executor) { synchronized (this) { - if (listener == null) { - throw new NullPointerException("Listener cannot be null"); - } - if (executor == null) { - throw new NullPointerException("Executor cannot be null"); - } + Preconditions.checkNotNull(listener, "OnCompleteListener cannot be null"); + Preconditions.checkNotNull(executor, "Executor cannot be null"); if (mListener != null) { throw new IllegalStateException( "Cannot set ContextHubTransaction listener multiple times"); @@ -308,18 +310,19 @@ public class ContextHubTransaction<T> { /** * Sets the listener to be invoked invoked when the transaction completes. * - * Equivalent to {@link #setOnCompleteListener(ContextHubTransaction.Listener, Executor)} - * with the executor using the main thread's Looper. + * Equivalent to {@link #setOnCompleteListener(ContextHubTransaction.OnCompleteListener, + * Executor)} with the executor using the main thread's Looper. * - * This method or {@link #setOnCompleteListener(ContextHubTransaction.Listener, Executor)} - * can only be invoked once, or an IllegalStateException will be thrown. + * This method or {@link #setOnCompleteListener(ContextHubTransaction.OnCompleteListener, + * Executor)} can only be invoked once, or an IllegalStateException will be thrown. * * @param listener the listener to be invoked upon completion * * @throws IllegalStateException if this method is called multiple times * @throws NullPointerException if the callback is null */ - public void setOnCompleteListener(@NonNull ContextHubTransaction.Listener<T> listener) { + public void setOnCompleteListener( + @NonNull ContextHubTransaction.OnCompleteListener<T> listener) { setOnCompleteListener(listener, new HandlerExecutor(Handler.getMain())); } @@ -337,9 +340,7 @@ public class ContextHubTransaction<T> { */ /* package */ void setResponse(ContextHubTransaction.Response<T> response) { synchronized (this) { - if (response == null) { - throw new NullPointerException("Response cannot be null"); - } + Preconditions.checkNotNull(response, "Response cannot be null"); if (mIsResponseSet) { throw new IllegalStateException( "Cannot set response of ContextHubTransaction multiple times"); diff --git a/core/java/android/hardware/location/NanoAppBinary.java b/core/java/android/hardware/location/NanoAppBinary.java index 934e9e48c01a..ba01ca251321 100644 --- a/core/java/android/hardware/location/NanoAppBinary.java +++ b/core/java/android/hardware/location/NanoAppBinary.java @@ -15,6 +15,7 @@ */ package android.hardware.location; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; @@ -27,6 +28,7 @@ import java.util.Arrays; /** * @hide */ +@SystemApi public final class NanoAppBinary implements Parcelable { private static final String TAG = "NanoAppBinary"; diff --git a/core/java/android/hardware/location/NanoAppInstanceInfo.java b/core/java/android/hardware/location/NanoAppInstanceInfo.java index f73fd87b1a19..c00819bde6dc 100644 --- a/core/java/android/hardware/location/NanoAppInstanceInfo.java +++ b/core/java/android/hardware/location/NanoAppInstanceInfo.java @@ -90,11 +90,6 @@ public class NanoAppInstanceInfo { /** * Get the application version * - * NOTE: There is a race condition where shortly after loading, this - * may return -1 instead of the correct version. - * - * TODO(b/30970527): Fix this race condition. - * * @return int - version of the app */ public int getAppVersion() { diff --git a/core/java/android/hardware/location/NanoAppMessage.java b/core/java/android/hardware/location/NanoAppMessage.java index 202867490fb9..716a1946c540 100644 --- a/core/java/android/hardware/location/NanoAppMessage.java +++ b/core/java/android/hardware/location/NanoAppMessage.java @@ -15,6 +15,7 @@ */ package android.hardware.location; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -25,6 +26,7 @@ import android.os.Parcelable; * * @hide */ +@SystemApi public final class NanoAppMessage implements Parcelable { private long mNanoAppId; private int mMessageType; diff --git a/core/java/android/hardware/location/NanoAppState.java b/core/java/android/hardware/location/NanoAppState.java index 644031b034d5..d05277d484a6 100644 --- a/core/java/android/hardware/location/NanoAppState.java +++ b/core/java/android/hardware/location/NanoAppState.java @@ -15,6 +15,7 @@ */ package android.hardware.location; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -23,6 +24,7 @@ import android.os.Parcelable; * * @hide */ +@SystemApi public final class NanoAppState implements Parcelable { private long mNanoAppId; private int mNanoAppVersion; diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java index 4d54e31b8c5d..9b6515c9ebf1 100644 --- a/core/java/android/hardware/radio/RadioManager.java +++ b/core/java/android/hardware/radio/RadioManager.java @@ -38,6 +38,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -645,7 +646,8 @@ public class RadioManager { private final boolean mAf; private final boolean mEa; - FmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing, + /** @hide */ + public FmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing, boolean stereo, boolean rds, boolean ta, boolean af, boolean ea) { super(region, type, lowerLimit, upperLimit, spacing); mStereo = stereo; @@ -771,7 +773,8 @@ public class RadioManager { private final boolean mStereo; - AmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing, + /** @hide */ + public AmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing, boolean stereo) { super(region, type, lowerLimit, upperLimit, spacing); mStereo = stereo; @@ -843,10 +846,10 @@ public class RadioManager { /** Radio band configuration. */ public static class BandConfig implements Parcelable { - final BandDescriptor mDescriptor; + @NonNull final BandDescriptor mDescriptor; BandConfig(BandDescriptor descriptor) { - mDescriptor = descriptor; + mDescriptor = Objects.requireNonNull(descriptor); } BandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing) { @@ -968,7 +971,8 @@ public class RadioManager { private final boolean mAf; private final boolean mEa; - FmBandConfig(FmBandDescriptor descriptor) { + /** @hide */ + public FmBandConfig(FmBandDescriptor descriptor) { super((BandDescriptor)descriptor); mStereo = descriptor.isStereoSupported(); mRds = descriptor.isRdsSupported(); @@ -1204,7 +1208,8 @@ public class RadioManager { public static class AmBandConfig extends BandConfig { private final boolean mStereo; - AmBandConfig(AmBandDescriptor descriptor) { + /** @hide */ + public AmBandConfig(AmBandDescriptor descriptor) { super((BandDescriptor)descriptor); mStereo = descriptor.isStereoSupported(); } diff --git a/core/java/android/net/NetworkWatchlistManager.java b/core/java/android/net/NetworkWatchlistManager.java index 42e43c8aea1e..5425bf534ebd 100644 --- a/core/java/android/net/NetworkWatchlistManager.java +++ b/core/java/android/net/NetworkWatchlistManager.java @@ -59,8 +59,8 @@ public class NetworkWatchlistManager { /** * Report network watchlist records if necessary. * - * Watchlist report process will run summarize records into a single report, then the - * report will be processed by differential privacy framework and store it on disk. + * Watchlist report process will summarize records into a single report, then the + * report will be processed by differential privacy framework and stored on disk. * * @hide */ @@ -72,4 +72,18 @@ public class NetworkWatchlistManager { e.rethrowFromSystemServer(); } } + + /** + * Reload network watchlist. + * + * @hide + */ + public void reloadWatchlist() { + try { + mNetworkWatchlistManager.reloadWatchlist(); + } catch (RemoteException e) { + Log.e(TAG, "Unable to reload watchlist"); + e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 1e847c595df0..d4d74f438e5d 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -180,6 +180,11 @@ public abstract class BatteryStats implements Parcelable { public static final int FOREGROUND_SERVICE = 22; /** + * A constant indicating an aggregate wifi multicast timer + */ + public static final int WIFI_AGGREGATE_MULTICAST_ENABLED = 23; + + /** * Include all of the data in the stats, including previously saved data. */ public static final int STATS_SINCE_CHARGED = 0; @@ -2334,6 +2339,22 @@ public abstract class BatteryStats implements Parcelable { }; /** + * Returns total time for WiFi Multicast Wakelock timer. + * Note that this may be different from the sum of per uid timer values. + * + * {@hide} + */ + public abstract long getWifiMulticastWakelockTime(long elapsedRealtimeUs, int which); + + /** + * Returns total time for WiFi Multicast Wakelock timer + * Note that this may be different from the sum of per uid timer values. + * + * {@hide} + */ + public abstract int getWifiMulticastWakelockCount(int which); + + /** * Returns the time in microseconds that wifi has been on while the device was * running on battery. * @@ -3442,16 +3463,13 @@ public abstract class BatteryStats implements Parcelable { screenDozeTime / 1000); - // Calculate both wakelock and wifi multicast wakelock times across all uids. + // Calculate wakelock times across all uids. long fullWakeLockTimeTotal = 0; long partialWakeLockTimeTotal = 0; - long multicastWakeLockTimeTotalMicros = 0; - int multicastWakeLockCountTotal = 0; for (int iu = 0; iu < NU; iu++) { final Uid u = uidStats.valueAt(iu); - // First calculating the wakelock stats final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelocks = u.getWakelockStats(); for (int iw=wakelocks.size()-1; iw>=0; iw--) { @@ -3469,13 +3487,6 @@ public abstract class BatteryStats implements Parcelable { rawRealtime, which); } } - - // Now calculating the wifi multicast wakelock stats - final Timer mcTimer = u.getMulticastWakelockStats(); - if (mcTimer != null) { - multicastWakeLockTimeTotalMicros += mcTimer.getTotalTimeLocked(rawRealtime, which); - multicastWakeLockCountTotal += mcTimer.getCountLocked(which); - } } // Dump network stats @@ -3592,6 +3603,9 @@ public abstract class BatteryStats implements Parcelable { dumpLine(pw, 0 /* uid */, category, WIFI_SIGNAL_STRENGTH_COUNT_DATA, args); // Dump Multicast total stats + final long multicastWakeLockTimeTotalMicros = + getWifiMulticastWakelockTime(rawRealtime, which); + final int multicastWakeLockCountTotal = getWifiMulticastWakelockCount(which); dumpLine(pw, 0 /* uid */, category, WIFI_MULTICAST_TOTAL_DATA, multicastWakeLockTimeTotalMicros / 1000, multicastWakeLockCountTotal); @@ -4456,18 +4470,15 @@ public abstract class BatteryStats implements Parcelable { pw.print(" Connectivity changes: "); pw.println(connChanges); } - // Calculate both wakelock and wifi multicast wakelock times across all uids. + // Calculate wakelock times across all uids. long fullWakeLockTimeTotalMicros = 0; long partialWakeLockTimeTotalMicros = 0; - long multicastWakeLockTimeTotalMicros = 0; - int multicastWakeLockCountTotal = 0; final ArrayList<TimerEntry> timers = new ArrayList<>(); for (int iu = 0; iu < NU; iu++) { final Uid u = uidStats.valueAt(iu); - // First calculate wakelock statistics final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelocks = u.getWakelockStats(); for (int iw=wakelocks.size()-1; iw>=0; iw--) { @@ -4495,13 +4506,6 @@ public abstract class BatteryStats implements Parcelable { } } } - - // Next calculate wifi multicast wakelock statistics - final Timer mcTimer = u.getMulticastWakelockStats(); - if (mcTimer != null) { - multicastWakeLockTimeTotalMicros += mcTimer.getTotalTimeLocked(rawRealtime, which); - multicastWakeLockCountTotal += mcTimer.getCountLocked(which); - } } final long mobileRxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which); @@ -4531,6 +4535,9 @@ public abstract class BatteryStats implements Parcelable { pw.println(sb.toString()); } + final long multicastWakeLockTimeTotalMicros = + getWifiMulticastWakelockTime(rawRealtime, which); + final int multicastWakeLockCountTotal = getWifiMulticastWakelockCount(which); if (multicastWakeLockTimeTotalMicros != 0) { sb.setLength(0); sb.append(prefix); @@ -7051,6 +7058,28 @@ public abstract class BatteryStats implements Parcelable { } } } + + for (int procState = 0; procState < Uid.NUM_PROCESS_STATE; ++procState) { + final long[] timesMs = u.getCpuFreqTimes(which, procState); + if (timesMs != null && timesMs.length == cpuFreqs.length) { + long[] screenOffTimesMs = u.getScreenOffCpuFreqTimes(which, procState); + if (screenOffTimesMs == null) { + screenOffTimesMs = new long[timesMs.length]; + } + final long procToken = proto.start(UidProto.Cpu.BY_PROCESS_STATE); + proto.write(UidProto.Cpu.ByProcessState.PROCESS_STATE, procState); + for (int ic = 0; ic < timesMs.length; ++ic) { + long cToken = proto.start(UidProto.Cpu.ByProcessState.BY_FREQUENCY); + proto.write(UidProto.Cpu.ByFrequency.FREQUENCY_INDEX, ic + 1); + proto.write(UidProto.Cpu.ByFrequency.TOTAL_DURATION_MS, + timesMs[ic]); + proto.write(UidProto.Cpu.ByFrequency.SCREEN_OFF_DURATION_MS, + screenOffTimesMs[ic]); + proto.end(cToken); + } + proto.end(procToken); + } + } proto.end(cpuToken); // Flashlight (FLASHLIGHT_DATA) @@ -7535,22 +7564,9 @@ public abstract class BatteryStats implements Parcelable { proto.end(mToken); // Wifi multicast wakelock total stats (WIFI_MULTICAST_WAKELOCK_TOTAL_DATA) - // Calculate multicast wakelock stats across all uids. - long multicastWakeLockTimeTotalUs = 0; - int multicastWakeLockCountTotal = 0; - - for (int iu = 0; iu < uidStats.size(); iu++) { - final Uid u = uidStats.valueAt(iu); - - final Timer mcTimer = u.getMulticastWakelockStats(); - - if (mcTimer != null) { - multicastWakeLockTimeTotalUs += - mcTimer.getTotalTimeLocked(rawRealtimeUs, which); - multicastWakeLockCountTotal += mcTimer.getCountLocked(which); - } - } - + final long multicastWakeLockTimeTotalUs = + getWifiMulticastWakelockTime(rawRealtimeUs, which); + final int multicastWakeLockCountTotal = getWifiMulticastWakelockCount(which); final long wmctToken = proto.start(SystemProto.WIFI_MULTICAST_WAKELOCK_TOTAL); proto.write(SystemProto.WifiMulticastWakelockTotal.DURATION_MS, multicastWakeLockTimeTotalUs / 1000); diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java index 3ca1005b8c98..5c5e351d2eeb 100644 --- a/core/java/android/os/Handler.java +++ b/core/java/android/os/Handler.java @@ -388,6 +388,8 @@ public class Handler { * The runnable will be run on the thread to which this handler is attached. * * @param r The Runnable that will be executed. + * @param token An instance which can be used to cancel {@code r} via + * {@link #removeCallbacksAndMessages}. * @param uptimeMillis The absolute time at which the callback should run, * using the {@link android.os.SystemClock#uptimeMillis} time-base. * @@ -430,6 +432,32 @@ public class Handler { } /** + * Causes the Runnable r to be added to the message queue, to be run + * after the specified amount of time elapses. + * The runnable will be run on the thread to which this handler + * is attached. + * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> + * Time spent in deep sleep will add an additional delay to execution. + * + * @param r The Runnable that will be executed. + * @param token An instance which can be used to cancel {@code r} via + * {@link #removeCallbacksAndMessages}. + * @param delayMillis The delay (in milliseconds) until the Runnable + * will be executed. + * + * @return Returns true if the Runnable was successfully placed in to the + * message queue. Returns false on failure, usually because the + * looper processing the message queue is exiting. Note that a + * result of true does not mean the Runnable will be processed -- + * if the looper is quit before the delivery time of the message + * occurs then the message will be dropped. + */ + public final boolean postDelayed(Runnable r, Object token, long delayMillis) + { + return sendMessageDelayed(getPostMessage(r, token), delayMillis); + } + + /** * Posts a message to an object that implements Runnable. * Causes the Runnable r to executed on the next iteration through the * message queue. The runnable will be run on the thread to which this diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl index 3db12ed0815f..29812e8ab06e 100644 --- a/core/java/android/os/IStatsManager.aidl +++ b/core/java/android/os/IStatsManager.aidl @@ -71,7 +71,7 @@ interface IStatsManager { * Fetches data for the specified configuration key. Returns a byte array representing proto * wire-encoded of ConfigMetricsReportList. */ - byte[] getData(in String key); + byte[] getData(in long key); /** * Fetches metadata across statsd. Returns byte array representing wire-encoded proto. @@ -86,7 +86,7 @@ interface IStatsManager { * * Returns if this configuration was correctly registered. */ - boolean addConfiguration(in String configKey, in byte[] config, in String pkg, in String cls); + boolean addConfiguration(in long configKey, in byte[] config, in String pkg, in String cls); /** * Removes the configuration with the matching config key. No-op if this config key does not @@ -94,5 +94,5 @@ interface IStatsManager { * * Returns if this configuration key was removed. */ - boolean removeConfiguration(in String configKey); + boolean removeConfiguration(in long configKey); } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index dd9fd93ed472..38993b71a31d 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -2197,7 +2197,8 @@ public class UserManager { /** * Similar to {@link #trySetQuietModeEnabled(boolean, UserHandle)}, except you can specify - * a target to start when user is unlocked. + * a target to start when user is unlocked. If {@code target} is specified, caller must have + * the {@link android.Manifest.permission#MANAGE_USERS} permission. * * @see {@link #trySetQuietModeEnabled(boolean, UserHandle)} * @hide diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 9833fe15dd3c..4c587a836de8 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -1115,12 +1115,14 @@ public class StorageManager { /** {@hide} */ public static Pair<String, Long> getPrimaryStoragePathAndSize() { return Pair.create(null, - FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace())); + FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace() + + Environment.getRootDirectory().getTotalSpace())); } /** {@hide} */ public long getPrimaryStorageSize() { - return FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace()); + return FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace() + + Environment.getRootDirectory().getTotalSpace()); } /** {@hide} */ diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index acac6718c9be..009fc395ff3b 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5778,6 +5778,14 @@ public final class Settings { "touch_exploration_granted_accessibility_services"; /** + * Uri of the slice that's presented on the keyguard. + * Defaults to a slice with the date and next alarm. + * + * @hide + */ + public static final String KEYGUARD_SLICE_URI = "keyguard_slice_uri"; + + /** * Whether to speak passwords while in accessibility mode. * * @deprecated The speaking of passwords is controlled by individual accessibility services. @@ -8782,6 +8790,7 @@ public final class Settings { * Type: string package name or null if the feature is either not provided or disabled. * @hide */ + @TestApi public static final String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package"; /** diff --git a/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java b/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java index 72a138a629cf..0cf8da5b3a86 100644 --- a/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java +++ b/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java @@ -426,7 +426,7 @@ public class RecoverableKeyStoreLoader { * Imports keys. * * @param sessionId Id for recovery session, same as in - * {@link #startRecoverySession(String, byte[], byte[], byte[], List)} on}. + * {@link #startRecoverySession(String, byte[], byte[], byte[], List)}. * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session. * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob * and session. KeyStore only uses package names from the application info in {@link diff --git a/core/java/android/service/autofill/EditDistanceScorer.java b/core/java/android/service/autofill/EditDistanceScorer.java index 0706b377bbe8..97a386866665 100644 --- a/core/java/android/service/autofill/EditDistanceScorer.java +++ b/core/java/android/service/autofill/EditDistanceScorer.java @@ -16,8 +16,7 @@ package android.service.autofill; import android.annotation.NonNull; -import android.os.Parcel; -import android.os.Parcelable; +import android.annotation.TestApi; import android.view.autofill.AutofillValue; /** @@ -25,13 +24,20 @@ import android.view.autofill.AutofillValue; * by the user and the expected value predicted by an autofill service. */ // TODO(b/70291841): explain algorithm once it's fully implemented -public final class EditDistanceScorer extends InternalScorer implements Scorer, Parcelable { +/** @hide */ +@TestApi +public final class EditDistanceScorer { private static final EditDistanceScorer sInstance = new EditDistanceScorer(); + /** @hide */ + public static final String NAME = "EDIT_DISTANCE"; + /** * Gets the singleton instance. */ + @TestApi + /** @hide */ public static EditDistanceScorer getInstance() { return sInstance; } @@ -39,59 +45,32 @@ public final class EditDistanceScorer extends InternalScorer implements Scorer, private EditDistanceScorer() { } - /** @hide */ - @Override - public float getScore(@NonNull AutofillValue actualValue, @NonNull String userData) { - if (actualValue == null || !actualValue.isText() || userData == null) return 0; + /** + * Returns the classification score between an actual {@link AutofillValue} filled + * by the user and the expected value predicted by an autofill service. + * + * <p>A full-match is {@code 1.0} (representing 100%), a full mismatch is {@code 0.0} and + * partial mathces are something in between, typically using edit-distance algorithms. + * + * @hide + */ + @TestApi + public float getScore(@NonNull AutofillValue actualValue, @NonNull String userDataValue) { + if (actualValue == null || !actualValue.isText() || userDataValue == null) return 0; // TODO(b/70291841): implement edit distance - currently it's returning either 0, 100%, or // partial match when number of chars match final String textValue = actualValue.getTextValue().toString(); final int total = textValue.length(); - if (total != userData.length()) return 0F; + if (total != userDataValue.length()) return 0F; int matches = 0; for (int i = 0; i < total; i++) { if (Character.toLowerCase(textValue.charAt(i)) == Character - .toLowerCase(userData.charAt(i))) { + .toLowerCase(userDataValue.charAt(i))) { matches++; } } return ((float) matches) / total; } - - ///////////////////////////////////// - // Object "contract" methods. // - ///////////////////////////////////// - @Override - public String toString() { - return "EditDistanceScorer"; - } - - ///////////////////////////////////// - // Parcelable "contract" methods. // - ///////////////////////////////////// - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel parcel, int flags) { - // Do nothing - } - - public static final Parcelable.Creator<EditDistanceScorer> CREATOR = - new Parcelable.Creator<EditDistanceScorer>() { - @Override - public EditDistanceScorer createFromParcel(Parcel parcel) { - return EditDistanceScorer.getInstance(); - } - - @Override - public EditDistanceScorer[] newArray(int size) { - return new EditDistanceScorer[size]; - } - }; } diff --git a/core/java/android/service/autofill/FieldClassification.aidl b/core/java/android/service/autofill/FieldClassification.aidl new file mode 100644 index 000000000000..42f7f0252d6d --- /dev/null +++ b/core/java/android/service/autofill/FieldClassification.aidl @@ -0,0 +1,19 @@ +/** + * 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 android.service.autofill; + +parcelable FieldClassification.AlgorithmInfo; diff --git a/core/java/android/service/autofill/FieldClassification.java b/core/java/android/service/autofill/FieldClassification.java index 001b2917aadf..536180335a83 100644 --- a/core/java/android/service/autofill/FieldClassification.java +++ b/core/java/android/service/autofill/FieldClassification.java @@ -106,18 +106,20 @@ public final class FieldClassification { /** * Represents the score of a {@link UserData} entry for the field. * - * <p>The score is defined by {@link #getScore()} and the entry is identified by - * {@link #getRemoteId()}. + * <p>The score is calculated by the given {@link #getAlgorithm() algorithm} and + * the entry is identified by {@link #getRemoteId()}. */ public static final class Match { private final String mRemoteId; private final float mScore; + private final String mAlgorithm; /** @hide */ - public Match(String remoteId, float score) { + public Match(String remoteId, float score, String algorithm) { mRemoteId = Preconditions.checkNotNull(remoteId); mScore = score; + mAlgorithm = algorithm; } /** @@ -140,29 +142,46 @@ public final class FieldClassification { * <li>Any other value is a partial match. * </ul> * - * <p>How the score is calculated depends on the algorithm used by the {@link Scorer} - * implementation. + * <p>How the score is calculated depends on the + * {@link UserData.Builder#setFieldClassificationAlgorithm(String, android.os.Bundle) + * algorithm} used. */ public float getScore() { return mScore; } + /** + * Gets the algorithm used to calculate this score. + * + * <p>Typically, this is either the algorithm set by + * {@link UserData.Builder#setFieldClassificationAlgorithm(String, android.os.Bundle)}, + * or the + * {@link android.view.autofill.AutofillManager#getDefaultFieldClassificationAlgorithm()}. + */ + @NonNull + public String getAlgorithm() { + return mAlgorithm; + } + @Override public String toString() { if (!sDebug) return super.toString(); final StringBuilder string = new StringBuilder("Match: remoteId="); Helper.appendRedacted(string, mRemoteId); - return string.append(", score=").append(mScore).toString(); + return string.append(", score=").append(mScore) + .append(", algorithm=").append(mAlgorithm) + .toString(); } private void writeToParcel(@NonNull Parcel parcel) { parcel.writeString(mRemoteId); parcel.writeFloat(mScore); + parcel.writeString(mAlgorithm); } private static Match readFromParcel(@NonNull Parcel parcel) { - return new Match(parcel.readString(), parcel.readFloat()); + return new Match(parcel.readString(), parcel.readFloat(), parcel.readString()); } } } diff --git a/core/java/android/service/autofill/InternalScorer.java b/core/java/android/service/autofill/InternalScorer.java deleted file mode 100644 index 0da5afc2331d..000000000000 --- a/core/java/android/service/autofill/InternalScorer.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 android.service.autofill; - -import android.annotation.NonNull; -import android.annotation.TestApi; -import android.os.Parcelable; -import android.view.autofill.AutofillValue; - -/** - * Superclass of all scorer the system understands. As this is not public all - * subclasses have to implement {@link Scorer} again. - * - * @hide - */ -@TestApi -public abstract class InternalScorer implements Scorer, Parcelable { - - /** - * Returns the classification score between an actual {@link AutofillValue} filled - * by the user and the expected value predicted by an autofill service. - * - * <p>A full-match is {@code 1.0} (representing 100%), a full mismatch is {@code 0.0} and - * partial mathces are something in between, typically using edit-distance algorithms. - */ - public abstract float getScore(@NonNull AutofillValue actualValue, @NonNull String userData); -} diff --git a/core/java/android/service/autofill/UserData.aidl b/core/java/android/service/autofill/UserData.aidl index 76016ded424a..19282e0e7c85 100644 --- a/core/java/android/service/autofill/UserData.aidl +++ b/core/java/android/service/autofill/UserData.aidl @@ -17,4 +17,3 @@ package android.service.autofill; parcelable UserData; -parcelable UserData.Constraints; diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java index f0cc360fe075..2f9225acc520 100644 --- a/core/java/android/service/autofill/UserData.java +++ b/core/java/android/service/autofill/UserData.java @@ -25,10 +25,13 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityThread; import android.content.ContentResolver; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.provider.Settings; +import android.service.autofill.FieldClassification.Match; import android.util.Log; +import android.view.autofill.AutofillManager; import android.view.autofill.Helper; import com.android.internal.util.Preconditions; @@ -49,21 +52,32 @@ public final class UserData implements Parcelable { private static final int DEFAULT_MIN_VALUE_LENGTH = 5; private static final int DEFAULT_MAX_VALUE_LENGTH = 100; - private final InternalScorer mScorer; + private final String mAlgorithm; + private final Bundle mAlgorithmArgs; private final String[] mRemoteIds; private final String[] mValues; private UserData(Builder builder) { - mScorer = builder.mScorer; + mAlgorithm = builder.mAlgorithm; + mAlgorithmArgs = builder.mAlgorithmArgs; mRemoteIds = new String[builder.mRemoteIds.size()]; builder.mRemoteIds.toArray(mRemoteIds); mValues = new String[builder.mValues.size()]; builder.mValues.toArray(mValues); } + /** + * Gets the name of the algorithm that is used to calculate + * {@link Match#getScore() match scores}. + */ + @Nullable + public String getFieldClassificationAlgorithm() { + return mAlgorithm; + } + /** @hide */ - public InternalScorer getScorer() { - return mScorer; + public Bundle getAlgorithmArgs() { + return mAlgorithmArgs; } /** @hide */ @@ -78,7 +92,9 @@ public final class UserData implements Parcelable { /** @hide */ public void dump(String prefix, PrintWriter pw) { - pw.print(prefix); pw.print("Scorer: "); pw.println(mScorer); + pw.print(prefix); pw.print("Algorithm: "); pw.print(mAlgorithm); + pw.print(" Args: "); pw.println(mAlgorithmArgs); + // Cannot disclose remote ids or values because they could contain PII pw.print(prefix); pw.print("Remote ids size: "); pw.println(mRemoteIds.length); for (int i = 0; i < mRemoteIds.length; i++) { @@ -105,9 +121,10 @@ public final class UserData implements Parcelable { * A builder for {@link UserData} objects. */ public static final class Builder { - private final InternalScorer mScorer; private final ArrayList<String> mRemoteIds; private final ArrayList<String> mValues; + private String mAlgorithm; + private Bundle mAlgorithmArgs; private boolean mDestroyed; /** @@ -120,13 +137,9 @@ public final class UserData implements Parcelable { * <li>{@code value} is empty * <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()} * <li>the length of {@code value} is higher than {@link UserData#getMaxValueLength()} - * <li>{@code scorer} is not instance of a class provided by the Android System. * </ol> */ - public Builder(@NonNull Scorer scorer, @NonNull String remoteId, @NonNull String value) { - Preconditions.checkArgument((scorer instanceof InternalScorer), - "not provided by Android System: " + scorer); - mScorer = (InternalScorer) scorer; + public Builder(@NonNull String remoteId, @NonNull String value) { checkValidRemoteId(remoteId); checkValidValue(value); final int capacity = getMaxUserDataSize(); @@ -137,6 +150,31 @@ public final class UserData implements Parcelable { } /** + * Sets the algorithm used for <a href="#FieldClassification">field classification</a>. + * + * <p>The currently available algorithms can be retrieve through + * {@link AutofillManager#getAvailableFieldClassificationAlgorithms()}. + * + * <p><b>Note: </b>The available algorithms in the Android System can change dinamically, + * so it's not guaranteed that the algorithm set here is the one that will be effectually + * used. If the algorithm set here is not available at runtime, the + * {@link AutofillManager#getDefaultFieldClassificationAlgorithm()} is used instead. + * You can verify which algorithm was used by calling + * {@link FieldClassification.Match#getAlgorithm()}. + * + * @param name name of the algorithm or {@code null} to used default. + * @param args optional arguments to the algorithm. + * + * @return this builder + */ + public Builder setFieldClassificationAlgorithm(@Nullable String name, + @Nullable Bundle args) { + mAlgorithm = name; + mAlgorithmArgs = args; + return this; + } + + /** * Adds a new value for user data. * * @param remoteId unique string used to identify the user data. @@ -211,7 +249,7 @@ public final class UserData implements Parcelable { public String toString() { if (!sDebug) return super.toString(); - final StringBuilder builder = new StringBuilder("UserData: [scorer=").append(mScorer); + final StringBuilder builder = new StringBuilder("UserData: [algorithm=").append(mAlgorithm); // Cannot disclose remote ids or values because they could contain PII builder.append(", remoteIds="); Helper.appendRedacted(builder, mRemoteIds); @@ -231,9 +269,10 @@ public final class UserData implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { - parcel.writeParcelable(mScorer, flags); parcel.writeStringArray(mRemoteIds); parcel.writeStringArray(mValues); + parcel.writeString(mAlgorithm); + parcel.writeBundle(mAlgorithmArgs); } public static final Parcelable.Creator<UserData> CREATOR = @@ -243,10 +282,10 @@ public final class UserData implements Parcelable { // Always go through the builder to ensure the data ingested by // the system obeys the contract of the builder to avoid attacks // using specially crafted parcels. - final InternalScorer scorer = parcel.readParcelable(null); final String[] remoteIds = parcel.readStringArray(); final String[] values = parcel.readStringArray(); - final Builder builder = new Builder(scorer, remoteIds[0], values[0]); + final Builder builder = new Builder(remoteIds[0], values[0]) + .setFieldClassificationAlgorithm(parcel.readString(), parcel.readBundle()); for (int i = 1; i < remoteIds.length; i++) { builder.add(remoteIds[i], values[i]); } diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java index df0842f7fb0d..fb530074d5b0 100644 --- a/core/java/android/service/euicc/EuiccService.java +++ b/core/java/android/service/euicc/EuiccService.java @@ -23,6 +23,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.telephony.euicc.DownloadableSubscription; import android.telephony.euicc.EuiccInfo; +import android.telephony.euicc.EuiccManager.OtaStatus; import android.util.ArraySet; import java.util.concurrent.LinkedBlockingQueue; @@ -203,6 +204,16 @@ public abstract class EuiccService extends Service { public abstract String onGetEid(int slotId); /** + * Return the status of OTA update. + * + * @param slotId ID of the SIM slot to use for the operation. This is currently not populated + * but is here to future-proof the APIs. + * @return The status of Euicc OTA update. + * @see android.telephony.euicc.EuiccManager#getOtaStatus + */ + public abstract @OtaStatus int onGetOtaStatus(int slotId); + + /** * Populate {@link DownloadableSubscription} metadata for the given downloadable subscription. * * @param slotId ID of the SIM slot to use for the operation. This is currently not populated @@ -385,6 +396,21 @@ public abstract class EuiccService extends Service { } @Override + public void getOtaStatus(int slotId, IGetOtaStatusCallback callback) { + mExecutor.execute(new Runnable() { + @Override + public void run() { + int status = EuiccService.this.onGetOtaStatus(slotId); + try { + callback.onSuccess(status); + } catch (RemoteException e) { + // Can't communicate with the phone process; ignore. + } + } + }); + } + + @Override public void getDownloadableSubscriptionMetadata(int slotId, DownloadableSubscription subscription, boolean forceDeactivateSim, diff --git a/core/java/android/service/euicc/IEuiccService.aidl b/core/java/android/service/euicc/IEuiccService.aidl index e10dd8cdf616..a24e5c35c1cb 100644 --- a/core/java/android/service/euicc/IEuiccService.aidl +++ b/core/java/android/service/euicc/IEuiccService.aidl @@ -24,6 +24,7 @@ import android.service.euicc.IGetDownloadableSubscriptionMetadataCallback; import android.service.euicc.IGetEidCallback; import android.service.euicc.IGetEuiccInfoCallback; import android.service.euicc.IGetEuiccProfileInfoListCallback; +import android.service.euicc.IGetOtaStatusCallback; import android.service.euicc.IRetainSubscriptionsForFactoryResetCallback; import android.service.euicc.ISwitchToSubscriptionCallback; import android.service.euicc.IUpdateSubscriptionNicknameCallback; @@ -37,6 +38,7 @@ oneway interface IEuiccService { void getDownloadableSubscriptionMetadata(int slotId, in DownloadableSubscription subscription, boolean forceDeactivateSim, in IGetDownloadableSubscriptionMetadataCallback callback); void getEid(int slotId, in IGetEidCallback callback); + void getOtaStatus(int slotId, in IGetOtaStatusCallback callback); void getEuiccProfileInfoList(int slotId, in IGetEuiccProfileInfoListCallback callback); void getDefaultDownloadableSubscriptionList(int slotId, boolean forceDeactivateSim, in IGetDefaultDownloadableSubscriptionListCallback callback); diff --git a/core/java/android/service/euicc/IGetOtaStatusCallback.aidl b/core/java/android/service/euicc/IGetOtaStatusCallback.aidl new file mode 100644 index 000000000000..f6678889ccc7 --- /dev/null +++ b/core/java/android/service/euicc/IGetOtaStatusCallback.aidl @@ -0,0 +1,22 @@ +/* + * 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 android.service.euicc; + +/** @hide */ +oneway interface IGetOtaStatusCallback { + void onSuccess(int status); +}
\ No newline at end of file diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java index 8c90156d159d..ad3b4b6d6343 100644 --- a/core/java/android/text/format/Formatter.java +++ b/core/java/android/text/format/Formatter.java @@ -20,11 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.res.Resources; -import android.icu.text.DecimalFormat; import android.icu.text.MeasureFormat; -import android.icu.text.NumberFormat; -import android.icu.text.UnicodeSet; -import android.icu.text.UnicodeSetSpanner; import android.icu.util.Measure; import android.icu.util.MeasureUnit; import android.net.NetworkUtils; @@ -32,8 +28,6 @@ import android.text.BidiFormatter; import android.text.TextUtils; import android.view.View; -import java.lang.reflect.Constructor; -import java.math.BigDecimal; import java.util.Locale; /** @@ -43,8 +37,6 @@ import java.util.Locale; public final class Formatter { /** {@hide} */ - public static final int FLAG_DEFAULT = 0; - /** {@hide} */ public static final int FLAG_SHORTER = 1 << 0; /** {@hide} */ public static final int FLAG_CALCULATE_ROUNDED = 1 << 1; @@ -66,9 +58,7 @@ public final class Formatter { return context.getResources().getConfiguration().getLocales().get(0); } - /** - * Wraps the source string in bidi formatting characters in RTL locales. - */ + /* Wraps the source string in bidi formatting characters in RTL locales */ private static String bidiWrap(@NonNull Context context, String source) { final Locale locale = localeFromContext(context); if (TextUtils.getLayoutDirectionFromLocale(locale) == View.LAYOUT_DIRECTION_RTL) { @@ -97,7 +87,12 @@ public final class Formatter { * @return formatted string with the number */ public static String formatFileSize(@Nullable Context context, long sizeBytes) { - return formatFileSize(context, sizeBytes, FLAG_DEFAULT); + if (context == null) { + return ""; + } + final BytesResult res = formatBytes(context.getResources(), sizeBytes, 0); + return bidiWrap(context, context.getString(com.android.internal.R.string.fileSizeSuffix, + res.value, res.units)); } /** @@ -105,207 +100,88 @@ public final class Formatter { * (showing fewer digits of precision). */ public static String formatShortFileSize(@Nullable Context context, long sizeBytes) { - return formatFileSize(context, sizeBytes, FLAG_SHORTER); - } - - private static String formatFileSize(@Nullable Context context, long sizeBytes, int flags) { if (context == null) { return ""; } - final RoundedBytesResult res = RoundedBytesResult.roundBytes(sizeBytes, flags); - return bidiWrap(context, formatRoundedBytesResult(context, res)); - } - - private static String getSuffixOverride(@NonNull Resources res, MeasureUnit unit) { - if (unit == MeasureUnit.BYTE) { - return res.getString(com.android.internal.R.string.byteShort); - } else { // unit == PETABYTE - return res.getString(com.android.internal.R.string.petabyteShort); - } - } - - private static NumberFormat getNumberFormatter(Locale locale, int fractionDigits) { - final NumberFormat numberFormatter = NumberFormat.getInstance(locale); - numberFormatter.setMinimumFractionDigits(fractionDigits); - numberFormatter.setMaximumFractionDigits(fractionDigits); - numberFormatter.setGroupingUsed(false); - if (numberFormatter instanceof DecimalFormat) { - // We do this only for DecimalFormat, since in the general NumberFormat case, calling - // setRoundingMode may throw an exception. - numberFormatter.setRoundingMode(BigDecimal.ROUND_HALF_UP); - } - return numberFormatter; - } - - private static String deleteFirstFromString(String source, String toDelete) { - final int location = source.indexOf(toDelete); - if (location == -1) { - return source; - } else { - return source.substring(0, location) - + source.substring(location + toDelete.length(), source.length()); - } - } - - private static String formatMeasureShort(Locale locale, NumberFormat numberFormatter, - float value, MeasureUnit units) { - final MeasureFormat measureFormatter = MeasureFormat.getInstance( - locale, MeasureFormat.FormatWidth.SHORT, numberFormatter); - return measureFormatter.format(new Measure(value, units)); - } - - private static final UnicodeSetSpanner SPACES_AND_CONTROLS = - new UnicodeSetSpanner(new UnicodeSet("[[:Zs:][:Cf:]]").freeze()); - - private static String formatRoundedBytesResult( - @NonNull Context context, @NonNull RoundedBytesResult input) { - final Locale locale = localeFromContext(context); - final NumberFormat numberFormatter = getNumberFormatter(locale, input.fractionDigits); - if (input.units == MeasureUnit.BYTE || input.units == PETABYTE) { - // ICU spells out "byte" instead of "B", and can't format petabytes yet. - final String formattedNumber = numberFormatter.format(input.value); - return context.getString(com.android.internal.R.string.fileSizeSuffix, - formattedNumber, getSuffixOverride(context.getResources(), input.units)); - } else { - return formatMeasureShort(locale, numberFormatter, input.value, input.units); - } + final BytesResult res = formatBytes(context.getResources(), sizeBytes, FLAG_SHORTER); + return bidiWrap(context, context.getString(com.android.internal.R.string.fileSizeSuffix, + res.value, res.units)); } /** {@hide} */ public static BytesResult formatBytes(Resources res, long sizeBytes, int flags) { - final RoundedBytesResult rounded = RoundedBytesResult.roundBytes(sizeBytes, flags); - final Locale locale = res.getConfiguration().getLocales().get(0); - final NumberFormat numberFormatter = getNumberFormatter(locale, rounded.fractionDigits); - final String formattedNumber = numberFormatter.format(rounded.value); - final String units; - if (rounded.units == MeasureUnit.BYTE || rounded.units == PETABYTE) { - // ICU spells out "byte" instead of "B", and can't format petabytes yet. - units = getSuffixOverride(res, rounded.units); - } else { - // Since ICU does not give us access to the pattern, we need to extract the unit string - // from ICU, which we do by taking out the formatted number out of the formatted string - // and trimming the result of spaces and controls. - final String formattedMeasure = formatMeasureShort( - locale, numberFormatter, rounded.value, rounded.units); - final String numberRemoved = deleteFirstFromString(formattedMeasure, formattedNumber); - units = SPACES_AND_CONTROLS.trim(numberRemoved).toString(); + final boolean isNegative = (sizeBytes < 0); + float result = isNegative ? -sizeBytes : sizeBytes; + int suffix = com.android.internal.R.string.byteShort; + long mult = 1; + if (result > 900) { + suffix = com.android.internal.R.string.kilobyteShort; + mult = 1000; + result = result / 1000; } - return new BytesResult(formattedNumber, units, rounded.roundedBytes); - } - - /** - * ICU doesn't support PETABYTE yet. Fake it so that we can treat all units the same way. - */ - private static final MeasureUnit PETABYTE = createPetaByte(); - - /** - * Create a petabyte MeasureUnit without registering it with ICU. - * ICU doesn't support user-create MeasureUnit and the only public (but hidden) method to do so - * is {@link MeasureUnit#internalGetInstance(String, String)} which also registers the unit as - * an available type and thus leaks it to code that doesn't expect or support it. - * <p>This method uses reflection to create an instance of MeasureUnit to avoid leaking it. This - * instance is <b>only</b> to be used in this class. - */ - private static MeasureUnit createPetaByte() { - try { - Constructor<MeasureUnit> constructor = MeasureUnit.class - .getDeclaredConstructor(String.class, String.class); - constructor.setAccessible(true); - return constructor.newInstance("digital", "petabyte"); - } catch (ReflectiveOperationException e) { - throw new RuntimeException("Failed to create petabyte MeasureUnit", e); + if (result > 900) { + suffix = com.android.internal.R.string.megabyteShort; + mult *= 1000; + result = result / 1000; } - } - - private static class RoundedBytesResult { - public final float value; - public final MeasureUnit units; - public final int fractionDigits; - public final long roundedBytes; - - private RoundedBytesResult( - float value, MeasureUnit units, int fractionDigits, long roundedBytes) { - this.value = value; - this.units = units; - this.fractionDigits = fractionDigits; - this.roundedBytes = roundedBytes; + if (result > 900) { + suffix = com.android.internal.R.string.gigabyteShort; + mult *= 1000; + result = result / 1000; } - - /** - * Returns a RoundedBytesResult object based on the input size in bytes and the rounding - * flags. The result can be used for formatting. - */ - static RoundedBytesResult roundBytes(long sizeBytes, int flags) { - final boolean isNegative = (sizeBytes < 0); - float result = isNegative ? -sizeBytes : sizeBytes; - MeasureUnit units = MeasureUnit.BYTE; - long mult = 1; - if (result > 900) { - units = MeasureUnit.KILOBYTE; - mult = 1000; - result = result / 1000; - } - if (result > 900) { - units = MeasureUnit.MEGABYTE; - mult *= 1000; - result = result / 1000; - } - if (result > 900) { - units = MeasureUnit.GIGABYTE; - mult *= 1000; - result = result / 1000; - } - if (result > 900) { - units = MeasureUnit.TERABYTE; - mult *= 1000; - result = result / 1000; - } - if (result > 900) { - units = PETABYTE; - mult *= 1000; - result = result / 1000; + if (result > 900) { + suffix = com.android.internal.R.string.terabyteShort; + mult *= 1000; + result = result / 1000; + } + if (result > 900) { + suffix = com.android.internal.R.string.petabyteShort; + mult *= 1000; + result = result / 1000; + } + // Note we calculate the rounded long by ourselves, but still let String.format() + // compute the rounded value. String.format("%f", 0.1) might not return "0.1" due to + // floating point errors. + final int roundFactor; + final String roundFormat; + if (mult == 1 || result >= 100) { + roundFactor = 1; + roundFormat = "%.0f"; + } else if (result < 1) { + roundFactor = 100; + roundFormat = "%.2f"; + } else if (result < 10) { + if ((flags & FLAG_SHORTER) != 0) { + roundFactor = 10; + roundFormat = "%.1f"; + } else { + roundFactor = 100; + roundFormat = "%.2f"; } - // Note we calculate the rounded long by ourselves, but still let NumberFormat compute - // the rounded value. NumberFormat.format(0.1) might not return "0.1" due to floating - // point errors. - final int roundFactor; - final int roundDigits; - if (mult == 1 || result >= 100) { + } else { // 10 <= result < 100 + if ((flags & FLAG_SHORTER) != 0) { roundFactor = 1; - roundDigits = 0; - } else if (result < 1) { + roundFormat = "%.0f"; + } else { roundFactor = 100; - roundDigits = 2; - } else if (result < 10) { - if ((flags & FLAG_SHORTER) != 0) { - roundFactor = 10; - roundDigits = 1; - } else { - roundFactor = 100; - roundDigits = 2; - } - } else { // 10 <= result < 100 - if ((flags & FLAG_SHORTER) != 0) { - roundFactor = 1; - roundDigits = 0; - } else { - roundFactor = 100; - roundDigits = 2; - } + roundFormat = "%.2f"; } + } - if (isNegative) { - result = -result; - } + if (isNegative) { + result = -result; + } + final String roundedString = String.format(roundFormat, result); - // Note this might overflow if abs(result) >= Long.MAX_VALUE / 100, but that's like - // 80PB so it's okay (for now)... - final long roundedBytes = - (flags & FLAG_CALCULATE_ROUNDED) == 0 ? 0 - : (((long) Math.round(result * roundFactor)) * mult / roundFactor); + // Note this might overflow if abs(result) >= Long.MAX_VALUE / 100, but that's like 80PB so + // it's okay (for now)... + final long roundedBytes = + (flags & FLAG_CALCULATE_ROUNDED) == 0 ? 0 + : (((long) Math.round(result * roundFactor)) * mult / roundFactor); - return new RoundedBytesResult(result, units, roundDigits, roundedBytes); - } + final String units = res.getString(suffix); + + return new BytesResult(roundedString, units, roundedBytes); } /** diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index bfb513092c31..d31bc1fbda76 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -38,7 +38,6 @@ public class FeatureFlagUtils { static { DEFAULT_FLAGS = new HashMap<>(); DEFAULT_FLAGS.put("device_info_v2", "true"); - DEFAULT_FLAGS.put("new_settings_suggestion", "true"); DEFAULT_FLAGS.put("settings_search_v2", "true"); DEFAULT_FLAGS.put("settings_app_info_v2", "false"); DEFAULT_FLAGS.put("settings_connected_device_v2", "true"); diff --git a/core/java/android/util/StatsManager.java b/core/java/android/util/StatsManager.java index 26a3c361e8c1..c25b272c11b9 100644 --- a/core/java/android/util/StatsManager.java +++ b/core/java/android/util/StatsManager.java @@ -53,7 +53,7 @@ public final class StatsManager { * @return true if successful */ @RequiresPermission(Manifest.permission.DUMP) - public boolean addConfiguration(String configKey, byte[] config, String pkg, String cls) { + public boolean addConfiguration(long configKey, byte[] config, String pkg, String cls) { synchronized (this) { try { IStatsManager service = getIStatsManagerLocked(); @@ -76,7 +76,7 @@ public final class StatsManager { * @return true if successful */ @RequiresPermission(Manifest.permission.DUMP) - public boolean removeConfiguration(String configKey) { + public boolean removeConfiguration(long configKey) { synchronized (this) { try { IStatsManager service = getIStatsManagerLocked(); @@ -100,7 +100,7 @@ public final class StatsManager { * @return Serialized ConfigMetricsReportList proto. Returns null on failure. */ @RequiresPermission(Manifest.permission.DUMP) - public byte[] getData(String configKey) { + public byte[] getData(long configKey) { synchronized (this) { try { IStatsManager service = getIStatsManagerLocked(); diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java index 0a54f3a59a7d..530937e720a7 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java @@ -16,6 +16,21 @@ package android.util.apk; +import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_DSA_WITH_SHA256; +import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA256; +import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA512; +import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256; +import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512; +import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PSS_WITH_SHA256; +import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PSS_WITH_SHA512; +import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm; +import static android.util.apk.ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm; +import static android.util.apk.ApkSigningBlockUtils.getLengthPrefixedSlice; +import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContentDigestAlgorithm; +import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm; +import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm; +import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray; + import android.util.ArrayMap; import android.util.Pair; @@ -23,56 +38,47 @@ import java.io.ByteArrayInputStream; import java.io.FileDescriptor; import java.io.IOException; import java.io.RandomAccessFile; -import java.math.BigInteger; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.security.DigestException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.Principal; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; -import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; -import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateFactory; -import java.security.cert.CertificateNotYetValidException; import java.security.cert.X509Certificate; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.InvalidKeySpecException; -import java.security.spec.MGF1ParameterSpec; -import java.security.spec.PSSParameterSpec; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.Arrays; -import java.util.Date; import java.util.List; import java.util.Map; -import java.util.Set; /** * APK Signature Scheme v2 verifier. * + * <p>APK Signature Scheme v2 is a whole-file signature scheme which aims to protect every single + * bit of the APK, as opposed to the JAR Signature Scheme which protects only the names and + * uncompressed contents of ZIP entries. + * + * @see <a href="https://source.android.com/security/apksigning/v2.html">APK Signature Scheme v2</a> + * * @hide for internal use only. */ public class ApkSignatureSchemeV2Verifier { /** - * {@code .SF} file header section attribute indicating that the APK is signed not just with - * JAR signature scheme but also with APK Signature Scheme v2 or newer. This attribute - * facilitates v2 signature stripping detection. - * - * <p>The attribute contains a comma-separated set of signature scheme IDs. + * ID of this signature scheme as used in X-Android-APK-Signed header used in JAR signing. */ - public static final String SF_ATTRIBUTE_ANDROID_APK_SIGNED_NAME = "X-Android-APK-Signed"; public static final int SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID = 2; + private static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a; + /** * Returns {@code true} if the provided APK contains an APK Signature Scheme V2 signature. * @@ -103,7 +109,7 @@ public class ApkSignatureSchemeV2Verifier { /** * Returns the certificates associated with each signer for the given APK without verification. * This method is dangerous and should not be used, unless the caller is absolutely certain the - * APK is trusted. Specifically, verification is only done for the APK Signature Scheme V2 + * APK is trusted. Specifically, verification is only done for the APK Signature Scheme v2 * Block while gathering signer information. The APK contents are not verified. * * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2. @@ -120,6 +126,7 @@ public class ApkSignatureSchemeV2Verifier { return verify(apk, verifyIntegrity); } } + /** * Verifies APK Signature Scheme v2 signatures of the provided APK and returns the certificates * associated with each signer. @@ -144,30 +151,7 @@ public class ApkSignatureSchemeV2Verifier { */ private static SignatureInfo findSignature(RandomAccessFile apk) throws IOException, SignatureNotFoundException { - // Find the ZIP End of Central Directory (EoCD) record. - Pair<ByteBuffer, Long> eocdAndOffsetInFile = getEocd(apk); - ByteBuffer eocd = eocdAndOffsetInFile.first; - long eocdOffset = eocdAndOffsetInFile.second; - if (ZipUtils.isZip64EndOfCentralDirectoryLocatorPresent(apk, eocdOffset)) { - throw new SignatureNotFoundException("ZIP64 APK not supported"); - } - - // Find the APK Signing Block. The block immediately precedes the Central Directory. - long centralDirOffset = getCentralDirOffset(eocd, eocdOffset); - Pair<ByteBuffer, Long> apkSigningBlockAndOffsetInFile = - findApkSigningBlock(apk, centralDirOffset); - ByteBuffer apkSigningBlock = apkSigningBlockAndOffsetInFile.first; - long apkSigningBlockOffset = apkSigningBlockAndOffsetInFile.second; - - // Find the APK Signature Scheme v2 Block inside the APK Signing Block. - ByteBuffer apkSignatureSchemeV2Block = findApkSignatureSchemeV2Block(apkSigningBlock); - - return new SignatureInfo( - apkSignatureSchemeV2Block, - apkSigningBlockOffset, - centralDirOffset, - eocdOffset, - eocd); + return ApkSigningBlockUtils.findSignature(apk, APK_SIGNATURE_SCHEME_V2_BLOCK_ID); } /** @@ -218,7 +202,7 @@ public class ApkSignatureSchemeV2Verifier { } if (doVerifyIntegrity) { - verifyIntegrity( + ApkSigningBlockUtils.verifyIntegrity( contentDigests, apkFileDescriptor, signatureInfo.apkSigningBlockOffset, @@ -349,7 +333,8 @@ public class ApkSignatureSchemeV2Verifier { } catch (CertificateException e) { throw new SecurityException("Failed to decode certificate #" + certificateCount, e); } - certificate = new VerbatimX509Certificate(certificate, encodedCert); + certificate = new VerbatimX509Certificate( + certificate, encodedCert); certs.add(certificate); } @@ -363,235 +348,44 @@ public class ApkSignatureSchemeV2Verifier { "Public key mismatch between certificate and signature record"); } + ByteBuffer additionalAttrs = getLengthPrefixedSlice(signedData); + verifyAdditionalAttributes(additionalAttrs); + return certs.toArray(new X509Certificate[certs.size()]); } - private static void verifyIntegrity( - Map<Integer, byte[]> expectedDigests, - FileDescriptor apkFileDescriptor, - long apkSigningBlockOffset, - long centralDirOffset, - long eocdOffset, - ByteBuffer eocdBuf) throws SecurityException { - - if (expectedDigests.isEmpty()) { - throw new SecurityException("No digests provided"); - } - - // We need to verify the integrity of the following three sections of the file: - // 1. Everything up to the start of the APK Signing Block. - // 2. ZIP Central Directory. - // 3. ZIP End of Central Directory (EoCD). - // Each of these sections is represented as a separate DataSource instance below. - - // To handle large APKs, these sections are read in 1 MB chunks using memory-mapped I/O to - // avoid wasting physical memory. In most APK verification scenarios, the contents of the - // APK are already there in the OS's page cache and thus mmap does not use additional - // physical memory. - DataSource beforeApkSigningBlock = - new MemoryMappedFileDataSource(apkFileDescriptor, 0, apkSigningBlockOffset); - DataSource centralDir = - new MemoryMappedFileDataSource( - apkFileDescriptor, centralDirOffset, eocdOffset - centralDirOffset); + // Attribute to check whether a newer APK Signature Scheme signature was stripped + private static final int STRIPPING_PROTECTION_ATTR_ID = 0xbeeff00d; - // For the purposes of integrity verification, ZIP End of Central Directory's field Start of - // Central Directory must be considered to point to the offset of the APK Signing Block. - eocdBuf = eocdBuf.duplicate(); - eocdBuf.order(ByteOrder.LITTLE_ENDIAN); - ZipUtils.setZipEocdCentralDirectoryOffset(eocdBuf, apkSigningBlockOffset); - DataSource eocd = new ByteBufferDataSource(eocdBuf); - - int[] digestAlgorithms = new int[expectedDigests.size()]; - int digestAlgorithmCount = 0; - for (int digestAlgorithm : expectedDigests.keySet()) { - digestAlgorithms[digestAlgorithmCount] = digestAlgorithm; - digestAlgorithmCount++; - } - byte[][] actualDigests; - try { - actualDigests = - computeContentDigests( - digestAlgorithms, - new DataSource[] {beforeApkSigningBlock, centralDir, eocd}); - } catch (DigestException e) { - throw new SecurityException("Failed to compute digest(s) of contents", e); - } - for (int i = 0; i < digestAlgorithms.length; i++) { - int digestAlgorithm = digestAlgorithms[i]; - byte[] expectedDigest = expectedDigests.get(digestAlgorithm); - byte[] actualDigest = actualDigests[i]; - if (!MessageDigest.isEqual(expectedDigest, actualDigest)) { - throw new SecurityException( - getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm) - + " digest of contents did not verify"); + private static void verifyAdditionalAttributes(ByteBuffer attrs) + throws SecurityException, IOException { + while (attrs.hasRemaining()) { + ByteBuffer attr = getLengthPrefixedSlice(attrs); + if (attr.remaining() < 4) { + throw new IOException("Remaining buffer too short to contain additional attribute " + + "ID. Remaining: " + attr.remaining()); } - } - } - - private static byte[][] computeContentDigests( - int[] digestAlgorithms, - DataSource[] contents) throws DigestException { - // For each digest algorithm the result is computed as follows: - // 1. Each segment of contents is split into consecutive chunks of 1 MB in size. - // The final chunk will be shorter iff the length of segment is not a multiple of 1 MB. - // No chunks are produced for empty (zero length) segments. - // 2. The digest of each chunk is computed over the concatenation of byte 0xa5, the chunk's - // length in bytes (uint32 little-endian) and the chunk's contents. - // 3. The output digest is computed over the concatenation of the byte 0x5a, the number of - // chunks (uint32 little-endian) and the concatenation of digests of chunks of all - // segments in-order. - - long totalChunkCountLong = 0; - for (DataSource input : contents) { - totalChunkCountLong += getChunkCount(input.size()); - } - if (totalChunkCountLong >= Integer.MAX_VALUE / 1024) { - throw new DigestException("Too many chunks: " + totalChunkCountLong); - } - int totalChunkCount = (int) totalChunkCountLong; - - byte[][] digestsOfChunks = new byte[digestAlgorithms.length][]; - for (int i = 0; i < digestAlgorithms.length; i++) { - int digestAlgorithm = digestAlgorithms[i]; - int digestOutputSizeBytes = getContentDigestAlgorithmOutputSizeBytes(digestAlgorithm); - byte[] concatenationOfChunkCountAndChunkDigests = - new byte[5 + totalChunkCount * digestOutputSizeBytes]; - concatenationOfChunkCountAndChunkDigests[0] = 0x5a; - setUnsignedInt32LittleEndian( - totalChunkCount, - concatenationOfChunkCountAndChunkDigests, - 1); - digestsOfChunks[i] = concatenationOfChunkCountAndChunkDigests; - } - - byte[] chunkContentPrefix = new byte[5]; - chunkContentPrefix[0] = (byte) 0xa5; - int chunkIndex = 0; - MessageDigest[] mds = new MessageDigest[digestAlgorithms.length]; - for (int i = 0; i < digestAlgorithms.length; i++) { - String jcaAlgorithmName = - getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithms[i]); - try { - mds[i] = MessageDigest.getInstance(jcaAlgorithmName); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(jcaAlgorithmName + " digest not supported", e); - } - } - // TODO: Compute digests of chunks in parallel when beneficial. This requires some research - // into how to parallelize (if at all) based on the capabilities of the hardware on which - // this code is running and based on the size of input. - DataDigester digester = new MultipleDigestDataDigester(mds); - int dataSourceIndex = 0; - for (DataSource input : contents) { - long inputOffset = 0; - long inputRemaining = input.size(); - while (inputRemaining > 0) { - int chunkSize = (int) Math.min(inputRemaining, CHUNK_SIZE_BYTES); - setUnsignedInt32LittleEndian(chunkSize, chunkContentPrefix, 1); - for (int i = 0; i < mds.length; i++) { - mds[i].update(chunkContentPrefix); - } - try { - input.feedIntoDataDigester(digester, inputOffset, chunkSize); - } catch (IOException e) { - throw new DigestException( - "Failed to digest chunk #" + chunkIndex + " of section #" - + dataSourceIndex, - e); - } - for (int i = 0; i < digestAlgorithms.length; i++) { - int digestAlgorithm = digestAlgorithms[i]; - byte[] concatenationOfChunkCountAndChunkDigests = digestsOfChunks[i]; - int expectedDigestSizeBytes = - getContentDigestAlgorithmOutputSizeBytes(digestAlgorithm); - MessageDigest md = mds[i]; - int actualDigestSizeBytes = - md.digest( - concatenationOfChunkCountAndChunkDigests, - 5 + chunkIndex * expectedDigestSizeBytes, - expectedDigestSizeBytes); - if (actualDigestSizeBytes != expectedDigestSizeBytes) { - throw new RuntimeException( - "Unexpected output size of " + md.getAlgorithm() + " digest: " - + actualDigestSizeBytes); + int id = attr.getInt(); + switch (id) { + case STRIPPING_PROTECTION_ATTR_ID: + if (attr.remaining() < 4) { + throw new IOException("V2 Signature Scheme Stripping Protection Attribute " + + " value too small. Expected 4 bytes, but found " + + attr.remaining()); } - } - inputOffset += chunkSize; - inputRemaining -= chunkSize; - chunkIndex++; - } - dataSourceIndex++; - } - - byte[][] result = new byte[digestAlgorithms.length][]; - for (int i = 0; i < digestAlgorithms.length; i++) { - int digestAlgorithm = digestAlgorithms[i]; - byte[] input = digestsOfChunks[i]; - String jcaAlgorithmName = getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm); - MessageDigest md; - try { - md = MessageDigest.getInstance(jcaAlgorithmName); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(jcaAlgorithmName + " digest not supported", e); + int vers = attr.getInt(); + if (vers == ApkSignatureSchemeV3Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID) { + throw new SecurityException("V2 signature indicates APK is signed using APK" + + " Signature Scheme v3, but none was found. Signature stripped?"); + } + break; + default: + // not the droid we're looking for, move along, move along. + break; } - byte[] output = md.digest(input); - result[i] = output; } - return result; + return; } - - /** - * Returns the ZIP End of Central Directory (EoCD) and its offset in the file. - * - * @throws IOException if an I/O error occurs while reading the file. - * @throws SignatureNotFoundException if the EoCD could not be found. - */ - private static Pair<ByteBuffer, Long> getEocd(RandomAccessFile apk) - throws IOException, SignatureNotFoundException { - Pair<ByteBuffer, Long> eocdAndOffsetInFile = - ZipUtils.findZipEndOfCentralDirectoryRecord(apk); - if (eocdAndOffsetInFile == null) { - throw new SignatureNotFoundException( - "Not an APK file: ZIP End of Central Directory record not found"); - } - return eocdAndOffsetInFile; - } - - private static long getCentralDirOffset(ByteBuffer eocd, long eocdOffset) - throws SignatureNotFoundException { - // Look up the offset of ZIP Central Directory. - long centralDirOffset = ZipUtils.getZipEocdCentralDirectoryOffset(eocd); - if (centralDirOffset > eocdOffset) { - throw new SignatureNotFoundException( - "ZIP Central Directory offset out of range: " + centralDirOffset - + ". ZIP End of Central Directory offset: " + eocdOffset); - } - long centralDirSize = ZipUtils.getZipEocdCentralDirectorySizeBytes(eocd); - if (centralDirOffset + centralDirSize != eocdOffset) { - throw new SignatureNotFoundException( - "ZIP Central Directory is not immediately followed by End of Central" - + " Directory"); - } - return centralDirOffset; - } - - private static final long getChunkCount(long inputSizeBytes) { - return (inputSizeBytes + CHUNK_SIZE_BYTES - 1) / CHUNK_SIZE_BYTES; - } - - private static final int CHUNK_SIZE_BYTES = 1024 * 1024; - - private static final int SIGNATURE_RSA_PSS_WITH_SHA256 = 0x0101; - private static final int SIGNATURE_RSA_PSS_WITH_SHA512 = 0x0102; - private static final int SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256 = 0x0103; - private static final int SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512 = 0x0104; - private static final int SIGNATURE_ECDSA_WITH_SHA256 = 0x0201; - private static final int SIGNATURE_ECDSA_WITH_SHA512 = 0x0202; - private static final int SIGNATURE_DSA_WITH_SHA256 = 0x0301; - - private static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1; - private static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2; - private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) { switch (sigAlgorithm) { case SIGNATURE_RSA_PSS_WITH_SHA256: @@ -606,519 +400,4 @@ public class ApkSignatureSchemeV2Verifier { return false; } } - - private static int compareSignatureAlgorithm(int sigAlgorithm1, int sigAlgorithm2) { - int digestAlgorithm1 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm1); - int digestAlgorithm2 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm2); - return compareContentDigestAlgorithm(digestAlgorithm1, digestAlgorithm2); - } - - private static int compareContentDigestAlgorithm(int digestAlgorithm1, int digestAlgorithm2) { - switch (digestAlgorithm1) { - case CONTENT_DIGEST_CHUNKED_SHA256: - switch (digestAlgorithm2) { - case CONTENT_DIGEST_CHUNKED_SHA256: - return 0; - case CONTENT_DIGEST_CHUNKED_SHA512: - return -1; - default: - throw new IllegalArgumentException( - "Unknown digestAlgorithm2: " + digestAlgorithm2); - } - case CONTENT_DIGEST_CHUNKED_SHA512: - switch (digestAlgorithm2) { - case CONTENT_DIGEST_CHUNKED_SHA256: - return 1; - case CONTENT_DIGEST_CHUNKED_SHA512: - return 0; - default: - throw new IllegalArgumentException( - "Unknown digestAlgorithm2: " + digestAlgorithm2); - } - default: - throw new IllegalArgumentException("Unknown digestAlgorithm1: " + digestAlgorithm1); - } - } - - private static int getSignatureAlgorithmContentDigestAlgorithm(int sigAlgorithm) { - switch (sigAlgorithm) { - case SIGNATURE_RSA_PSS_WITH_SHA256: - case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256: - case SIGNATURE_ECDSA_WITH_SHA256: - case SIGNATURE_DSA_WITH_SHA256: - return CONTENT_DIGEST_CHUNKED_SHA256; - case SIGNATURE_RSA_PSS_WITH_SHA512: - case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512: - case SIGNATURE_ECDSA_WITH_SHA512: - return CONTENT_DIGEST_CHUNKED_SHA512; - default: - throw new IllegalArgumentException( - "Unknown signature algorithm: 0x" - + Long.toHexString(sigAlgorithm & 0xffffffff)); - } - } - - private static String getContentDigestAlgorithmJcaDigestAlgorithm(int digestAlgorithm) { - switch (digestAlgorithm) { - case CONTENT_DIGEST_CHUNKED_SHA256: - return "SHA-256"; - case CONTENT_DIGEST_CHUNKED_SHA512: - return "SHA-512"; - default: - throw new IllegalArgumentException( - "Unknown content digest algorthm: " + digestAlgorithm); - } - } - - private static int getContentDigestAlgorithmOutputSizeBytes(int digestAlgorithm) { - switch (digestAlgorithm) { - case CONTENT_DIGEST_CHUNKED_SHA256: - return 256 / 8; - case CONTENT_DIGEST_CHUNKED_SHA512: - return 512 / 8; - default: - throw new IllegalArgumentException( - "Unknown content digest algorthm: " + digestAlgorithm); - } - } - - private static String getSignatureAlgorithmJcaKeyAlgorithm(int sigAlgorithm) { - switch (sigAlgorithm) { - case SIGNATURE_RSA_PSS_WITH_SHA256: - case SIGNATURE_RSA_PSS_WITH_SHA512: - case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256: - case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512: - return "RSA"; - case SIGNATURE_ECDSA_WITH_SHA256: - case SIGNATURE_ECDSA_WITH_SHA512: - return "EC"; - case SIGNATURE_DSA_WITH_SHA256: - return "DSA"; - default: - throw new IllegalArgumentException( - "Unknown signature algorithm: 0x" - + Long.toHexString(sigAlgorithm & 0xffffffff)); - } - } - - private static Pair<String, ? extends AlgorithmParameterSpec> - getSignatureAlgorithmJcaSignatureAlgorithm(int sigAlgorithm) { - switch (sigAlgorithm) { - case SIGNATURE_RSA_PSS_WITH_SHA256: - return Pair.create( - "SHA256withRSA/PSS", - new PSSParameterSpec( - "SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 256 / 8, 1)); - case SIGNATURE_RSA_PSS_WITH_SHA512: - return Pair.create( - "SHA512withRSA/PSS", - new PSSParameterSpec( - "SHA-512", "MGF1", MGF1ParameterSpec.SHA512, 512 / 8, 1)); - case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256: - return Pair.create("SHA256withRSA", null); - case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512: - return Pair.create("SHA512withRSA", null); - case SIGNATURE_ECDSA_WITH_SHA256: - return Pair.create("SHA256withECDSA", null); - case SIGNATURE_ECDSA_WITH_SHA512: - return Pair.create("SHA512withECDSA", null); - case SIGNATURE_DSA_WITH_SHA256: - return Pair.create("SHA256withDSA", null); - default: - throw new IllegalArgumentException( - "Unknown signature algorithm: 0x" - + Long.toHexString(sigAlgorithm & 0xffffffff)); - } - } - - /** - * Returns new byte buffer whose content is a shared subsequence of this buffer's content - * between the specified start (inclusive) and end (exclusive) positions. As opposed to - * {@link ByteBuffer#slice()}, the returned buffer's byte order is the same as the source - * buffer's byte order. - */ - private static ByteBuffer sliceFromTo(ByteBuffer source, int start, int end) { - if (start < 0) { - throw new IllegalArgumentException("start: " + start); - } - if (end < start) { - throw new IllegalArgumentException("end < start: " + end + " < " + start); - } - int capacity = source.capacity(); - if (end > source.capacity()) { - throw new IllegalArgumentException("end > capacity: " + end + " > " + capacity); - } - int originalLimit = source.limit(); - int originalPosition = source.position(); - try { - source.position(0); - source.limit(end); - source.position(start); - ByteBuffer result = source.slice(); - result.order(source.order()); - return result; - } finally { - source.position(0); - source.limit(originalLimit); - source.position(originalPosition); - } - } - - /** - * Relative <em>get</em> method for reading {@code size} number of bytes from the current - * position of this buffer. - * - * <p>This method reads the next {@code size} bytes at this buffer's current position, - * returning them as a {@code ByteBuffer} with start set to 0, limit and capacity set to - * {@code size}, byte order set to this buffer's byte order; and then increments the position by - * {@code size}. - */ - private static ByteBuffer getByteBuffer(ByteBuffer source, int size) - throws BufferUnderflowException { - if (size < 0) { - throw new IllegalArgumentException("size: " + size); - } - int originalLimit = source.limit(); - int position = source.position(); - int limit = position + size; - if ((limit < position) || (limit > originalLimit)) { - throw new BufferUnderflowException(); - } - source.limit(limit); - try { - ByteBuffer result = source.slice(); - result.order(source.order()); - source.position(limit); - return result; - } finally { - source.limit(originalLimit); - } - } - - private static ByteBuffer getLengthPrefixedSlice(ByteBuffer source) throws IOException { - if (source.remaining() < 4) { - throw new IOException( - "Remaining buffer too short to contain length of length-prefixed field." - + " Remaining: " + source.remaining()); - } - int len = source.getInt(); - if (len < 0) { - throw new IllegalArgumentException("Negative length"); - } else if (len > source.remaining()) { - throw new IOException("Length-prefixed field longer than remaining buffer." - + " Field length: " + len + ", remaining: " + source.remaining()); - } - return getByteBuffer(source, len); - } - - private static byte[] readLengthPrefixedByteArray(ByteBuffer buf) throws IOException { - int len = buf.getInt(); - if (len < 0) { - throw new IOException("Negative length"); - } else if (len > buf.remaining()) { - throw new IOException("Underflow while reading length-prefixed value. Length: " + len - + ", available: " + buf.remaining()); - } - byte[] result = new byte[len]; - buf.get(result); - return result; - } - - private static void setUnsignedInt32LittleEndian(int value, byte[] result, int offset) { - result[offset] = (byte) (value & 0xff); - result[offset + 1] = (byte) ((value >>> 8) & 0xff); - result[offset + 2] = (byte) ((value >>> 16) & 0xff); - result[offset + 3] = (byte) ((value >>> 24) & 0xff); - } - - private static final long APK_SIG_BLOCK_MAGIC_HI = 0x3234206b636f6c42L; - private static final long APK_SIG_BLOCK_MAGIC_LO = 0x20676953204b5041L; - private static final int APK_SIG_BLOCK_MIN_SIZE = 32; - - private static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a; - - private static Pair<ByteBuffer, Long> findApkSigningBlock( - RandomAccessFile apk, long centralDirOffset) - throws IOException, SignatureNotFoundException { - // FORMAT: - // OFFSET DATA TYPE DESCRIPTION - // * @+0 bytes uint64: size in bytes (excluding this field) - // * @+8 bytes payload - // * @-24 bytes uint64: size in bytes (same as the one above) - // * @-16 bytes uint128: magic - - if (centralDirOffset < APK_SIG_BLOCK_MIN_SIZE) { - throw new SignatureNotFoundException( - "APK too small for APK Signing Block. ZIP Central Directory offset: " - + centralDirOffset); - } - // Read the magic and offset in file from the footer section of the block: - // * uint64: size of block - // * 16 bytes: magic - ByteBuffer footer = ByteBuffer.allocate(24); - footer.order(ByteOrder.LITTLE_ENDIAN); - apk.seek(centralDirOffset - footer.capacity()); - apk.readFully(footer.array(), footer.arrayOffset(), footer.capacity()); - if ((footer.getLong(8) != APK_SIG_BLOCK_MAGIC_LO) - || (footer.getLong(16) != APK_SIG_BLOCK_MAGIC_HI)) { - throw new SignatureNotFoundException( - "No APK Signing Block before ZIP Central Directory"); - } - // Read and compare size fields - long apkSigBlockSizeInFooter = footer.getLong(0); - if ((apkSigBlockSizeInFooter < footer.capacity()) - || (apkSigBlockSizeInFooter > Integer.MAX_VALUE - 8)) { - throw new SignatureNotFoundException( - "APK Signing Block size out of range: " + apkSigBlockSizeInFooter); - } - int totalSize = (int) (apkSigBlockSizeInFooter + 8); - long apkSigBlockOffset = centralDirOffset - totalSize; - if (apkSigBlockOffset < 0) { - throw new SignatureNotFoundException( - "APK Signing Block offset out of range: " + apkSigBlockOffset); - } - ByteBuffer apkSigBlock = ByteBuffer.allocate(totalSize); - apkSigBlock.order(ByteOrder.LITTLE_ENDIAN); - apk.seek(apkSigBlockOffset); - apk.readFully(apkSigBlock.array(), apkSigBlock.arrayOffset(), apkSigBlock.capacity()); - long apkSigBlockSizeInHeader = apkSigBlock.getLong(0); - if (apkSigBlockSizeInHeader != apkSigBlockSizeInFooter) { - throw new SignatureNotFoundException( - "APK Signing Block sizes in header and footer do not match: " - + apkSigBlockSizeInHeader + " vs " + apkSigBlockSizeInFooter); - } - return Pair.create(apkSigBlock, apkSigBlockOffset); - } - - private static ByteBuffer findApkSignatureSchemeV2Block(ByteBuffer apkSigningBlock) - throws SignatureNotFoundException { - checkByteOrderLittleEndian(apkSigningBlock); - // FORMAT: - // OFFSET DATA TYPE DESCRIPTION - // * @+0 bytes uint64: size in bytes (excluding this field) - // * @+8 bytes pairs - // * @-24 bytes uint64: size in bytes (same as the one above) - // * @-16 bytes uint128: magic - ByteBuffer pairs = sliceFromTo(apkSigningBlock, 8, apkSigningBlock.capacity() - 24); - - int entryCount = 0; - while (pairs.hasRemaining()) { - entryCount++; - if (pairs.remaining() < 8) { - throw new SignatureNotFoundException( - "Insufficient data to read size of APK Signing Block entry #" + entryCount); - } - long lenLong = pairs.getLong(); - if ((lenLong < 4) || (lenLong > Integer.MAX_VALUE)) { - throw new SignatureNotFoundException( - "APK Signing Block entry #" + entryCount - + " size out of range: " + lenLong); - } - int len = (int) lenLong; - int nextEntryPos = pairs.position() + len; - if (len > pairs.remaining()) { - throw new SignatureNotFoundException( - "APK Signing Block entry #" + entryCount + " size out of range: " + len - + ", available: " + pairs.remaining()); - } - int id = pairs.getInt(); - if (id == APK_SIGNATURE_SCHEME_V2_BLOCK_ID) { - return getByteBuffer(pairs, len - 4); - } - pairs.position(nextEntryPos); - } - - throw new SignatureNotFoundException( - "No APK Signature Scheme v2 block in APK Signing Block"); - } - - private static void checkByteOrderLittleEndian(ByteBuffer buffer) { - if (buffer.order() != ByteOrder.LITTLE_ENDIAN) { - throw new IllegalArgumentException("ByteBuffer byte order must be little endian"); - } - } - - /** - * {@link DataDigester} that updates multiple {@link MessageDigest}s whenever data is feeded. - */ - private static class MultipleDigestDataDigester implements DataDigester { - private final MessageDigest[] mMds; - - MultipleDigestDataDigester(MessageDigest[] mds) { - mMds = mds; - } - - @Override - public void consume(ByteBuffer buffer) { - buffer = buffer.slice(); - for (MessageDigest md : mMds) { - buffer.position(0); - md.update(buffer); - } - } - - @Override - public void finish() {} - } - - /** - * For legacy reasons we need to return exactly the original encoded certificate bytes, instead - * of letting the underlying implementation have a shot at re-encoding the data. - */ - private static class VerbatimX509Certificate extends WrappedX509Certificate { - private byte[] encodedVerbatim; - - public VerbatimX509Certificate(X509Certificate wrapped, byte[] encodedVerbatim) { - super(wrapped); - this.encodedVerbatim = encodedVerbatim; - } - - @Override - public byte[] getEncoded() throws CertificateEncodingException { - return encodedVerbatim; - } - } - - private static class WrappedX509Certificate extends X509Certificate { - private final X509Certificate wrapped; - - public WrappedX509Certificate(X509Certificate wrapped) { - this.wrapped = wrapped; - } - - @Override - public Set<String> getCriticalExtensionOIDs() { - return wrapped.getCriticalExtensionOIDs(); - } - - @Override - public byte[] getExtensionValue(String oid) { - return wrapped.getExtensionValue(oid); - } - - @Override - public Set<String> getNonCriticalExtensionOIDs() { - return wrapped.getNonCriticalExtensionOIDs(); - } - - @Override - public boolean hasUnsupportedCriticalExtension() { - return wrapped.hasUnsupportedCriticalExtension(); - } - - @Override - public void checkValidity() - throws CertificateExpiredException, CertificateNotYetValidException { - wrapped.checkValidity(); - } - - @Override - public void checkValidity(Date date) - throws CertificateExpiredException, CertificateNotYetValidException { - wrapped.checkValidity(date); - } - - @Override - public int getVersion() { - return wrapped.getVersion(); - } - - @Override - public BigInteger getSerialNumber() { - return wrapped.getSerialNumber(); - } - - @Override - public Principal getIssuerDN() { - return wrapped.getIssuerDN(); - } - - @Override - public Principal getSubjectDN() { - return wrapped.getSubjectDN(); - } - - @Override - public Date getNotBefore() { - return wrapped.getNotBefore(); - } - - @Override - public Date getNotAfter() { - return wrapped.getNotAfter(); - } - - @Override - public byte[] getTBSCertificate() throws CertificateEncodingException { - return wrapped.getTBSCertificate(); - } - - @Override - public byte[] getSignature() { - return wrapped.getSignature(); - } - - @Override - public String getSigAlgName() { - return wrapped.getSigAlgName(); - } - - @Override - public String getSigAlgOID() { - return wrapped.getSigAlgOID(); - } - - @Override - public byte[] getSigAlgParams() { - return wrapped.getSigAlgParams(); - } - - @Override - public boolean[] getIssuerUniqueID() { - return wrapped.getIssuerUniqueID(); - } - - @Override - public boolean[] getSubjectUniqueID() { - return wrapped.getSubjectUniqueID(); - } - - @Override - public boolean[] getKeyUsage() { - return wrapped.getKeyUsage(); - } - - @Override - public int getBasicConstraints() { - return wrapped.getBasicConstraints(); - } - - @Override - public byte[] getEncoded() throws CertificateEncodingException { - return wrapped.getEncoded(); - } - - @Override - public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException, - InvalidKeyException, NoSuchProviderException, SignatureException { - wrapped.verify(key); - } - - @Override - public void verify(PublicKey key, String sigProvider) - throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, - NoSuchProviderException, SignatureException { - wrapped.verify(key, sigProvider); - } - - @Override - public String toString() { - return wrapped.toString(); - } - - @Override - public PublicKey getPublicKey() { - return wrapped.getPublicKey(); - } - } } diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java new file mode 100644 index 000000000000..e43dee356064 --- /dev/null +++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java @@ -0,0 +1,558 @@ +/* + * 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 android.util.apk; + +import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_DSA_WITH_SHA256; +import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA256; +import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA512; +import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256; +import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512; +import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PSS_WITH_SHA256; +import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PSS_WITH_SHA512; +import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm; +import static android.util.apk.ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm; +import static android.util.apk.ApkSigningBlockUtils.getLengthPrefixedSlice; +import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContentDigestAlgorithm; +import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm; +import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm; +import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray; + +import android.os.Build; +import android.util.ArrayMap; +import android.util.Pair; + +import java.io.ByteArrayInputStream; +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * APK Signature Scheme v3 verifier. + * + * @hide for internal use only. + */ +public class ApkSignatureSchemeV3Verifier { + + /** + * ID of this signature scheme as used in X-Android-APK-Signed header used in JAR signing. + */ + public static final int SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID = 3; + + private static final int APK_SIGNATURE_SCHEME_V3_BLOCK_ID = 0xf05368c0; + + /** + * Returns {@code true} if the provided APK contains an APK Signature Scheme V3 signature. + * + * <p><b>NOTE: This method does not verify the signature.</b> + */ + public static boolean hasSignature(String apkFile) throws IOException { + try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) { + findSignature(apk); + return true; + } catch (SignatureNotFoundException e) { + return false; + } + } + + /** + * Verifies APK Signature Scheme v3 signatures of the provided APK and returns the certificates + * associated with each signer. + * + * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3. + * @throws SecurityException if the APK Signature Scheme v3 signature of this APK does not + * verify. + * @throws IOException if an I/O error occurs while reading the APK file. + */ + public static VerifiedSigner verify(String apkFile) + throws SignatureNotFoundException, SecurityException, IOException { + return verify(apkFile, true); + } + + /** + * Returns the certificates associated with each signer for the given APK without verification. + * This method is dangerous and should not be used, unless the caller is absolutely certain the + * APK is trusted. Specifically, verification is only done for the APK Signature Scheme v3 + * Block while gathering signer information. The APK contents are not verified. + * + * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3. + * @throws IOException if an I/O error occurs while reading the APK file. + */ + public static VerifiedSigner plsCertsNoVerifyOnlyCerts(String apkFile) + throws SignatureNotFoundException, SecurityException, IOException { + return verify(apkFile, false); + } + + private static VerifiedSigner verify(String apkFile, boolean verifyIntegrity) + throws SignatureNotFoundException, SecurityException, IOException { + try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) { + return verify(apk, verifyIntegrity); + } + } + + /** + * Verifies APK Signature Scheme v3 signatures of the provided APK and returns the certificates + * associated with each signer. + * + * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3. + * @throws SecurityException if an APK Signature Scheme v3 signature of this APK does not + * verify. + * @throws IOException if an I/O error occurs while reading the APK file. + */ + private static VerifiedSigner verify(RandomAccessFile apk, boolean verifyIntegrity) + throws SignatureNotFoundException, SecurityException, IOException { + SignatureInfo signatureInfo = findSignature(apk); + return verify(apk.getFD(), signatureInfo, verifyIntegrity); + } + + /** + * Returns the APK Signature Scheme v3 block contained in the provided APK file and the + * additional information relevant for verifying the block against the file. + * + * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3. + * @throws IOException if an I/O error occurs while reading the APK file. + */ + private static SignatureInfo findSignature(RandomAccessFile apk) + throws IOException, SignatureNotFoundException { + return ApkSigningBlockUtils.findSignature(apk, APK_SIGNATURE_SCHEME_V3_BLOCK_ID); + } + + /** + * Verifies the contents of the provided APK file against the provided APK Signature Scheme v3 + * Block. + * + * @param signatureInfo APK Signature Scheme v3 Block and information relevant for verifying it + * against the APK file. + */ + private static VerifiedSigner verify( + FileDescriptor apkFileDescriptor, + SignatureInfo signatureInfo, + boolean doVerifyIntegrity) throws SecurityException { + int signerCount = 0; + Map<Integer, byte[]> contentDigests = new ArrayMap<>(); + VerifiedSigner result = null; + CertificateFactory certFactory; + try { + certFactory = CertificateFactory.getInstance("X.509"); + } catch (CertificateException e) { + throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e); + } + ByteBuffer signers; + try { + signers = getLengthPrefixedSlice(signatureInfo.signatureBlock); + } catch (IOException e) { + throw new SecurityException("Failed to read list of signers", e); + } + while (signers.hasRemaining()) { + try { + ByteBuffer signer = getLengthPrefixedSlice(signers); + result = verifySigner(signer, contentDigests, certFactory); + signerCount++; + } catch (PlatformNotSupportedException e) { + // this signer is for a different platform, ignore it. + continue; + } catch (IOException | BufferUnderflowException | SecurityException e) { + throw new SecurityException( + "Failed to parse/verify signer #" + signerCount + " block", + e); + } + } + + if (signerCount < 1 || result == null) { + throw new SecurityException("No signers found"); + } + + if (signerCount != 1) { + throw new SecurityException("APK Signature Scheme V3 only supports one signer: " + + "multiple signers found."); + } + + if (contentDigests.isEmpty()) { + throw new SecurityException("No content digests found"); + } + + if (doVerifyIntegrity) { + ApkSigningBlockUtils.verifyIntegrity( + contentDigests, + apkFileDescriptor, + signatureInfo.apkSigningBlockOffset, + signatureInfo.centralDirOffset, + signatureInfo.eocdOffset, + signatureInfo.eocd); + } + + return result; + } + + private static VerifiedSigner verifySigner( + ByteBuffer signerBlock, + Map<Integer, byte[]> contentDigests, + CertificateFactory certFactory) + throws SecurityException, IOException, PlatformNotSupportedException { + ByteBuffer signedData = getLengthPrefixedSlice(signerBlock); + int minSdkVersion = signerBlock.getInt(); + int maxSdkVersion = signerBlock.getInt(); + + if (Build.VERSION.SDK_INT < minSdkVersion || Build.VERSION.SDK_INT > maxSdkVersion) { + // this signature isn't meant to be used with this platform, skip it. + throw new PlatformNotSupportedException( + "Signer not supported by this platform " + + "version. This platform: " + Build.VERSION.SDK_INT + + ", signer minSdkVersion: " + minSdkVersion + + ", maxSdkVersion: " + maxSdkVersion); + } + + ByteBuffer signatures = getLengthPrefixedSlice(signerBlock); + byte[] publicKeyBytes = readLengthPrefixedByteArray(signerBlock); + + int signatureCount = 0; + int bestSigAlgorithm = -1; + byte[] bestSigAlgorithmSignatureBytes = null; + List<Integer> signaturesSigAlgorithms = new ArrayList<>(); + while (signatures.hasRemaining()) { + signatureCount++; + try { + ByteBuffer signature = getLengthPrefixedSlice(signatures); + if (signature.remaining() < 8) { + throw new SecurityException("Signature record too short"); + } + int sigAlgorithm = signature.getInt(); + signaturesSigAlgorithms.add(sigAlgorithm); + if (!isSupportedSignatureAlgorithm(sigAlgorithm)) { + continue; + } + if ((bestSigAlgorithm == -1) + || (compareSignatureAlgorithm(sigAlgorithm, bestSigAlgorithm) > 0)) { + bestSigAlgorithm = sigAlgorithm; + bestSigAlgorithmSignatureBytes = readLengthPrefixedByteArray(signature); + } + } catch (IOException | BufferUnderflowException e) { + throw new SecurityException( + "Failed to parse signature record #" + signatureCount, + e); + } + } + if (bestSigAlgorithm == -1) { + if (signatureCount == 0) { + throw new SecurityException("No signatures found"); + } else { + throw new SecurityException("No supported signatures found"); + } + } + + String keyAlgorithm = getSignatureAlgorithmJcaKeyAlgorithm(bestSigAlgorithm); + Pair<String, ? extends AlgorithmParameterSpec> signatureAlgorithmParams = + getSignatureAlgorithmJcaSignatureAlgorithm(bestSigAlgorithm); + String jcaSignatureAlgorithm = signatureAlgorithmParams.first; + AlgorithmParameterSpec jcaSignatureAlgorithmParams = signatureAlgorithmParams.second; + boolean sigVerified; + try { + PublicKey publicKey = + KeyFactory.getInstance(keyAlgorithm) + .generatePublic(new X509EncodedKeySpec(publicKeyBytes)); + Signature sig = Signature.getInstance(jcaSignatureAlgorithm); + sig.initVerify(publicKey); + if (jcaSignatureAlgorithmParams != null) { + sig.setParameter(jcaSignatureAlgorithmParams); + } + sig.update(signedData); + sigVerified = sig.verify(bestSigAlgorithmSignatureBytes); + } catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException + | InvalidAlgorithmParameterException | SignatureException e) { + throw new SecurityException( + "Failed to verify " + jcaSignatureAlgorithm + " signature", e); + } + if (!sigVerified) { + throw new SecurityException(jcaSignatureAlgorithm + " signature did not verify"); + } + + // Signature over signedData has verified. + + byte[] contentDigest = null; + signedData.clear(); + ByteBuffer digests = getLengthPrefixedSlice(signedData); + List<Integer> digestsSigAlgorithms = new ArrayList<>(); + int digestCount = 0; + while (digests.hasRemaining()) { + digestCount++; + try { + ByteBuffer digest = getLengthPrefixedSlice(digests); + if (digest.remaining() < 8) { + throw new IOException("Record too short"); + } + int sigAlgorithm = digest.getInt(); + digestsSigAlgorithms.add(sigAlgorithm); + if (sigAlgorithm == bestSigAlgorithm) { + contentDigest = readLengthPrefixedByteArray(digest); + } + } catch (IOException | BufferUnderflowException e) { + throw new IOException("Failed to parse digest record #" + digestCount, e); + } + } + + if (!signaturesSigAlgorithms.equals(digestsSigAlgorithms)) { + throw new SecurityException( + "Signature algorithms don't match between digests and signatures records"); + } + int digestAlgorithm = getSignatureAlgorithmContentDigestAlgorithm(bestSigAlgorithm); + byte[] previousSignerDigest = contentDigests.put(digestAlgorithm, contentDigest); + if ((previousSignerDigest != null) + && (!MessageDigest.isEqual(previousSignerDigest, contentDigest))) { + throw new SecurityException( + getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm) + + " contents digest does not match the digest specified by a preceding signer"); + } + + ByteBuffer certificates = getLengthPrefixedSlice(signedData); + List<X509Certificate> certs = new ArrayList<>(); + int certificateCount = 0; + while (certificates.hasRemaining()) { + certificateCount++; + byte[] encodedCert = readLengthPrefixedByteArray(certificates); + X509Certificate certificate; + try { + certificate = (X509Certificate) + certFactory.generateCertificate(new ByteArrayInputStream(encodedCert)); + } catch (CertificateException e) { + throw new SecurityException("Failed to decode certificate #" + certificateCount, e); + } + certificate = new VerbatimX509Certificate( + certificate, encodedCert); + certs.add(certificate); + } + + if (certs.isEmpty()) { + throw new SecurityException("No certificates listed"); + } + X509Certificate mainCertificate = certs.get(0); + byte[] certificatePublicKeyBytes = mainCertificate.getPublicKey().getEncoded(); + if (!Arrays.equals(publicKeyBytes, certificatePublicKeyBytes)) { + throw new SecurityException( + "Public key mismatch between certificate and signature record"); + } + + int signedMinSDK = signedData.getInt(); + if (signedMinSDK != minSdkVersion) { + throw new SecurityException( + "minSdkVersion mismatch between signed and unsigned in v3 signer block."); + } + + int signedMaxSDK = signedData.getInt(); + if (signedMaxSDK != maxSdkVersion) { + throw new SecurityException( + "maxSdkVersion mismatch between signed and unsigned in v3 signer block."); + } + + ByteBuffer additionalAttrs = getLengthPrefixedSlice(signedData); + return verifyAdditionalAttributes(additionalAttrs, certs, certFactory); + } + + private static final int PROOF_OF_ROTATION_ATTR_ID = 0x3ba06f8c; + + private static VerifiedSigner verifyAdditionalAttributes(ByteBuffer attrs, + List<X509Certificate> certs, CertificateFactory certFactory) throws IOException { + X509Certificate[] certChain = certs.toArray(new X509Certificate[certs.size()]); + VerifiedProofOfRotation por = null; + + while (attrs.hasRemaining()) { + ByteBuffer attr = getLengthPrefixedSlice(attrs); + if (attr.remaining() < 4) { + throw new IOException("Remaining buffer too short to contain additional attribute " + + "ID. Remaining: " + attr.remaining()); + } + int id = attr.getInt(); + switch(id) { + case PROOF_OF_ROTATION_ATTR_ID: + if (por != null) { + throw new SecurityException("Encountered multiple Proof-of-rotation records" + + " when verifying APK Signature Scheme v3 signature"); + } + por = verifyProofOfRotationStruct(attr, certFactory); + // make sure that the last certificate in the Proof-of-rotation record matches + // the one used to sign this APK. + try { + if (por.certs.size() > 0 + && !Arrays.equals(por.certs.get(por.certs.size() - 1).getEncoded(), + certChain[0].getEncoded())) { + throw new SecurityException("Terminal certificate in Proof-of-rotation" + + " record does not match APK signing certificate"); + } + } catch (CertificateEncodingException e) { + throw new SecurityException("Failed to encode certificate when comparing" + + " Proof-of-rotation record and signing certificate", e); + } + + break; + default: + // not the droid we're looking for, move along, move along. + break; + } + } + return new VerifiedSigner(certChain, por); + } + + private static VerifiedProofOfRotation verifyProofOfRotationStruct( + ByteBuffer porBuf, + CertificateFactory certFactory) + throws SecurityException, IOException { + int levelCount = 0; + int lastSigAlgorithm = -1; + X509Certificate lastCert = null; + List<X509Certificate> certs = new ArrayList<>(); + List<Integer> flagsList = new ArrayList<>(); + + // Proof-of-rotation struct: + // is basically a singly linked list of nodes, called levels here, each of which have the + // following structure: + // * length-prefix for the entire level + // - length-prefixed signed data (if previous level exists) + // * length-prefixed X509 Certificate + // * uint32 signature algorithm ID describing how this signed data was signed + // - uint32 flags describing how to treat the cert contained in this level + // - uint32 signature algorithm ID to use to verify the signature of the next level. The + // algorithm here must match the one in the signed data section of the next level. + // - length-prefixed signature over the signed data in this level. The signature here + // is verified using the certificate from the previous level. + // The linking is provided by the certificate of each level signing the one of the next. + while (porBuf.hasRemaining()) { + levelCount++; + try { + ByteBuffer level = getLengthPrefixedSlice(porBuf); + ByteBuffer signedData = getLengthPrefixedSlice(level); + int flags = level.getInt(); + int sigAlgorithm = level.getInt(); + byte[] signature = readLengthPrefixedByteArray(level); + + if (lastCert != null) { + // Use previous level cert to verify current level + Pair<String, ? extends AlgorithmParameterSpec> sigAlgParams = + getSignatureAlgorithmJcaSignatureAlgorithm(lastSigAlgorithm); + PublicKey publicKey = lastCert.getPublicKey(); + Signature sig = Signature.getInstance(sigAlgParams.first); + sig.initVerify(publicKey); + if (sigAlgParams.second != null) { + sig.setParameter(sigAlgParams.second); + } + sig.update(signedData); + if (!sig.verify(signature)) { + throw new SecurityException("Unable to verify signature of certificate #" + + levelCount + " using " + sigAlgParams.first + " when verifying" + + " Proof-of-rotation record"); + } + } + + byte[] encodedCert = readLengthPrefixedByteArray(signedData); + int signedSigAlgorithm = signedData.getInt(); + if (lastCert != null && lastSigAlgorithm != signedSigAlgorithm) { + throw new SecurityException("Signing algorithm ID mismatch for certificate #" + + levelCount + " when verifying Proof-of-rotation record"); + } + lastCert = (X509Certificate) + certFactory.generateCertificate(new ByteArrayInputStream(encodedCert)); + lastCert = new VerbatimX509Certificate(lastCert, encodedCert); + + lastSigAlgorithm = sigAlgorithm; + certs.add(lastCert); + flagsList.add(flags); + } catch (IOException | BufferUnderflowException e) { + throw new IOException("Failed to parse Proof-of-rotation record", e); + } catch (NoSuchAlgorithmException | InvalidKeyException + | InvalidAlgorithmParameterException | SignatureException e) { + throw new SecurityException( + "Failed to verify signature over signed data for certificate #" + + levelCount + " when verifying Proof-of-rotation record", e); + } catch (CertificateException e) { + throw new SecurityException("Failed to decode certificate #" + levelCount + + " when verifying Proof-of-rotation record", e); + } + } + return new VerifiedProofOfRotation(certs, flagsList); + } + + private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) { + switch (sigAlgorithm) { + case SIGNATURE_RSA_PSS_WITH_SHA256: + case SIGNATURE_RSA_PSS_WITH_SHA512: + case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256: + case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512: + case SIGNATURE_ECDSA_WITH_SHA256: + case SIGNATURE_ECDSA_WITH_SHA512: + case SIGNATURE_DSA_WITH_SHA256: + return true; + default: + return false; + } + } + + /** + * Verified processed proof of rotation. + * + * @hide for internal use only. + */ + public static class VerifiedProofOfRotation { + public final List<X509Certificate> certs; + public final List<Integer> flagsList; + + public VerifiedProofOfRotation(List<X509Certificate> certs, List<Integer> flagsList) { + this.certs = certs; + this.flagsList = flagsList; + } + } + + /** + * Verified APK Signature Scheme v3 signer, including the proof of rotation structure. + * + * @hide for internal use only. + */ + public static class VerifiedSigner { + public final X509Certificate[] certs; + public final VerifiedProofOfRotation por; + + public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por) { + this.certs = certs; + this.por = por; + } + + } + + private static class PlatformNotSupportedException extends Exception { + + PlatformNotSupportedException(String s) { + super(s); + } + } +} diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java index 17b11a9b5170..81467292d491 100644 --- a/core/java/android/util/apk/ApkSignatureVerifier.java +++ b/core/java/android/util/apk/ApkSignatureVerifier.java @@ -54,6 +54,7 @@ public class ApkSignatureVerifier { public static final int VERSION_JAR_SIGNATURE_SCHEME = 1; public static final int VERSION_APK_SIGNATURE_SCHEME_V2 = 2; + public static final int VERSION_APK_SIGNATURE_SCHEME_V3 = 3; private static final AtomicReference<byte[]> sBuffer = new AtomicReference<>(); @@ -65,7 +66,45 @@ public class ApkSignatureVerifier { public static Result verify(String apkPath, int minSignatureSchemeVersion) throws PackageParserException { - // first try v2 + if (minSignatureSchemeVersion > VERSION_APK_SIGNATURE_SCHEME_V3) { + // V3 and before are older than the requested minimum signing version + throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, + "No signature found in package of version " + minSignatureSchemeVersion + + " or newer for package " + apkPath); + } + + // first try v3 + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV3"); + try { + ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner = + ApkSignatureSchemeV3Verifier.verify(apkPath); + Certificate[][] signerCerts = new Certificate[][] { vSigner.certs }; + Signature[] signerSigs = convertToSignatures(signerCerts); + return new Result(signerCerts, signerSigs, VERSION_APK_SIGNATURE_SCHEME_V3); + } catch (SignatureNotFoundException e) { + // not signed with v2, try older if allowed + if (minSignatureSchemeVersion >= VERSION_APK_SIGNATURE_SCHEME_V3) { + throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, + "No APK Signature Scheme v3 signature in package " + apkPath, e); + } + } catch (Exception e) { + // APK Signature Scheme v2 signature found but did not verify + throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, + "Failed to collect certificates from " + apkPath + + " using APK Signature Scheme v2", e); + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + + // redundant, protective version check + if (minSignatureSchemeVersion > VERSION_APK_SIGNATURE_SCHEME_V2) { + // V2 and before are older than the requested minimum signing version + throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, + "No signature found in package of version " + minSignatureSchemeVersion + + " or newer for package " + apkPath); + } + + // try v2 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV2"); try { Certificate[][] signerCerts = ApkSignatureSchemeV2Verifier.verify(apkPath); @@ -87,6 +126,14 @@ public class ApkSignatureVerifier { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } + // redundant, protective version check + if (minSignatureSchemeVersion > VERSION_JAR_SIGNATURE_SCHEME) { + // V1 and is older than the requested minimum signing version + throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, + "No signature found in package of version " + minSignatureSchemeVersion + + " or newer for package " + apkPath); + } + // v2 didn't work, try jarsigner return verifyV1Signature(apkPath, true); } @@ -245,6 +292,44 @@ public class ApkSignatureVerifier { public static Result plsCertsNoVerifyOnlyCerts(String apkPath, int minSignatureSchemeVersion) throws PackageParserException { + if (minSignatureSchemeVersion > VERSION_APK_SIGNATURE_SCHEME_V3) { + // V3 and before are older than the requested minimum signing version + throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, + "No signature found in package of version " + minSignatureSchemeVersion + + " or newer for package " + apkPath); + } + + // first try v3 + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV3"); + try { + ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner = + ApkSignatureSchemeV3Verifier.plsCertsNoVerifyOnlyCerts(apkPath); + Certificate[][] signerCerts = new Certificate[][] { vSigner.certs }; + Signature[] signerSigs = convertToSignatures(signerCerts); + return new Result(signerCerts, signerSigs, VERSION_APK_SIGNATURE_SCHEME_V3); + } catch (SignatureNotFoundException e) { + // not signed with v2, try older if allowed + if (minSignatureSchemeVersion >= VERSION_APK_SIGNATURE_SCHEME_V3) { + throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, + "No APK Signature Scheme v3 signature in package " + apkPath, e); + } + } catch (Exception e) { + // APK Signature Scheme v2 signature found but did not verify + throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, + "Failed to collect certificates from " + apkPath + + " using APK Signature Scheme v2", e); + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + + // redundant, protective version check + if (minSignatureSchemeVersion > VERSION_APK_SIGNATURE_SCHEME_V2) { + // V2 and before are older than the requested minimum signing version + throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, + "No signature found in package of version " + minSignatureSchemeVersion + + " or newer for package " + apkPath); + } + // first try v2 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "certsOnlyV2"); try { @@ -267,6 +352,14 @@ public class ApkSignatureVerifier { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } + // redundant, protective version check + if (minSignatureSchemeVersion > VERSION_JAR_SIGNATURE_SCHEME) { + // V1 and is older than the requested minimum signing version + throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, + "No signature found in package of version " + minSignatureSchemeVersion + + " or newer for package " + apkPath); + } + // v2 didn't work, try jarsigner return verifyV1Signature(apkPath, false); } diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java new file mode 100644 index 000000000000..9279510ae58f --- /dev/null +++ b/core/java/android/util/apk/ApkSigningBlockUtils.java @@ -0,0 +1,663 @@ +/* + * 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 android.util.apk; + +import android.util.Pair; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.security.DigestException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.MGF1ParameterSpec; +import java.security.spec.PSSParameterSpec; +import java.util.Map; + +/** + * Utility class for an APK Signature Scheme using the APK Signing Block. + * + * @hide for internal use only. + */ +final class ApkSigningBlockUtils { + + private ApkSigningBlockUtils() { + } + + /** + * Returns the APK Signature Scheme block contained in the provided APK file and the + * additional information relevant for verifying the block against the file. + * + * @param blockId the ID value in the APK Signing Block's sequence of ID-value pairs + * identifying the appropriate block to find, e.g. the APK Signature Scheme v2 + * block ID. + * + * @throws SignatureNotFoundException if the APK is not signed using this scheme. + * @throws IOException if an I/O error occurs while reading the APK file. + */ + static SignatureInfo findSignature(RandomAccessFile apk, int blockId) + throws IOException, SignatureNotFoundException { + // Find the ZIP End of Central Directory (EoCD) record. + Pair<ByteBuffer, Long> eocdAndOffsetInFile = getEocd(apk); + ByteBuffer eocd = eocdAndOffsetInFile.first; + long eocdOffset = eocdAndOffsetInFile.second; + if (ZipUtils.isZip64EndOfCentralDirectoryLocatorPresent(apk, eocdOffset)) { + throw new SignatureNotFoundException("ZIP64 APK not supported"); + } + + // Find the APK Signing Block. The block immediately precedes the Central Directory. + long centralDirOffset = getCentralDirOffset(eocd, eocdOffset); + Pair<ByteBuffer, Long> apkSigningBlockAndOffsetInFile = + findApkSigningBlock(apk, centralDirOffset); + ByteBuffer apkSigningBlock = apkSigningBlockAndOffsetInFile.first; + long apkSigningBlockOffset = apkSigningBlockAndOffsetInFile.second; + + // Find the APK Signature Scheme Block inside the APK Signing Block. + ByteBuffer apkSignatureSchemeBlock = findApkSignatureSchemeBlock(apkSigningBlock, + blockId); + + return new SignatureInfo( + apkSignatureSchemeBlock, + apkSigningBlockOffset, + centralDirOffset, + eocdOffset, + eocd); + } + + static void verifyIntegrity( + Map<Integer, byte[]> expectedDigests, + FileDescriptor apkFileDescriptor, + long apkSigningBlockOffset, + long centralDirOffset, + long eocdOffset, + ByteBuffer eocdBuf) throws SecurityException { + + if (expectedDigests.isEmpty()) { + throw new SecurityException("No digests provided"); + } + + // We need to verify the integrity of the following three sections of the file: + // 1. Everything up to the start of the APK Signing Block. + // 2. ZIP Central Directory. + // 3. ZIP End of Central Directory (EoCD). + // Each of these sections is represented as a separate DataSource instance below. + + // To handle large APKs, these sections are read in 1 MB chunks using memory-mapped I/O to + // avoid wasting physical memory. In most APK verification scenarios, the contents of the + // APK are already there in the OS's page cache and thus mmap does not use additional + // physical memory. + DataSource beforeApkSigningBlock = + new MemoryMappedFileDataSource(apkFileDescriptor, 0, apkSigningBlockOffset); + DataSource centralDir = + new MemoryMappedFileDataSource( + apkFileDescriptor, centralDirOffset, eocdOffset - centralDirOffset); + + // For the purposes of integrity verification, ZIP End of Central Directory's field Start of + // Central Directory must be considered to point to the offset of the APK Signing Block. + eocdBuf = eocdBuf.duplicate(); + eocdBuf.order(ByteOrder.LITTLE_ENDIAN); + ZipUtils.setZipEocdCentralDirectoryOffset(eocdBuf, apkSigningBlockOffset); + DataSource eocd = new ByteBufferDataSource(eocdBuf); + + int[] digestAlgorithms = new int[expectedDigests.size()]; + int digestAlgorithmCount = 0; + for (int digestAlgorithm : expectedDigests.keySet()) { + digestAlgorithms[digestAlgorithmCount] = digestAlgorithm; + digestAlgorithmCount++; + } + byte[][] actualDigests; + try { + actualDigests = + computeContentDigests( + digestAlgorithms, + new DataSource[] {beforeApkSigningBlock, centralDir, eocd}); + } catch (DigestException e) { + throw new SecurityException("Failed to compute digest(s) of contents", e); + } + for (int i = 0; i < digestAlgorithms.length; i++) { + int digestAlgorithm = digestAlgorithms[i]; + byte[] expectedDigest = expectedDigests.get(digestAlgorithm); + byte[] actualDigest = actualDigests[i]; + if (!MessageDigest.isEqual(expectedDigest, actualDigest)) { + throw new SecurityException( + getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm) + + " digest of contents did not verify"); + } + } + } + + private static byte[][] computeContentDigests( + int[] digestAlgorithms, + DataSource[] contents) throws DigestException { + // For each digest algorithm the result is computed as follows: + // 1. Each segment of contents is split into consecutive chunks of 1 MB in size. + // The final chunk will be shorter iff the length of segment is not a multiple of 1 MB. + // No chunks are produced for empty (zero length) segments. + // 2. The digest of each chunk is computed over the concatenation of byte 0xa5, the chunk's + // length in bytes (uint32 little-endian) and the chunk's contents. + // 3. The output digest is computed over the concatenation of the byte 0x5a, the number of + // chunks (uint32 little-endian) and the concatenation of digests of chunks of all + // segments in-order. + + long totalChunkCountLong = 0; + for (DataSource input : contents) { + totalChunkCountLong += getChunkCount(input.size()); + } + if (totalChunkCountLong >= Integer.MAX_VALUE / 1024) { + throw new DigestException("Too many chunks: " + totalChunkCountLong); + } + int totalChunkCount = (int) totalChunkCountLong; + + byte[][] digestsOfChunks = new byte[digestAlgorithms.length][]; + for (int i = 0; i < digestAlgorithms.length; i++) { + int digestAlgorithm = digestAlgorithms[i]; + int digestOutputSizeBytes = getContentDigestAlgorithmOutputSizeBytes(digestAlgorithm); + byte[] concatenationOfChunkCountAndChunkDigests = + new byte[5 + totalChunkCount * digestOutputSizeBytes]; + concatenationOfChunkCountAndChunkDigests[0] = 0x5a; + setUnsignedInt32LittleEndian( + totalChunkCount, + concatenationOfChunkCountAndChunkDigests, + 1); + digestsOfChunks[i] = concatenationOfChunkCountAndChunkDigests; + } + + byte[] chunkContentPrefix = new byte[5]; + chunkContentPrefix[0] = (byte) 0xa5; + int chunkIndex = 0; + MessageDigest[] mds = new MessageDigest[digestAlgorithms.length]; + for (int i = 0; i < digestAlgorithms.length; i++) { + String jcaAlgorithmName = + getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithms[i]); + try { + mds[i] = MessageDigest.getInstance(jcaAlgorithmName); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(jcaAlgorithmName + " digest not supported", e); + } + } + // TODO: Compute digests of chunks in parallel when beneficial. This requires some research + // into how to parallelize (if at all) based on the capabilities of the hardware on which + // this code is running and based on the size of input. + DataDigester digester = new MultipleDigestDataDigester(mds); + int dataSourceIndex = 0; + for (DataSource input : contents) { + long inputOffset = 0; + long inputRemaining = input.size(); + while (inputRemaining > 0) { + int chunkSize = (int) Math.min(inputRemaining, CHUNK_SIZE_BYTES); + setUnsignedInt32LittleEndian(chunkSize, chunkContentPrefix, 1); + for (int i = 0; i < mds.length; i++) { + mds[i].update(chunkContentPrefix); + } + try { + input.feedIntoDataDigester(digester, inputOffset, chunkSize); + } catch (IOException e) { + throw new DigestException( + "Failed to digest chunk #" + chunkIndex + " of section #" + + dataSourceIndex, + e); + } + for (int i = 0; i < digestAlgorithms.length; i++) { + int digestAlgorithm = digestAlgorithms[i]; + byte[] concatenationOfChunkCountAndChunkDigests = digestsOfChunks[i]; + int expectedDigestSizeBytes = + getContentDigestAlgorithmOutputSizeBytes(digestAlgorithm); + MessageDigest md = mds[i]; + int actualDigestSizeBytes = + md.digest( + concatenationOfChunkCountAndChunkDigests, + 5 + chunkIndex * expectedDigestSizeBytes, + expectedDigestSizeBytes); + if (actualDigestSizeBytes != expectedDigestSizeBytes) { + throw new RuntimeException( + "Unexpected output size of " + md.getAlgorithm() + " digest: " + + actualDigestSizeBytes); + } + } + inputOffset += chunkSize; + inputRemaining -= chunkSize; + chunkIndex++; + } + dataSourceIndex++; + } + + byte[][] result = new byte[digestAlgorithms.length][]; + for (int i = 0; i < digestAlgorithms.length; i++) { + int digestAlgorithm = digestAlgorithms[i]; + byte[] input = digestsOfChunks[i]; + String jcaAlgorithmName = getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm); + MessageDigest md; + try { + md = MessageDigest.getInstance(jcaAlgorithmName); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(jcaAlgorithmName + " digest not supported", e); + } + byte[] output = md.digest(input); + result[i] = output; + } + return result; + } + + /** + * Returns the ZIP End of Central Directory (EoCD) and its offset in the file. + * + * @throws IOException if an I/O error occurs while reading the file. + * @throws SignatureNotFoundException if the EoCD could not be found. + */ + static Pair<ByteBuffer, Long> getEocd(RandomAccessFile apk) + throws IOException, SignatureNotFoundException { + Pair<ByteBuffer, Long> eocdAndOffsetInFile = + ZipUtils.findZipEndOfCentralDirectoryRecord(apk); + if (eocdAndOffsetInFile == null) { + throw new SignatureNotFoundException( + "Not an APK file: ZIP End of Central Directory record not found"); + } + return eocdAndOffsetInFile; + } + + static long getCentralDirOffset(ByteBuffer eocd, long eocdOffset) + throws SignatureNotFoundException { + // Look up the offset of ZIP Central Directory. + long centralDirOffset = ZipUtils.getZipEocdCentralDirectoryOffset(eocd); + if (centralDirOffset > eocdOffset) { + throw new SignatureNotFoundException( + "ZIP Central Directory offset out of range: " + centralDirOffset + + ". ZIP End of Central Directory offset: " + eocdOffset); + } + long centralDirSize = ZipUtils.getZipEocdCentralDirectorySizeBytes(eocd); + if (centralDirOffset + centralDirSize != eocdOffset) { + throw new SignatureNotFoundException( + "ZIP Central Directory is not immediately followed by End of Central" + + " Directory"); + } + return centralDirOffset; + } + + private static long getChunkCount(long inputSizeBytes) { + return (inputSizeBytes + CHUNK_SIZE_BYTES - 1) / CHUNK_SIZE_BYTES; + } + + private static final int CHUNK_SIZE_BYTES = 1024 * 1024; + + static final int SIGNATURE_RSA_PSS_WITH_SHA256 = 0x0101; + static final int SIGNATURE_RSA_PSS_WITH_SHA512 = 0x0102; + static final int SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256 = 0x0103; + static final int SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512 = 0x0104; + static final int SIGNATURE_ECDSA_WITH_SHA256 = 0x0201; + static final int SIGNATURE_ECDSA_WITH_SHA512 = 0x0202; + static final int SIGNATURE_DSA_WITH_SHA256 = 0x0301; + + static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1; + static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2; + + static int compareSignatureAlgorithm(int sigAlgorithm1, int sigAlgorithm2) { + int digestAlgorithm1 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm1); + int digestAlgorithm2 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm2); + return compareContentDigestAlgorithm(digestAlgorithm1, digestAlgorithm2); + } + + private static int compareContentDigestAlgorithm(int digestAlgorithm1, int digestAlgorithm2) { + switch (digestAlgorithm1) { + case CONTENT_DIGEST_CHUNKED_SHA256: + switch (digestAlgorithm2) { + case CONTENT_DIGEST_CHUNKED_SHA256: + return 0; + case CONTENT_DIGEST_CHUNKED_SHA512: + return -1; + default: + throw new IllegalArgumentException( + "Unknown digestAlgorithm2: " + digestAlgorithm2); + } + case CONTENT_DIGEST_CHUNKED_SHA512: + switch (digestAlgorithm2) { + case CONTENT_DIGEST_CHUNKED_SHA256: + return 1; + case CONTENT_DIGEST_CHUNKED_SHA512: + return 0; + default: + throw new IllegalArgumentException( + "Unknown digestAlgorithm2: " + digestAlgorithm2); + } + default: + throw new IllegalArgumentException("Unknown digestAlgorithm1: " + digestAlgorithm1); + } + } + + static int getSignatureAlgorithmContentDigestAlgorithm(int sigAlgorithm) { + switch (sigAlgorithm) { + case SIGNATURE_RSA_PSS_WITH_SHA256: + case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256: + case SIGNATURE_ECDSA_WITH_SHA256: + case SIGNATURE_DSA_WITH_SHA256: + return CONTENT_DIGEST_CHUNKED_SHA256; + case SIGNATURE_RSA_PSS_WITH_SHA512: + case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512: + case SIGNATURE_ECDSA_WITH_SHA512: + return CONTENT_DIGEST_CHUNKED_SHA512; + default: + throw new IllegalArgumentException( + "Unknown signature algorithm: 0x" + + Long.toHexString(sigAlgorithm & 0xffffffff)); + } + } + + static String getContentDigestAlgorithmJcaDigestAlgorithm(int digestAlgorithm) { + switch (digestAlgorithm) { + case CONTENT_DIGEST_CHUNKED_SHA256: + return "SHA-256"; + case CONTENT_DIGEST_CHUNKED_SHA512: + return "SHA-512"; + default: + throw new IllegalArgumentException( + "Unknown content digest algorthm: " + digestAlgorithm); + } + } + + private static int getContentDigestAlgorithmOutputSizeBytes(int digestAlgorithm) { + switch (digestAlgorithm) { + case CONTENT_DIGEST_CHUNKED_SHA256: + return 256 / 8; + case CONTENT_DIGEST_CHUNKED_SHA512: + return 512 / 8; + default: + throw new IllegalArgumentException( + "Unknown content digest algorthm: " + digestAlgorithm); + } + } + + static String getSignatureAlgorithmJcaKeyAlgorithm(int sigAlgorithm) { + switch (sigAlgorithm) { + case SIGNATURE_RSA_PSS_WITH_SHA256: + case SIGNATURE_RSA_PSS_WITH_SHA512: + case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256: + case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512: + return "RSA"; + case SIGNATURE_ECDSA_WITH_SHA256: + case SIGNATURE_ECDSA_WITH_SHA512: + return "EC"; + case SIGNATURE_DSA_WITH_SHA256: + return "DSA"; + default: + throw new IllegalArgumentException( + "Unknown signature algorithm: 0x" + + Long.toHexString(sigAlgorithm & 0xffffffff)); + } + } + + static Pair<String, ? extends AlgorithmParameterSpec> + getSignatureAlgorithmJcaSignatureAlgorithm(int sigAlgorithm) { + switch (sigAlgorithm) { + case SIGNATURE_RSA_PSS_WITH_SHA256: + return Pair.create( + "SHA256withRSA/PSS", + new PSSParameterSpec( + "SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 256 / 8, 1)); + case SIGNATURE_RSA_PSS_WITH_SHA512: + return Pair.create( + "SHA512withRSA/PSS", + new PSSParameterSpec( + "SHA-512", "MGF1", MGF1ParameterSpec.SHA512, 512 / 8, 1)); + case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256: + return Pair.create("SHA256withRSA", null); + case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512: + return Pair.create("SHA512withRSA", null); + case SIGNATURE_ECDSA_WITH_SHA256: + return Pair.create("SHA256withECDSA", null); + case SIGNATURE_ECDSA_WITH_SHA512: + return Pair.create("SHA512withECDSA", null); + case SIGNATURE_DSA_WITH_SHA256: + return Pair.create("SHA256withDSA", null); + default: + throw new IllegalArgumentException( + "Unknown signature algorithm: 0x" + + Long.toHexString(sigAlgorithm & 0xffffffff)); + } + } + + /** + * Returns new byte buffer whose content is a shared subsequence of this buffer's content + * between the specified start (inclusive) and end (exclusive) positions. As opposed to + * {@link ByteBuffer#slice()}, the returned buffer's byte order is the same as the source + * buffer's byte order. + */ + static ByteBuffer sliceFromTo(ByteBuffer source, int start, int end) { + if (start < 0) { + throw new IllegalArgumentException("start: " + start); + } + if (end < start) { + throw new IllegalArgumentException("end < start: " + end + " < " + start); + } + int capacity = source.capacity(); + if (end > source.capacity()) { + throw new IllegalArgumentException("end > capacity: " + end + " > " + capacity); + } + int originalLimit = source.limit(); + int originalPosition = source.position(); + try { + source.position(0); + source.limit(end); + source.position(start); + ByteBuffer result = source.slice(); + result.order(source.order()); + return result; + } finally { + source.position(0); + source.limit(originalLimit); + source.position(originalPosition); + } + } + + /** + * Relative <em>get</em> method for reading {@code size} number of bytes from the current + * position of this buffer. + * + * <p>This method reads the next {@code size} bytes at this buffer's current position, + * returning them as a {@code ByteBuffer} with start set to 0, limit and capacity set to + * {@code size}, byte order set to this buffer's byte order; and then increments the position by + * {@code size}. + */ + static ByteBuffer getByteBuffer(ByteBuffer source, int size) + throws BufferUnderflowException { + if (size < 0) { + throw new IllegalArgumentException("size: " + size); + } + int originalLimit = source.limit(); + int position = source.position(); + int limit = position + size; + if ((limit < position) || (limit > originalLimit)) { + throw new BufferUnderflowException(); + } + source.limit(limit); + try { + ByteBuffer result = source.slice(); + result.order(source.order()); + source.position(limit); + return result; + } finally { + source.limit(originalLimit); + } + } + + static ByteBuffer getLengthPrefixedSlice(ByteBuffer source) throws IOException { + if (source.remaining() < 4) { + throw new IOException( + "Remaining buffer too short to contain length of length-prefixed field." + + " Remaining: " + source.remaining()); + } + int len = source.getInt(); + if (len < 0) { + throw new IllegalArgumentException("Negative length"); + } else if (len > source.remaining()) { + throw new IOException("Length-prefixed field longer than remaining buffer." + + " Field length: " + len + ", remaining: " + source.remaining()); + } + return getByteBuffer(source, len); + } + + static byte[] readLengthPrefixedByteArray(ByteBuffer buf) throws IOException { + int len = buf.getInt(); + if (len < 0) { + throw new IOException("Negative length"); + } else if (len > buf.remaining()) { + throw new IOException("Underflow while reading length-prefixed value. Length: " + len + + ", available: " + buf.remaining()); + } + byte[] result = new byte[len]; + buf.get(result); + return result; + } + + static void setUnsignedInt32LittleEndian(int value, byte[] result, int offset) { + result[offset] = (byte) (value & 0xff); + result[offset + 1] = (byte) ((value >>> 8) & 0xff); + result[offset + 2] = (byte) ((value >>> 16) & 0xff); + result[offset + 3] = (byte) ((value >>> 24) & 0xff); + } + + private static final long APK_SIG_BLOCK_MAGIC_HI = 0x3234206b636f6c42L; + private static final long APK_SIG_BLOCK_MAGIC_LO = 0x20676953204b5041L; + private static final int APK_SIG_BLOCK_MIN_SIZE = 32; + + static Pair<ByteBuffer, Long> findApkSigningBlock( + RandomAccessFile apk, long centralDirOffset) + throws IOException, SignatureNotFoundException { + // FORMAT: + // OFFSET DATA TYPE DESCRIPTION + // * @+0 bytes uint64: size in bytes (excluding this field) + // * @+8 bytes payload + // * @-24 bytes uint64: size in bytes (same as the one above) + // * @-16 bytes uint128: magic + + if (centralDirOffset < APK_SIG_BLOCK_MIN_SIZE) { + throw new SignatureNotFoundException( + "APK too small for APK Signing Block. ZIP Central Directory offset: " + + centralDirOffset); + } + // Read the magic and offset in file from the footer section of the block: + // * uint64: size of block + // * 16 bytes: magic + ByteBuffer footer = ByteBuffer.allocate(24); + footer.order(ByteOrder.LITTLE_ENDIAN); + apk.seek(centralDirOffset - footer.capacity()); + apk.readFully(footer.array(), footer.arrayOffset(), footer.capacity()); + if ((footer.getLong(8) != APK_SIG_BLOCK_MAGIC_LO) + || (footer.getLong(16) != APK_SIG_BLOCK_MAGIC_HI)) { + throw new SignatureNotFoundException( + "No APK Signing Block before ZIP Central Directory"); + } + // Read and compare size fields + long apkSigBlockSizeInFooter = footer.getLong(0); + if ((apkSigBlockSizeInFooter < footer.capacity()) + || (apkSigBlockSizeInFooter > Integer.MAX_VALUE - 8)) { + throw new SignatureNotFoundException( + "APK Signing Block size out of range: " + apkSigBlockSizeInFooter); + } + int totalSize = (int) (apkSigBlockSizeInFooter + 8); + long apkSigBlockOffset = centralDirOffset - totalSize; + if (apkSigBlockOffset < 0) { + throw new SignatureNotFoundException( + "APK Signing Block offset out of range: " + apkSigBlockOffset); + } + ByteBuffer apkSigBlock = ByteBuffer.allocate(totalSize); + apkSigBlock.order(ByteOrder.LITTLE_ENDIAN); + apk.seek(apkSigBlockOffset); + apk.readFully(apkSigBlock.array(), apkSigBlock.arrayOffset(), apkSigBlock.capacity()); + long apkSigBlockSizeInHeader = apkSigBlock.getLong(0); + if (apkSigBlockSizeInHeader != apkSigBlockSizeInFooter) { + throw new SignatureNotFoundException( + "APK Signing Block sizes in header and footer do not match: " + + apkSigBlockSizeInHeader + " vs " + apkSigBlockSizeInFooter); + } + return Pair.create(apkSigBlock, apkSigBlockOffset); + } + + static ByteBuffer findApkSignatureSchemeBlock(ByteBuffer apkSigningBlock, int blockId) + throws SignatureNotFoundException { + checkByteOrderLittleEndian(apkSigningBlock); + // FORMAT: + // OFFSET DATA TYPE DESCRIPTION + // * @+0 bytes uint64: size in bytes (excluding this field) + // * @+8 bytes pairs + // * @-24 bytes uint64: size in bytes (same as the one above) + // * @-16 bytes uint128: magic + ByteBuffer pairs = sliceFromTo(apkSigningBlock, 8, apkSigningBlock.capacity() - 24); + + int entryCount = 0; + while (pairs.hasRemaining()) { + entryCount++; + if (pairs.remaining() < 8) { + throw new SignatureNotFoundException( + "Insufficient data to read size of APK Signing Block entry #" + entryCount); + } + long lenLong = pairs.getLong(); + if ((lenLong < 4) || (lenLong > Integer.MAX_VALUE)) { + throw new SignatureNotFoundException( + "APK Signing Block entry #" + entryCount + + " size out of range: " + lenLong); + } + int len = (int) lenLong; + int nextEntryPos = pairs.position() + len; + if (len > pairs.remaining()) { + throw new SignatureNotFoundException( + "APK Signing Block entry #" + entryCount + " size out of range: " + len + + ", available: " + pairs.remaining()); + } + int id = pairs.getInt(); + if (id == blockId) { + return getByteBuffer(pairs, len - 4); + } + pairs.position(nextEntryPos); + } + + throw new SignatureNotFoundException( + "No block with ID " + blockId + " in APK Signing Block."); + } + + private static void checkByteOrderLittleEndian(ByteBuffer buffer) { + if (buffer.order() != ByteOrder.LITTLE_ENDIAN) { + throw new IllegalArgumentException("ByteBuffer byte order must be little endian"); + } + } + + /** + * {@link DataDigester} that updates multiple {@link MessageDigest}s whenever data is fed. + */ + private static class MultipleDigestDataDigester implements DataDigester { + private final MessageDigest[] mMds; + + MultipleDigestDataDigester(MessageDigest[] mds) { + mMds = mds; + } + + @Override + public void consume(ByteBuffer buffer) { + buffer = buffer.slice(); + for (MessageDigest md : mMds) { + buffer.position(0); + md.update(buffer); + } + } + + @Override + public void finish() {} + } + +} diff --git a/core/java/android/util/apk/VerbatimX509Certificate.java b/core/java/android/util/apk/VerbatimX509Certificate.java new file mode 100644 index 000000000000..9984c6d26c64 --- /dev/null +++ b/core/java/android/util/apk/VerbatimX509Certificate.java @@ -0,0 +1,38 @@ +/* + * 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 android.util.apk; + +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; + +/** + * For legacy reasons we need to return exactly the original encoded certificate bytes, instead + * of letting the underlying implementation have a shot at re-encoding the data. + */ +class VerbatimX509Certificate extends WrappedX509Certificate { + private final byte[] mEncodedVerbatim; + + VerbatimX509Certificate(X509Certificate wrapped, byte[] encodedVerbatim) { + super(wrapped); + this.mEncodedVerbatim = encodedVerbatim; + } + + @Override + public byte[] getEncoded() throws CertificateEncodingException { + return mEncodedVerbatim; + } +} diff --git a/core/java/android/util/apk/WrappedX509Certificate.java b/core/java/android/util/apk/WrappedX509Certificate.java new file mode 100644 index 000000000000..fdaa42028e8d --- /dev/null +++ b/core/java/android/util/apk/WrappedX509Certificate.java @@ -0,0 +1,175 @@ +/* + * 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 android.util.apk; + +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Principal; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509Certificate; +import java.util.Date; +import java.util.Set; + +class WrappedX509Certificate extends X509Certificate { + private final X509Certificate mWrapped; + + WrappedX509Certificate(X509Certificate wrapped) { + this.mWrapped = wrapped; + } + + @Override + public Set<String> getCriticalExtensionOIDs() { + return mWrapped.getCriticalExtensionOIDs(); + } + + @Override + public byte[] getExtensionValue(String oid) { + return mWrapped.getExtensionValue(oid); + } + + @Override + public Set<String> getNonCriticalExtensionOIDs() { + return mWrapped.getNonCriticalExtensionOIDs(); + } + + @Override + public boolean hasUnsupportedCriticalExtension() { + return mWrapped.hasUnsupportedCriticalExtension(); + } + + @Override + public void checkValidity() + throws CertificateExpiredException, CertificateNotYetValidException { + mWrapped.checkValidity(); + } + + @Override + public void checkValidity(Date date) + throws CertificateExpiredException, CertificateNotYetValidException { + mWrapped.checkValidity(date); + } + + @Override + public int getVersion() { + return mWrapped.getVersion(); + } + + @Override + public BigInteger getSerialNumber() { + return mWrapped.getSerialNumber(); + } + + @Override + public Principal getIssuerDN() { + return mWrapped.getIssuerDN(); + } + + @Override + public Principal getSubjectDN() { + return mWrapped.getSubjectDN(); + } + + @Override + public Date getNotBefore() { + return mWrapped.getNotBefore(); + } + + @Override + public Date getNotAfter() { + return mWrapped.getNotAfter(); + } + + @Override + public byte[] getTBSCertificate() throws CertificateEncodingException { + return mWrapped.getTBSCertificate(); + } + + @Override + public byte[] getSignature() { + return mWrapped.getSignature(); + } + + @Override + public String getSigAlgName() { + return mWrapped.getSigAlgName(); + } + + @Override + public String getSigAlgOID() { + return mWrapped.getSigAlgOID(); + } + + @Override + public byte[] getSigAlgParams() { + return mWrapped.getSigAlgParams(); + } + + @Override + public boolean[] getIssuerUniqueID() { + return mWrapped.getIssuerUniqueID(); + } + + @Override + public boolean[] getSubjectUniqueID() { + return mWrapped.getSubjectUniqueID(); + } + + @Override + public boolean[] getKeyUsage() { + return mWrapped.getKeyUsage(); + } + + @Override + public int getBasicConstraints() { + return mWrapped.getBasicConstraints(); + } + + @Override + public byte[] getEncoded() throws CertificateEncodingException { + return mWrapped.getEncoded(); + } + + @Override + public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException, + InvalidKeyException, NoSuchProviderException, SignatureException { + mWrapped.verify(key); + } + + @Override + public void verify(PublicKey key, String sigProvider) + throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, + NoSuchProviderException, SignatureException { + mWrapped.verify(key, sigProvider); + } + + @Override + public String toString() { + return mWrapped.toString(); + } + + @Override + public PublicKey getPublicKey() { + return mWrapped.getPublicKey(); + } +} diff --git a/core/java/android/util/jar/StrictJarVerifier.java b/core/java/android/util/jar/StrictJarVerifier.java index debc170fa537..45254908c5c9 100644 --- a/core/java/android/util/jar/StrictJarVerifier.java +++ b/core/java/android/util/jar/StrictJarVerifier.java @@ -18,6 +18,8 @@ package android.util.jar; import android.util.apk.ApkSignatureSchemeV2Verifier; +import android.util.apk.ApkSignatureSchemeV3Verifier; + import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; @@ -36,6 +38,7 @@ import java.util.Map; import java.util.StringTokenizer; import java.util.jar.Attributes; import java.util.jar.JarFile; + import sun.security.jca.Providers; import sun.security.pkcs.PKCS7; import sun.security.pkcs.SignerInfo; @@ -56,6 +59,15 @@ import sun.security.pkcs.SignerInfo; */ class StrictJarVerifier { /** + * {@code .SF} file header section attribute indicating that the APK is signed not just with + * JAR signature scheme but also with APK Signature Scheme v2 or newer. This attribute + * facilitates v2 signature stripping detection. + * + * <p>The attribute contains a comma-separated set of signature scheme IDs. + */ + private static final String SF_ATTRIBUTE_ANDROID_APK_SIGNED_NAME = "X-Android-APK-Signed"; + + /** * List of accepted digest algorithms. This list is in order from most * preferred to least preferred. */ @@ -373,17 +385,17 @@ class StrictJarVerifier { return; } - // If requested, check whether APK Signature Scheme v2 signature was stripped. + // If requested, check whether a newer APK Signature Scheme signature was stripped. if (signatureSchemeRollbackProtectionsEnforced) { String apkSignatureSchemeIdList = - attributes.getValue( - ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_NAME); + attributes.getValue(SF_ATTRIBUTE_ANDROID_APK_SIGNED_NAME); if (apkSignatureSchemeIdList != null) { // This field contains a comma-separated list of APK signature scheme IDs which // were used to sign this APK. If an ID is known to us, it means signatures of that // scheme were stripped from the APK because otherwise we wouldn't have fallen back // to verifying the APK using the JAR signature scheme. boolean v2SignatureGenerated = false; + boolean v3SignatureGenerated = false; StringTokenizer tokenizer = new StringTokenizer(apkSignatureSchemeIdList, ","); while (tokenizer.hasMoreTokens()) { String idText = tokenizer.nextToken().trim(); @@ -402,6 +414,12 @@ class StrictJarVerifier { v2SignatureGenerated = true; break; } + if (id == ApkSignatureSchemeV3Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID) { + // This APK was supposed to be signed with APK Signature Scheme v3 but no + // such signature was found. + v3SignatureGenerated = true; + break; + } } if (v2SignatureGenerated) { @@ -409,6 +427,11 @@ class StrictJarVerifier { + " is signed using APK Signature Scheme v2, but no such signature was" + " found. Signature stripped?"); } + if (v3SignatureGenerated) { + throw new SecurityException(signatureFile + " indicates " + jarName + + " is signed using APK Signature Scheme v3, but no such signature was" + + " found. Signature stripped?"); + } } } diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java index e448f14ca97d..3dcfd00bc34c 100644 --- a/core/java/android/view/DisplayCutout.java +++ b/core/java/android/view/DisplayCutout.java @@ -16,6 +16,8 @@ package android.view; +import static android.view.DisplayCutoutProto.BOUNDS; +import static android.view.DisplayCutoutProto.INSETS; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_180; import static android.view.Surface.ROTATION_270; @@ -28,6 +30,7 @@ import android.graphics.RectF; import android.graphics.Region; import android.os.Parcel; import android.os.Parcelable; +import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; @@ -155,6 +158,16 @@ public final class DisplayCutout { } /** + * @hide + */ + public void writeToProto(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + mSafeInsets.writeToProto(proto, INSETS); + mBounds.getBounds().writeToProto(proto, BOUNDS); + proto.end(token); + } + + /** * Insets the reference frame of the cutout in the given directions. * * @return a copy of this instance which has been inset diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index cc63a62c5651..841888f96d63 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -4182,6 +4182,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private static boolean sUseDefaultFocusHighlight; + /** + * True if zero-sized views can be focused. + */ + private static boolean sCanFocusZeroSized; + private String mTransitionName; static class TintInfo { @@ -4798,6 +4803,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, sThrowOnInvalidFloatProperties = targetSdkVersion >= Build.VERSION_CODES.P; + sCanFocusZeroSized = targetSdkVersion < Build.VERSION_CODES.P; + sCompatibilityDone = true; } } @@ -7010,6 +7017,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, void clearFocusInternal(View focused, boolean propagate, boolean refocus) { if ((mPrivateFlags & PFLAG_FOCUSED) != 0) { mPrivateFlags &= ~PFLAG_FOCUSED; + clearParentsWantFocus(); if (propagate && mParent != null) { mParent.clearChildFocus(this); @@ -10046,6 +10054,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @return {@code true} if laid-out and not about to do another layout. + */ + boolean isLayoutValid() { + return isLaidOut() && ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == 0); + } + + /** * If this view doesn't do any drawing on its own, set this flag to * allow further optimizations. By default, this flag is not set on * View, but could be set on some View subclasses such as ViewGroup. @@ -10817,7 +10832,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (views == null) { return; } - if (!isFocusable() || !isEnabled()) { + if (!canTakeFocus()) { return; } if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE @@ -11031,8 +11046,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * descendants. * * A view will not actually take focus if it is not focusable ({@link #isFocusable} returns - * false), or if it is focusable and it is not focusable in touch mode - * ({@link #isFocusableInTouchMode}) while the device is in touch mode. + * false), or if it can't be focused due to other conditions (not focusable in touch mode + * ({@link #isFocusableInTouchMode}) while the device is in touch mode, not visible, not + * enabled, or has no size). * * See also {@link #focusSearch(int)}, which is what you call to say that you * have focus, and you want your parent to look for the next one. @@ -11139,9 +11155,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private boolean requestFocusNoSearch(int direction, Rect previouslyFocusedRect) { // need to be focusable - if ((mViewFlags & FOCUSABLE) != FOCUSABLE - || (mViewFlags & VISIBILITY_MASK) != VISIBLE - || (mViewFlags & ENABLED_MASK) != ENABLED) { + if (!canTakeFocus()) { return false; } @@ -11156,10 +11170,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return false; } + if (!isLayoutValid()) { + mPrivateFlags |= PFLAG_WANTS_FOCUS; + } + handleFocusGainInternal(direction, previouslyFocusedRect); return true; } + void clearParentsWantFocus() { + if (mParent instanceof View) { + ((View) mParent).mPrivateFlags &= ~PFLAG_WANTS_FOCUS; + ((View) mParent).clearParentsWantFocus(); + } + } + /** * Call this to try to give focus to a specific view or to one of its descendants. This is a * special variant of {@link #requestFocus() } that will allow views that are not focusable in @@ -13531,6 +13556,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mAttachInfo.mUnbufferedDispatchRequested = true; } + private boolean canTakeFocus() { + return ((mViewFlags & VISIBILITY_MASK) == VISIBLE) + && ((mViewFlags & FOCUSABLE) == FOCUSABLE) + && ((mViewFlags & ENABLED_MASK) == ENABLED) + && (sCanFocusZeroSized || !isLayoutValid() || (mBottom > mTop) && (mRight > mLeft)); + } + /** * Set flags controlling behavior of this view. * @@ -13550,6 +13582,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return; } int privateFlags = mPrivateFlags; + boolean shouldNotifyFocusableAvailable = false; // If focusable is auto, update the FOCUSABLE bit. int focusableChangedByAuto = 0; @@ -13588,7 +13621,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, || focusableChangedByAuto == 0 || viewRootImpl == null || viewRootImpl.mThread == Thread.currentThread()) { - mParent.focusableViewAvailable(this); + shouldNotifyFocusableAvailable = true; } } } @@ -13611,10 +13644,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // about in case nothing has focus. even if this specific view // isn't focusable, it may contain something that is, so let // the root view try to give this focus if nothing else does. - if ((mParent != null) && ((mViewFlags & ENABLED_MASK) == ENABLED) - && (mBottom > mTop) && (mRight > mLeft)) { - mParent.focusableViewAvailable(this); - } + shouldNotifyFocusableAvailable = true; } } @@ -13623,17 +13653,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // a view becoming enabled should notify the parent as long as the view is also // visible and the parent wasn't already notified by becoming visible during this // setFlags invocation. - if ((mViewFlags & VISIBILITY_MASK) == VISIBLE - && ((changed & VISIBILITY_MASK) == 0)) { - if ((mParent != null) && (mViewFlags & ENABLED_MASK) == ENABLED) { - mParent.focusableViewAvailable(this); - } - } + shouldNotifyFocusableAvailable = true; } else { if (hasFocus()) clearFocus(); } } + if (shouldNotifyFocusableAvailable) { + if (mParent != null && canTakeFocus()) { + mParent.focusableViewAvailable(this); + } + } + /* Check if the GONE bit has changed */ if ((changed & GONE) != 0) { needGlobalAttributesUpdate(false); @@ -20160,15 +20191,58 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } + final boolean wasLayoutValid = isLayoutValid(); + mPrivateFlags &= ~PFLAG_FORCE_LAYOUT; mPrivateFlags3 |= PFLAG3_IS_LAID_OUT; + if (!wasLayoutValid && isFocused()) { + mPrivateFlags &= ~PFLAG_WANTS_FOCUS; + if (canTakeFocus()) { + // We have a robust focus, so parents should no longer be wanting focus. + clearParentsWantFocus(); + } else if (!getViewRootImpl().isInLayout()) { + // This is a weird case. Most-likely the user, rather than ViewRootImpl, called + // layout. In this case, there's no guarantee that parent layouts will be evaluated + // and thus the safest action is to clear focus here. + clearFocusInternal(null, /* propagate */ true, /* refocus */ false); + clearParentsWantFocus(); + } else if (!hasParentWantsFocus()) { + // original requestFocus was likely on this view directly, so just clear focus + clearFocusInternal(null, /* propagate */ true, /* refocus */ false); + } + // otherwise, we let parents handle re-assigning focus during their layout passes. + } else if ((mPrivateFlags & PFLAG_WANTS_FOCUS) != 0) { + mPrivateFlags &= ~PFLAG_WANTS_FOCUS; + View focused = findFocus(); + if (focused != null) { + // Try to restore focus as close as possible to our starting focus. + if (!restoreDefaultFocus() && !hasParentWantsFocus()) { + // Give up and clear focus once we've reached the top-most parent which wants + // focus. + focused.clearFocusInternal(null, /* propagate */ true, /* refocus */ false); + } + } + } + if ((mPrivateFlags3 & PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT) != 0) { mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT; notifyEnterOrExitForAutoFillIfNeeded(true); } } + private boolean hasParentWantsFocus() { + ViewParent parent = mParent; + while (parent instanceof ViewGroup) { + ViewGroup pv = (ViewGroup) parent; + if ((pv.mPrivateFlags & PFLAG_WANTS_FOCUS) != 0) { + return true; + } + parent = pv.mParent; + } + return false; + } + /** * Called from layout when this view should * assign a size and position to each of its children. @@ -20275,6 +20349,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mOverlay.getOverlayView().setRight(newWidth); mOverlay.getOverlayView().setBottom(newHeight); } + // If this isn't laid out yet, focus assignment will be handled during the "deferment/ + // backtracking" of requestFocus during layout, so don't touch focus here. + if (!sCanFocusZeroSized && isLayoutValid()) { + if (newWidth <= 0 || newHeight <= 0) { + if (hasFocus()) { + clearFocus(); + if (mParent instanceof ViewGroup) { + ((ViewGroup) mParent).clearFocusedInCluster(); + } + } + clearAccessibilityFocus(); + } else if (oldWidth <= 0 || oldHeight <= 0) { + if (mParent != null && canTakeFocus()) { + mParent.focusableViewAvailable(this); + } + } + } rebuildOutline(); } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 122df934111d..e21abf0a637b 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -3215,22 +3215,31 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } int descendantFocusability = getDescendantFocusability(); + boolean result; switch (descendantFocusability) { case FOCUS_BLOCK_DESCENDANTS: - return super.requestFocus(direction, previouslyFocusedRect); + result = super.requestFocus(direction, previouslyFocusedRect); + break; case FOCUS_BEFORE_DESCENDANTS: { final boolean took = super.requestFocus(direction, previouslyFocusedRect); - return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect); + result = took ? took : onRequestFocusInDescendants(direction, + previouslyFocusedRect); + break; } case FOCUS_AFTER_DESCENDANTS: { final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect); - return took ? took : super.requestFocus(direction, previouslyFocusedRect); + result = took ? took : super.requestFocus(direction, previouslyFocusedRect); + break; } default: throw new IllegalStateException("descendant focusability must be " + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS " + "but is " + descendantFocusability); } + if (result && !isLayoutValid() && ((mPrivateFlags & PFLAG_WANTS_FOCUS) == 0)) { + mPrivateFlags |= PFLAG_WANTS_FOCUS; + } + return result; } /** diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java index d665dde39afe..1d94abeb51a2 100644 --- a/core/java/android/view/ViewStructure.java +++ b/core/java/android/view/ViewStructure.java @@ -26,6 +26,8 @@ import android.util.Pair; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; +import com.android.internal.util.Preconditions; + import java.util.List; /** @@ -204,6 +206,16 @@ public abstract class ViewStructure { public abstract void setTextLines(int[] charOffsets, int[] baselines); /** + * Sets the identifier used to set the text associated with this view. + * + * <p>Should only be set when the node is used for autofill purposes - it will be ignored + * when used for Assist. + */ + public void setTextIdEntry(@NonNull String entryName) { + Preconditions.checkNotNull(entryName); + } + + /** * Set optional hint text associated with this view; this is for example the text that is * shown by an EditText when it is empty to indicate to the user the kind of text to input. */ diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index cbe012af0b21..a65aba152c83 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -17,6 +17,32 @@ package android.view; import static android.content.pm.ActivityInfo.COLOR_MODE_DEFAULT; +import static android.view.WindowLayoutParamsProto.ALPHA; +import static android.view.WindowLayoutParamsProto.BUTTON_BRIGHTNESS; +import static android.view.WindowLayoutParamsProto.COLOR_MODE; +import static android.view.WindowLayoutParamsProto.FLAGS; +import static android.view.WindowLayoutParamsProto.FLAGS_EXTRA; +import static android.view.WindowLayoutParamsProto.FORMAT; +import static android.view.WindowLayoutParamsProto.GRAVITY; +import static android.view.WindowLayoutParamsProto.HAS_SYSTEM_UI_LISTENERS; +import static android.view.WindowLayoutParamsProto.HEIGHT; +import static android.view.WindowLayoutParamsProto.HORIZONTAL_MARGIN; +import static android.view.WindowLayoutParamsProto.INPUT_FEATURE_FLAGS; +import static android.view.WindowLayoutParamsProto.NEEDS_MENU_KEY; +import static android.view.WindowLayoutParamsProto.PREFERRED_REFRESH_RATE; +import static android.view.WindowLayoutParamsProto.PRIVATE_FLAGS; +import static android.view.WindowLayoutParamsProto.ROTATION_ANIMATION; +import static android.view.WindowLayoutParamsProto.SCREEN_BRIGHTNESS; +import static android.view.WindowLayoutParamsProto.SOFT_INPUT_MODE; +import static android.view.WindowLayoutParamsProto.SUBTREE_SYSTEM_UI_VISIBILITY_FLAGS; +import static android.view.WindowLayoutParamsProto.SYSTEM_UI_VISIBILITY_FLAGS; +import static android.view.WindowLayoutParamsProto.TYPE; +import static android.view.WindowLayoutParamsProto.USER_ACTIVITY_TIMEOUT; +import static android.view.WindowLayoutParamsProto.VERTICAL_MARGIN; +import static android.view.WindowLayoutParamsProto.WIDTH; +import static android.view.WindowLayoutParamsProto.WINDOW_ANIMATIONS; +import static android.view.WindowLayoutParamsProto.X; +import static android.view.WindowLayoutParamsProto.Y; import android.Manifest.permission; import android.annotation.IntDef; @@ -2722,7 +2748,33 @@ public interface WindowManager extends ViewManager { */ public void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); - proto.write(WindowLayoutParamsProto.TYPE, type); + proto.write(TYPE, type); + proto.write(X, x); + proto.write(Y, y); + proto.write(WIDTH, width); + proto.write(HEIGHT, height); + proto.write(HORIZONTAL_MARGIN, horizontalMargin); + proto.write(VERTICAL_MARGIN, verticalMargin); + proto.write(GRAVITY, gravity); + proto.write(SOFT_INPUT_MODE, softInputMode); + proto.write(FORMAT, format); + proto.write(WINDOW_ANIMATIONS, windowAnimations); + proto.write(ALPHA, alpha); + proto.write(SCREEN_BRIGHTNESS, screenBrightness); + proto.write(BUTTON_BRIGHTNESS, buttonBrightness); + proto.write(ROTATION_ANIMATION, rotationAnimation); + proto.write(PREFERRED_REFRESH_RATE, preferredRefreshRate); + proto.write(WindowLayoutParamsProto.PREFERRED_DISPLAY_MODE_ID, preferredDisplayModeId); + proto.write(HAS_SYSTEM_UI_LISTENERS, hasSystemUiListeners); + proto.write(INPUT_FEATURE_FLAGS, inputFeatures); + proto.write(USER_ACTIVITY_TIMEOUT, userActivityTimeout); + proto.write(NEEDS_MENU_KEY, needsMenuKey); + proto.write(COLOR_MODE, mColorMode); + proto.write(FLAGS, flags); + proto.write(FLAGS_EXTRA, flags2); + proto.write(PRIVATE_FLAGS, privateFlags); + proto.write(SYSTEM_UI_VISIBILITY_FLAGS, systemUiVisibility); + proto.write(SUBTREE_SYSTEM_UI_VISIBILITY_FLAGS, subtreeSystemUiVisibility); proto.end(token); } diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index 1d19a9f5969a..6c2d34988896 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -38,19 +38,14 @@ import java.util.List; * <p> * An accessibility event is fired by an individual view which populates the event with * data for its state and requests from its parent to send the event to interested - * parties. The parent can optionally add an {@link AccessibilityRecord} for itself before - * dispatching a similar request to its parent. A parent can also choose not to respect the - * request for sending an event. The accessibility event is sent by the topmost view in the - * view tree. Therefore, an {@link android.accessibilityservice.AccessibilityService} can - * explore all records in an accessibility event to obtain more information about the - * context in which the event was fired. + * parties. The parent can optionally modify or even block the event based on its broader + * understanding of the user interface's context. * </p> * <p> - * The main purpose of an accessibility event is to expose enough information for an - * {@link android.accessibilityservice.AccessibilityService} to provide meaningful feedback - * to the user. Sometimes however, an accessibility service may need more contextual - * information then the one in the event pay-load. In such cases the service can obtain - * the event source which is an {@link AccessibilityNodeInfo} (snapshot of a View state) + * The main purpose of an accessibility event is to communicate changes in the UI to an + * {@link android.accessibilityservice.AccessibilityService}. The service may then inspect, + * if needed the user interface by examining the View hierarchy, as represented by a tree of + * {@link AccessibilityNodeInfo}s (snapshot of a View state) * which can be used for exploring the window content. Note that the privilege for accessing * an event's source, thus the window content, has to be explicitly requested. For more * details refer to {@link android.accessibilityservice.AccessibilityService}. If an @@ -85,21 +80,6 @@ import java.util.List; * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> - * <li>{@link #getText()} - The text of the source's sub-tree.</li> - * <li>{@link #isEnabled()} - Whether the source is enabled.</li> - * <li>{@link #isPassword()} - Whether the source is password.</li> - * <li>{@link #isChecked()} - Whether the source is checked.</li> - * <li>{@link #getContentDescription()} - The content description of the source.</li> - * <li>{@link #getScrollX()} - The offset of the source left edge in pixels - * (without descendants of AdapterView).</li> - * <li>{@link #getScrollY()} - The offset of the source top edge in pixels - * (without descendants of AdapterView).</li> - * <li>{@link #getFromIndex()} - The zero based index of the first visible item of the source, - * inclusive (for descendants of AdapterView).</li> - * <li>{@link #getToIndex()} - The zero based index of the last visible item of the source, - * inclusive (for descendants of AdapterView).</li> - * <li>{@link #getItemCount()} - The total items of the source - * (for descendants of AdapterView).</li> * </ul> * </p> * <p> @@ -113,21 +93,6 @@ import java.util.List; * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> - * <li>{@link #getText()} - The text of the source's sub-tree.</li> - * <li>{@link #isEnabled()} - Whether the source is enabled.</li> - * <li>{@link #isPassword()} - Whether the source is password.</li> - * <li>{@link #isChecked()} - Whether the source is checked.</li> - * <li>{@link #getContentDescription()} - The content description of the source.</li> - * <li>{@link #getScrollX()} - The offset of the source left edge in pixels - * (without descendants of AdapterView).</li> - * <li>{@link #getScrollY()} - The offset of the source top edge in pixels - * (without descendants of AdapterView).</li> - * <li>{@link #getFromIndex()} - The zero based index of the first visible item of the source, - * inclusive (for descendants of AdapterView).</li> - * <li>{@link #getToIndex()} - The zero based index of the last visible item of the source, - * inclusive (for descendants of AdapterView).</li> - * <li>{@link #getItemCount()} - The total items of the source - * (for descendants of AdapterView).</li> * </ul> * </p> * <p> @@ -141,23 +106,6 @@ import java.util.List; * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> - * <li>{@link #getText()} - The text of the source's sub-tree.</li> - * <li>{@link #isEnabled()} - Whether the source is enabled.</li> - * <li>{@link #isPassword()} - Whether the source is password.</li> - * <li>{@link #isChecked()} - Whether the source is checked.</li> - * <li>{@link #getItemCount()} - The number of selectable items of the source.</li> - * <li>{@link #getCurrentItemIndex()} - The currently selected item index.</li> - * <li>{@link #getContentDescription()} - The content description of the source.</li> - * <li>{@link #getScrollX()} - The offset of the source left edge in pixels - * (without descendants of AdapterView).</li> - * <li>{@link #getScrollY()} - The offset of the source top edge in pixels - * (without descendants of AdapterView).</li> - * <li>{@link #getFromIndex()} - The zero based index of the first visible item of the source, - * inclusive (for descendants of AdapterView).</li> - * <li>{@link #getToIndex()} - The zero based index of the last visible item of the source, - * inclusive (for descendants of AdapterView).</li> - * <li>{@link #getItemCount()} - The total items of the source - * (for descendants of AdapterView).</li> * </ul> * </p> * <p> @@ -171,23 +119,6 @@ import java.util.List; * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> - * <li>{@link #getText()} - The text of the source's sub-tree.</li> - * <li>{@link #isEnabled()} - Whether the source is enabled.</li> - * <li>{@link #isPassword()} - Whether the source is password.</li> - * <li>{@link #isChecked()} - Whether the source is checked.</li> - * <li>{@link #getItemCount()} - The number of focusable items on the screen.</li> - * <li>{@link #getCurrentItemIndex()} - The currently focused item index.</li> - * <li>{@link #getContentDescription()} - The content description of the source.</li> - * <li>{@link #getScrollX()} - The offset of the source left edge in pixels - * (without descendants of AdapterView).</li> - * <li>{@link #getScrollY()} - The offset of the source top edge in pixels - * (without descendants of AdapterView).</li> - * <li>{@link #getFromIndex()} - The zero based index of the first visible item of the source, - * inclusive (for descendants of AdapterView).</li> - * <li>{@link #getToIndex()} - The zero based index of the last visible item of the source, - * inclusive (for descendants of AdapterView).</li> - * <li>{@link #getItemCount()} - The total items of the source - * (for descendants of AdapterView).</li> * </ul> * </p> * <p> @@ -201,15 +132,11 @@ import java.util.List; * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> - * <li>{@link #getText()} - The text of the source.</li> - * <li>{@link #isEnabled()} - Whether the source is enabled.</li> - * <li>{@link #isPassword()} - Whether the source is password.</li> - * <li>{@link #isChecked()} - Whether the source is checked.</li> + * <li>{@link #getText()} - The new text of the source.</li> + * <li>{@link #getBeforeText()} - The text of the source before the change.</li> * <li>{@link #getFromIndex()} - The text change start index.</li> * <li>{@link #getAddedCount()} - The number of added characters.</li> * <li>{@link #getRemovedCount()} - The number of removed characters.</li> - * <li>{@link #getBeforeText()} - The text of the source before the change.</li> - * <li>{@link #getContentDescription()} - The content description of the source.</li> * </ul> * </p> * <p> @@ -223,13 +150,6 @@ import java.util.List; * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> - * <li>{@link #getText()} - The text of the source.</li> - * <li>{@link #isPassword()} - Whether the source is password.</li> - * <li>{@link #getFromIndex()} - The selection start index.</li> - * <li>{@link #getToIndex()} - The selection end index.</li> - * <li>{@link #getItemCount()} - The length of the source text.</li> - * <li>{@link #isEnabled()} - Whether the source is enabled.</li> - * <li>{@link #getContentDescription()} - The content description of the source.</li> * </ul> * </p> * <b>View text traversed at movement granularity</b> - represents the event of traversing the @@ -251,23 +171,11 @@ import java.util.List; * <li>{@link #getToIndex()} - The end of the text that was skipped over in this movement. * This is the ending point when moving forward through the text, but not when moving * back.</li> - * <li>{@link #isPassword()} - Whether the source is password.</li> - * <li>{@link #isEnabled()} - Whether the source is enabled.</li> - * <li>{@link #getContentDescription()} - The content description of the source.</li> - * <li>{@link #getMovementGranularity()} - Sets the granularity at which a view's text - * was traversed.</li> * <li>{@link #getAction()} - Gets traversal action which specifies the direction.</li> * </ul> * </p> * <p> - * <b>View scrolled</b> - represents the event of scrolling a view. If - * the source is a descendant of {@link android.widget.AdapterView} the - * scroll is reported in terms of visible items - the first visible item, - * the last visible item, and the total items - because the the source - * is unaware of its pixel size since its adapter is responsible for - * creating views. In all other cases the scroll is reported as the current - * scroll on the X and Y axis respectively plus the height of the source in - * pixels.</br> + * <b>View scrolled</b> - represents the event of scrolling a view. </br> * <em>Type:</em> {@link #TYPE_VIEW_SCROLLED}</br> * <em>Properties:</em></br> * <ul> @@ -276,29 +184,9 @@ import java.util.List; * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> - * <li>{@link #getText()} - The text of the source's sub-tree.</li> - * <li>{@link #isEnabled()} - Whether the source is enabled.</li> - * <li>{@link #getContentDescription()} - The content description of the source.</li> - * <li>{@link #getScrollX()} - The offset of the source left edge in pixels - * (without descendants of AdapterView).</li> - * <li>{@link #getScrollY()} - The offset of the source top edge in pixels - * (without descendants of AdapterView).</li> - * <li>{@link #getFromIndex()} - The zero based index of the first visible item of the source, - * inclusive (for descendants of AdapterView).</li> - * <li>{@link #getToIndex()} - The zero based index of the last visible item of the source, - * inclusive (for descendants of AdapterView).</li> - * <li>{@link #getItemCount()} - The total items of the source - * (for descendants of AdapterView).</li> + * <li>{@link #getScrollDeltaX()} - The difference in the horizontal position.</li> + * <li>{@link #getScrollDeltaY()} - The difference in the vertical position.</li> * </ul> - * <em>Note:</em> This event type is not dispatched to descendants though - * {@link android.view.View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) - * View.dispatchPopulateAccessibilityEvent(AccessibilityEvent)}, hence the event - * source {@link android.view.View} and the sub-tree rooted at it will not receive - * calls to {@link android.view.View#onPopulateAccessibilityEvent(AccessibilityEvent) - * View.onPopulateAccessibilityEvent(AccessibilityEvent)}. The preferred way to add - * text content to such events is by setting the - * {@link android.R.styleable#View_contentDescription contentDescription} of the source - * view.</br> * </p> * <p> * <b>TRANSITION TYPES</b></br> @@ -316,7 +204,6 @@ import java.util.List; * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> * <li>{@link #getText()} - The text of the source's sub-tree.</li> - * <li>{@link #isEnabled()} - Whether the source is enabled.</li> * </ul> * </p> * <p> @@ -325,10 +212,6 @@ import java.util.List; * a view size, etc.</br> * </p> * <p> - * <strong>Note:</strong> This event is fired only for the window source of the - * last accessibility event different from {@link #TYPE_NOTIFICATION_STATE_CHANGED} - * and its purpose is to notify clients that the content of the user interaction - * window has changed.</br> * <em>Type:</em> {@link #TYPE_WINDOW_CONTENT_CHANGED}</br> * <em>Properties:</em></br> * <ul> @@ -339,32 +222,26 @@ import java.util.List; * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> * </ul> - * <em>Note:</em> This event type is not dispatched to descendants though - * {@link android.view.View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) - * View.dispatchPopulateAccessibilityEvent(AccessibilityEvent)}, hence the event - * source {@link android.view.View} and the sub-tree rooted at it will not receive - * calls to {@link android.view.View#onPopulateAccessibilityEvent(AccessibilityEvent) - * View.onPopulateAccessibilityEvent(AccessibilityEvent)}. The preferred way to add - * text content to such events is by setting the - * {@link android.R.styleable#View_contentDescription contentDescription} of the source - * view.</br> * </p> * <p> - * <b>Windows changed</b> - represents the event of changes in the windows shown on + * <b>Windows changed</b> - represents a change in the windows shown on * the screen such as a window appeared, a window disappeared, a window size changed, - * a window layer changed, etc.</br> + * a window layer changed, etc. These events should only come from the system, which is responsible + * for managing windows. The list of windows is available from + * {@link android.accessibilityservice.AccessibilityService#getWindows()}. + * For regions of the user interface that are presented as windows but are + * controlled by an app's process, use {@link #TYPE_WINDOW_STATE_CHANGED}.</br> * <em>Type:</em> {@link #TYPE_WINDOWS_CHANGED}</br> * <em>Properties:</em></br> * <ul> * <li>{@link #getEventType()} - The type of the event.</li> * <li>{@link #getEventTime()} - The event time.</li> + * <li>{@link #getWindowChanges()}</li> - The specific change to the source window * </ul> * <em>Note:</em> You can retrieve the {@link AccessibilityWindowInfo} for the window - * source of the event via {@link AccessibilityEvent#getSource()} to get the source - * node on which then call {@link AccessibilityNodeInfo#getWindow() - * AccessibilityNodeInfo.getWindow()} to get the window. Also all windows on the screen can - * be retrieved by a call to {@link android.accessibilityservice.AccessibilityService#getWindows() - * android.accessibilityservice.AccessibilityService.getWindows()}. + * source of the event by looking through the list returned by + * {@link android.accessibilityservice.AccessibilityService#getWindows()} for the window whose ID + * matches {@link #getWindowId()}. * </p> * <p> * <b>NOTIFICATION TYPES</b></br> @@ -402,19 +279,6 @@ import java.util.List; * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> - * <li>{@link #getText()} - The text of the source's sub-tree.</li> - * <li>{@link #isEnabled()} - Whether the source is enabled.</li> - * <li>{@link #getContentDescription()} - The content description of the source.</li> - * <li>{@link #getScrollX()} - The offset of the source left edge in pixels - * (without descendants of AdapterView).</li> - * <li>{@link #getScrollY()} - The offset of the source top edge in pixels - * (without descendants of AdapterView).</li> - * <li>{@link #getFromIndex()} - The zero based index of the first visible item of the source, - * inclusive (for descendants of AdapterView).</li> - * <li>{@link #getToIndex()} - The zero based index of the last visible item of the source, - * inclusive (for descendants of AdapterView).</li> - * <li>{@link #getItemCount()} - The total items of the source - * (for descendants of AdapterView).</li> * </ul> * </p> * <b>View hover exit</b> - represents the event of stopping to hover @@ -428,19 +292,6 @@ import java.util.List; * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> - * <li>{@link #getText()} - The text of the source's sub-tree.</li> - * <li>{@link #isEnabled()} - Whether the source is enabled.</li> - * <li>{@link #getContentDescription()} - The content description of the source.</li> - * <li>{@link #getScrollX()} - The offset of the source left edge in pixels - * (without descendants of AdapterView).</li> - * <li>{@link #getScrollY()} - The offset of the source top edge in pixels - * (without descendants of AdapterView).</li> - * <li>{@link #getFromIndex()} - The zero based index of the first visible item of the source, - * inclusive (for descendants of AdapterView).</li> - * <li>{@link #getToIndex()} - The zero based index of the last visible item of the source, - * inclusive (for descendants of AdapterView).</li> - * <li>{@link #getItemCount()} - The total items of the source - * (for descendants of AdapterView).</li> * </ul> * </p> * <p> @@ -513,10 +364,10 @@ import java.util.List; * <b>MISCELLANEOUS TYPES</b></br> * </p> * <p> - * <b>Announcement</b> - represents the event of an application making an - * announcement. Usually this announcement is related to some sort of a context - * change for which none of the events representing UI transitions is a good fit. - * For example, announcing a new page in a book.</br> + * <b>Announcement</b> - represents the event of an application requesting a screen reader to make + * an announcement. Because the event carries no semantic meaning, this event is appropriate only + * in exceptional situations where additional screen reader output is needed but other types of + * accessibility services do not need to be aware of the change.</br> * <em>Type:</em> {@link #TYPE_ANNOUNCEMENT}</br> * <em>Properties:</em></br> * <ul> @@ -526,7 +377,6 @@ import java.util.List; * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> * <li>{@link #getText()} - The text of the announcement.</li> - * <li>{@link #isEnabled()} - Whether the source is enabled.</li> * </ul> * </p> * @@ -674,7 +524,8 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par public static final int TYPE_TOUCH_INTERACTION_END = 0x00200000; /** - * Represents the event change in the windows shown on the screen. + * Represents the event change in the system windows shown on the screen. This event type should + * only be dispatched by the system. */ public static final int TYPE_WINDOWS_CHANGED = 0x00400000; @@ -696,7 +547,8 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par /** * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event: - * A node in the subtree rooted at the source node was added or removed. + * One or more content changes occurred in the the subtree rooted at the source node, + * or the subtree's structure changed when a node was added or removed. */ public static final int CONTENT_CHANGE_TYPE_SUBTREE = 0x00000001; @@ -712,6 +564,99 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par */ public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 0x00000004; + /** + * Change type for {@link #TYPE_WINDOWS_CHANGED} event: + * The window was added. + */ + public static final int WINDOWS_CHANGE_ADDED = 0x00000001; + + /** + * Change type for {@link #TYPE_WINDOWS_CHANGED} event: + * A window was removed. + */ + public static final int WINDOWS_CHANGE_REMOVED = 0x00000002; + + /** + * Change type for {@link #TYPE_WINDOWS_CHANGED} event: + * The window's title changed. + */ + public static final int WINDOWS_CHANGE_TITLE = 0x00000004; + + /** + * Change type for {@link #TYPE_WINDOWS_CHANGED} event: + * The window's bounds changed. + */ + public static final int WINDOWS_CHANGE_BOUNDS = 0x00000008; + + /** + * Change type for {@link #TYPE_WINDOWS_CHANGED} event: + * The window's layer changed. + */ + public static final int WINDOWS_CHANGE_LAYER = 0x00000010; + + /** + * Change type for {@link #TYPE_WINDOWS_CHANGED} event: + * The window's {@link AccessibilityWindowInfo#isActive()} changed. + */ + public static final int WINDOWS_CHANGE_ACTIVE = 0x00000020; + + /** + * Change type for {@link #TYPE_WINDOWS_CHANGED} event: + * The window's {@link AccessibilityWindowInfo#isFocused()} changed. + */ + public static final int WINDOWS_CHANGE_FOCUSED = 0x00000040; + + /** + * Change type for {@link #TYPE_WINDOWS_CHANGED} event: + * The window's {@link AccessibilityWindowInfo#isAccessibilityFocused()} changed. + */ + public static final int WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED = 0x00000080; + + /** + * Change type for {@link #TYPE_WINDOWS_CHANGED} event: + * The window's parent changed. + */ + public static final int WINDOWS_CHANGE_PARENT = 0x00000100; + + /** + * Change type for {@link #TYPE_WINDOWS_CHANGED} event: + * The window's children changed. + */ + public static final int WINDOWS_CHANGE_CHILDREN = 0x00000200; + + /** + * Change type for {@link #TYPE_WINDOWS_CHANGED} event: + * The window either entered or exited picture-in-picture mode. + */ + public static final int WINDOWS_CHANGE_PIP = 0x00000400; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = { "WINDOWS_CHANGE_" }, value = { + WINDOWS_CHANGE_ADDED, + WINDOWS_CHANGE_REMOVED, + WINDOWS_CHANGE_TITLE, + WINDOWS_CHANGE_BOUNDS, + WINDOWS_CHANGE_LAYER, + WINDOWS_CHANGE_ACTIVE, + WINDOWS_CHANGE_FOCUSED, + WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED, + WINDOWS_CHANGE_PARENT, + WINDOWS_CHANGE_CHILDREN, + WINDOWS_CHANGE_PIP + }) + public @interface WindowsChangeTypes {} + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = { "CONTENT_CHANGE_TYPE_" }, + value = { + CONTENT_CHANGE_TYPE_UNDEFINED, + CONTENT_CHANGE_TYPE_SUBTREE, + CONTENT_CHANGE_TYPE_TEXT, + CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION + }) + public @interface ContentChangeTypes {} /** @hide */ @IntDef(flag = true, prefix = { "TYPE_" }, value = { @@ -782,6 +727,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par int mMovementGranularity; int mAction; int mContentChangeTypes; + int mWindowChangeTypes; private ArrayList<AccessibilityRecord> mRecords; @@ -802,6 +748,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par mMovementGranularity = event.mMovementGranularity; mAction = event.mAction; mContentChangeTypes = event.mContentChangeTypes; + mWindowChangeTypes = event.mWindowChangeTypes; mEventTime = event.mEventTime; mPackageName = event.mPackageName; } @@ -885,6 +832,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_UNDEFINED} * </ul> */ + @ContentChangeTypes public int getContentChangeTypes() { return mContentChangeTypes; } @@ -913,12 +861,49 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * @throws IllegalStateException If called from an AccessibilityService. * @see #getContentChangeTypes() */ - public void setContentChangeTypes(int changeTypes) { + public void setContentChangeTypes(@ContentChangeTypes int changeTypes) { enforceNotSealed(); mContentChangeTypes = changeTypes; } /** + * Get the bit mask of change types signaled by a {@link #TYPE_WINDOWS_CHANGED} event. A + * single event may represent multiple change types. + * + * @return The bit mask of change types. + */ + @WindowsChangeTypes + public int getWindowChanges() { + return mWindowChangeTypes; + } + + /** @hide */ + public void setWindowChanges(@WindowsChangeTypes int changes) { + mWindowChangeTypes = changes; + } + + private static String windowChangeTypesToString(@WindowsChangeTypes int types) { + return BitUtils.flagsToString(types, AccessibilityEvent::singleWindowChangeTypeToString); + } + + private static String singleWindowChangeTypeToString(int type) { + switch (type) { + case WINDOWS_CHANGE_ADDED: return "WINDOWS_CHANGE_ADDED"; + case WINDOWS_CHANGE_REMOVED: return "WINDOWS_CHANGE_REMOVED"; + case WINDOWS_CHANGE_TITLE: return "WINDOWS_CHANGE_TITLE"; + case WINDOWS_CHANGE_BOUNDS: return "WINDOWS_CHANGE_BOUNDS"; + case WINDOWS_CHANGE_LAYER: return "WINDOWS_CHANGE_LAYER"; + case WINDOWS_CHANGE_ACTIVE: return "WINDOWS_CHANGE_ACTIVE"; + case WINDOWS_CHANGE_FOCUSED: return "WINDOWS_CHANGE_FOCUSED"; + case WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED: + return "WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED"; + case WINDOWS_CHANGE_PARENT: return "WINDOWS_CHANGE_PARENT"; + case WINDOWS_CHANGE_CHILDREN: return "WINDOWS_CHANGE_CHILDREN"; + default: return Integer.toHexString(type); + } + } + + /** * Sets the event type. * * @param eventType The event type. @@ -1025,6 +1010,26 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par } /** + * Convenience method to obtain a {@link #TYPE_WINDOWS_CHANGED} event for a specific window and + * change set. + * + * @param windowId The ID of the window that changed + * @param windowChangeTypes The changes to populate + * @return An instance of a TYPE_WINDOWS_CHANGED, populated with the requested fields and with + * importantForAccessibility set to {@code true}. + * + * @hide + */ + public static AccessibilityEvent obtainWindowsChangedEvent( + int windowId, int windowChangeTypes) { + final AccessibilityEvent event = AccessibilityEvent.obtain(TYPE_WINDOWS_CHANGED); + event.setWindowId(windowId); + event.setWindowChanges(windowChangeTypes); + event.setImportantForAccessibility(true); + return event; + } + + /** * Returns a cached instance if such is available or a new one is * instantiated with its type property set. * @@ -1099,6 +1104,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par mMovementGranularity = 0; mAction = 0; mContentChangeTypes = 0; + mWindowChangeTypes = 0; mPackageName = null; mEventTime = 0; if (mRecords != null) { @@ -1120,6 +1126,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par mMovementGranularity = parcel.readInt(); mAction = parcel.readInt(); mContentChangeTypes = parcel.readInt(); + mWindowChangeTypes = parcel.readInt(); mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); mEventTime = parcel.readLong(); mConnectionId = parcel.readInt(); @@ -1178,6 +1185,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par parcel.writeInt(mMovementGranularity); parcel.writeInt(mAction); parcel.writeInt(mContentChangeTypes); + parcel.writeInt(mWindowChangeTypes); TextUtils.writeToParcel(mPackageName, parcel, 0); parcel.writeLong(mEventTime); parcel.writeInt(mConnectionId); @@ -1238,11 +1246,13 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par builder.append("; PackageName: ").append(mPackageName); builder.append("; MovementGranularity: ").append(mMovementGranularity); builder.append("; Action: ").append(mAction); + builder.append("; ContentChangeTypes: ").append( + contentChangeTypesToString(mContentChangeTypes)); + builder.append("; WindowChangeTypes: ").append( + windowChangeTypesToString(mWindowChangeTypes)); builder.append(super.toString()); if (DEBUG) { builder.append("\n"); - builder.append("; ContentChangeTypes: ").append( - contentChangeTypesToString(mContentChangeTypes)); builder.append("; sourceWindowId: ").append(mSourceWindowId); builder.append("; mSourceNodeId: ").append(mSourceNodeId); for (int i = 0; i < getRecordCount(); i++) { diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java index ef1a3f3bcc8f..c1c9174c0f9f 100644 --- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java +++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java @@ -21,9 +21,12 @@ import android.annotation.TestApi; import android.graphics.Rect; import android.os.Parcel; import android.os.Parcelable; +import android.text.TextUtils; import android.util.LongArray; import android.util.Pools.SynchronizedPool; +import android.view.accessibility.AccessibilityEvent.WindowsChangeTypes; +import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; /** @@ -575,7 +578,7 @@ public final class AccessibilityWindowInfo implements Parcelable { StringBuilder builder = new StringBuilder(); builder.append("AccessibilityWindowInfo["); builder.append("title=").append(mTitle); - builder.append("id=").append(mId); + builder.append(", id=").append(mId); builder.append(", type=").append(typeToString(mType)); builder.append(", layer=").append(mLayer); builder.append(", bounds=").append(mBoundsInScreen); @@ -713,6 +716,60 @@ public final class AccessibilityWindowInfo implements Parcelable { return false; } + /** + * Reports how this window differs from a possibly different state of the same window. The + * argument must have the same id and type as neither of those properties may change. + * + * @param other The new state. + * @return A set of flags showing how the window has changes, or 0 if the two states are the + * same. + * + * @hide + */ + @WindowsChangeTypes + public int differenceFrom(AccessibilityWindowInfo other) { + if (other.mId != mId) { + throw new IllegalArgumentException("Not same window."); + } + if (other.mType != mType) { + throw new IllegalArgumentException("Not same type."); + } + int changes = 0; + if (!TextUtils.equals(mTitle, other.mTitle)) { + changes |= AccessibilityEvent.WINDOWS_CHANGE_TITLE; + } + + if (!mBoundsInScreen.equals(other.mBoundsInScreen)) { + changes |= AccessibilityEvent.WINDOWS_CHANGE_BOUNDS; + } + if (mLayer != other.mLayer) { + changes |= AccessibilityEvent.WINDOWS_CHANGE_LAYER; + } + if (getBooleanProperty(BOOLEAN_PROPERTY_ACTIVE) + != other.getBooleanProperty(BOOLEAN_PROPERTY_ACTIVE)) { + changes |= AccessibilityEvent.WINDOWS_CHANGE_ACTIVE; + } + if (getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED) + != other.getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED)) { + changes |= AccessibilityEvent.WINDOWS_CHANGE_FOCUSED; + } + if (getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED) + != other.getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED)) { + changes |= AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED; + } + if (getBooleanProperty(BOOLEAN_PROPERTY_PICTURE_IN_PICTURE) + != other.getBooleanProperty(BOOLEAN_PROPERTY_PICTURE_IN_PICTURE)) { + changes |= AccessibilityEvent.WINDOWS_CHANGE_PIP; + } + if (mParentId != other.mParentId) { + changes |= AccessibilityEvent.WINDOWS_CHANGE_PARENT; + } + if (!Objects.equals(mChildIds, other.mChildIds)) { + changes |= AccessibilityEvent.WINDOWS_CHANGE_CHILDREN; + } + return changes; + } + public static final Parcelable.Creator<AccessibilityWindowInfo> CREATOR = new Creator<AccessibilityWindowInfo>() { @Override diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 269745455517..78b41c6f4c7b 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -53,6 +53,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Objects; @@ -1027,7 +1028,9 @@ public final class AutofillManager { * Gets the user data used for * <a href="AutofillService.html#FieldClassification">field classification</a>. * - * <p><b>Note:</b> This method should only be called by an app providing an autofill service. + * <p><b>Note:</b> This method should only be called by an app providing an autofill service, + * and it's ignored if the caller currently doesn't have an enabled autofill service for + * the user. * * @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was * reset or if the caller currently does not have an enabled autofill service for the user. @@ -1079,6 +1082,49 @@ public final class AutofillManager { } /** + * Gets the name of the default algorithm used for + * <a href="AutofillService.html#FieldClassification">field classification</a>. + * + * <p>The default algorithm is used when the algorithm on {@link UserData} is invalid or not + * set. + * + * <p><b>Note:</b> This method should only be called by an app providing an autofill service, + * and it's ignored if the caller currently doesn't have an enabled autofill service for + * the user. + */ + @Nullable + public String getDefaultFieldClassificationAlgorithm() { + try { + return mService.getDefaultFieldClassificationAlgorithm(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + return null; + } + } + + /** + * Gets the name of all algorithms currently available for + * <a href="AutofillService.html#FieldClassification">field classification</a>. + * + * <p><b>Note:</b> This method should only be called by an app providing an autofill service, + * and it's ignored if the caller currently doesn't have an enabled autofill service for + * the user. + * + * @return list of all algorithms currently available, or an empty list if the caller currently + * does not have an enabled autofill service for the user. + */ + @NonNull + public List<String> getAvailableFieldClassificationAlgorithms() { + try { + final List<String> names = mService.getAvailableFieldClassificationAlgorithms(); + return names != null ? names : Collections.emptyList(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + return null; + } + } + + /** * Returns {@code true} if autofill is supported by the current device and * is supported for this user. * diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl index 38bb311986e8..1afa35e80a26 100644 --- a/core/java/android/view/autofill/IAutoFillManager.aidl +++ b/core/java/android/view/autofill/IAutoFillManager.aidl @@ -58,4 +58,6 @@ interface IAutoFillManager { void setUserData(in UserData userData); boolean isFieldClassificationEnabled(); ComponentName getAutofillServiceComponentName(); + List<String> getAvailableFieldClassificationAlgorithms(); + String getDefaultFieldClassificationAlgorithm(); } diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java index 56c3e4a5ee77..336c20cdcdc0 100644 --- a/core/java/android/widget/EditText.java +++ b/core/java/android/widget/EditText.java @@ -105,6 +105,11 @@ public class EditText extends TextView { @Override public Editable getText() { + CharSequence text = super.getText(); + if (text instanceof Editable) { + return (Editable) super.getText(); + } + super.setText(text, BufferType.EDITABLE); return (Editable) super.getText(); } diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java index bd48f4554c5d..26dfcc2d668a 100644 --- a/core/java/android/widget/Magnifier.java +++ b/core/java/android/widget/Magnifier.java @@ -125,7 +125,7 @@ public final class Magnifier { mView.getWidth() - mBitmap.getWidth())); final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2; - if (startX != mPrevStartCoordsInSurface.x || startY != mPrevStartCoordsInSurface.y) { + if (xPosInView != mPrevPosInView.x || yPosInView != mPrevPosInView.y) { performPixelCopy(startX, startY); mPrevPosInView.x = xPosInView; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 1e17f34af2a6..1618d620738f 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -44,6 +44,7 @@ import android.content.UndoManager; import android.content.res.ColorStateList; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; +import android.content.res.ResourceId; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; @@ -785,9 +786,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // mAutoSizeStepGranularityInPx. private boolean mHasPresetAutoSizeValues = false; - // Indicates whether the text was set from resources or dynamically, so it can be used to + // Indicates whether the text was set statically or dynamically, so it can be used to // sanitize autofill requests. - private boolean mTextFromResource = false; + private boolean mSetFromXmlOrResourceId = false; + // Resource id used to set the text - used for autofill purposes. + private @StringRes int mTextId = ResourceId.ID_NULL; /** * Kick-start the font cache for the zygote process (to pay the cost of @@ -926,7 +929,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int n = a.getIndexCount(); - boolean fromResourceId = false; + // Must set id in a temporary variable because it will be reset by setText() + boolean setFromXml = false; for (int i = 0; i < n; i++) { int attr = a.getIndex(i); @@ -1068,7 +1072,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener break; case com.android.internal.R.styleable.TextView_text: - fromResourceId = true; + setFromXml = true; + mTextId = a.getResourceId(attr, ResourceId.ID_NULL); text = a.getText(attr); break; @@ -1460,8 +1465,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } setText(text, bufferType); - if (fromResourceId) { - mTextFromResource = true; + if (setFromXml) { + mSetFromXmlOrResourceId = true; } if (hint != null) setHint(hint); @@ -5278,7 +5283,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private void setText(CharSequence text, BufferType type, boolean notifyBefore, int oldlen) { - mTextFromResource = false; + mSetFromXmlOrResourceId = false; if (text == null) { text = ""; } @@ -5516,7 +5521,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @android.view.RemotableViewMethod public final void setText(@StringRes int resid) { setText(getContext().getResources().getText(resid)); - mTextFromResource = true; + mSetFromXmlOrResourceId = true; + mTextId = resid; } /** @@ -5543,7 +5549,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ public final void setText(@StringRes int resid, BufferType type) { setText(getContext().getResources().getText(resid), type); - mTextFromResource = true; + mSetFromXmlOrResourceId = true; + mTextId = resid; } /** @@ -10234,7 +10241,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final boolean isPassword = hasPasswordTransformationMethod() || isPasswordInputType(getInputType()); if (forAutofill) { - structure.setDataIsSensitive(!mTextFromResource); + structure.setDataIsSensitive(!mSetFromXmlOrResourceId); + if (mTextId != ResourceId.ID_NULL) { + structure.setTextIdEntry(getResources().getResourceEntryName(mTextId)); + } } if (!isPassword || forAutofill) { diff --git a/core/java/com/android/internal/net/INetworkWatchlistManager.aidl b/core/java/com/android/internal/net/INetworkWatchlistManager.aidl index 7e88369055b8..ee01a23af686 100644 --- a/core/java/com/android/internal/net/INetworkWatchlistManager.aidl +++ b/core/java/com/android/internal/net/INetworkWatchlistManager.aidl @@ -22,5 +22,6 @@ import android.os.SharedMemory; interface INetworkWatchlistManager { boolean startWatchlistLogging(); boolean stopWatchlistLogging(); + void reloadWatchlist(); void reportWatchlistIfNecessary(); } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 72f07b7c8f20..1739bed7112f 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -506,8 +506,8 @@ public class BatteryStatsImpl extends BatteryStats { final HistoryEventTracker mActiveEvents = new HistoryEventTracker(); long mHistoryBaseTime; - boolean mHaveBatteryLevel = false; - boolean mRecordingHistory = false; + protected boolean mHaveBatteryLevel = false; + protected boolean mRecordingHistory = false; int mNumHistoryItems; final Parcel mHistoryBuffer = Parcel.obtain(); @@ -652,6 +652,14 @@ public class BatteryStatsImpl extends BatteryStats { new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES]; /** + * The WiFi Overall wakelock timer + * This timer tracks the actual aggregate time for which MC wakelocks are enabled + * since addition of per UID timers would not result in an accurate value due to overlapp of + * per uid wakelock timers + */ + StopwatchTimer mWifiMulticastWakelockTimer; + + /** * The WiFi controller activity (time in tx, rx, idle, and power consumed) for the device. */ ControllerActivityCounterImpl mWifiActivity; @@ -3975,30 +3983,85 @@ public class BatteryStatsImpl extends BatteryStats { addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_JOB_FINISH, name, uid); } - public void noteAlarmStartLocked(String name, int uid) { + public void noteAlarmStartLocked(String name, WorkSource workSource, int uid) { + noteAlarmStartOrFinishLocked(HistoryItem.EVENT_ALARM_START, name, workSource, uid); + } + + public void noteAlarmFinishLocked(String name, WorkSource workSource, int uid) { + noteAlarmStartOrFinishLocked(HistoryItem.EVENT_ALARM_FINISH, name, workSource, uid); + } + + private void noteAlarmStartOrFinishLocked(int historyItem, String name, WorkSource workSource, + int uid) { if (!mRecordAllHistory) { return; } - uid = mapUid(uid); + final long elapsedRealtime = mClocks.elapsedRealtime(); final long uptime = mClocks.uptimeMillis(); - if (!mActiveEvents.updateState(HistoryItem.EVENT_ALARM_START, name, uid, 0)) { - return; + + if (workSource != null) { + for (int i = 0; i < workSource.size(); ++i) { + uid = mapUid(workSource.get(i)); + if (mActiveEvents.updateState(historyItem, name, uid, 0)) { + addHistoryEventLocked(elapsedRealtime, uptime, historyItem, name, uid); + } + } + + List<WorkChain> workChains = workSource.getWorkChains(); + if (workChains != null) { + for (int i = 0; i < workChains.size(); ++i) { + uid = mapUid(workChains.get(i).getAttributionUid()); + if (mActiveEvents.updateState(historyItem, name, uid, 0)) { + addHistoryEventLocked(elapsedRealtime, uptime, historyItem, name, uid); + } + } + } + } else { + uid = mapUid(uid); + + if (mActiveEvents.updateState(historyItem, name, uid, 0)) { + addHistoryEventLocked(elapsedRealtime, uptime, historyItem, name, uid); + } } - addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_ALARM_START, name, uid); } - public void noteAlarmFinishLocked(String name, int uid) { - if (!mRecordAllHistory) { + public void noteWakupAlarmLocked(String packageName, int uid, WorkSource workSource, + String tag) { + if (!isOnBattery()) { return; } - uid = mapUid(uid); - final long elapsedRealtime = mClocks.elapsedRealtime(); - final long uptime = mClocks.uptimeMillis(); - if (!mActiveEvents.updateState(HistoryItem.EVENT_ALARM_FINISH, name, uid, 0)) { - return; + + if (workSource != null) { + for (int i = 0; i < workSource.size(); ++i) { + uid = workSource.get(i); + final String workSourceName = workSource.getName(i); + + BatteryStatsImpl.Uid.Pkg pkg = getPackageStatsLocked(uid, + workSourceName != null ? workSourceName : packageName); + pkg.noteWakeupAlarmLocked(tag); + + StatsLog.write(StatsLog.WAKEUP_ALARM_OCCURRED, uid, tag); + } + + ArrayList<WorkChain> workChains = workSource.getWorkChains(); + if (workChains != null) { + for (int i = 0; i < workChains.size(); ++i) { + final WorkChain wc = workChains.get(i); + uid = wc.getAttributionUid(); + + BatteryStatsImpl.Uid.Pkg pkg = getPackageStatsLocked(uid, packageName); + pkg.noteWakeupAlarmLocked(tag); + + // TODO(statsd): Log the full attribution chain here once it's available + StatsLog.write(StatsLog.WAKEUP_ALARM_OCCURRED, uid, tag); + } + } + } else { + BatteryStatsImpl.Uid.Pkg pkg = getPackageStatsLocked(uid, packageName); + pkg.noteWakeupAlarmLocked(tag); + StatsLog.write(StatsLog.WAKEUP_ALARM_OCCURRED, uid, tag); } - addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_ALARM_FINISH, name, uid); } private void requestWakelockCpuUpdate() { @@ -5534,6 +5597,12 @@ public class BatteryStatsImpl extends BatteryStats { if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast on to: " + Integer.toHexString(mHistoryCur.states)); addHistoryRecordLocked(elapsedRealtime, uptime); + + // Start Wifi Multicast overall timer + if (!mWifiMulticastWakelockTimer.isRunningLocked()) { + if (DEBUG_HISTORY) Slog.v(TAG, "WiFi Multicast Overall Timer Started"); + mWifiMulticastWakelockTimer.startRunningLocked(elapsedRealtime); + } } mWifiMulticastNesting++; getUidStatsLocked(uid).noteWifiMulticastEnabledLocked(elapsedRealtime); @@ -5549,6 +5618,12 @@ public class BatteryStatsImpl extends BatteryStats { if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast off to: " + Integer.toHexString(mHistoryCur.states)); addHistoryRecordLocked(elapsedRealtime, uptime); + + // Stop Wifi Multicast overall timer + if (mWifiMulticastWakelockTimer.isRunningLocked()) { + if (DEBUG_HISTORY) Slog.v(TAG, "Multicast Overall Timer Stopped"); + mWifiMulticastWakelockTimer.stopRunningLocked(elapsedRealtime); + } } getUidStatsLocked(uid).noteWifiMulticastDisabledLocked(elapsedRealtime); } @@ -5835,6 +5910,16 @@ public class BatteryStatsImpl extends BatteryStats { return (int)mMobileRadioActiveUnknownCount.getCountLocked(which); } + @Override public long getWifiMulticastWakelockTime( + long elapsedRealtimeUs, int which) { + return mWifiMulticastWakelockTimer.getTotalTimeLocked( + elapsedRealtimeUs, which); + } + + @Override public int getWifiMulticastWakelockCount(int which) { + return mWifiMulticastWakelockTimer.getCountLocked(which); + } + @Override public long getWifiOnTime(long elapsedRealtimeUs, int which) { return mWifiOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which); } @@ -9435,6 +9520,8 @@ public class BatteryStatsImpl extends BatteryStats { mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase); mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase); mMobileRadioActiveUnknownCount = new LongSamplingCounter(mOnBatteryTimeBase); + mWifiMulticastWakelockTimer = new StopwatchTimer(mClocks, null, + WIFI_AGGREGATE_MULTICAST_ENABLED, null, mOnBatteryTimeBase); mWifiOnTimer = new StopwatchTimer(mClocks, null, -4, null, mOnBatteryTimeBase); mGlobalWifiRunningTimer = new StopwatchTimer(mClocks, null, -5, null, mOnBatteryTimeBase); for (int i=0; i<NUM_WIFI_STATES; i++) { @@ -10136,6 +10223,7 @@ public class BatteryStatsImpl extends BatteryStats { for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) { mWifiSignalStrengthsTimer[i].reset(false); } + mWifiMulticastWakelockTimer.reset(false); mWifiActivity.reset(false); mBluetoothActivity.reset(false); mModemActivity.reset(false); @@ -12582,6 +12670,7 @@ public class BatteryStatsImpl extends BatteryStats { mMobileRadioActiveAdjustedTime.readSummaryFromParcelLocked(in); mMobileRadioActiveUnknownTime.readSummaryFromParcelLocked(in); mMobileRadioActiveUnknownCount.readSummaryFromParcelLocked(in); + mWifiMulticastWakelockTimer.readSummaryFromParcelLocked(in); mWifiRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW; mWifiOn = false; mWifiOnTimer.readSummaryFromParcelLocked(in); @@ -13022,6 +13111,7 @@ public class BatteryStatsImpl extends BatteryStats { mMobileRadioActiveAdjustedTime.writeSummaryFromParcelLocked(out); mMobileRadioActiveUnknownTime.writeSummaryFromParcelLocked(out); mMobileRadioActiveUnknownCount.writeSummaryFromParcelLocked(out); + mWifiMulticastWakelockTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); mWifiOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); mGlobalWifiRunningTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); for (int i=0; i<NUM_WIFI_STATES; i++) { @@ -13485,6 +13575,8 @@ public class BatteryStatsImpl extends BatteryStats { mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase, in); mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase, in); mMobileRadioActiveUnknownCount = new LongSamplingCounter(mOnBatteryTimeBase, in); + mWifiMulticastWakelockTimer = new StopwatchTimer(mClocks, null, -4, null, + mOnBatteryTimeBase, in); mWifiRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW; mWifiOn = false; mWifiOnTimer = new StopwatchTimer(mClocks, null, -4, null, mOnBatteryTimeBase, in); @@ -13691,6 +13783,7 @@ public class BatteryStatsImpl extends BatteryStats { mMobileRadioActiveAdjustedTime.writeToParcel(out); mMobileRadioActiveUnknownTime.writeToParcel(out); mMobileRadioActiveUnknownCount.writeToParcel(out); + mWifiMulticastWakelockTimer.writeToParcel(out, uSecRealtime); mWifiOnTimer.writeToParcel(out, uSecRealtime); mGlobalWifiRunningTimer.writeToParcel(out, uSecRealtime); for (int i=0; i<NUM_WIFI_STATES; i++) { @@ -13877,6 +13970,8 @@ public class BatteryStatsImpl extends BatteryStats { mMobileRadioActiveTimer.logState(pr, " "); pr.println("*** Mobile network active adjusted timer:"); mMobileRadioActiveAdjustedTime.logState(pr, " "); + pr.println("*** Wifi Multicast WakeLock Timer:"); + mWifiMulticastWakelockTimer.logState(pr, " "); pr.println("*** mWifiRadioPowerState=" + mWifiRadioPowerState); pr.println("*** Wifi timer:"); mWifiOnTimer.logState(pr, " "); diff --git a/core/java/com/android/internal/widget/ButtonBarLayout.java b/core/java/com/android/internal/widget/ButtonBarLayout.java index 82affe2f86b1..ab8be33599fa 100644 --- a/core/java/com/android/internal/widget/ButtonBarLayout.java +++ b/core/java/com/android/internal/widget/ButtonBarLayout.java @@ -30,9 +30,6 @@ import com.android.internal.R; * orientation when it can't fit its child views horizontally. */ public class ButtonBarLayout extends LinearLayout { - /** Minimum screen height required for button stacking. */ - private static final int ALLOW_STACKING_MIN_HEIGHT_DP = 320; - /** Amount of the second button to "peek" above the fold when stacked. */ private static final int PEEK_BUTTON_DP = 16; @@ -46,12 +43,8 @@ public class ButtonBarLayout extends LinearLayout { public ButtonBarLayout(Context context, AttributeSet attrs) { super(context, attrs); - final boolean allowStackingDefault = - context.getResources().getConfiguration().screenHeightDp - >= ALLOW_STACKING_MIN_HEIGHT_DP; final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ButtonBarLayout); - mAllowStacking = ta.getBoolean(R.styleable.ButtonBarLayout_allowStacking, - allowStackingDefault); + mAllowStacking = ta.getBoolean(R.styleable.ButtonBarLayout_allowStacking, true); ta.recycle(); } diff --git a/core/proto/android/graphics/pixelformat.proto b/core/proto/android/graphics/pixelformat.proto new file mode 100644 index 000000000000..4e42c926a95f --- /dev/null +++ b/core/proto/android/graphics/pixelformat.proto @@ -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. + */ + +syntax = "proto2"; +package android.graphics; +option java_multiple_files = true; + +message PixelFormatProto { + enum Format { + UNKNOWN = 0; + TRANSLUCENT = -3; + TRANSPARENT = -2; + OPAQUE = -1; + RGBA_8888 = 1; + RGBX_8888 = 2; + RGB_888 = 3; + RGB_565 = 4; + RGBA_F16 = 0x16; + RGBA_1010102 = 0x2B; + } +} diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto index 0fa428e9c114..9999b4e26a68 100644 --- a/core/proto/android/os/batterystats.proto +++ b/core/proto/android/os/batterystats.proto @@ -537,7 +537,24 @@ message UidProto { // Screen-off CPU time in milliseconds. optional int64 screen_off_duration_ms = 3; } + // CPU times accumulated across all process states. repeated ByFrequency by_frequency = 3; + + enum ProcessState { + TOP = 0; + FOREGROUND_SERVICE = 1; + FOREGROUND = 2; + BACKGROUND = 3; + TOP_SLEEPING = 4; + HEAVY_WEIGHT = 5; + CACHED = 6; + } + // CPU times at different process states. + message ByProcessState { + optional ProcessState process_state = 1; + repeated ByFrequency by_frequency = 2; + } + repeated ByProcessState by_process_state = 4; } optional Cpu cpu = 7; @@ -655,7 +672,7 @@ message UidProto { // In approximate order or priority (top being what the framework considers // most important and is thus least likely to kill when resources are // needed: - // top > foreground service > top sleeping > foreground > background > cache + // top > foreground service > foreground > background > top sleeping > heavy weight > cache enum State { // Time this uid has any processes in the top state (or above such as // persistent). @@ -663,20 +680,26 @@ message UidProto { // Time this uid has any process with a started out bound foreground // service, but none in the "top" state. PROCESS_STATE_FOREGROUND_SERVICE = 1; - // Time this uid has any process that is top while the device is sleeping, - // but none in the "foreground service" or better state. Sleeping is - // mostly screen off, but also includes the time when the screen is on but - // the device has not yet been unlocked. - PROCESS_STATE_TOP_SLEEPING = 2; // Time this uid has any process in an active foreground state, but none // in the "top sleeping" or better state. - PROCESS_STATE_FOREGROUND = 3; + PROCESS_STATE_FOREGROUND = 2; // Time this uid has any process in an active background state, but none // in the "foreground" or better state. - PROCESS_STATE_BACKGROUND = 4; + PROCESS_STATE_BACKGROUND = 3; + // Time this uid has any process that is top while the device is sleeping, + // but not active for any other reason. We consider is a kind of cached + // process for execution restrictions. Sleeping is mostly screen off, but + // also includes the time when the screen is on but the device has not yet + // been unlocked. + PROCESS_STATE_TOP_SLEEPING = 4; + // Time this uid has any process that is in the background but it has an + // activity marked as "can't save state". This is essentially a cached + // process, though the system will try much harder than normal to avoid + // killing it. + PROCESS_STATE_HEAVY_WEIGHT = 5; // Time this uid has any processes that are sitting around cached, not in // one of the other active states. - PROCESS_STATE_CACHED = 5; + PROCESS_STATE_CACHED = 6; } optional State state = 1; optional int64 duration_ms = 2; diff --git a/core/proto/android/os/batterytype.proto b/core/proto/android/os/batterytype.proto new file mode 100644 index 000000000000..75d0dd3504e3 --- /dev/null +++ b/core/proto/android/os/batterytype.proto @@ -0,0 +1,25 @@ +/* + * 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. + */ + +syntax = "proto2"; + +package android.os; + +option java_multiple_files = true; + +message BatteryTypeProto { + optional string type = 1; +} diff --git a/core/proto/android/os/cpuinfo.proto b/core/proto/android/os/cpuinfo.proto index 522ff24c1e60..cd151e253e7a 100644 --- a/core/proto/android/os/cpuinfo.proto +++ b/core/proto/android/os/cpuinfo.proto @@ -81,7 +81,9 @@ message CpuInfo { optional string virt = 8; // virtual memory size, i.e. 14.0G, 13.5M optional string res = 9; // Resident size, i.e. 0, 3.1G - // How Android memory manager will treat the task + // How Android memory manager will treat the task. + // TODO: use PsDumpProto.Process.Policy instead once we extern variables + // and are able to include the same .h file in two files. enum Policy { POLICY_UNKNOWN = 0; POLICY_fg = 1; // foreground, the name is lower case for parsing the value diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index ac8f26d94ca2..a6db31f67b33 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -18,12 +18,14 @@ syntax = "proto2"; option java_multiple_files = true; option java_outer_classname = "IncidentProtoMetadata"; +import "frameworks/base/core/proto/android/os/batterytype.proto"; import "frameworks/base/core/proto/android/os/cpufreq.proto"; import "frameworks/base/core/proto/android/os/cpuinfo.proto"; import "frameworks/base/core/proto/android/os/incidentheader.proto"; import "frameworks/base/core/proto/android/os/kernelwake.proto"; import "frameworks/base/core/proto/android/os/pagetypeinfo.proto"; import "frameworks/base/core/proto/android/os/procrank.proto"; +import "frameworks/base/core/proto/android/os/ps.proto"; import "frameworks/base/core/proto/android/os/system_properties.proto"; import "frameworks/base/core/proto/android/providers/settings.proto"; import "frameworks/base/core/proto/android/server/activitymanagerservice.proto"; @@ -65,7 +67,7 @@ message IncidentProto { // Device information optional SystemPropertiesProto system_properties = 1000 [ (section).type = SECTION_COMMAND, - (section).args = "/system/bin/getprop" + (section).args = "getprop" ]; // Linux services @@ -86,7 +88,7 @@ message IncidentProto { optional CpuInfo cpu_info = 2003 [ (section).type = SECTION_COMMAND, - (section).args = "/system/bin/top -b -n 1 -H -s 6 -o pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name" + (section).args = "top -b -n 1 -H -s 6 -o pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name" ]; optional CpuFreq cpu_freq = 2004 [ @@ -94,6 +96,16 @@ message IncidentProto { (section).args = "/sys/devices/system/cpu/cpufreq/all_time_in_state" ]; + optional PsDumpProto processes_and_threads = 2005 [ + (section).type = SECTION_COMMAND, + (section).args = "ps -A -T -Z -O pri,nice,rtprio,sched,pcy,time" + ]; + + optional BatteryTypeProto battery_type = 2006 [ + (section).type = SECTION_FILE, + (section).args = "/sys/class/power_supply/bms/battery_type" + ]; + // System Services optional com.android.server.fingerprint.FingerprintServiceDumpProto fingerprint = 3000 [ (section).type = SECTION_DUMPSYS, diff --git a/core/proto/android/os/ps.proto b/core/proto/android/os/ps.proto new file mode 100644 index 000000000000..88c6609a92b6 --- /dev/null +++ b/core/proto/android/os/ps.proto @@ -0,0 +1,110 @@ +/* + * 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. + */ + +syntax = "proto2"; + +package android.os; + +option java_multiple_files = true; + +message PsDumpProto { + message Process { + // Security label, most commonly used for SELinux context data. + optional string label = 1; + optional string user = 2; + // Process ID number. + optional int32 pid = 3; + // The unique number representing a dispatchable entity (alias lwp, + // spid). This value may also appear as: a process ID (pid); a process + // group ID (pgrp); a session ID for the session leader (sid); a thread + // group ID for the thread group leader (tgid); and a tty process group + // ID for the process group leader (tpgid). + optional int32 tid = 4; + // Parent process ID. + optional int32 ppid = 5; + // Virtual set size (memory size) of the process, in KiB. + optional int32 vsz = 6; + // Resident set size. How many physical pages are associated with the + // process; real memory usage, in KiB. + optional int32 rss = 7; + // Name of the kernel function in which the process is sleeping, a "-" + // if the process is running, or a "*" if the process is multi-threaded + // and ps is not displaying threads. + optional string wchan = 8; + // Memory address of the process. + optional string addr = 9; + + enum ProcessStateCode { + STATE_UNKNOWN = 0; + // Uninterruptible sleep (usually IO). + STATE_D = 1; + // Running or runnable (on run queue). + STATE_R = 2; + // Interruptible sleep (waiting for an event to complete). + STATE_S = 3; + // Stopped by job control signal. + STATE_T = 4; + // Stopped by debugger during the tracing. + STATE_TRACING = 5; + // Dead (should never be seen). + STATE_X = 6; + // Defunct ("zombie") process. Terminated but not reaped by its + // parent. + STATE_Z = 7; + } + // Minimal state display + optional ProcessStateCode s = 10; + // Priority of the process. Higher number means lower priority. + optional int32 pri = 11; + // Nice value. This ranges from 19 (nicest) to -20 (not nice to others). + optional sint32 ni = 12; + // Realtime priority. + optional string rtprio = 13; // Number or - + + enum SchedulingPolicy { + option allow_alias = true; + + // Regular names conflict with macros defined in + // bionic/libc/kernel/uapi/linux/sched.h. + SCH_OTHER = 0; + SCH_NORMAL = 0; + + SCH_FIFO = 1; + SCH_RR = 2; + SCH_BATCH = 3; + SCH_ISO = 4; + SCH_IDLE = 5; + } + // Scheduling policy of the process. + optional SchedulingPolicy sch = 14; + + // How Android memory manager will treat the task + enum Policy { + POLICY_UNKNOWN = 0; + // Foreground. + POLICY_FG = 1; + // Background. + POLICY_BG = 2; + POLICY_TA = 3; // TODO: figure out what this value is + } + optional Policy pcy = 15; + // Total CPU time, "[DD-]HH:MM:SS" format. + optional string time = 16; + // Command with all its arguments as a string. + optional string cmd = 17; + } + repeated Process processes = 1; +} diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index ef7b6b0019f3..12aca787745e 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -18,6 +18,7 @@ syntax = "proto2"; import "frameworks/base/core/proto/android/content/configuration.proto"; import "frameworks/base/core/proto/android/graphics/rect.proto"; +import "frameworks/base/core/proto/android/view/displaycutout.proto"; import "frameworks/base/core/proto/android/view/displayinfo.proto"; import "frameworks/base/core/proto/android/view/windowlayoutparams.proto"; @@ -172,6 +173,27 @@ message WindowStateProto { repeated WindowStateProto child_windows = 15; optional .android.graphics.RectProto surface_position = 16; optional .android.graphics.RectProto shown_position = 17; + optional int32 requested_width = 18; + optional int32 requested_height = 19; + optional int32 view_visibility = 20; + optional int32 system_ui_visibility = 21; + optional bool has_surface = 22; + optional bool is_ready_for_display = 23; + optional .android.graphics.RectProto display_frame = 24; + optional .android.graphics.RectProto overscan_frame = 25; + optional .android.graphics.RectProto visible_frame = 26; + optional .android.graphics.RectProto decor_frame = 27; + optional .android.graphics.RectProto outset_frame = 28; + optional .android.graphics.RectProto overscan_insets = 29; + optional .android.graphics.RectProto visible_insets = 30; + optional .android.graphics.RectProto stable_insets = 31; + optional .android.graphics.RectProto outsets = 32; + optional .android.view.DisplayCutoutProto cutout = 33; + optional bool remove_on_exit = 34; + optional bool destroying = 35; + optional bool removed = 36; + optional bool is_on_screen = 37; + optional bool is_visible = 38; } message IdentifierProto { @@ -184,6 +206,15 @@ message IdentifierProto { message WindowStateAnimatorProto { optional .android.graphics.RectProto last_clip_rect = 1; optional WindowSurfaceControllerProto surface = 2; + enum DrawState { + NO_SURFACE = 0; + DRAW_PENDING = 1; + COMMIT_DRAW_PENDING = 2; + READY_TO_SHOW = 3; + HAS_DRAWN = 4; + } + optional DrawState draw_state = 3; + optional .android.graphics.RectProto system_decor_rect = 4; } /* represents WindowSurfaceController */ diff --git a/core/proto/android/view/display.proto b/core/proto/android/view/display.proto index 210c6d103faa..cac083075ec3 100644 --- a/core/proto/android/view/display.proto +++ b/core/proto/android/view/display.proto @@ -38,4 +38,16 @@ message DisplayProto { // The display is on and optimized for VR mode. DISPLAY_STATE_VR = 5; } + enum ColorMode { + COLOR_MODE_INVALID = -1; + COLOR_MODE_BT601_625 = 1; + COLOR_MODE_BT601_625_UNADJUSTED = 2; + COLOR_MODE_BT601_525 = 3; + COLOR_MODE_BT601_525_UNADJUSTED = 4; + COLOR_MODE_BT709 = 5; + COLOR_MODE_DCI_P3 = 6; + COLOR_MODE_SRGB = 7; + COLOR_MODE_ADOBE_RGB = 8; + COLOR_MODE_DISPLAY_P3 = 9; + } } diff --git a/core/proto/android/view/displaycutout.proto b/core/proto/android/view/displaycutout.proto new file mode 100644 index 000000000000..ff13fab35bf2 --- /dev/null +++ b/core/proto/android/view/displaycutout.proto @@ -0,0 +1,27 @@ +/* + * 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. + */ + +syntax = "proto2"; + +import "frameworks/base/core/proto/android/graphics/rect.proto"; + +package android.view; +option java_multiple_files = true; + +message DisplayCutoutProto { + optional .android.graphics.RectProto insets = 1; + optional .android.graphics.RectProto bounds = 2; +} diff --git a/core/proto/android/view/windowlayoutparams.proto b/core/proto/android/view/windowlayoutparams.proto index 78212125c16d..b81cd1f50f91 100644 --- a/core/proto/android/view/windowlayoutparams.proto +++ b/core/proto/android/view/windowlayoutparams.proto @@ -15,11 +15,51 @@ */ syntax = "proto2"; -package android.view; +import "frameworks/base/core/proto/android/graphics/pixelformat.proto"; +import "frameworks/base/core/proto/android/view/display.proto"; + +package android.view; option java_multiple_files = true; /* represents WindowManager.LayoutParams */ message WindowLayoutParamsProto { optional int32 type = 1; + optional int32 x = 2; + optional int32 y = 3; + optional int32 width = 4; + optional int32 height = 5; + optional float horizontal_margin = 6; + optional float vertical_margin = 7; + optional int32 gravity = 8; + optional int32 soft_input_mode = 9; + optional .android.graphics.PixelFormatProto.Format format = 10; + optional int32 window_animations = 11; + optional float alpha = 12; + optional float screen_brightness = 13; + optional float button_brightness = 14; + enum RotationAnimation { + ROTATION_ANIMATION_UNSPECIFIED = -1; + ROTATION_ANIMATION_CROSSFADE = 1; + ROTATION_ANIMATION_JUMPCUT = 2; + ROTATION_ANIMATION_SEAMLESS = 3; + } + optional RotationAnimation rotation_animation = 15; + optional float preferred_refresh_rate = 16; + optional int32 preferred_display_mode_id = 17; + optional bool has_system_ui_listeners = 18; + optional uint32 input_feature_flags = 19; + optional int64 user_activity_timeout = 20; + enum NeedsMenuState { + NEEDS_MENU_UNSET = 0; + NEEDS_MENU_SET_TRUE = 1; + NEEDS_MENU_SET_FALSE = 2; + } + optional NeedsMenuState needs_menu_key = 22; + optional .android.view.DisplayProto.ColorMode color_mode = 23; + optional uint32 flags = 24; + optional uint64 flags_extra = 25; + optional uint32 private_flags = 26; + optional uint32 system_ui_visibility_flags = 27; + optional uint32 subtree_system_ui_visibility_flags = 28; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 272e3c73bf89..52bfcdebf436 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3870,6 +3870,14 @@ </intent-filter> </receiver> + <receiver android:name="com.android.server.updates.NetworkWatchlistInstallReceiver" + android:permission="android.permission.UPDATE_CONFIG"> + <intent-filter> + <action android:name="android.intent.action.UPDATE_NETWORK_WATCHLIST" /> + <data android:scheme="content" android:host="*" android:mimeType="*/*" /> + </intent-filter> + </receiver> + <receiver android:name="com.android.server.updates.ApnDbInstallReceiver" android:permission="android.permission.UPDATE_CONFIG"> <intent-filter> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 64febf1f7ee4..1b3d6ce36247 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -8699,8 +8699,11 @@ common values are 400 for regular weight and 700 for bold weight. If unspecified, the value in the font's header tables will be used. --> <attr name="fontWeight" format="integer" /> - <!-- The index of the font in the tcc font file. If the font file referenced is not in the - tcc format, this attribute needs not be specified. --> + <!-- The index of the font in the ttc (TrueType Collection) font file. If the font file + referenced is not in the ttc format, this attribute needs not be specified. + {@see android.graphics.Typeface#Builder.setTtcIndex(int)}. + The default value is 0. More details about the TrueType Collection font format can be found + here: https://en.wikipedia.org/wiki/TrueType#TrueType_Collection. --> <attr name="ttcIndex" format="integer" /> <!-- The variation settings to be applied to the font. The string should be in the following format: "'tag1' value1, 'tag2' value2, ...". If the default variation settings should be diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml index e2498df71e45..70485111d208 100644 --- a/core/res/res/values/colors_device_defaults.xml +++ b/core/res/res/values/colors_device_defaults.xml @@ -31,10 +31,8 @@ <color name="tertiary_device_default_settings">@color/tertiary_material_settings</color> <color name="quaternary_device_default_settings">@color/quaternary_material_settings</color> - <color name="accent_device_default_700">@color/accent_material_700</color> <color name="accent_device_default_light">@color/accent_material_light</color> <color name="accent_device_default_dark">@color/accent_material_dark</color> - <color name="accent_device_default_50">@color/accent_material_50</color> <color name="background_device_default_dark">@color/background_material_dark</color> <color name="background_device_default_light">@color/background_material_light</color> diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml index 8c37d4b053a1..04131009b141 100644 --- a/core/res/res/values/colors_material.xml +++ b/core/res/res/values/colors_material.xml @@ -39,10 +39,8 @@ <color name="tertiary_material_settings">@color/material_blue_grey_700</color> <color name="quaternary_material_settings">@color/material_blue_grey_200</color> - <color name="accent_material_700">@color/material_deep_teal_700</color> <color name="accent_material_light">@color/material_deep_teal_500</color> <color name="accent_material_dark">@color/material_deep_teal_200</color> - <color name="accent_material_50">@color/material_deep_teal_50</color> <color name="button_material_dark">#ff5a595b</color> <color name="button_material_light">#ffd6d7d7</color> @@ -95,12 +93,10 @@ <color name="material_grey_100">#fff5f5f5</color> <color name="material_grey_50">#fffafafa</color> - <color name="material_deep_teal_50">#ffe0f2f1</color> <color name="material_deep_teal_100">#ffb2dfdb</color> <color name="material_deep_teal_200">#ff80cbc4</color> <color name="material_deep_teal_300">#ff4db6ac</color> <color name="material_deep_teal_500">#ff009688</color> - <color name="material_deep_teal_700">#ff00796b</color> <color name="material_blue_grey_200">#ffb0bec5</color> <color name="material_blue_grey_700">#ff455a64</color> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index dc791cf68b6b..fd05bb442247 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1302,8 +1302,8 @@ If this is defined then: - config_autoBrightnessLcdBacklightValues should not be defined - - config_screenBrightnessMinimumNits must be defined - - config_screenBrightnessMaximumNits must be defined + - config_screenBrightnessNits must be defined + - config_screenBrightnessBacklight must be defined This array should have size one greater than the size of the config_autoBrightnessLevels array. The brightness values must be non-negative and non-decreasing. This must be @@ -1347,28 +1347,23 @@ <item>200</item> </integer-array> - <!-- The minimum brightness of the display in nits. On OLED displays this should be measured - with an all white image while the display is fully on and the backlight is set to - config_screenBrightnessSettingMinimum or config_screenBrightnessSettingDark, whichever - is darker. - - If this and config_screenBrightnessMinimumNits are set, then the display's brightness - range is assumed to be linear between - (config_screenBrightnessSettingMinimum, config_screenBrightnessMinimumNits) and - (config_screenBrightnessSettingMaximum, config_screenBrightnessMaximumNits). --> - <item name="config_screenBrightnessMinimumNits" format="float" type="dimen">-1.0</item> - - <!-- The maximum brightness of the display in nits. On OLED displays this should be measured - with an all white image while the display is fully on and the "backlight" is set to - config_screenBrightnessSettingMaximum. Note that this value should *not* reflect the - maximum brightness value for any high brightness modes but only the maximum brightness - value obtainable in a sustainable manner. - - If this and config_screenBrightnessMinimumNits are set to something non-negative, then the - display's brightness range is assumed to be linear between - (config_screenBrightnessSettingMinimum, config_screenBrightnessMaximumNits) and - (config_screenBrightnessSettingMaximum, config_screenBrightnessMaximumNits). --> - <item name="config_screenBrightnessMaximumNits" format="float" type="dimen">-1.0</item> + <!-- An array describing the screen's backlight values corresponding to the brightness + values in the config_screenBrightnessNits array. + + This array should be equal in size to config_screenBrightnessBacklight. --> + <integer-array name="config_screenBrightnessBacklight"> + </integer-array> + + <!-- An array of floats describing the screen brightness in nits corresponding to the backlight + values in the config_screenBrightnessBacklight array. On OLED displays these values + should be measured with an all white image while the display is in the fully on state. + Note that this value should *not* reflect the maximum brightness value for any high + brightness modes but only the maximum brightness value obtainable in a sustainable manner. + + This array should be equal in size to config_screenBrightnessBacklight --> + <array name="config_screenBrightnessNits"> + </array> + <!-- Array of ambient lux threshold values. This is used for determining hysteresis constraint values by calculating the index to use for lookup and then setting the constraint value diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 5783435e13bd..c54f79983f02 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -20,13 +20,23 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- Suffix added to a number to signify size in bytes. --> <string name="byteShort">B</string> + <!-- Suffix added to a number to signify size in kilobytes (1000 bytes). + If you retain the Latin script for the localization, please use the lowercase + 'k', as it signifies 1000 bytes as opposed to 1024 bytes. --> + <string name="kilobyteShort">kB</string> + <!-- Suffix added to a number to signify size in megabytes. --> + <string name="megabyteShort">MB</string> + <!-- Suffix added to a number to signify size in gigabytes. --> + <string name="gigabyteShort">GB</string> + <!-- Suffix added to a number to signify size in terabytes. --> + <string name="terabyteShort">TB</string> <!-- Suffix added to a number to signify size in petabytes. --> <string name="petabyteShort">PB</string> - <!-- Format string used to add a suffix like "B" or "PB" to a number - to display a size in bytes or petabytes. - Some languages may want to remove the space between the placeholders - or replace it with a non-breaking space. --> - <string name="fileSizeSuffix"><xliff:g id="number" example="123">%1$s</xliff:g> <xliff:g id="unit" example="B">%2$s</xliff:g></string> + <!-- Format string used to add a suffix like "kB" or "MB" to a number + to display a size in kilobytes, megabytes, or other size units. + Some languages (like French) will want to add a space between + the placeholders. --> + <string name="fileSizeSuffix"><xliff:g id="number" example="123">%1$s</xliff:g> <xliff:g id="unit" example="MB">%2$s</xliff:g></string> <!-- Used in Contacts for a field that has no label and in Note Pad for a note with no name. --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 8e391d3c0122..4343ba01702b 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -705,6 +705,7 @@ <java-symbol type="string" name="fileSizeSuffix" /> <java-symbol type="string" name="force_close" /> <java-symbol type="string" name="gadget_host_error_inflating" /> + <java-symbol type="string" name="gigabyteShort" /> <java-symbol type="string" name="gpsNotifMessage" /> <java-symbol type="string" name="gpsNotifTicker" /> <java-symbol type="string" name="gpsNotifTitle" /> @@ -760,6 +761,7 @@ <java-symbol type="string" name="keyboardview_keycode_enter" /> <java-symbol type="string" name="keyboardview_keycode_mode_change" /> <java-symbol type="string" name="keyboardview_keycode_shift" /> + <java-symbol type="string" name="kilobyteShort" /> <java-symbol type="string" name="last_month" /> <java-symbol type="string" name="launchBrowserDefault" /> <java-symbol type="string" name="lock_to_app_toast" /> @@ -779,6 +781,7 @@ <java-symbol type="string" name="lockscreen_emergency_call" /> <java-symbol type="string" name="lockscreen_return_to_call" /> <java-symbol type="string" name="low_memory" /> + <java-symbol type="string" name="megabyteShort" /> <java-symbol type="string" name="midnight" /> <java-symbol type="string" name="mismatchPin" /> <java-symbol type="string" name="mmiComplete" /> @@ -981,6 +984,7 @@ <java-symbol type="string" name="sync_really_delete" /> <java-symbol type="string" name="sync_too_many_deletes_desc" /> <java-symbol type="string" name="sync_undo_deletes" /> + <java-symbol type="string" name="terabyteShort" /> <java-symbol type="string" name="text_copied" /> <java-symbol type="string" name="time_of_day" /> <java-symbol type="string" name="time_picker_decrement_hour_button" /> @@ -3193,7 +3197,7 @@ <java-symbol type="string" name="global_action_logout" /> <java-symbol type="drawable" name="ic_logout" /> - <java-symbol type="dimen" name="config_screenBrightnessMinimumNits" /> - <java-symbol type="dimen" name="config_screenBrightnessMaximumNits" /> <java-symbol type="array" name="config_autoBrightnessDisplayValuesNits" /> + <java-symbol type="array" name="config_screenBrightnessBacklight" /> + <java-symbol type="array" name="config_screenBrightnessNits" /> </resources> diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java index aefc47e95512..c19a343957c0 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java @@ -42,7 +42,8 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) @SmallTest -@Presubmit +// TODO: b/70616950 +//@Presubmit public class ObjectPoolTests { // 1. Check if two obtained objects from pool are not the same. diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index eef9866d7691..fc8650086cd8 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -525,7 +525,8 @@ public class SettingsBackupTest { Settings.Secure.VOICE_INTERACTION_SERVICE, Settings.Secure.VOICE_RECOGNITION_SERVICE, Settings.Secure.INSTANT_APPS_ENABLED, - Settings.Secure.BACKUP_MANAGER_CONSTANTS); + Settings.Secure.BACKUP_MANAGER_CONSTANTS, + Settings.Secure.KEYGUARD_SLICE_URI); @Test public void systemSettingsBackedUpOrBlacklisted() { diff --git a/core/tests/coretests/src/android/text/format/FormatterTest.java b/core/tests/coretests/src/android/text/format/FormatterTest.java index 9c544f47717c..04d2dad4e224 100644 --- a/core/tests/coretests/src/android/text/format/FormatterTest.java +++ b/core/tests/coretests/src/android/text/format/FormatterTest.java @@ -17,7 +17,6 @@ package android.text.format; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; import android.content.Context; import android.content.res.Configuration; @@ -210,24 +209,4 @@ public class FormatterTest { Locale.setDefault(locale); } - - /** - * Verifies that Formatter doesn't "leak" the locally defined petabyte unit into ICU via the - * {@link MeasureUnit} registry. This test can fail for two reasons: - * 1. we regressed and started leaking again. In this case the code needs to be fixed. - * 2. ICU started supporting petabyte as a unit, in which case change one needs to revert this - * change (I494fb59a3b3742f35cbdf6b8705817f404a2c6b0), remove Formatter.PETABYTE and replace any - * usages of that field with just MeasureUnit.PETABYTE. - */ - // http://b/65632959 - @Test - public void doesNotLeakPetabyte() { - // Ensure that the Formatter class is loaded when we call .getAvailable(). - Formatter.formatFileSize(mContext, Long.MAX_VALUE); - Set<MeasureUnit> digitalUnits = MeasureUnit.getAvailable("digital"); - for (MeasureUnit unit : digitalUnits) { - // This assert can fail if we don't leak PETABYTE, but ICU has added it, see #2 above. - assertNotEquals("petabyte", unit.getSubtype()); - } - } } diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java new file mode 100644 index 000000000000..fed197ed2505 --- /dev/null +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java @@ -0,0 +1,68 @@ +/* + * Copyright 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 android.view.accessibility; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import android.os.Parcel; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * AccessibilityEvent is public, so CTS covers it pretty well. Verifying hidden methods here. + */ +@RunWith(AndroidJUnit4.class) +public class AccessibilityEventTest { + @Test + public void testImportantForAccessibiity_getSetWorkAcrossParceling() { + AccessibilityEvent event = AccessibilityEvent.obtain(); + event.setImportantForAccessibility(true); + assertTrue(copyEventViaParcel(event).isImportantForAccessibility()); + + event.setImportantForAccessibility(false); + assertFalse(copyEventViaParcel(event).isImportantForAccessibility()); + } + + @Test + public void testSouceNodeId_getSetWorkAcrossParceling() { + final long sourceNodeId = 0x1234567890ABCDEFL; + AccessibilityEvent event = AccessibilityEvent.obtain(); + event.setSourceNodeId(sourceNodeId); + assertEquals(sourceNodeId, copyEventViaParcel(event).getSourceNodeId()); + } + + @Test + public void testWindowChanges_getSetWorkAcrossParceling() { + final int windowChanges = AccessibilityEvent.WINDOWS_CHANGE_TITLE + | AccessibilityEvent.WINDOWS_CHANGE_ACTIVE + | AccessibilityEvent.WINDOWS_CHANGE_FOCUSED; + AccessibilityEvent event = AccessibilityEvent.obtain(); + event.setWindowChanges(windowChanges); + assertEquals(windowChanges, copyEventViaParcel(event).getWindowChanges()); + } + + private AccessibilityEvent copyEventViaParcel(AccessibilityEvent event) { + Parcel parcel = Parcel.obtain(); + event.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + return AccessibilityEvent.CREATOR.createFromParcel(parcel); + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java index 0afec34da958..a55563afbcc9 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java @@ -15,16 +15,19 @@ */ package com.android.internal.os; +import static android.os.BatteryStats.STATS_CURRENT; import static android.os.BatteryStats.STATS_SINCE_CHARGED; import static android.os.BatteryStats.WAKE_TYPE_PARTIAL; import android.app.ActivityManager; import android.os.BatteryManager; import android.os.BatteryStats; +import android.os.BatteryStats.HistoryItem; import android.os.WorkSource; import android.support.test.filters.SmallTest; import android.view.Display; +import com.android.internal.os.BatteryStatsImpl.Uid; import junit.framework.TestCase; import java.util.ArrayList; @@ -44,11 +47,14 @@ import java.util.Map; * Run: adb shell am instrument -e class com.android.internal.os.BatteryStatsNoteTest -w \ * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner */ -public class BatteryStatsNoteTest extends TestCase{ +public class BatteryStatsNoteTest extends TestCase { + private static final int UID = 10500; private static final WorkSource WS = new WorkSource(UID); - /** Test BatteryStatsImpl.Uid.noteBluetoothScanResultLocked. */ + /** + * Test BatteryStatsImpl.Uid.noteBluetoothScanResultLocked. + */ @SmallTest public void testNoteBluetoothScanResultLocked() throws Exception { MockBatteryStatsImpl bi = new MockBatteryStatsImpl(new MockClocks()); @@ -75,7 +81,9 @@ public class BatteryStatsNoteTest extends TestCase{ .getCountLocked(STATS_SINCE_CHARGED)); } - /** Test BatteryStatsImpl.Uid.noteStartWakeLocked. */ + /** + * Test BatteryStatsImpl.Uid.noteStartWakeLocked. + */ @SmallTest public void testNoteStartWakeLocked() throws Exception { final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms @@ -86,7 +94,8 @@ public class BatteryStatsNoteTest extends TestCase{ bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0); bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP); - bi.getUidStatsLocked(UID).noteStartWakeLocked(pid, name, WAKE_TYPE_PARTIAL, clocks.realtime); + bi.getUidStatsLocked(UID) + .noteStartWakeLocked(pid, name, WAKE_TYPE_PARTIAL, clocks.realtime); clocks.realtime = clocks.uptime = 100; bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); @@ -94,7 +103,8 @@ public class BatteryStatsNoteTest extends TestCase{ clocks.realtime = clocks.uptime = 220; bi.getUidStatsLocked(UID).noteStopWakeLocked(pid, name, WAKE_TYPE_PARTIAL, clocks.realtime); - BatteryStats.Timer aggregTimer = bi.getUidStats().get(UID).getAggregatedPartialWakelockTimer(); + BatteryStats.Timer aggregTimer = bi.getUidStats().get(UID) + .getAggregatedPartialWakelockTimer(); long actualTime = aggregTimer.getTotalTimeLocked(300_000, STATS_SINCE_CHARGED); long bgTime = aggregTimer.getSubTimer().getTotalTimeLocked(300_000, STATS_SINCE_CHARGED); assertEquals(220_000, actualTime); @@ -102,7 +112,9 @@ public class BatteryStatsNoteTest extends TestCase{ } - /** Test BatteryStatsImpl.noteUidProcessStateLocked. */ + /** + * Test BatteryStatsImpl.noteUidProcessStateLocked. + */ @SmallTest public void testNoteUidProcessStateLocked() throws Exception { final MockClocks clocks = new MockClocks(); @@ -145,7 +157,6 @@ public class BatteryStatsNoteTest extends TestCase{ expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_TOP); assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs); - actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE, elapsedTimeUs, STATS_SINCE_CHARGED); expectedRunTimeMs = stateRuntimeMap.get( @@ -153,19 +164,16 @@ public class BatteryStatsNoteTest extends TestCase{ + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs); - actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING, elapsedTimeUs, STATS_SINCE_CHARGED); expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_TOP_SLEEPING); assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs); - actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_FOREGROUND, elapsedTimeUs, STATS_SINCE_CHARGED); expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND); assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs); - actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_BACKGROUND, elapsedTimeUs, STATS_SINCE_CHARGED); expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) @@ -174,7 +182,6 @@ public class BatteryStatsNoteTest extends TestCase{ + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_RECEIVER); assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs); - actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_CACHED, elapsedTimeUs, STATS_SINCE_CHARGED); expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_HOME) @@ -191,7 +198,9 @@ public class BatteryStatsNoteTest extends TestCase{ assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs); } - /** Test BatteryStatsImpl.updateTimeBasesLocked. */ + /** + * Test BatteryStatsImpl.updateTimeBasesLocked. + */ @SmallTest public void testUpdateTimeBasesLocked() throws Exception { final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms @@ -213,7 +222,9 @@ public class BatteryStatsNoteTest extends TestCase{ assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning()); } - /** Test BatteryStatsImpl.noteScreenStateLocked sets timebases and screen states correctly. */ + /** + * Test BatteryStatsImpl.noteScreenStateLocked sets timebases and screen states correctly. + */ @SmallTest public void testNoteScreenStateLocked() throws Exception { final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms @@ -232,15 +243,13 @@ public class BatteryStatsNoteTest extends TestCase{ assertEquals(bi.getScreenState(), Display.STATE_OFF); } - /** Test BatteryStatsImpl.noteScreenStateLocked updates timers correctly. + /** + * Test BatteryStatsImpl.noteScreenStateLocked updates timers correctly. * - * Unknown and doze should both be subset of off state + * Unknown and doze should both be subset of off state * - * Timeline 0----100----200----310----400------------1000 - * Unknown ------- - * On ------- - * Off ------- ---------------------- - * Doze ---------------- + * Timeline 0----100----200----310----400------------1000 Unknown ------- On ------- Off + * ------- ---------------------- Doze ---------------- */ @SmallTest public void testNoteScreenStateTimersLocked() throws Exception { @@ -280,4 +289,161 @@ public class BatteryStatsNoteTest extends TestCase{ assertEquals(600_000, bi.getScreenDozeTime(1500_000, STATS_SINCE_CHARGED)); } + @SmallTest + public void testAlarmStartAndFinishLocked() throws Exception { + final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + bi.setRecordAllHistoryLocked(true); + bi.forceRecordAllHistory(); + + bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0); + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP); + + clocks.realtime = clocks.uptime = 100; + bi.noteAlarmStartLocked("foo", null, UID); + clocks.realtime = clocks.uptime = 5000; + bi.noteAlarmFinishLocked("foo", null, UID); + + HistoryItem item = new HistoryItem(); + assertTrue(bi.startIteratingHistoryLocked()); + + assertTrue(bi.getNextHistoryLocked(item)); + assertEquals(HistoryItem.EVENT_ALARM_START, item.eventCode); + assertEquals("foo", item.eventTag.string); + assertEquals(UID, item.eventTag.uid); + + // TODO(narayan): Figure out why this event is written to the history buffer. See + // test below where it is being interspersed between multiple START events too. + assertTrue(bi.getNextHistoryLocked(item)); + assertEquals(HistoryItem.EVENT_NONE, item.eventCode); + + assertTrue(bi.getNextHistoryLocked(item)); + assertEquals(HistoryItem.EVENT_ALARM_FINISH, item.eventCode); + assertTrue(item.isDeltaData()); + assertEquals("foo", item.eventTag.string); + assertEquals(UID, item.eventTag.uid); + + assertFalse(bi.getNextHistoryLocked(item)); + } + + @SmallTest + public void testAlarmStartAndFinishLocked_workSource() throws Exception { + final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + bi.setRecordAllHistoryLocked(true); + bi.forceRecordAllHistory(); + + bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0); + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP); + + WorkSource ws = new WorkSource(); + ws.add(100); + ws.createWorkChain().addNode(500, "tag"); + bi.noteAlarmStartLocked("foo", ws, UID); + clocks.realtime = clocks.uptime = 5000; + bi.noteAlarmFinishLocked("foo", ws, UID); + + HistoryItem item = new HistoryItem(); + assertTrue(bi.startIteratingHistoryLocked()); + + assertTrue(bi.getNextHistoryLocked(item)); + assertEquals(HistoryItem.EVENT_ALARM_START, item.eventCode); + assertEquals("foo", item.eventTag.string); + assertEquals(100, item.eventTag.uid); + + assertTrue(bi.getNextHistoryLocked(item)); + assertEquals(HistoryItem.EVENT_NONE, item.eventCode); + + assertTrue(bi.getNextHistoryLocked(item)); + assertEquals(HistoryItem.EVENT_ALARM_START, item.eventCode); + assertEquals("foo", item.eventTag.string); + assertEquals(500, item.eventTag.uid); + + assertTrue(bi.getNextHistoryLocked(item)); + assertEquals(HistoryItem.EVENT_ALARM_FINISH, item.eventCode); + assertEquals("foo", item.eventTag.string); + assertEquals(100, item.eventTag.uid); + + assertTrue(bi.getNextHistoryLocked(item)); + assertEquals(HistoryItem.EVENT_ALARM_FINISH, item.eventCode); + assertEquals("foo", item.eventTag.string); + assertEquals(500, item.eventTag.uid); + } + + @SmallTest + public void testNoteWakupAlarmLocked() { + final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + bi.setRecordAllHistoryLocked(true); + bi.forceRecordAllHistory(); + bi.mForceOnBattery = true; + + bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0); + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP); + + bi.noteWakupAlarmLocked("com.foo.bar", UID, null, "tag"); + + Uid.Pkg pkg = bi.getPackageStatsLocked(UID, "com.foo.bar"); + assertEquals(1, pkg.getWakeupAlarmStats().get("tag").getCountLocked(STATS_CURRENT)); + assertEquals(1, pkg.getWakeupAlarmStats().size()); + } + + @SmallTest + public void testNoteWakupAlarmLocked_workSource_uid() { + final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + bi.setRecordAllHistoryLocked(true); + bi.forceRecordAllHistory(); + bi.mForceOnBattery = true; + + bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0); + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP); + + WorkSource ws = new WorkSource(); + ws.add(100); + + // When a WorkSource is present, "UID" should not be used - only the uids present in the + // WorkSource should be reported. + bi.noteWakupAlarmLocked("com.foo.bar", UID, ws, "tag"); + Uid.Pkg pkg = bi.getPackageStatsLocked(UID, "com.foo.bar"); + assertEquals(0, pkg.getWakeupAlarmStats().size()); + pkg = bi.getPackageStatsLocked(100, "com.foo.bar"); + assertEquals(1, pkg.getWakeupAlarmStats().size()); + + // If the WorkSource contains a "name", it should be interpreted as a package name and + // the packageName supplied as an argument must be ignored. + ws = new WorkSource(); + ws.add(100, "com.foo.baz_alternate"); + bi.noteWakupAlarmLocked("com.foo.baz", UID, ws, "tag"); + pkg = bi.getPackageStatsLocked(100, "com.foo.baz"); + assertEquals(0, pkg.getWakeupAlarmStats().size()); + pkg = bi.getPackageStatsLocked(100, "com.foo.baz_alternate"); + assertEquals(1, pkg.getWakeupAlarmStats().size()); + } + + @SmallTest + public void testNoteWakupAlarmLocked_workSource_workChain() { + final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + bi.setRecordAllHistoryLocked(true); + bi.forceRecordAllHistory(); + bi.mForceOnBattery = true; + + bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0); + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP); + + WorkSource ws = new WorkSource(); + ws.createWorkChain().addNode(100, "com.foo.baz_alternate"); + bi.noteWakupAlarmLocked("com.foo.bar", UID, ws, "tag"); + + // For WorkChains, again we must only attribute to the uids present in the WorkSource + // (and not to "UID"). However, unlike the older "tags" we do not change the packagename + // supplied as an argument, given that we're logging the entire attribution chain. + Uid.Pkg pkg = bi.getPackageStatsLocked(UID, "com.foo.bar"); + assertEquals(0, pkg.getWakeupAlarmStats().size()); + pkg = bi.getPackageStatsLocked(100, "com.foo.bar"); + assertEquals(1, pkg.getWakeupAlarmStats().size()); + pkg = bi.getPackageStatsLocked(100, "com.foo.baz_alternate"); + assertEquals(0, pkg.getWakeupAlarmStats().size()); + } } 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 de2fd12267d5..f19ff6708c69 100644 --- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java +++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java @@ -16,6 +16,8 @@ package com.android.internal.os; +import android.os.Handler; +import android.os.Looper; import android.util.SparseIntArray; import java.util.ArrayList; @@ -37,6 +39,9 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { mOnBatteryTimeBase); mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase); setExternalStatsSyncLocked(new DummyExternalStatsSync()); + + // A no-op handler. + mHandler = new Handler(Looper.getMainLooper()) {}; } MockBatteryStatsImpl() { @@ -59,6 +64,12 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { return mForceOnBattery ? true : super.isOnBattery(); } + public void forceRecordAllHistory() { + mHaveBatteryLevel = true; + mRecordingHistory = true; + mRecordAllHistory = true; + } + public TimeBase getOnBatteryBackgroundTimeBase(int uid) { return getUidStatsLocked(uid).mOnBatteryBackgroundTimeBase; } diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 3d65bd226faf..68b7ac287e98 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -429,7 +429,7 @@ public class Typeface { } /** - * Sets an index of the font collection. + * Sets an index of the font collection. See {@link android.R.attr#ttcIndex}. * * Can not be used for Typeface source. build() method will return null for invalid index. * @param ttcIndex An index of the font collection. If the font source is not font diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl index 7c7417dfaaac..5a8fa0700328 100644 --- a/keystore/java/android/security/IKeyChainService.aidl +++ b/keystore/java/android/security/IKeyChainService.aidl @@ -34,7 +34,8 @@ interface IKeyChainService { void setUserSelectable(String alias, boolean isUserSelectable); boolean generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec); - boolean attestKey(in String alias, in byte[] challenge, out KeymasterCertificateChain chain); + boolean attestKey(in String alias, in byte[] challenge, in int[] idAttestationFlags, + out KeymasterCertificateChain chain); boolean setKeyPairCertificate(String alias, in byte[] userCert, in byte[] certChain); // APIs used by CertInstaller and DevicePolicyManager diff --git a/keystore/java/android/security/keystore/AttestationUtils.java b/keystore/java/android/security/keystore/AttestationUtils.java index 0811100f74c7..efee8b497ab8 100644 --- a/keystore/java/android/security/keystore/AttestationUtils.java +++ b/keystore/java/android/security/keystore/AttestationUtils.java @@ -99,48 +99,35 @@ public abstract class AttestationUtils { } } + @NonNull private static KeymasterArguments prepareAttestationArgumentsForDeviceId( + Context context, @NonNull int[] idTypes, @NonNull byte[] attestationChallenge) throws + DeviceIdAttestationException { + // Verify that device ID attestation types are provided. + if (idTypes == null) { + throw new NullPointerException("Missing id types"); + } + + return prepareAttestationArguments(context, idTypes, attestationChallenge); + } + /** - * Performs attestation of the device's identifiers. This method returns a certificate chain - * whose first element contains the requested device identifiers in an extension. The device's - * manufacturer, model, brand, device and product are always also included in the attestation. - * If the device supports attestation in secure hardware, the chain will be rooted at a - * trustworthy CA key. Otherwise, the chain will be rooted at an untrusted certificate. See - * <a href="https://developer.android.com/training/articles/security-key-attestation.html"> - * Key Attestation</a> for the format of the certificate extension. - * <p> - * Attestation will only be successful when all of the following are true: - * 1) The device has been set up to support device identifier attestation at the factory. - * 2) The user has not permanently disabled device identifier attestation. - * 3) You have permission to access the device identifiers you are requesting attestation for. - * <p> - * For privacy reasons, you cannot distinguish between (1) and (2). If attestation is - * unsuccessful, the device may not support it in general or the user may have permanently - * disabled it. - * - * @param context the context to use for retrieving device identifiers. - * @param idTypes the types of device identifiers to attest. - * @param attestationChallenge a blob to include in the certificate alongside the device - * identifiers. - * - * @return a certificate chain containing the requested device identifiers in the first element - * - * @exception SecurityException if you are not permitted to obtain an attestation of the - * device's identifiers. - * @exception DeviceIdAttestationException if the attestation operation fails. + * Prepares Keymaster Arguments with attestation data. + * @hide should only be used by KeyChain. */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - @NonNull public static X509Certificate[] attestDeviceIds(Context context, + @NonNull public static KeymasterArguments prepareAttestationArguments(Context context, @NonNull int[] idTypes, @NonNull byte[] attestationChallenge) throws DeviceIdAttestationException { // Check method arguments, retrieve requested device IDs and prepare attestation arguments. - if (idTypes == null) { - throw new NullPointerException("Missing id types"); - } if (attestationChallenge == null) { throw new NullPointerException("Missing attestation challenge"); } final KeymasterArguments attestArgs = new KeymasterArguments(); attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_CHALLENGE, attestationChallenge); + // Return early if the caller did not request any device identifiers to be included in the + // attestation record. + if (idTypes == null) { + return attestArgs; + } final Set<Integer> idTypesSet = new ArraySet<>(idTypes.length); for (int idType : idTypes) { idTypesSet.add(idType); @@ -191,6 +178,44 @@ public abstract class AttestationUtils { Build.MANUFACTURER.getBytes(StandardCharsets.UTF_8)); attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MODEL, Build.MODEL.getBytes(StandardCharsets.UTF_8)); + return attestArgs; + } + + /** + * Performs attestation of the device's identifiers. This method returns a certificate chain + * whose first element contains the requested device identifiers in an extension. The device's + * manufacturer, model, brand, device and product are always also included in the attestation. + * If the device supports attestation in secure hardware, the chain will be rooted at a + * trustworthy CA key. Otherwise, the chain will be rooted at an untrusted certificate. See + * <a href="https://developer.android.com/training/articles/security-key-attestation.html"> + * Key Attestation</a> for the format of the certificate extension. + * <p> + * Attestation will only be successful when all of the following are true: + * 1) The device has been set up to support device identifier attestation at the factory. + * 2) The user has not permanently disabled device identifier attestation. + * 3) You have permission to access the device identifiers you are requesting attestation for. + * <p> + * For privacy reasons, you cannot distinguish between (1) and (2). If attestation is + * unsuccessful, the device may not support it in general or the user may have permanently + * disabled it. + * + * @param context the context to use for retrieving device identifiers. + * @param idTypes the types of device identifiers to attest. + * @param attestationChallenge a blob to include in the certificate alongside the device + * identifiers. + * + * @return a certificate chain containing the requested device identifiers in the first element + * + * @exception SecurityException if you are not permitted to obtain an attestation of the + * device's identifiers. + * @exception DeviceIdAttestationException if the attestation operation fails. + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @NonNull public static X509Certificate[] attestDeviceIds(Context context, + @NonNull int[] idTypes, @NonNull byte[] attestationChallenge) throws + DeviceIdAttestationException { + final KeymasterArguments attestArgs = prepareAttestationArgumentsForDeviceId( + context, idTypes, attestationChallenge); // Perform attestation. final KeymasterCertificateChain outChain = new KeymasterCertificateChain(); diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp index 1e424258b016..4a0d6ee19978 100644 --- a/libs/hwui/tests/common/TestUtils.cpp +++ b/libs/hwui/tests/common/TestUtils.cpp @@ -122,15 +122,19 @@ void TestUtils::layoutTextUnscaled(const SkPaint& paint, const char* text, void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint& paint, float x, float y) { auto utf16 = asciiToUtf16(text); - canvas->drawText(utf16.get(), 0, strlen(text), strlen(text), x, y, minikin::Bidi::LTR, paint, - nullptr); + SkPaint glyphPaint(paint); + glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + canvas->drawText(utf16.get(), 0, strlen(text), strlen(text), x, y, minikin::Bidi::LTR, + glyphPaint, nullptr); } void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint& paint, const SkPath& path) { auto utf16 = asciiToUtf16(text); - canvas->drawTextOnPath(utf16.get(), strlen(text), minikin::Bidi::LTR, path, 0, 0, paint, - nullptr); + SkPaint glyphPaint(paint); + glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + canvas->drawTextOnPath(utf16.get(), strlen(text), minikin::Bidi::LTR, path, 0, 0, glyphPaint, + nullptr); } void TestUtils::TestTask::run() { diff --git a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp index bf0aed98cd70..38999cb1d2ec 100644 --- a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp +++ b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp @@ -40,22 +40,18 @@ public: } void doFrame(int frameNr) override { - std::unique_ptr<uint16_t[]> text = - TestUtils::asciiToUtf16("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); - ssize_t textLength = 26 * 2; + const char* text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; std::unique_ptr<Canvas> canvas( Canvas::create_recording_canvas(container->stagingProperties().getWidth(), container->stagingProperties().getHeight())); Paint paint; - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setAntiAlias(true); paint.setColor(Color::Black); for (int i = 0; i < 5; i++) { paint.setTextSize(10 + (frameNr % 20) + i * 20); - canvas->drawText(text.get(), 0, textLength, textLength, 0, 100 * (i + 2), - minikin::Bidi::FORCE_LTR, paint, nullptr); + TestUtils::drawUtf8ToCanvas(canvas.get(), text, paint, 0, 100 * (i + 2)); } container->setStagingDisplayList(canvas->finishRecording()); diff --git a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp index 6bae80c120ab..58c99800875b 100644 --- a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp @@ -36,7 +36,6 @@ class ListOfFadedTextAnimation : public TestListViewSceneBase { SkPaint textPaint; textPaint.setTextSize(dp(20)); textPaint.setAntiAlias(true); - textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); TestUtils::drawUtf8ToCanvas(&canvas, "not that long long text", textPaint, dp(10), dp(30)); SkPoint pts[2]; diff --git a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp index d7ec288a8a7b..fd8c252ff318 100644 --- a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp @@ -83,7 +83,6 @@ class ListViewAnimation : public TestListViewSceneBase { canvas.drawRoundRect(0, 0, itemWidth, itemHeight, dp(6), dp(6), roundRectPaint); SkPaint textPaint; - textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); textPaint.setColor(rand() % 2 ? Color::Black : Color::Grey_500); textPaint.setTextSize(dp(20)); textPaint.setAntiAlias(true); diff --git a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp index 9eddc209a9a9..aa537b4f329c 100644 --- a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp +++ b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp @@ -38,7 +38,6 @@ public: card = TestUtils::createNode( 0, 0, width, height, [&](RenderProperties& props, Canvas& canvas) { SkPaint paint; - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setAntiAlias(true); paint.setTextSize(50); diff --git a/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp index fee0659fa81d..3befce4a395f 100644 --- a/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp +++ b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp @@ -42,10 +42,8 @@ public: mBluePaint.setColor(SkColorSetARGB(255, 0, 0, 255)); mBluePaint.setTextSize(padding); - mBluePaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); mGreenPaint.setColor(SkColorSetARGB(255, 0, 255, 0)); mGreenPaint.setTextSize(padding); - mGreenPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); // interleave drawText and drawRect with saveLayer ops for (int i = 0; i < regions; i++, top += smallRectHeight) { @@ -54,18 +52,15 @@ public: canvas.drawColor(SkColorSetARGB(255, 255, 255, 0), SkBlendMode::kSrcOver); std::string stri = std::to_string(i); std::string offscreen = "offscreen line " + stri; - std::unique_ptr<uint16_t[]> offtext = TestUtils::asciiToUtf16(offscreen.c_str()); - canvas.drawText(offtext.get(), 0, offscreen.length(), offscreen.length(), bounds.fLeft, - top + padding, minikin::Bidi::FORCE_LTR, mBluePaint, nullptr); + TestUtils::drawUtf8ToCanvas(&canvas, offscreen.c_str(), mBluePaint, bounds.fLeft, + top + padding); canvas.restore(); canvas.drawRect(bounds.fLeft, top + padding, bounds.fRight, top + smallRectHeight - padding, mBluePaint); std::string onscreen = "onscreen line " + stri; - std::unique_ptr<uint16_t[]> ontext = TestUtils::asciiToUtf16(onscreen.c_str()); - canvas.drawText(ontext.get(), 0, onscreen.length(), onscreen.length(), bounds.fLeft, - top + smallRectHeight - padding, minikin::Bidi::FORCE_LTR, mGreenPaint, - nullptr); + TestUtils::drawUtf8ToCanvas(&canvas, onscreen.c_str(), mGreenPaint, bounds.fLeft, + top + smallRectHeight - padding); } } void doFrame(int frameNr) override {} diff --git a/libs/hwui/tests/common/scenes/TextAnimation.cpp b/libs/hwui/tests/common/scenes/TextAnimation.cpp index a502116c30a0..a16b17849fc6 100644 --- a/libs/hwui/tests/common/scenes/TextAnimation.cpp +++ b/libs/hwui/tests/common/scenes/TextAnimation.cpp @@ -29,7 +29,6 @@ public: card = TestUtils::createNode(0, 0, width, height, [](RenderProperties& props, Canvas& canvas) { SkPaint paint; - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setAntiAlias(true); paint.setTextSize(50); diff --git a/libs/hwui/tests/common/scenes/TvApp.cpp b/libs/hwui/tests/common/scenes/TvApp.cpp index c845e6c89747..003d8e92fd9f 100644 --- a/libs/hwui/tests/common/scenes/TvApp.cpp +++ b/libs/hwui/tests/common/scenes/TvApp.cpp @@ -117,7 +117,6 @@ private: canvas.drawColor(0xFFFFEEEE, SkBlendMode::kSrcOver); SkPaint paint; - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setAntiAlias(true); paint.setTextSize(24); diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp index e56d2f864f77..4eb77514f4ae 100644 --- a/libs/hwui/tests/unit/FrameBuilderTests.cpp +++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp @@ -537,7 +537,6 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, regionClipStopsMerge) { canvas.save(SaveFlags::MatrixClip); canvas.clipPath(&path, SkClipOp::kIntersect); SkPaint paint; - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setAntiAlias(true); paint.setTextSize(50); TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); @@ -569,7 +568,6 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textMerging) { auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, [](RenderProperties& props, RecordingCanvas& canvas) { SkPaint paint; - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setAntiAlias(true); paint.setTextSize(50); TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped @@ -603,7 +601,6 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textStrikethrough) { textPaint.setAntiAlias(true); textPaint.setTextSize(20); textPaint.setFlags(textPaint.getFlags() | SkPaint::kStrikeThruText_ReserveFlag); - textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); for (int i = 0; i < LOOPS; i++) { TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1)); } @@ -654,7 +651,6 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textStyle) { auto node = TestUtils::createNode<RecordingCanvas>( 0, 0, 400, 400, [](RenderProperties& props, RecordingCanvas& canvas) { SkPaint paint; - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setAntiAlias(true); paint.setTextSize(50); paint.setStrokeWidth(10); diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp index 5aae15f478b8..8a9e34f81c6d 100644 --- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp +++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp @@ -175,7 +175,6 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs) { SkPaint paint; paint.setAntiAlias(true); paint.setTextSize(20); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25); }); @@ -196,7 +195,6 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs_strikeThruAndUnderline) { SkPaint paint; paint.setAntiAlias(true); paint.setTextSize(20); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { uint32_t flags = paint.getFlags(); @@ -238,7 +236,6 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs_forceAlignLeft) { SkPaint paint; paint.setAntiAlias(true); paint.setTextSize(20); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setTextAlign(SkPaint::kLeft_Align); TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25); paint.setTextAlign(SkPaint::kCenter_Align); @@ -805,9 +802,7 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, drawText) { Paint paint; paint.setAntiAlias(true); paint.setTextSize(20); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO"); - canvas.drawText(dst.get(), 0, 5, 5, 25, 25, minikin::Bidi::FORCE_LTR, paint, NULL); + TestUtils::drawUtf8ToCanvas(&canvas, "HELLO", paint, 25, 25); }); int count = 0; @@ -829,9 +824,7 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, drawTextInHighContrast) { paint.setColor(SK_ColorWHITE); paint.setAntiAlias(true); paint.setTextSize(20); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO"); - canvas.drawText(dst.get(), 0, 5, 5, 25, 25, minikin::Bidi::FORCE_LTR, paint, NULL); + TestUtils::drawUtf8ToCanvas(&canvas, "HELLO", paint, 25, 25); }); Properties::enableHighContrastText = false; diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp index 4138f595b091..1d7dc3d06ee4 100644 --- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp +++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp @@ -36,7 +36,6 @@ OPENGL_PIPELINE_TEST(SkiaCanvasProxy, drawGlyphsViaPicture) { SkPaint paint; paint.setAntiAlias(true); paint.setTextSize(20); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); static const char* text = "testing text bounds"; // draw text directly into Recording canvas diff --git a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp index 78d75d65837c..92d05e44c6ca 100644 --- a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp +++ b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp @@ -29,6 +29,7 @@ using namespace android::uirenderer; RENDERTHREAD_OPENGL_PIPELINE_TEST(TextDropShadowCache, addRemove) { SkPaint paint; paint.setTextSize(20); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); GammaFontRenderer gammaFontRenderer; FontRenderer& fontRenderer = gammaFontRenderer.getFontRenderer(); diff --git a/media/java/android/media/AudioFocusInfo.java b/media/java/android/media/AudioFocusInfo.java index 6d9c5e2ad5fc..5d0c8e234d40 100644 --- a/media/java/android/media/AudioFocusInfo.java +++ b/media/java/android/media/AudioFocusInfo.java @@ -130,13 +130,11 @@ public final class AudioFocusInfo implements Parcelable { dest.writeInt(mSdkTarget); } - @SystemApi @Override public int hashCode() { return Objects.hash(mAttributes, mClientUid, mClientId, mPackageName, mGainRequest, mFlags); } - @SystemApi @Override public boolean equals(Object obj) { if (this == obj) diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintOptionsLayout.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintOptionsLayout.java index 7a80a8bd426e..24cf218fd0d8 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintOptionsLayout.java +++ b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintOptionsLayout.java @@ -21,6 +21,7 @@ import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; + import com.android.printspooler.R; /** @@ -126,6 +127,7 @@ public final class PrintOptionsLayout extends ViewGroup { protected void onLayout(boolean changed, int l, int t, int r, int b) { final int childCount = getChildCount(); final int rowCount = childCount / mColumnCount + childCount % mColumnCount; + final boolean isLayoutRtl = isLayoutRtl(); int cellStart = getPaddingStart(); int cellTop = getPaddingTop(); @@ -134,7 +136,13 @@ public final class PrintOptionsLayout extends ViewGroup { int rowHeight = 0; for (int col = 0; col < mColumnCount; col++) { - final int childIndex = row * mColumnCount + col; + final int childIndex; + if (isLayoutRtl) { + // if RTL, layout the right most child first + childIndex = row * mColumnCount + (mColumnCount - col - 1); + } else { + childIndex = row * mColumnCount + col; + } if (childIndex >= childCount) { break; @@ -148,14 +156,14 @@ public final class PrintOptionsLayout extends ViewGroup { MarginLayoutParams childParams = (MarginLayoutParams) child.getLayoutParams(); - final int childLeft = cellStart + childParams.getMarginStart(); + final int childStart = cellStart + childParams.getMarginStart(); final int childTop = cellTop + childParams.topMargin; - final int childRight = childLeft + child.getMeasuredWidth(); + final int childEnd = childStart + child.getMeasuredWidth(); final int childBottom = childTop + child.getMeasuredHeight(); - child.layout(childLeft, childTop, childRight, childBottom); + child.layout(childStart, childTop, childEnd, childBottom); - cellStart = childRight + childParams.getMarginEnd(); + cellStart = childEnd + childParams.getMarginEnd(); rowHeight = Math.max(rowHeight, child.getMeasuredHeight() + childParams.topMargin + childParams.bottomMargin); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index f4ec93666490..bef2bcbd80f1 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -1710,18 +1710,9 @@ public class SettingsProvider extends ContentProvider { } private List<String> getSettingsNamesLocked(int settingsType, int userId) { - boolean instantApp; - if (UserHandle.getAppId(Binder.getCallingUid()) < Process.FIRST_APPLICATION_UID) { - instantApp = false; - } else { - ApplicationInfo ai = getCallingApplicationInfoOrThrow(); - instantApp = ai.isInstantApp(); - } - if (instantApp) { - return new ArrayList<String>(getInstantAppAccessibleSettings(settingsType)); - } else { - return mSettingsRegistry.getSettingsNamesLocked(settingsType, userId); - } + // Don't enforce the instant app whitelist for now -- its too prone to unintended breakage + // in the current form. + return mSettingsRegistry.getSettingsNamesLocked(settingsType, userId); } private void enforceSettingReadable(String settingName, int settingsType, int userId) { @@ -1734,8 +1725,10 @@ public class SettingsProvider extends ContentProvider { } if (!getInstantAppAccessibleSettings(settingsType).contains(settingName) && !getOverlayInstantAppAccessibleSettings(settingsType).contains(settingName)) { - throw new SecurityException("Setting " + settingName + " is not accessible from" - + " ephemeral package " + getCallingPackage()); + // Don't enforce the instant app whitelist for now -- its too prone to unintended + // breakage in the current form. + Slog.w(LOG_TAG, "Instant App " + ai.packageName + + " trying to access unexposed setting, this will be an error in the future."); } } diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk index 2c5eb27abe3d..73fcdd7aa90d 100644 --- a/packages/SystemUI/Android.mk +++ b/packages/SystemUI/Android.mk @@ -39,7 +39,12 @@ LOCAL_STATIC_ANDROID_LIBRARIES := \ android-support-v7-mediarouter \ android-support-v7-palette \ android-support-v14-preference \ - android-support-v17-leanback + android-support-v17-leanback \ + android-slices-core \ + android-slices-view \ + android-slices-builders \ + apptoolkit-arch-core-runtime \ + apptoolkit-lifecycle-extensions \ LOCAL_STATIC_JAVA_LIBRARIES := \ SystemUI-tags \ diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml index 020cfeeaa194..b154d46f162c 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml @@ -24,31 +24,20 @@ android:layout_marginEnd="16dp" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="@dimen/date_owner_info_margin" android:layout_gravity="center_horizontal" - android:paddingTop="4dp" android:clipToPadding="false" android:orientation="vertical" android:layout_centerHorizontal="true"> <TextView android:id="@+id/title" android:layout_width="match_parent" android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="end" - android:fadingEdge="horizontal" - android:gravity="center" - android:textSize="22sp" - android:textColor="?attr/wallpaperTextColor" + android:layout_marginBottom="@dimen/widget_vertical_padding" + android:theme="@style/TextAppearance.Keyguard" /> - <TextView android:id="@+id/text" + <LinearLayout android:id="@+id/row" android:layout_width="match_parent" android:layout_height="wrap_content" - android:singleLine="true" + android:orientation="horizontal" android:gravity="center" - android:visibility="gone" - android:textSize="16sp" - android:textColor="?attr/wallpaperTextColor" - android:layout_marginTop="4dp" - android:ellipsize="end" /> </com.android.keyguard.KeyguardSliceView>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml index 138733e646ce..c97cfc4bb835 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml @@ -34,6 +34,7 @@ android:orientation="vertical"> <RelativeLayout android:id="@+id/keyguard_clock_container" + android:animateLayoutChanges="true" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal|top"> @@ -59,14 +60,23 @@ android:layout_toEndOf="@id/clock_view" android:visibility="invisible" android:src="@drawable/ic_aod_charging_24dp" - android:contentDescription="@string/accessibility_ambient_display_charging" - /> + android:contentDescription="@string/accessibility_ambient_display_charging" /> + <View + android:id="@+id/clock_separator" + android:layout_width="16dp" + android:layout_height="1dp" + android:layout_marginTop="10dp" + android:layout_below="@id/clock_view" + android:background="#f00" + android:layout_centerHorizontal="true" /> <include layout="@layout/keyguard_status_area" android:id="@+id/keyguard_status_area" + android:layout_marginTop="10dp" + android:layout_marginBottom="@dimen/widget_vertical_padding" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_below="@id/clock_view" /> + android:layout_below="@id/clock_separator" /> </RelativeLayout> <TextView @@ -83,6 +93,5 @@ android:letterSpacing="0.05" android:ellipsize="marquee" android:singleLine="true" /> - </LinearLayout> </com.android.keyguard.KeyguardStatusView> diff --git a/packages/SystemUI/res-keyguard/values-h560dp/dimens.xml b/packages/SystemUI/res-keyguard/values-h560dp/dimens.xml index 1b6fa4cf4892..3fb86d03a167 100644 --- a/packages/SystemUI/res-keyguard/values-h560dp/dimens.xml +++ b/packages/SystemUI/res-keyguard/values-h560dp/dimens.xml @@ -16,5 +16,5 @@ --> <resources> - <dimen name="widget_big_font_size">72dp</dimen> + <dimen name="widget_big_font_size">64dp</dimen> </resources>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml b/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml index 1b6fa4cf4892..3fb86d03a167 100644 --- a/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml +++ b/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml @@ -16,5 +16,5 @@ --> <resources> - <dimen name="widget_big_font_size">72dp</dimen> + <dimen name="widget_big_font_size">64dp</dimen> </resources>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index bcac07295cce..463af61c16f1 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -42,9 +42,22 @@ <dimen name="eca_overlap">-10dip</dimen> <!-- Default clock parameters --> - <dimen name="bottom_text_spacing_digital">-1dp</dimen> - <dimen name="widget_label_font_size">14sp</dimen> - <dimen name="widget_big_font_size">72dp</dimen> + <dimen name="bottom_text_spacing_digital">-10dp</dimen> + <!-- Slice header --> + <dimen name="widget_title_font_size">28sp</dimen> + <!-- Slice subtitle --> + <dimen name="widget_label_font_size">16sp</dimen> + <!-- Clock without header --> + <dimen name="widget_big_font_size">64dp</dimen> + <!-- Clock with header --> + <dimen name="widget_small_font_size">22dp</dimen> + <!-- Dash between clock and header --> + <dimen name="widget_vertical_padding">16dp</dimen> + <!-- Subtitle paddings --> + <dimen name="widget_separator_thickness">2dp</dimen> + <dimen name="widget_horizontal_padding">8dp</dimen> + <dimen name="widget_icon_size">16dp</dimen> + <dimen name="widget_icon_padding">4dp</dimen> <!-- The y translation to apply at the start in appear animations. --> <dimen name="appear_y_translation_start">32dp</dimen> diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml index 826e3ea1f7e5..d50bab533856 100644 --- a/packages/SystemUI/res-keyguard/values/styles.xml +++ b/packages/SystemUI/res-keyguard/values/styles.xml @@ -78,4 +78,18 @@ <item name="wallpaperTextColorSecondary">@*android:color/secondary_text_material_dark</item> </style> + <style name="TextAppearance.Keyguard" parent="Theme.SystemUI"> + <item name="android:textSize">@dimen/widget_title_font_size</item> + <item name="android:gravity">center</item> + <item name="android:ellipsize">end</item> + <item name="android:maxLines">2</item> + </style> + + <style name="TextAppearance.Keyguard.Secondary"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:textSize">@dimen/widget_label_font_size</item> + <item name="android:singleLine">true</item> + </style> + </resources> diff --git a/packages/SystemUI/res/layout/pip_menu_activity.xml b/packages/SystemUI/res/layout/pip_menu_activity.xml index 8b7f6926c9f7..03d587bd447c 100644 --- a/packages/SystemUI/res/layout/pip_menu_activity.xml +++ b/packages/SystemUI/res/layout/pip_menu_activity.xml @@ -60,14 +60,24 @@ </FrameLayout> </FrameLayout> - <ImageView - android:id="@+id/dismiss" - android:layout_width="@dimen/pip_action_size" - android:layout_height="@dimen/pip_action_size" - android:layout_gravity="top|end" - android:padding="@dimen/pip_action_padding" - android:contentDescription="@string/pip_phone_close" - android:src="@drawable/ic_close_white" - android:background="?android:selectableItemBackgroundBorderless" /> + <ImageView + android:id="@+id/settings" + android:layout_width="@dimen/pip_action_size" + android:layout_height="@dimen/pip_action_size" + android:layout_gravity="top|start" + android:padding="@dimen/pip_action_padding" + android:contentDescription="@string/pip_phone_settings" + android:src="@drawable/ic_settings" + android:background="?android:selectableItemBackgroundBorderless" /> + + <ImageView + android:id="@+id/dismiss" + android:layout_width="@dimen/pip_action_size" + android:layout_height="@dimen/pip_action_size" + android:layout_gravity="top|end" + android:padding="@dimen/pip_action_padding" + android:contentDescription="@string/pip_phone_close" + android:src="@drawable/ic_close_white" + android:background="?android:selectableItemBackgroundBorderless" /> </FrameLayout> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index fd205dd55662..98537a102e1a 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1895,6 +1895,9 @@ <!-- Label for PIP close button [CHAR LIMIT=NONE]--> <string name="pip_phone_close">Close</string> + <!-- Label for PIP settings button [CHAR LIMIT=NONE]--> + <string name="pip_phone_settings">Settings</string> + <!-- Label for PIP the drag to dismiss hint [CHAR LIMIT=NONE]--> <string name="pip_phone_dismiss_hint">Drag down to dismiss</string> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java index cb3d59c4dff6..b9bf80de304b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java @@ -17,38 +17,56 @@ package com.android.keyguard; import android.app.PendingIntent; -import android.app.slice.Slice; -import android.app.slice.SliceItem; -import android.app.slice.SliceQuery; +import android.arch.lifecycle.LiveData; +import android.arch.lifecycle.Observer; import android.content.Context; -import android.database.ContentObserver; +import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; import android.net.Uri; -import android.os.Handler; +import android.provider.Settings; import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; import com.android.internal.graphics.ColorUtils; +import com.android.settingslib.Utils; +import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.keyguard.KeyguardSliceProvider; +import com.android.systemui.tuner.TunerService; -import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.function.Consumer; + +import androidx.app.slice.Slice; +import androidx.app.slice.SliceItem; +import androidx.app.slice.core.SliceQuery; +import androidx.app.slice.widget.SliceLiveData; /** * View visible under the clock on the lock screen and AoD. */ -public class KeyguardSliceView extends LinearLayout { +public class KeyguardSliceView extends LinearLayout implements View.OnClickListener, + Observer<Slice>, TunerService.Tunable { - private final Uri mKeyguardSliceUri; + private static final String TAG = "KeyguardSliceView"; + private final HashMap<View, PendingIntent> mClickActions; + private Uri mKeyguardSliceUri; private TextView mTitle; - private TextView mText; - private Slice mSlice; - private PendingIntent mSliceAction; + private LinearLayout mRow; private int mTextColor; private float mDarkAmount = 0; - private final ContentObserver mObserver; + private LiveData<Slice> mLiveData; + private int mIconSize; + private Consumer<Boolean> mListener; + private boolean mHasHeader; public KeyguardSliceView(Context context) { this(context, null, 0); @@ -60,16 +78,20 @@ public class KeyguardSliceView extends LinearLayout { public KeyguardSliceView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - mObserver = new KeyguardSliceObserver(new Handler()); - mKeyguardSliceUri = Uri.parse(KeyguardSliceProvider.KEYGUARD_SLICE_URI);; + + TunerService tunerService = Dependency.get(TunerService.class); + tunerService.addTunable(this, Settings.Secure.KEYGUARD_SLICE_URI); + + mClickActions = new HashMap<>(); } @Override protected void onFinishInflate() { super.onFinishInflate(); mTitle = findViewById(R.id.title); - mText = findViewById(R.id.text); - mTextColor = mTitle.getCurrentTextColor(); + mRow = findViewById(R.id.row); + mTextColor = Utils.getColorAttr(mContext, R.attr.wallpaperTextColor); + mIconSize = (int) mContext.getResources().getDimension(R.dimen.widget_icon_size); } @Override @@ -77,57 +99,103 @@ public class KeyguardSliceView extends LinearLayout { super.onAttachedToWindow(); // Set initial content - showSlice(Slice.bindSlice(getContext().getContentResolver(), mKeyguardSliceUri, - Collections.emptyList())); + showSlice(Slice.bindSlice(getContext(), mKeyguardSliceUri)); // Make sure we always have the most current slice - getContext().getContentResolver().registerContentObserver(mKeyguardSliceUri, - false /* notifyDescendants */, mObserver); + mLiveData.observeForever(this); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - getContext().getContentResolver().unregisterContentObserver(mObserver); + mLiveData.removeObserver(this); } private void showSlice(Slice slice) { - // Items will be wrapped into an action when they have tap targets. - SliceItem actionSlice = SliceQuery.find(slice, SliceItem.FORMAT_ACTION); - if (actionSlice != null) { - mSlice = actionSlice.getSlice(); - mSliceAction = actionSlice.getAction(); - } else { - mSlice = slice; - mSliceAction = null; - } - if (mSlice == null) { - setVisibility(GONE); - return; - } + // Main area + SliceItem mainItem = SliceQuery.find(slice, android.app.slice.SliceItem.FORMAT_SLICE, + null /* hints */, new String[]{android.app.slice.Slice.HINT_LIST_ITEM}); + mHasHeader = mainItem != null; - SliceItem title = SliceQuery.find(mSlice, SliceItem.FORMAT_TEXT, Slice.HINT_TITLE, null); - if (title == null) { + List<SliceItem> subItems = SliceQuery.findAll(slice, + android.app.slice.SliceItem.FORMAT_SLICE, + new String[]{android.app.slice.Slice.HINT_LIST_ITEM}, + null /* nonHints */); + + if (!mHasHeader) { mTitle.setVisibility(GONE); } else { mTitle.setVisibility(VISIBLE); - mTitle.setText(title.getText()); + SliceItem mainTitle = SliceQuery.find(mainItem.getSlice(), + android.app.slice.SliceItem.FORMAT_TEXT, + new String[]{android.app.slice.Slice.HINT_TITLE}, + null /* nonHints */); + mTitle.setText(mainTitle.getText()); } - SliceItem text = SliceQuery.find(mSlice, SliceItem.FORMAT_TEXT, null, Slice.HINT_TITLE); - if (text == null) { - mText.setVisibility(GONE); - } else { - mText.setVisibility(VISIBLE); - mText.setText(text.getText()); + mClickActions.clear(); + final int subItemsCount = subItems.size(); + + for (int i = 0; i < subItemsCount; i++) { + SliceItem item = subItems.get(i); + final Uri itemTag = item.getSlice().getUri(); + // Try to reuse the view if already exists in the layout + KeyguardSliceButton button = mRow.findViewWithTag(itemTag); + if (button == null) { + button = new KeyguardSliceButton(mContext); + button.setTextColor(mTextColor); + button.setTag(itemTag); + } else { + mRow.removeView(button); + } + button.setHasDivider(i < subItemsCount - 1); + mRow.addView(button, i); + + PendingIntent pendingIntent; + try { + pendingIntent = item.getAction(); + } catch (RuntimeException e) { + Log.w(TAG, "Cannot retrieve action from keyguard slice", e); + pendingIntent = null; + } + mClickActions.put(button, pendingIntent); + + SliceItem title = SliceQuery.find(item.getSlice(), + android.app.slice.SliceItem.FORMAT_TEXT, + new String[]{android.app.slice.Slice.HINT_TITLE}, + null /* nonHints */); + button.setText(title.getText()); + + Drawable iconDrawable = null; + SliceItem icon = SliceQuery.find(item.getSlice(), + android.app.slice.SliceItem.FORMAT_IMAGE); + if (icon != null) { + iconDrawable = icon.getIcon().loadDrawable(mContext); + final int width = (int) (iconDrawable.getIntrinsicWidth() + / (float) iconDrawable.getIntrinsicHeight() * mIconSize); + iconDrawable.setBounds(0, 0, Math.max(width, 1), mIconSize); + } + button.setCompoundDrawablesRelative(iconDrawable, null, null, null); + button.setOnClickListener(this); + } + + // Removing old views + for (int i = 0; i < mRow.getChildCount(); i++) { + View child = mRow.getChildAt(i); + if (!mClickActions.containsKey(child)) { + mRow.removeView(child); + i--; + } } - final int visibility = title == null && text == null ? GONE : VISIBLE; + final int visibility = mHasHeader || subItemsCount > 0 ? VISIBLE : GONE; if (visibility != getVisibility()) { setVisibility(visibility); } + + mListener.accept(mHasHeader); } public void setDark(float darkAmount) { @@ -135,30 +203,113 @@ public class KeyguardSliceView extends LinearLayout { updateTextColors(); } - public void setTextColor(int textColor) { - mTextColor = textColor; - } - private void updateTextColors() { final int blendedColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount); mTitle.setTextColor(blendedColor); - mText.setTextColor(blendedColor); + int childCount = mRow.getChildCount(); + for (int i = 0; i < childCount; i++) { + View v = mRow.getChildAt(i); + if (v instanceof Button) { + ((Button) v).setTextColor(blendedColor); + } + } + } + + @Override + public void onClick(View v) { + final PendingIntent action = mClickActions.get(v); + if (action != null) { + try { + action.send(); + } catch (PendingIntent.CanceledException e) { + Log.i(TAG, "Pending intent cancelled, nothing to launch", e); + } + } } - private class KeyguardSliceObserver extends ContentObserver { - KeyguardSliceObserver(Handler handler) { - super(handler); + public void setListener(Consumer<Boolean> listener) { + mListener = listener; + } + + public boolean hasHeader() { + return mHasHeader; + } + + /** + * LiveData observer lifecycle. + * @param slice the new slice content. + */ + @Override + public void onChanged(Slice slice) { + showSlice(slice); + } + + @Override + public void onTuningChanged(String key, String newValue) { + setupUri(newValue); + } + + public void setupUri(String uriString) { + if (uriString == null) { + uriString = KeyguardSliceProvider.KEYGUARD_SLICE_URI; + } + + boolean wasObserving = false; + if (mLiveData != null && mLiveData.hasActiveObservers()) { + wasObserving = true; + mLiveData.removeObserver(this); + } + + mKeyguardSliceUri = Uri.parse(uriString); + mLiveData = SliceLiveData.fromUri(mContext, mKeyguardSliceUri); + + if (wasObserving) { + mLiveData.observeForever(this); + showSlice(Slice.bindSlice(getContext(), mKeyguardSliceUri)); + } + } + + /** + * Representation of an item that appears under the clock on main keyguard message. + * Shows optional separator. + */ + private class KeyguardSliceButton extends Button { + + private final Paint mPaint; + private boolean mHasDivider; + + public KeyguardSliceButton(Context context) { + super(context, null /* attrs */, + com.android.keyguard.R.style.TextAppearance_Keyguard_Secondary); + mPaint = new Paint(); + mPaint.setStyle(Paint.Style.STROKE); + float dividerWidth = context.getResources() + .getDimension(R.dimen.widget_separator_thickness); + mPaint.setStrokeWidth(dividerWidth); + int horizontalPadding = (int) context.getResources() + .getDimension(R.dimen.widget_horizontal_padding); + setPadding(horizontalPadding, 0, horizontalPadding, 0); + setCompoundDrawablePadding((int) context.getResources() + .getDimension(R.dimen.widget_icon_padding)); + } + + public void setHasDivider(boolean hasDivider) { + mHasDivider = hasDivider; } @Override - public void onChange(boolean selfChange) { - this.onChange(selfChange, null); + public void setTextColor(int color) { + super.setTextColor(color); + mPaint.setColor(color); } @Override - public void onChange(boolean selfChange, Uri uri) { - showSlice(Slice.bindSlice(getContext().getContentResolver(), mKeyguardSliceUri, - Collections.emptyList())); + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (mHasDivider) { + final int lineX = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? 0 : getWidth(); + canvas.drawLine(lineX, 0, lineX, getHeight(), mPaint); + } } } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index 78cf2b9bf1ed..4b9a8744900d 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -28,6 +28,7 @@ import android.os.UserHandle; import android.support.v4.graphics.ColorUtils; import android.text.TextUtils; import android.text.format.DateFormat; +import android.util.ArraySet; import android.util.AttributeSet; import android.util.Log; import android.util.Slog; @@ -38,11 +39,11 @@ import android.widget.GridLayout; import android.widget.TextClock; import android.widget.TextView; -import com.android.internal.util.ArrayUtils; import com.android.internal.widget.LockPatternUtils; -import com.android.settingslib.Utils; import com.android.systemui.ChargingView; +import com.google.android.collect.Sets; + import java.util.Locale; public class KeyguardStatusView extends GridLayout { @@ -52,8 +53,11 @@ public class KeyguardStatusView extends GridLayout { private final LockPatternUtils mLockPatternUtils; private final AlarmManager mAlarmManager; + private final float mSmallClockScale; + private final float mWidgetPadding; private TextClock mClockView; + private View mClockSeparator; private TextView mOwnerInfo; private ViewGroup mClockContainer; private ChargingView mBatteryDoze; @@ -61,7 +65,7 @@ public class KeyguardStatusView extends GridLayout { private Runnable mPendingMarqueeStart; private Handler mHandler; - private View[] mVisibleInDoze; + private ArraySet<View> mVisibleInDoze; private boolean mPulsing; private float mDarkAmount = 0; private int mTextColor; @@ -112,6 +116,9 @@ public class KeyguardStatusView extends GridLayout { mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); mLockPatternUtils = new LockPatternUtils(getContext()); mHandler = new Handler(Looper.myLooper()); + mSmallClockScale = getResources().getDimension(R.dimen.widget_small_font_size) + / getResources().getDimension(R.dimen.widget_big_font_size); + mWidgetPadding = getResources().getDimension(R.dimen.widget_vertical_padding); } private void setEnableMarquee(boolean enabled) { @@ -150,9 +157,14 @@ public class KeyguardStatusView extends GridLayout { mOwnerInfo = findViewById(R.id.owner_info); mBatteryDoze = findViewById(R.id.battery_doze); mKeyguardSlice = findViewById(R.id.keyguard_status_area); - mVisibleInDoze = new View[]{mBatteryDoze, mClockView, mKeyguardSlice}; + mClockSeparator = findViewById(R.id.clock_separator); + mVisibleInDoze = Sets.newArraySet(mBatteryDoze, mClockView, mKeyguardSlice, + mClockSeparator); mTextColor = mClockView.getCurrentTextColor(); + mKeyguardSlice.setListener(this::onSliceContentChanged); + onSliceContentChanged(mKeyguardSlice.hasHeader()); + boolean shouldMarquee = KeyguardUpdateMonitor.getInstance(mContext).isDeviceInteractive(); setEnableMarquee(shouldMarquee); refresh(); @@ -163,6 +175,22 @@ public class KeyguardStatusView extends GridLayout { mClockView.setElegantTextHeight(false); } + private void onSliceContentChanged(boolean hasHeader) { + final float clockScale = hasHeader ? mSmallClockScale : 1; + float translation = (mClockView.getHeight() - (mClockView.getHeight() * clockScale)) / 2f; + if (hasHeader) { + translation -= mWidgetPadding; + } + mClockView.setTranslationY(translation); + mClockView.setScaleX(clockScale); + mClockView.setScaleY(clockScale); + final float batteryTranslation = + -(mClockView.getWidth() - (mClockView.getWidth() * clockScale)) / 2; + mBatteryDoze.setTranslationX(batteryTranslation); + mBatteryDoze.setTranslationY(translation); + mClockSeparator.setVisibility(hasHeader ? VISIBLE : GONE); + } + @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); @@ -201,17 +229,6 @@ public class KeyguardStatusView extends GridLayout { return mClockView.getTextSize(); } - public static String formatNextAlarm(Context context, AlarmManager.AlarmClockInfo info) { - if (info == null) { - return ""; - } - String skeleton = DateFormat.is24HourFormat(context, ActivityManager.getCurrentUser()) - ? "EHm" - : "Ehma"; - String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton); - return DateFormat.format(pattern, info.getTriggerTime()).toString(); - } - private void updateOwnerInfo() { if (mOwnerInfo == null) return; String ownerInfo = getOwnerInfo(); @@ -303,7 +320,7 @@ public class KeyguardStatusView extends GridLayout { final int N = mClockContainer.getChildCount(); for (int i = 0; i < N; i++) { View child = mClockContainer.getChildAt(i); - if (ArrayUtils.contains(mVisibleInDoze, child)) { + if (mVisibleInDoze.contains(child)) { continue; } child.setAlpha(dark ? 0 : 1); @@ -312,10 +329,12 @@ public class KeyguardStatusView extends GridLayout { mOwnerInfo.setAlpha(dark ? 0 : 1); } + final int blendedTextColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, darkAmount); updateDozeVisibleViews(); mBatteryDoze.setDark(dark); mKeyguardSlice.setDark(darkAmount); - mClockView.setTextColor(ColorUtils.blendARGB(mTextColor, Color.WHITE, darkAmount)); + mClockView.setTextColor(blendedTextColor); + mClockSeparator.setBackgroundColor(blendedTextColor); } public void setPulsing(boolean pulsing) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index 6ddc76b595b2..bd46c5f8ad0b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -16,38 +16,55 @@ package com.android.systemui.keyguard; +import android.app.ActivityManager; +import android.app.AlarmManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.graphics.drawable.Icon; import android.icu.text.DateFormat; import android.icu.text.DisplayContext; import android.net.Uri; import android.os.Handler; -import android.app.slice.Slice; -import android.app.slice.SliceProvider; +import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; +import com.android.systemui.statusbar.policy.NextAlarmController; +import com.android.systemui.statusbar.policy.NextAlarmControllerImpl; import java.util.Date; import java.util.Locale; +import androidx.app.slice.Slice; +import androidx.app.slice.SliceProvider; +import androidx.app.slice.builders.ListBuilder; +import androidx.app.slice.builders.ListBuilder.RowBuilder; + /** * Simple Slice provider that shows the current date. */ -public class KeyguardSliceProvider extends SliceProvider { +public class KeyguardSliceProvider extends SliceProvider implements + NextAlarmController.NextAlarmChangeCallback { public static final String KEYGUARD_SLICE_URI = "content://com.android.systemui.keyguard/main"; + public static final String KEYGUARD_DATE_URI = "content://com.android.systemui.keyguard/date"; + public static final String KEYGUARD_NEXT_ALARM_URI = + "content://com.android.systemui.keyguard/alarm"; private final Date mCurrentTime = new Date(); protected final Uri mSliceUri; + protected final Uri mDateUri; + protected final Uri mAlarmUri; private final Handler mHandler; private String mDatePattern; private DateFormat mDateFormat; private String mLastText; private boolean mRegistered; private boolean mRegisteredEveryMinute; + private String mNextAlarm; + private NextAlarmController mNextAlarmController; /** * Receiver responsible for time ticking and updating the date format. @@ -80,23 +97,49 @@ public class KeyguardSliceProvider extends SliceProvider { KeyguardSliceProvider(Handler handler) { mHandler = handler; mSliceUri = Uri.parse(KEYGUARD_SLICE_URI); + mDateUri = Uri.parse(KEYGUARD_DATE_URI); + mAlarmUri = Uri.parse(KEYGUARD_NEXT_ALARM_URI); } @Override public Slice onBindSlice(Uri sliceUri) { - return new Slice.Builder(sliceUri).addText(mLastText, null, Slice.HINT_TITLE).build(); + ListBuilder builder = new ListBuilder(mSliceUri) + .addRow(new RowBuilder(mDateUri).setTitle(mLastText)); + if (!TextUtils.isEmpty(mNextAlarm)) { + Icon icon = Icon.createWithResource(getContext(), R.drawable.ic_access_alarms_big); + builder.addRow(new RowBuilder(mAlarmUri).setTitle(mNextAlarm).addEndItem(icon)); + } + + return builder.build(); } @Override - public boolean onCreate() { - + public boolean onCreateSliceProvider() { + mNextAlarmController = new NextAlarmControllerImpl(getContext()); + mNextAlarmController.addCallback(this); mDatePattern = getContext().getString(R.string.system_ui_date_pattern); - registerClockUpdate(false /* everyMinute */); updateClock(); return true; } + public static String formatNextAlarm(Context context, AlarmManager.AlarmClockInfo info) { + if (info == null) { + return ""; + } + String skeleton = android.text.format.DateFormat + .is24HourFormat(context, ActivityManager.getCurrentUser()) ? "EHm" : "Ehma"; + String pattern = android.text.format.DateFormat + .getBestDateTimePattern(Locale.getDefault(), skeleton); + return android.text.format.DateFormat.format(pattern, info.getTriggerTime()).toString(); + } + + /** + * Registers a broadcast receiver for clock updates, include date, time zone and manually + * changing the date/time via the settings app. + * + * @param everyMinute {@code true} if you also want updates every minute. + */ protected void registerClockUpdate(boolean everyMinute) { if (mRegistered) { if (mRegisteredEveryMinute == everyMinute) { @@ -156,4 +199,10 @@ public class KeyguardSliceProvider extends SliceProvider { void cleanDateFormat() { mDateFormat = null; } + + @Override + public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) { + mNextAlarm = formatNextAlarm(getContext(), nextAlarm); + getContext().getContentResolver().notifyChange(mSliceUri, null /* observer */); + } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java new file mode 100644 index 000000000000..f0e4ccc139ca --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java @@ -0,0 +1,89 @@ +/* + * 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.pip.phone; + +import static android.app.AppOpsManager.MODE_ALLOWED; +import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE; + +import android.app.AppOpsManager; +import android.app.AppOpsManager.OnOpChangedListener; +import android.app.IActivityManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.util.Pair; + +public class PipAppOpsListener { + private static final String TAG = PipAppOpsListener.class.getSimpleName(); + + private Context mContext; + private IActivityManager mActivityManager; + private AppOpsManager mAppOpsManager; + + private PipMotionHelper mMotionHelper; + + private AppOpsManager.OnOpChangedListener mAppOpsChangedListener = new OnOpChangedListener() { + @Override + public void onOpChanged(String op, String packageName) { + try { + // Dismiss the PiP once the user disables the app ops setting for that package + final Pair<ComponentName, Integer> topPipActivityInfo = + PipUtils.getTopPinnedActivity(mContext, mActivityManager); + if (topPipActivityInfo.first != null) { + final ApplicationInfo appInfo = mContext.getPackageManager() + .getApplicationInfoAsUser(packageName, 0, topPipActivityInfo.second); + if (appInfo.packageName.equals(topPipActivityInfo.first.getPackageName()) && + mAppOpsManager.checkOpNoThrow(OP_PICTURE_IN_PICTURE, appInfo.uid, + packageName) != MODE_ALLOWED) { + mMotionHelper.dismissPip(); + } + } + } catch (NameNotFoundException e) { + // Unregister the listener if the package can't be found + unregisterAppOpsListener(); + } + } + }; + + public PipAppOpsListener(Context context, IActivityManager activityManager, + PipMotionHelper motionHelper) { + mContext = context; + mActivityManager = activityManager; + mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); + mMotionHelper = motionHelper; + } + + public void onActivityPinned(String packageName) { + // Register for changes to the app ops setting for this package while it is in PiP + registerAppOpsListener(packageName); + } + + public void onActivityUnpinned() { + // Unregister for changes to the previously PiP'ed package + unregisterAppOpsListener(); + } + + private void registerAppOpsListener(String packageName) { + mAppOpsManager.startWatchingMode(OP_PICTURE_IN_PICTURE, packageName, + mAppOpsChangedListener); + } + + private void unregisterAppOpsListener() { + mAppOpsManager.stopWatchingMode(mAppOpsChangedListener); + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index db999c45dbde..36531bb727a4 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -64,8 +64,8 @@ public class PipManager implements BasePipManager { private InputConsumerController mInputConsumerController; private PipMenuActivityController mMenuController; private PipMediaController mMediaController; - private PipNotificationController mNotificationController; private PipTouchHandler mTouchHandler; + private PipAppOpsListener mAppOpsListener; /** * Handler for system task stack changes. @@ -76,8 +76,7 @@ public class PipManager implements BasePipManager { mTouchHandler.onActivityPinned(); mMediaController.onActivityPinned(); mMenuController.onActivityPinned(); - mNotificationController.onActivityPinned(packageName, userId, - true /* deferUntilAnimationEnds */); + mAppOpsListener.onActivityPinned(packageName); SystemServicesProxy.getInstance(mContext).setPipVisibility(true); } @@ -90,7 +89,7 @@ public class PipManager implements BasePipManager { final int userId = topActivity != null ? topPipActivityInfo.second : 0; mMenuController.onActivityUnpinned(); mTouchHandler.onActivityUnpinned(topActivity); - mNotificationController.onActivityUnpinned(topActivity, userId); + mAppOpsListener.onActivityUnpinned(); SystemServicesProxy.getInstance(mContext).setPipVisibility(topActivity != null); } @@ -107,7 +106,6 @@ public class PipManager implements BasePipManager { mTouchHandler.setTouchEnabled(true); mTouchHandler.onPinnedStackAnimationEnded(); mMenuController.onPinnedStackAnimationEnded(); - mNotificationController.onPinnedStackAnimationEnded(); } @Override @@ -182,7 +180,7 @@ public class PipManager implements BasePipManager { mInputConsumerController); mTouchHandler = new PipTouchHandler(context, mActivityManager, mMenuController, mInputConsumerController); - mNotificationController = new PipNotificationController(context, mActivityManager, + mAppOpsListener = new PipAppOpsListener(context, mActivityManager, mTouchHandler.getMotionHelper()); EventBus.getDefault().register(this); } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java index 90f7b8db1c59..bfe07a980ce7 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java @@ -16,6 +16,10 @@ package com.android.systemui.pip.phone; +import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; +import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS; + import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ACTIONS; import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ALLOW_TIMEOUT; import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_CONTROLLER_MESSENGER; @@ -39,6 +43,7 @@ import android.app.Activity; import android.app.ActivityManager; import android.app.PendingIntent.CanceledException; import android.app.RemoteAction; +import android.content.ComponentName; import android.content.Intent; import android.content.pm.ParceledListSlice; import android.graphics.Color; @@ -46,12 +51,15 @@ import android.graphics.PointF; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; +import android.util.Pair; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -105,6 +113,7 @@ public class PipMenuActivity extends Activity { private Drawable mBackgroundDrawable; private View mMenuContainer; private LinearLayout mActionsGroup; + private View mSettingsButton; private View mDismissButton; private ImageView mExpandButton; private int mBetweenActionPaddingLand; @@ -218,6 +227,11 @@ public class PipMenuActivity extends Activity { } return true; }); + mSettingsButton = findViewById(R.id.settings); + mSettingsButton.setAlpha(0); + mSettingsButton.setOnClickListener((v) -> { + showSettings(); + }); mDismissButton = findViewById(R.id.dismiss); mDismissButton.setAlpha(0); mDismissButton.setOnClickListener((v) -> { @@ -352,12 +366,14 @@ public class PipMenuActivity extends Activity { ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA, mMenuContainer.getAlpha(), 1f); menuAnim.addUpdateListener(mMenuBgUpdateListener); + ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, + mSettingsButton.getAlpha(), 1f); ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA, mDismissButton.getAlpha(), 1f); if (menuState == MENU_STATE_FULL) { - mMenuContainerAnimator.playTogether(menuAnim, dismissAnim); + mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim); } else { - mMenuContainerAnimator.play(dismissAnim); + mMenuContainerAnimator.playTogether(settingsAnim, dismissAnim); } mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN); mMenuContainerAnimator.setDuration(MENU_FADE_DURATION); @@ -394,9 +410,11 @@ public class PipMenuActivity extends Activity { ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA, mMenuContainer.getAlpha(), 0f); menuAnim.addUpdateListener(mMenuBgUpdateListener); + ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, + mSettingsButton.getAlpha(), 0f); ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA, mDismissButton.getAlpha(), 0f); - mMenuContainerAnimator.playTogether(menuAnim, dismissAnim); + mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim); mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT); mMenuContainerAnimator.setDuration(MENU_FADE_DURATION); mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() { @@ -526,12 +544,14 @@ public class PipMenuActivity extends Activity { final float menuAlpha = 1 - fraction; if (mMenuState == MENU_STATE_FULL) { mMenuContainer.setAlpha(menuAlpha); + mSettingsButton.setAlpha(menuAlpha); mDismissButton.setAlpha(menuAlpha); final float interpolatedAlpha = MENU_BACKGROUND_ALPHA * menuAlpha + DISMISS_BACKGROUND_ALPHA * fraction; alpha = (int) (interpolatedAlpha * 255); } else { if (mMenuState == MENU_STATE_CLOSE) { + mSettingsButton.setAlpha(menuAlpha); mDismissButton.setAlpha(menuAlpha); } alpha = (int) (fraction * DISMISS_BACKGROUND_ALPHA * 255); @@ -588,6 +608,19 @@ public class PipMenuActivity extends Activity { sendMessage(m, "Could not notify controller to show PIP menu"); } + private void showSettings() { + final Pair<ComponentName, Integer> topPipActivityInfo = + PipUtils.getTopPinnedActivity(this, ActivityManager.getService()); + if (topPipActivityInfo.first != null) { + final UserHandle user = UserHandle.of(topPipActivityInfo.second); + final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS, + Uri.fromParts("package", topPipActivityInfo.first.getPackageName(), null)); + settingsIntent.putExtra(Intent.EXTRA_USER_HANDLE, user); + settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK); + startActivity(settingsIntent); + } + } + private void notifyActivityCallback(Messenger callback) { Message m = Message.obtain(); m.what = PipMenuActivityController.MESSAGE_UPDATE_ACTIVITY_CALLBACK; diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java deleted file mode 100644 index 6d083e9d601d..000000000000 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * 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.pip.phone; - -import static android.app.AppOpsManager.MODE_ALLOWED; -import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE; -import static android.app.PendingIntent.FLAG_CANCEL_CURRENT; -import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; -import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; -import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS; - -import android.app.AppOpsManager; -import android.app.AppOpsManager.OnOpChangedListener; -import android.app.IActivityManager; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.graphics.Canvas; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.os.UserHandle; -import android.util.IconDrawableFactory; -import android.util.Log; -import android.util.Pair; - -import com.android.systemui.R; -import com.android.systemui.SystemUI; -import com.android.systemui.util.NotificationChannels; - -/** - * Manages the BTW notification that shows whenever an activity enters or leaves picture-in-picture. - */ -public class PipNotificationController { - private static final String TAG = PipNotificationController.class.getSimpleName(); - - private static final String NOTIFICATION_TAG = PipNotificationController.class.getName(); - private static final int NOTIFICATION_ID = 0; - - private Context mContext; - private IActivityManager mActivityManager; - private AppOpsManager mAppOpsManager; - private NotificationManager mNotificationManager; - private IconDrawableFactory mIconDrawableFactory; - - private PipMotionHelper mMotionHelper; - - // Used when building a deferred notification - private String mDeferredNotificationPackageName; - private int mDeferredNotificationUserId; - - private AppOpsManager.OnOpChangedListener mAppOpsChangedListener = new OnOpChangedListener() { - @Override - public void onOpChanged(String op, String packageName) { - try { - // Dismiss the PiP once the user disables the app ops setting for that package - final Pair<ComponentName, Integer> topPipActivityInfo = - PipUtils.getTopPinnedActivity(mContext, mActivityManager); - if (topPipActivityInfo.first != null) { - final ApplicationInfo appInfo = mContext.getPackageManager() - .getApplicationInfoAsUser(packageName, 0, topPipActivityInfo.second); - if (appInfo.packageName.equals(topPipActivityInfo.first.getPackageName()) && - mAppOpsManager.checkOpNoThrow(OP_PICTURE_IN_PICTURE, appInfo.uid, - packageName) != MODE_ALLOWED) { - mMotionHelper.dismissPip(); - } - } - } catch (NameNotFoundException e) { - // Unregister the listener if the package can't be found - unregisterAppOpsListener(); - } - } - }; - - public PipNotificationController(Context context, IActivityManager activityManager, - PipMotionHelper motionHelper) { - mContext = context; - mActivityManager = activityManager; - mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); - mNotificationManager = NotificationManager.from(context); - mMotionHelper = motionHelper; - mIconDrawableFactory = IconDrawableFactory.newInstance(context); - } - - public void onActivityPinned(String packageName, int userId, boolean deferUntilAnimationEnds) { - // Clear any existing notification - mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID); - - if (deferUntilAnimationEnds) { - mDeferredNotificationPackageName = packageName; - mDeferredNotificationUserId = userId; - } else { - showNotificationForApp(packageName, userId); - } - - // Register for changes to the app ops setting for this package while it is in PiP - registerAppOpsListener(packageName); - } - - public void onPinnedStackAnimationEnded() { - if (mDeferredNotificationPackageName != null) { - showNotificationForApp(mDeferredNotificationPackageName, mDeferredNotificationUserId); - mDeferredNotificationPackageName = null; - mDeferredNotificationUserId = 0; - } - } - - public void onActivityUnpinned(ComponentName topPipActivity, int userId) { - // Unregister for changes to the previously PiP'ed package - unregisterAppOpsListener(); - - // Reset the deferred notification package - mDeferredNotificationPackageName = null; - mDeferredNotificationUserId = 0; - - if (topPipActivity != null) { - // onActivityUnpinned() is only called after the transition is complete, so we don't - // need to defer until the animation ends to update the notification - onActivityPinned(topPipActivity.getPackageName(), userId, - false /* deferUntilAnimationEnds */); - } else { - mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID); - } - } - - /** - * Builds and shows the notification for the given app. - */ - private void showNotificationForApp(String packageName, int userId) { - // Build a new notification - try { - final UserHandle user = UserHandle.of(userId); - final Context userContext = mContext.createPackageContextAsUser( - mContext.getPackageName(), 0, user); - final Notification.Builder builder = - new Notification.Builder(userContext, NotificationChannels.GENERAL) - .setLocalOnly(true) - .setOngoing(true) - .setSmallIcon(R.drawable.pip_notification_icon) - .setColor(mContext.getColor( - com.android.internal.R.color.system_notification_accent_color)); - if (updateNotificationForApp(builder, packageName, user)) { - SystemUI.overrideNotificationAppName(mContext, builder); - - // Show the new notification - mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build()); - } - } catch (NameNotFoundException e) { - Log.e(TAG, "Could not show notification for application", e); - } - } - - /** - * Updates the notification builder with app-specific information, returning whether it was - * successful. - */ - private boolean updateNotificationForApp(Notification.Builder builder, String packageName, - UserHandle user) throws NameNotFoundException { - final PackageManager pm = mContext.getPackageManager(); - final ApplicationInfo appInfo; - try { - appInfo = pm.getApplicationInfoAsUser(packageName, 0, user.getIdentifier()); - } catch (NameNotFoundException e) { - Log.e(TAG, "Could not update notification for application", e); - return false; - } - - if (appInfo != null) { - final String appName = pm.getUserBadgedLabel(pm.getApplicationLabel(appInfo), user) - .toString(); - final String message = mContext.getString(R.string.pip_notification_message, appName); - final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS, - Uri.fromParts("package", packageName, null)); - settingsIntent.putExtra(Intent.EXTRA_USER_HANDLE, user); - settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK); - - final Drawable iconDrawable = mIconDrawableFactory.getBadgedIcon(appInfo); - builder.setContentTitle(mContext.getString(R.string.pip_notification_title, appName)) - .setContentText(message) - .setContentIntent(PendingIntent.getActivityAsUser(mContext, packageName.hashCode(), - settingsIntent, FLAG_CANCEL_CURRENT, null, user)) - .setStyle(new Notification.BigTextStyle().bigText(message)) - .setLargeIcon(createBitmap(iconDrawable).createAshmemBitmap()); - return true; - } - return false; - } - - private void registerAppOpsListener(String packageName) { - mAppOpsManager.startWatchingMode(OP_PICTURE_IN_PICTURE, packageName, - mAppOpsChangedListener); - } - - private void unregisterAppOpsListener() { - mAppOpsManager.stopWatchingMode(mAppOpsChangedListener); - } - - /** - * Bakes a drawable into a bitmap. - */ - private Bitmap createBitmap(Drawable d) { - Bitmap bitmap = Bitmap.createBitmap(d.getIntrinsicWidth(), d.getIntrinsicHeight(), - Config.ARGB_8888); - Canvas c = new Canvas(bitmap); - d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); - d.draw(c); - c.setBitmap(null); - return bitmap; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java index 0b7b6d555fdf..927a49cb60f3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java @@ -18,11 +18,6 @@ package com.android.systemui.qs; import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS; -import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_DATE; - -import android.app.ActivityManager; -import android.app.AlarmManager; -import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; @@ -31,7 +26,6 @@ import android.graphics.PorterDuff.Mode; import android.graphics.drawable.Drawable; import android.graphics.drawable.RippleDrawable; import android.os.UserManager; -import android.provider.AlarmClock; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import android.util.AttributeSet; @@ -39,24 +33,19 @@ import android.view.View; import android.view.View.OnClickListener; import android.widget.FrameLayout; import android.widget.ImageView; -import android.widget.TextView; import android.widget.Toast; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; -import com.android.keyguard.KeyguardStatusView; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.settingslib.Utils; import com.android.settingslib.drawable.UserIconDrawable; import com.android.systemui.Dependency; -import com.android.systemui.FontSizeUtils; import com.android.systemui.R; import com.android.systemui.R.dimen; -import com.android.systemui.R.id; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.qs.TouchAnimator.Builder; -import com.android.systemui.qs.TouchAnimator.ListenerAdapter; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.ExpandableIndicator; import com.android.systemui.statusbar.phone.MultiUserSwitch; @@ -65,8 +54,6 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener; import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; -import com.android.systemui.statusbar.policy.NextAlarmController; -import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback; import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener; import com.android.systemui.tuner.TunerService; diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk index 9d44895dbe8d..066cfe5862ef 100644 --- a/packages/SystemUI/tests/Android.mk +++ b/packages/SystemUI/tests/Android.mk @@ -46,7 +46,12 @@ LOCAL_STATIC_ANDROID_LIBRARIES := \ android-support-v7-mediarouter \ android-support-v7-palette \ android-support-v14-preference \ - android-support-v17-leanback + android-support-v17-leanback \ + android-slices-core \ + android-slices-view \ + android-slices-builders \ + apptoolkit-arch-core-runtime \ + apptoolkit-lifecycle-extensions \ LOCAL_STATIC_JAVA_LIBRARIES := \ metrics-helper-lib \ diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java index 4eae3426f815..be28569ef629 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java @@ -16,11 +16,10 @@ package com.android.systemui.keyguard; -import android.app.slice.Slice; -import android.app.slice.SliceItem; -import android.app.slice.SliceQuery; +import androidx.app.slice.Slice; import android.content.Intent; import android.net.Uri; +import android.os.Debug; import android.os.Handler; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; @@ -34,6 +33,9 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import androidx.app.slice.SliceItem; +import androidx.app.slice.core.SliceQuery; + @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper(setAsMainLooper = true) @@ -63,7 +65,8 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { @Test public void returnsValidSlice() { Slice slice = mProvider.onBindSlice(Uri.parse(KeyguardSliceProvider.KEYGUARD_SLICE_URI)); - SliceItem text = SliceQuery.find(slice, SliceItem.FORMAT_TEXT, Slice.HINT_TITLE, + SliceItem text = SliceQuery.find(slice, android.app.slice.SliceItem.FORMAT_TEXT, + android.app.slice.Slice.HINT_TITLE, null /* nonHints */); Assert.assertNotNull("Slice must provide a title.", text); } @@ -78,9 +81,10 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { @Test public void updatesClock() { + mProvider.mUpdateClockInvokations = 0; mProvider.mIntentReceiver.onReceive(getContext(), new Intent(Intent.ACTION_TIME_TICK)); TestableLooper.get(this).processAllMessages(); - Assert.assertEquals("Clock should have been updated.", 2 /* expected */, + Assert.assertEquals("Clock should have been updated.", 1 /* expected */, mProvider.mUpdateClockInvokations); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/leak/LeakDetectorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/leak/LeakDetectorTest.java index f1965a223dbc..56de32d3a401 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/leak/LeakDetectorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/leak/LeakDetectorTest.java @@ -33,6 +33,8 @@ import java.io.FileOutputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; +import java.util.function.BiConsumer; +import java.util.function.Consumer; @SmallTest @RunWith(AndroidJUnit4.class) @@ -40,6 +42,30 @@ public class LeakDetectorTest extends SysuiTestCase { private LeakDetector mLeakDetector; + // The references for which collection is observed are stored in fields. The allocation and + // of these references happens in separate methods (trackObjectWith/trackCollectionWith) + // from where they are set to null. The generated code might keep the allocated reference + // alive in a dex register when compiling in release mode. As R8 is used to compile this + // test the --dontoptimize flag is also required to ensure that these methods are not + // inlined, as that would defeat the purpose of having the mutation in methods. + private Object mObject; + private Collection<?> mCollection; + + private CollectionWaiter trackObjectWith(Consumer<Object> tracker) { + mObject = new Object(); + CollectionWaiter collectionWaiter = ReferenceTestUtils.createCollectionWaiter(mObject); + tracker.accept(mObject); + return collectionWaiter; + } + + private CollectionWaiter trackCollectionWith( + BiConsumer<? super Collection<?>, String> tracker) { + mCollection = new ArrayList<>(); + CollectionWaiter collectionWaiter = ReferenceTestUtils.createCollectionWaiter(mCollection); + tracker.accept(mCollection, "tag"); + return collectionWaiter; + } + @Before public void setup() { mLeakDetector = LeakDetector.create(); @@ -51,31 +77,22 @@ public class LeakDetectorTest extends SysuiTestCase { @Test public void trackInstance_doesNotLeakTrackedObject() { - Object object = new Object(); - CollectionWaiter collectionWaiter = ReferenceTestUtils.createCollectionWaiter(object); - - mLeakDetector.trackInstance(object); - object = null; + CollectionWaiter collectionWaiter = trackObjectWith(mLeakDetector::trackInstance); + mObject = null; collectionWaiter.waitForCollection(); } @Test public void trackCollection_doesNotLeakTrackedObject() { - Collection<?> object = new ArrayList<>(); - CollectionWaiter collectionWaiter = ReferenceTestUtils.createCollectionWaiter(object); - - mLeakDetector.trackCollection(object, "tag"); - object = null; + CollectionWaiter collectionWaiter = trackCollectionWith(mLeakDetector::trackCollection); + mCollection = null; collectionWaiter.waitForCollection(); } @Test public void trackGarbage_doesNotLeakTrackedObject() { - Object object = new Object(); - CollectionWaiter collectionWaiter = ReferenceTestUtils.createCollectionWaiter(object); - - mLeakDetector.trackGarbage(object); - object = null; + CollectionWaiter collectionWaiter = trackObjectWith(mLeakDetector::trackGarbage); + mObject = null; collectionWaiter.waitForCollection(); } @@ -108,4 +125,4 @@ public class LeakDetectorTest extends SysuiTestCase { FileOutputStream fos = new FileOutputStream("/dev/null"); mLeakDetector.dump(fos.getFD(), new PrintWriter(fos), new String[0]); } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/leak/WeakIdentityHashMapTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/leak/WeakIdentityHashMapTest.java index 9787df91f5de..ce6212ef5aae 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/leak/WeakIdentityHashMapTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/leak/WeakIdentityHashMapTest.java @@ -41,6 +41,13 @@ public class WeakIdentityHashMapTest extends SysuiTestCase { mMap = new WeakIdentityHashMap<>(); } + private CollectionWaiter addObjectToMap(WeakIdentityHashMap<Object, Object> map) { + Object object = new Object(); + CollectionWaiter collectionWaiter = ReferenceTestUtils.createCollectionWaiter(object); + map.put(object, "value"); + return collectionWaiter; + } + @Test public void testUsesIdentity() { String a1 = new String("a"); @@ -56,11 +63,12 @@ public class WeakIdentityHashMapTest extends SysuiTestCase { @Test public void testWeaklyReferences() { - Object object = new Object(); - CollectionWaiter collectionWaiter = ReferenceTestUtils.createCollectionWaiter(object); - - mMap.put(object, "value"); - object = null; + // Allocate and add an object to the weak map in a separate method to avoid a live + // reference to the allocated object in a dex register. As R8 is used to compile this + // test the --dontoptimize flag is also required to ensure that the method is not + // inlined, as that would defeat the purpose of having the allocation in a separate + // method. + CollectionWaiter collectionWaiter = addObjectToMap(mMap); // Wait until object has been collected. We'll also need to wait for mMap to become empty, // because our collection waiter may be told about the collection earlier than mMap. @@ -70,4 +78,4 @@ public class WeakIdentityHashMapTest extends SysuiTestCase { assertEquals(0, mMap.size()); assertTrue(mMap.isEmpty()); } -}
\ No newline at end of file +} diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index 935b787250fb..1aaa53837bf9 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -5094,6 +5094,36 @@ message MetricsEvent { // Tag used to report autofill field classification scores FIELD_AUTOFILL_MATCH_SCORE = 1274; + // ACTION: Usb config has been changed to charging + // CATEGORY: SETTINGS + // OS: P + ACTION_USB_CONFIG_CHARGING = 1275; + + // ACTION: Usb config has been changed to mtp (file transfer) + // CATEGORY: SETTINGS + // OS: P + ACTION_USB_CONFIG_MTP = 1276; + + // ACTION: Usb config has been changed to ptp (photo transfer) + // CATEGORY: SETTINGS + // OS: P + ACTION_USB_CONFIG_PTP = 1277; + + // ACTION: Usb config has been changed to rndis (usb tethering) + // CATEGORY: SETTINGS + // OS: P + ACTION_USB_CONFIG_RNDIS = 1278; + + // ACTION: Usb config has been changed to midi + // CATEGORY: SETTINGS + // OS: P + ACTION_USB_CONFIG_MIDI = 1279; + + // ACTION: Usb config has been changed to accessory + // CATEGORY: SETTINGS + // OS: P + ACTION_USB_CONFIG_ACCESSORY = 1280; + // ---- End P Constants, all P constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 50b0be1a11d2..ba8ce59803f7 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -17,6 +17,7 @@ package com.android.server.accessibility; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; +import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS; @@ -762,7 +763,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mPictureInPictureActionReplacingConnection = wrapper; wrapper.linkToDeath(); } - mSecurityPolicy.notifyWindowsChanged(); } } @@ -2283,6 +2283,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } + private void sendAccessibilityEventLocked(AccessibilityEvent event, int userId) { + // Resync to avoid calling out with the lock held + event.setEventTime(SystemClock.uptimeMillis()); + mMainHandler.obtainMessage( + MainHandler.MSG_SEND_ACCESSIBILITY_EVENT, userId, 0 /* unused */, event) + .sendToTarget(); + } + /** * AIDL-exposed method. System only. * Inform accessibility that a fingerprint gesture was performed @@ -2419,6 +2427,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub public static final int MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER = 13; public static final int MSG_SHOW_ACCESSIBILITY_BUTTON_CHOOSER = 14; public static final int MSG_INIT_SERVICE = 15; + public static final int MSG_SEND_ACCESSIBILITY_EVENT = 16; public MainHandler(Looper looper) { super(looper); @@ -2519,6 +2528,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub (AccessibilityServiceConnection) msg.obj; service.initializeService(); } break; + + case MSG_SEND_ACCESSIBILITY_EVENT: { + final AccessibilityEvent event = (AccessibilityEvent) msg.obj; + final int userId = msg.arg1; + sendAccessibilityEvent(event, userId); + } } } @@ -2533,7 +2548,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub AccessibilityEvent event = AccessibilityEvent.obtain( AccessibilityEvent.TYPE_ANNOUNCEMENT); event.getText().add(message); - sendAccessibilityEvent(event, mCurrentUserId); + sendAccessibilityEventLocked(event, mCurrentUserId); } } } @@ -2961,21 +2976,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub public class SecurityPolicy { public static final int INVALID_WINDOW_ID = -1; - private static final int RETRIEVAL_ALLOWING_EVENT_TYPES = - AccessibilityEvent.TYPE_VIEW_CLICKED - | AccessibilityEvent.TYPE_VIEW_FOCUSED - | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER - | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT - | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED - | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED - | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED - | AccessibilityEvent.TYPE_VIEW_SELECTED - | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED - | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED - | AccessibilityEvent.TYPE_VIEW_SCROLLED - | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED - | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED - | AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY; + private static final int KEEP_SOURCE_EVENT_TYPES = AccessibilityEvent.TYPE_VIEW_CLICKED + | AccessibilityEvent.TYPE_VIEW_FOCUSED + | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER + | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT + | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED + | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED + | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED + | AccessibilityEvent.TYPE_WINDOWS_CHANGED + | AccessibilityEvent.TYPE_VIEW_SELECTED + | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED + | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED + | AccessibilityEvent.TYPE_VIEW_SCROLLED + | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED + | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED + | AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY; // In Z order public List<AccessibilityWindowInfo> mWindows; @@ -3137,10 +3152,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mWindows = new ArrayList<>(); } - final int oldWindowCount = mWindows.size(); - for (int i = oldWindowCount - 1; i >= 0; i--) { - mWindows.remove(i).recycle(); - } + List<AccessibilityWindowInfo> oldWindowList = new ArrayList<>(mWindows); + SparseArray<AccessibilityWindowInfo> oldWindowsById = mA11yWindowInfoById.clone(); + + mWindows.clear(); mA11yWindowInfoById.clear(); for (int i = 0; i < mWindowInfoById.size(); i++) { @@ -3202,7 +3217,49 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - notifyWindowsChanged(); + sendEventsForChangedWindowsLocked(oldWindowList, oldWindowsById); + + final int oldWindowCount = oldWindowList.size(); + for (int i = oldWindowCount - 1; i >= 0; i--) { + oldWindowList.remove(i).recycle(); + } + } + + private void sendEventsForChangedWindowsLocked(List<AccessibilityWindowInfo> oldWindows, + SparseArray<AccessibilityWindowInfo> oldWindowsById) { + List<AccessibilityEvent> events = new ArrayList<>(); + // Send events for all removed windows + final int oldWindowsCount = oldWindows.size(); + for (int i = 0; i < oldWindowsCount; i++) { + final AccessibilityWindowInfo window = oldWindows.get(i); + if (mA11yWindowInfoById.get(window.getId()) == null) { + events.add(AccessibilityEvent.obtainWindowsChangedEvent( + window.getId(), AccessibilityEvent.WINDOWS_CHANGE_REMOVED)); + } + } + + // Look for other changes + int oldWindowIndex = 0; + final int newWindowCount = mWindows.size(); + for (int i = 0; i < newWindowCount; i++) { + final AccessibilityWindowInfo newWindow = mWindows.get(i); + final AccessibilityWindowInfo oldWindow = oldWindowsById.get(newWindow.getId()); + if (oldWindow == null) { + events.add(AccessibilityEvent.obtainWindowsChangedEvent( + newWindow.getId(), AccessibilityEvent.WINDOWS_CHANGE_ADDED)); + } else { + int changes = newWindow.differenceFrom(oldWindow); + if (changes != 0) { + events.add(AccessibilityEvent.obtainWindowsChangedEvent( + newWindow.getId(), changes)); + } + } + } + + final int numEvents = events.size(); + for (int i = 0; i < numEvents; i++) { + sendAccessibilityEventLocked(events.get(i), mCurrentUserId); + } } public boolean computePartialInteractiveRegionForWindowLocked(int windowId, @@ -3243,7 +3300,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } public void updateEventSourceLocked(AccessibilityEvent event) { - if ((event.getEventType() & RETRIEVAL_ALLOWING_EVENT_TYPES) == 0) { + if ((event.getEventType() & KEEP_SOURCE_EVENT_TYPES) == 0) { event.setSource((View) null); } } @@ -3357,46 +3414,55 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private void setActiveWindowLocked(int windowId) { if (mActiveWindowId != windowId) { + sendAccessibilityEventLocked( + AccessibilityEvent.obtainWindowsChangedEvent( + mActiveWindowId, AccessibilityEvent.WINDOWS_CHANGE_ACTIVE), + mCurrentUserId); + mActiveWindowId = windowId; if (mWindows != null) { final int windowCount = mWindows.size(); for (int i = 0; i < windowCount; i++) { AccessibilityWindowInfo window = mWindows.get(i); - window.setActive(window.getId() == windowId); + if (window.getId() == windowId) { + window.setActive(true); + sendAccessibilityEventLocked( + AccessibilityEvent.obtainWindowsChangedEvent(windowId, + AccessibilityEvent.WINDOWS_CHANGE_ACTIVE), + mCurrentUserId); + } else { + window.setActive(false); + } } } - notifyWindowsChanged(); } } private void setAccessibilityFocusedWindowLocked(int windowId) { if (mAccessibilityFocusedWindowId != windowId) { + sendAccessibilityEventLocked( + AccessibilityEvent.obtainWindowsChangedEvent( + mAccessibilityFocusedWindowId, + WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED), + mCurrentUserId); + mAccessibilityFocusedWindowId = windowId; if (mWindows != null) { final int windowCount = mWindows.size(); for (int i = 0; i < windowCount; i++) { AccessibilityWindowInfo window = mWindows.get(i); - window.setAccessibilityFocused(window.getId() == windowId); + if (window.getId() == windowId) { + window.setAccessibilityFocused(true); + sendAccessibilityEventLocked( + AccessibilityEvent.obtainWindowsChangedEvent( + windowId, WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED), + mCurrentUserId); + + } else { + window.setAccessibilityFocused(false); + } } } - - notifyWindowsChanged(); - } - } - - public void notifyWindowsChanged() { - if (mWindowsForAccessibilityCallback == null) { - return; - } - final long identity = Binder.clearCallingIdentity(); - try { - // Let the client know the windows changed. - AccessibilityEvent event = AccessibilityEvent.obtain( - AccessibilityEvent.TYPE_WINDOWS_CHANGED); - event.setEventTime(SystemClock.uptimeMillis()); - sendAccessibilityEvent(event, mCurrentUserId); - } finally { - Binder.restoreCallingIdentity(identity); } } diff --git a/services/accessibility/java/com/android/server/accessibility/GestureUtils.java b/services/accessibility/java/com/android/server/accessibility/GestureUtils.java index abfdb683c04c..d5b53bc686da 100644 --- a/services/accessibility/java/com/android/server/accessibility/GestureUtils.java +++ b/services/accessibility/java/com/android/server/accessibility/GestureUtils.java @@ -40,12 +40,6 @@ final class GestureUtils { return (deltaTime >= timeout); } - public static boolean isSamePointerContext(MotionEvent first, MotionEvent second) { - return (first.getPointerIdBits() == second.getPointerIdBits() - && first.getPointerId(first.getActionIndex()) - == second.getPointerId(second.getActionIndex())); - } - /** * Determines whether a two pointer gesture is a dragging one. * diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java index 9b2b4eb7ebee..74d2dddcdfb3 100644 --- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java @@ -229,7 +229,7 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { } void clearAndTransitionToStateDetecting() { - mCurrentState = mDelegatingState; + mCurrentState = mDetectingState; mDetectingState.clear(); mViewportDraggingState.clear(); mPanningScalingState.clear(); @@ -649,14 +649,19 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { break; case ACTION_MOVE: { if (isFingerDown() - && distance(mLastDown, /* move */ event) > mSwipeMinDistance - // For convenience, viewport dragging on 3tap&hold takes precedence - // over insta-delegating on 3tap&swipe - // (which is a rare combo to be used aside from magnification) - && !isMultiTapTriggered(2 /* taps */)) { - - // Swipe detected - delegate skipping timeout - transitionToDelegatingStateAndClear(); + && distance(mLastDown, /* move */ event) > mSwipeMinDistance) { + + // Swipe detected - transition immediately + + // For convenience, viewport dragging takes precedence + // over insta-delegating on 3tap&swipe + // (which is a rare combo to be used aside from magnification) + if (isMultiTapTriggered(2 /* taps */)) { + transitionTo(mViewportDraggingState); + clear(); + } else { + transitionToDelegatingStateAndClear(); + } } } break; @@ -755,10 +760,10 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { int policyFlags) { if (event.getActionMasked() == ACTION_DOWN) { mPreLastDown = mLastDown; - mLastDown = event; + mLastDown = MotionEvent.obtain(event); } else if (event.getActionMasked() == ACTION_UP) { mPreLastUp = mLastUp; - mLastUp = event; + mLastUp = MotionEvent.obtain(event); } MotionEventInfo info = MotionEventInfo.obtain(event, rawEvent, diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index e1cb154c88f5..cac7fedd0b00 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -479,7 +479,7 @@ public final class AutofillManagerService extends SystemService { if (service != null) { service.destroySessionsLocked(); service.updateLocked(disabled); - if (!service.isEnabled()) { + if (!service.isEnabledLocked()) { removeCachedServiceLocked(userId); } } @@ -621,6 +621,34 @@ public final class AutofillManagerService extends SystemService { } @Override + public String getDefaultFieldClassificationAlgorithm() throws RemoteException { + final int userId = UserHandle.getCallingUserId(); + + synchronized (mLock) { + final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + if (service != null) { + return service.getDefaultFieldClassificationAlgorithm(getCallingUid()); + } + } + + return null; + } + + @Override + public List<String> getAvailableFieldClassificationAlgorithms() throws RemoteException { + final int userId = UserHandle.getCallingUserId(); + + synchronized (mLock) { + final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + if (service != null) { + return service.getAvailableFieldClassificationAlgorithms(getCallingUid()); + } + } + + return null; + } + + @Override public ComponentName getAutofillServiceComponentName() throws RemoteException { final int userId = UserHandle.getCallingUserId(); diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 4cdfd625a8f0..65984dd5fdbe 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -51,6 +51,7 @@ import android.os.UserManager; import android.provider.Settings; import android.service.autofill.AutofillService; import android.service.autofill.AutofillServiceInfo; +import android.service.autofill.EditDistanceScorer; import android.service.autofill.FieldClassification; import android.service.autofill.FieldClassification.Match; import android.service.autofill.FillEventHistory; @@ -63,6 +64,8 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.DebugUtils; import android.util.LocalLog; +import android.util.Log; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; @@ -81,6 +84,7 @@ import com.android.server.autofill.ui.AutoFillUI; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Random; @@ -105,7 +109,10 @@ final class AutofillManagerServiceImpl { private final AutoFillUI mUi; private final MetricsLogger mMetricsLogger = new MetricsLogger(); + @GuardedBy("mLock") private RemoteCallbackList<IAutoFillManagerClient> mClients; + + @GuardedBy("mLock") private AutofillServiceInfo mInfo; private static final Random sRandom = new Random(); @@ -113,25 +120,74 @@ final class AutofillManagerServiceImpl { private final LocalLog mRequestsHistory; private final LocalLog mUiLatencyHistory; + // TODO(b/70939974): temporary, will be moved to ExtServices + static final class FieldClassificationAlgorithmService { + + /** + * Gets the name of all available algorithms. + */ + @NonNull + public List<String> getAvailableAlgorithms() { + return Arrays.asList(EditDistanceScorer.NAME); + } + + /** + * Gets the default algorithm that's used when an algorithm is not specified or is invalid. + */ + @NonNull + public String getDefaultAlgorithm() { + return EditDistanceScorer.NAME; + } + + /** + * Gets a field classification score. + * + * @param algorithmName algorithm to be used. If invalid, the default algorithm will be used + * instead. + * @param algorithmArgs optional arguments to be passed to the algorithm. + * @param actualValue value entered by the user. + * @param userDataValue value from the user data. + * + * @return pair containing the algorithm used and the score. + */ + // TODO(b/70939974): use parcelable instead of pair + Pair<String, Float> getScore(@NonNull String algorithmName, @Nullable Bundle algorithmArgs, + @NonNull AutofillValue actualValue, @NonNull String userDataValue) { + if (!EditDistanceScorer.NAME.equals(algorithmName)) { + Log.w(TAG, "Ignoring invalid algorithm (" + algorithmName + ") and using " + + EditDistanceScorer.NAME + " instead"); + } + return new Pair<>(EditDistanceScorer.NAME, + EditDistanceScorer.getInstance().getScore(actualValue, userDataValue)); + } + } + + private final FieldClassificationAlgorithmService mFcService = + new FieldClassificationAlgorithmService(); + /** * Apps disabled by the service; key is package name, value is when they will be enabled again. */ + @GuardedBy("mLock") private ArrayMap<String, Long> mDisabledApps; /** * Activities disabled by the service; key is component name, value is when they will be enabled * again. */ + @GuardedBy("mLock") private ArrayMap<ComponentName, Long> mDisabledActivities; /** * Whether service was disabled for user due to {@link UserManager} restrictions. */ + @GuardedBy("mLock") private boolean mDisabled; /** * Data used for field classification. */ + @GuardedBy("mLock") private UserData mUserData; /** @@ -235,7 +291,7 @@ final class AutofillManagerServiceImpl { } void updateLocked(boolean disabled) { - final boolean wasEnabled = isEnabled(); + final boolean wasEnabled = isEnabledLocked(); if (sVerbose) { Slog.v(TAG, "updateLocked(u=" + mUserId + "): wasEnabled=" + wasEnabled + ", mSetupComplete= " + mSetupComplete @@ -274,7 +330,7 @@ final class AutofillManagerServiceImpl { Slog.e(TAG, "Bad AutofillServiceInfo for '" + componentName + "': " + e); mInfo = null; } - final boolean isEnabled = isEnabled(); + final boolean isEnabled = isEnabledLocked(); if (wasEnabled != isEnabled) { if (!isEnabled) { final int sessionCount = mSessions.size(); @@ -292,7 +348,7 @@ final class AutofillManagerServiceImpl { mClients = new RemoteCallbackList<>(); } mClients.register(client); - return isEnabled(); + return isEnabledLocked(); } void removeClientLocked(IAutoFillManagerClient client) { @@ -302,7 +358,7 @@ final class AutofillManagerServiceImpl { } void setAuthenticationResultLocked(Bundle data, int sessionId, int authenticationId, int uid) { - if (!isEnabled()) { + if (!isEnabledLocked()) { return; } final Session session = mSessions.get(sessionId); @@ -312,7 +368,7 @@ final class AutofillManagerServiceImpl { } void setHasCallback(int sessionId, int uid, boolean hasIt) { - if (!isEnabled()) { + if (!isEnabledLocked()) { return; } final Session session = mSessions.get(sessionId); @@ -327,7 +383,7 @@ final class AutofillManagerServiceImpl { @NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId, @NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback, int flags, @NonNull ComponentName componentName) { - if (!isEnabled()) { + if (!isEnabledLocked()) { return 0; } @@ -388,7 +444,7 @@ final class AutofillManagerServiceImpl { } void finishSessionLocked(int sessionId, int uid) { - if (!isEnabled()) { + if (!isEnabledLocked()) { return; } @@ -411,7 +467,7 @@ final class AutofillManagerServiceImpl { } void cancelSessionLocked(int sessionId, int uid) { - if (!isEnabled()) { + if (!isEnabledLocked()) { return; } @@ -799,14 +855,15 @@ final class AutofillManagerServiceImpl { // Called by AutofillManager void setUserData(int callingUid, UserData userData) { synchronized (mLock) { - if (isCalledByServiceLocked("setUserData", callingUid)) { - mUserData = userData; - // Log it - int numberFields = mUserData == null ? 0: mUserData.getRemoteIds().length; - mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_USERDATA_UPDATED, - getServicePackageName(), null) - .setCounterValue(numberFields)); + if (!isCalledByServiceLocked("setUserData", callingUid)) { + return; } + mUserData = userData; + // Log it + int numberFields = mUserData == null ? 0: mUserData.getRemoteIds().length; + mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_USERDATA_UPDATED, + getServicePackageName(), null) + .setCounterValue(numberFields)); } } @@ -917,6 +974,11 @@ final class AutofillManagerServiceImpl { pw.println(); mUserData.dump(prefix2, pw); } + + pw.print(prefix); pw.print("Available Field Classification algorithms: "); + pw.println(mFcService.getAvailableAlgorithms()); + pw.print(prefix); pw.print("Default Field Classification algorithm: "); + pw.println(mFcService.getDefaultAlgorithm()); } void destroySessionsLocked() { @@ -964,11 +1026,13 @@ final class AutofillManagerServiceImpl { final IAutoFillManagerClient client = clients.getBroadcastItem(i); try { final boolean resetSession; + final boolean isEnabled; synchronized (mLock) { resetSession = resetClient || isClientSessionDestroyedLocked(client); + isEnabled = isEnabledLocked(); } int flags = 0; - if (isEnabled()) { + if (isEnabled) { flags |= AutofillManager.SET_STATE_FLAG_ENABLED; } if (resetSession) { @@ -1004,7 +1068,7 @@ final class AutofillManagerServiceImpl { return true; } - boolean isEnabled() { + boolean isEnabledLocked() { return mSetupComplete && mInfo != null && !mDisabled; } @@ -1093,9 +1157,9 @@ final class AutofillManagerServiceImpl { } // Called by AutofillManager, checks UID. - boolean isFieldClassificationEnabled(int uid) { + boolean isFieldClassificationEnabled(int callingUid) { synchronized (mLock) { - if (!isCalledByServiceLocked("isFieldClassificationEnabled", uid)) { + if (!isCalledByServiceLocked("isFieldClassificationEnabled", callingUid)) { return false; } return isFieldClassificationEnabledLocked(); @@ -1110,6 +1174,28 @@ final class AutofillManagerServiceImpl { mUserId) == 1; } + FieldClassificationAlgorithmService getFieldClassificationService() { + return mFcService; + } + + List<String> getAvailableFieldClassificationAlgorithms(int callingUid) { + synchronized (mLock) { + if (!isCalledByServiceLocked("getFCAlgorithms()", callingUid)) { + return null; + } + } + return mFcService.getAvailableAlgorithms(); + } + + String getDefaultFieldClassificationAlgorithm(int callingUid) { + synchronized (mLock) { + if (!isCalledByServiceLocked("getDefaultFCAlgorithm()", callingUid)) { + return null; + } + } + return mFcService.getDefaultAlgorithm(); + } + @Override public String toString() { return "AutofillManagerServiceImpl: [userId=" + mUserId diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 01f908407691..96296907b2c5 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -69,6 +69,7 @@ import android.service.autofill.FieldClassification; import android.util.ArrayMap; import android.util.ArraySet; import android.util.LocalLog; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; @@ -84,6 +85,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.HandlerCaller; import com.android.internal.util.ArrayUtils; +import com.android.server.autofill.AutofillManagerServiceImpl.FieldClassificationAlgorithmService; import com.android.server.autofill.ui.AutoFillUI; import com.android.server.autofill.ui.PendingUi; @@ -1088,7 +1090,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Sets field classification score for field if (userData!= null) { - setScore(detectedFieldIds, detectedFieldClassifications, userData, + setFieldClassificationScore(mService.getFieldClassificationService(), + detectedFieldIds, detectedFieldClassifications, userData, viewState.id, currentValue); } } // else @@ -1133,7 +1136,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * Adds the matches to {@code detectedFieldsIds} and {@code detectedFieldClassifications} for * {@code fieldId} based on its {@code currentValue} and {@code userData}. */ - private static void setScore(@NonNull ArrayList<AutofillId> detectedFieldIds, + private static void setFieldClassificationScore( + @NonNull AutofillManagerServiceImpl.FieldClassificationAlgorithmService service, + @NonNull ArrayList<AutofillId> detectedFieldIds, @NonNull ArrayList<FieldClassification> detectedFieldClassifications, @NonNull UserData userData, @NonNull AutofillId fieldId, @NonNull AutofillValue currentValue) { @@ -1150,11 +1155,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } + final String algorithm = userData.getFieldClassificationAlgorithm(); + final Bundle algorithmArgs = userData.getAlgorithmArgs(); ArrayList<Match> matches = null; for (int i = 0; i < userValues.length; i++) { String remoteId = remoteIds[i]; final String value = userValues[i]; - final float score = userData.getScorer().getScore(currentValue, value); + final Pair<String, Float> result = service.getScore(algorithm, algorithmArgs, + currentValue, value); + final String actualAlgorithm = result.first; + final float score = result.second; if (score > 0) { if (sVerbose) { Slog.v(TAG, "adding score " + score + " at index " + i + " and id " + fieldId); @@ -1162,7 +1172,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (matches == null) { matches = new ArrayList<>(userValues.length); } - matches.add(new Match(remoteId, score)); + matches.add(new Match(remoteId, score, actualAlgorithm)); } else if (sVerbose) Slog.v(TAG, "skipping score 0 at index " + i + " and id " + fieldId); } diff --git a/services/backup/java/com/android/server/backup/BackupManagerConstants.java b/services/backup/java/com/android/server/backup/BackupManagerConstants.java index 537592e33d5c..b17f79480036 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerConstants.java +++ b/services/backup/java/com/android/server/backup/BackupManagerConstants.java @@ -121,7 +121,7 @@ class BackupManagerConstants extends ContentObserver { DEFAULT_FULL_BACKUP_REQUIRE_CHARGING); mFullBackupRequiredNetworkType = mParser.getInt(FULL_BACKUP_REQUIRED_NETWORK_TYPE, DEFAULT_FULL_BACKUP_REQUIRED_NETWORK_TYPE); - final String backupFinishedNotificationReceivers = mParser.getString( + String backupFinishedNotificationReceivers = mParser.getString( BACKUP_FINISHED_NOTIFICATION_RECEIVERS, DEFAULT_BACKUP_FINISHED_NOTIFICATION_RECEIVERS); if (backupFinishedNotificationReceivers.isEmpty()) { @@ -190,6 +190,9 @@ class BackupManagerConstants extends ContentObserver { return mFullBackupRequiredNetworkType; } + /** + * Returns an array of package names that should be notified whenever a backup finishes. + */ public synchronized String[] getBackupFinishedNotificationReceivers() { if (RefactoredBackupManagerService.DEBUG_SCHEDULING) { Slog.v(TAG, "getBackupFinishedNotificationReceivers(...) returns " diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java index 3a37459843be..51c44e103cc4 100644 --- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java @@ -1424,6 +1424,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter final Intent notification = new Intent(); notification.setAction(BACKUP_FINISHED_ACTION); notification.setPackage(receiver); + notification.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES | + Intent.FLAG_RECEIVER_FOREGROUND); notification.putExtra(BACKUP_FINISHED_PACKAGE_EXTRA, packageName); mContext.sendBroadcastAsUser(notification, UserHandle.OWNER); } diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 06cf9820ca5f..472723dfa909 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -3035,15 +3035,8 @@ class AlarmManagerService extends SystemService { Slog.v(TAG, "sending alarm " + alarm); } if (RECORD_ALARMS_IN_HISTORY) { - if (alarm.workSource != null && alarm.workSource.size() > 0) { - for (int wi=0; wi<alarm.workSource.size(); wi++) { - ActivityManager.noteAlarmStart( - alarm.operation, alarm.workSource.get(wi), alarm.statsTag); - } - } else { - ActivityManager.noteAlarmStart( - alarm.operation, alarm.uid, alarm.statsTag); - } + ActivityManager.noteAlarmStart(alarm.operation, alarm.workSource, alarm.uid, + alarm.statsTag); } mDeliveryTracker.deliverLocked(alarm, nowELAPSED, allowWhileIdle); } catch (RuntimeException e) { @@ -3553,15 +3546,8 @@ class AlarmManagerService extends SystemService { fs.aggregateTime += nowELAPSED - fs.startTime; } if (RECORD_ALARMS_IN_HISTORY) { - if (inflight.mWorkSource != null && inflight.mWorkSource.size() > 0) { - for (int wi=0; wi<inflight.mWorkSource.size(); wi++) { - ActivityManager.noteAlarmFinish( - inflight.mPendingIntent, inflight.mWorkSource.get(wi), inflight.mTag); - } - } else { - ActivityManager.noteAlarmFinish( - inflight.mPendingIntent, inflight.mUid, inflight.mTag); - } + ActivityManager.noteAlarmFinish(inflight.mPendingIntent, inflight.mWorkSource, + inflight.mUid, inflight.mTag); } } @@ -3771,18 +3757,9 @@ class AlarmManagerService extends SystemService { || alarm.type == RTC_WAKEUP) { bs.numWakeup++; fs.numWakeup++; - if (alarm.workSource != null && alarm.workSource.size() > 0) { - for (int wi=0; wi<alarm.workSource.size(); wi++) { - final String wsName = alarm.workSource.getName(wi); - ActivityManager.noteWakeupAlarm( - alarm.operation, alarm.workSource.get(wi), - (wsName != null) ? wsName : alarm.packageName, - alarm.statsTag); - } - } else { - ActivityManager.noteWakeupAlarm( - alarm.operation, alarm.uid, alarm.packageName, alarm.statsTag); - } + ActivityManager.noteWakeupAlarm( + alarm.operation, alarm.workSource, alarm.uid, alarm.packageName, + alarm.statsTag); } } } diff --git a/services/core/java/com/android/server/EntropyMixer.java b/services/core/java/com/android/server/EntropyMixer.java index 9877717943df..5e6e9d34dc25 100644 --- a/services/core/java/com/android/server/EntropyMixer.java +++ b/services/core/java/com/android/server/EntropyMixer.java @@ -196,11 +196,14 @@ public class EntropyMixer extends Binder { * Mixes in the output from HW RNG (if present) into the Linux RNG. */ private void addHwRandomEntropy() { + if (!new File(hwRandomDevice).exists()) { + // HW RNG not present/exposed -- ignore + return; + } + try { RandomBlock.fromFile(hwRandomDevice).toFile(randomDevice, false); Slog.i(TAG, "Added HW RNG output to entropy pool"); - } catch (FileNotFoundException ignored) { - // HW RNG not present/exposed -- ignore } catch (IOException e) { Slog.w(TAG, "Failed to add HW RNG output to entropy pool", e); } diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index d3ab1259c9ed..989cb886c126 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -19,6 +19,7 @@ package com.android.server; import static android.Manifest.permission.DUMP; import static android.net.IpSecManager.INVALID_RESOURCE_ID; import static android.system.OsConstants.AF_INET; +import static android.system.OsConstants.EINVAL; import static android.system.OsConstants.IPPROTO_UDP; import static android.system.OsConstants.SOCK_DGRAM; import static com.android.internal.util.Preconditions.checkNotNull; @@ -1220,7 +1221,11 @@ public class IpSecService extends IIpSecService.Stub { info.getSpiRecord(direction).getSpi()); } } catch (ServiceSpecificException e) { - // FIXME: get the error code and throw is at an IOException from Errno Exception + if (e.errorCode == EINVAL) { + throw new IllegalArgumentException(e.toString()); + } else { + throw e; + } } } diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java index 44c02270fe6c..33f77697f88c 100644 --- a/services/core/java/com/android/server/NetworkScoreService.java +++ b/services/core/java/com/android/server/NetworkScoreService.java @@ -26,6 +26,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.database.ContentObserver; import android.location.LocationManager; import android.net.INetworkRecommendationProvider; @@ -50,14 +51,17 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings.Global; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.IntArray; import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.PackageMonitor; import com.android.internal.os.TransferPipe; +import com.android.internal.telephony.SmsApplication; import com.android.internal.util.DumpUtils; import java.io.FileDescriptor; @@ -91,7 +95,8 @@ public class NetworkScoreService extends INetworkScoreService.Stub { private final Object mPackageMonitorLock = new Object(); private final Object mServiceConnectionLock = new Object(); private final Handler mHandler; - private final DispatchingContentObserver mContentObserver; + private final DispatchingContentObserver mRecommendationSettingsObserver; + private final ContentObserver mUseOpenWifiPackageObserver; private final Function<NetworkScorerAppData, ScoringServiceConnection> mServiceConnProducer; @GuardedBy("mPackageMonitorLock") @@ -255,8 +260,40 @@ public class NetworkScoreService extends INetworkScoreService.Stub { mContext.registerReceiverAsUser( mLocationModeReceiver, UserHandle.SYSTEM, locationModeFilter, null /* broadcastPermission*/, mHandler); - mContentObserver = new DispatchingContentObserver(context, mHandler); + mRecommendationSettingsObserver = new DispatchingContentObserver(context, mHandler); mServiceConnProducer = serviceConnProducer; + mUseOpenWifiPackageObserver = new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange, Uri uri, int userId) { + Uri useOpenWifiPkgUri = Global.getUriFor(Global.USE_OPEN_WIFI_PACKAGE); + if (useOpenWifiPkgUri.equals(uri)) { + String useOpenWifiPackage = Global.getString(mContext.getContentResolver(), + Global.USE_OPEN_WIFI_PACKAGE); + if (!TextUtils.isEmpty(useOpenWifiPackage)) { + LocalServices.getService(PackageManagerInternal.class) + .grantDefaultPermissionsToDefaultUseOpenWifiApp(useOpenWifiPackage, + userId); + } + } + } + }; + mContext.getContentResolver().registerContentObserver( + Global.getUriFor(Global.USE_OPEN_WIFI_PACKAGE), + false /*notifyForDescendants*/, + mUseOpenWifiPackageObserver); + // Set a callback for the package manager to query the use open wifi app. + LocalServices.getService(PackageManagerInternal.class).setUseOpenWifiAppPackagesProvider( + new PackageManagerInternal.PackagesProvider() { + @Override + public String[] getPackages(int userId) { + String useOpenWifiPackage = Global.getString(mContext.getContentResolver(), + Global.USE_OPEN_WIFI_PACKAGE); + if (!TextUtils.isEmpty(useOpenWifiPackage)) { + return new String[]{useOpenWifiPackage}; + } + return null; + } + }); } /** Called when the system is ready to run third-party code but before it actually does so. */ @@ -287,11 +324,11 @@ public class NetworkScoreService extends INetworkScoreService.Stub { private void registerRecommendationSettingsObserver() { final Uri packageNameUri = Global.getUriFor(Global.NETWORK_RECOMMENDATIONS_PACKAGE); - mContentObserver.observe(packageNameUri, + mRecommendationSettingsObserver.observe(packageNameUri, ServiceHandler.MSG_RECOMMENDATIONS_PACKAGE_CHANGED); final Uri settingUri = Global.getUriFor(Global.NETWORK_RECOMMENDATIONS_ENABLED); - mContentObserver.observe(settingUri, + mRecommendationSettingsObserver.observe(settingUri, ServiceHandler.MSG_RECOMMENDATION_ENABLED_SETTING_CHANGED); } diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 31aea63875ea..46eea78c7f8b 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -5595,24 +5595,25 @@ public class AccountManagerService long ident = Binder.clearCallingIdentity(); try { packages = mPackageManager.getPackagesForUid(callingUid); - } finally { - Binder.restoreCallingIdentity(ident); - } - if (packages != null) { - for (String name : packages) { - try { - PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */); - if (packageInfo != null - && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) - != 0) { - return true; + if (packages != null) { + for (String name : packages) { + try { + PackageInfo packageInfo = + mPackageManager.getPackageInfo(name, 0 /* flags */); + if (packageInfo != null + && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) + != 0) { + return true; + } + } catch (NameNotFoundException e) { + Log.w(TAG, String.format("Could not find package [%s]", name), e); } - } catch (NameNotFoundException e) { - Log.w(TAG, String.format("Could not find package [%s]", name), e); } + } else { + Log.w(TAG, "No known packages with uid " + callingUid); } - } else { - Log.w(TAG, "No known packages with uid " + callingUid); + } finally { + Binder.restoreCallingIdentity(ident); } return false; } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index d92b3b86d47a..5936ce1f65de 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -13858,68 +13858,100 @@ public class ActivityManagerService extends IActivityManager.Stub Context.WINDOW_SERVICE)).addView(v, lp); } - public void noteWakeupAlarm(IIntentSender sender, int sourceUid, String sourcePkg, String tag) { - if (sender != null && !(sender instanceof PendingIntentRecord)) { - return; + @Override + public void noteWakeupAlarm(IIntentSender sender, WorkSource workSource, int sourceUid, + String sourcePkg, String tag) { + if (workSource != null && workSource.isEmpty()) { + workSource = null; } - final PendingIntentRecord rec = (PendingIntentRecord)sender; - final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); - synchronized (stats) { - if (mBatteryStatsService.isOnBattery()) { - mBatteryStatsService.enforceCallingPermission(); - int MY_UID = Binder.getCallingUid(); - final int uid; - if (sender == null) { - uid = sourceUid; - } else { - uid = rec.uid == MY_UID ? SYSTEM_UID : rec.uid; + + if (sourceUid <= 0 && workSource == null) { + // Try and derive a UID to attribute things to based on the caller. + if (sender != null) { + if (!(sender instanceof PendingIntentRecord)) { + return; } - BatteryStatsImpl.Uid.Pkg pkg = - stats.getPackageStatsLocked(sourceUid >= 0 ? sourceUid : uid, - sourcePkg != null ? sourcePkg : rec.key.packageName); - pkg.noteWakeupAlarmLocked(tag); - StatsLog.write(StatsLog.WAKEUP_ALARM_OCCURRED, sourceUid >= 0 ? sourceUid : uid, - tag); + + final PendingIntentRecord rec = (PendingIntentRecord) sender; + final int callerUid = Binder.getCallingUid(); + sourceUid = rec.uid == callerUid ? SYSTEM_UID : rec.uid; + } else { + // TODO(narayan): Should we throw an exception in this case ? It means that we + // haven't been able to derive a UID to attribute things to. + return; } } + + if (DEBUG_POWER) { + Slog.w(TAG, "noteWakupAlarm[ sourcePkg=" + sourcePkg + ", sourceUid=" + sourceUid + + ", workSource=" + workSource + ", tag=" + tag + "]"); + } + + mBatteryStatsService.noteWakupAlarm(sourcePkg, sourceUid, workSource, tag); } - public void noteAlarmStart(IIntentSender sender, int sourceUid, String tag) { - if (sender != null && !(sender instanceof PendingIntentRecord)) { - return; + @Override + public void noteAlarmStart(IIntentSender sender, WorkSource workSource, int sourceUid, + String tag) { + if (workSource != null && workSource.isEmpty()) { + workSource = null; } - final PendingIntentRecord rec = (PendingIntentRecord)sender; - final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); - synchronized (stats) { - mBatteryStatsService.enforceCallingPermission(); - int MY_UID = Binder.getCallingUid(); - final int uid; - if (sender == null) { - uid = sourceUid; + + if (sourceUid <= 0 && workSource == null) { + // Try and derive a UID to attribute things to based on the caller. + if (sender != null) { + if (!(sender instanceof PendingIntentRecord)) { + return; + } + + final PendingIntentRecord rec = (PendingIntentRecord) sender; + final int callerUid = Binder.getCallingUid(); + sourceUid = rec.uid == callerUid ? SYSTEM_UID : rec.uid; } else { - uid = rec.uid == MY_UID ? SYSTEM_UID : rec.uid; + // TODO(narayan): Should we throw an exception in this case ? It means that we + // haven't been able to derive a UID to attribute things to. + return; } - mBatteryStatsService.noteAlarmStart(tag, sourceUid >= 0 ? sourceUid : uid); } + + if (DEBUG_POWER) { + Slog.w(TAG, "noteAlarmStart[sourceUid=" + sourceUid + ", workSource=" + workSource + + ", tag=" + tag + "]"); + } + + mBatteryStatsService.noteAlarmStart(tag, workSource, sourceUid); } - public void noteAlarmFinish(IIntentSender sender, int sourceUid, String tag) { - if (sender != null && !(sender instanceof PendingIntentRecord)) { - return; + @Override + public void noteAlarmFinish(IIntentSender sender, WorkSource workSource, int sourceUid, + String tag) { + if (workSource != null && workSource.isEmpty()) { + workSource = null; } - final PendingIntentRecord rec = (PendingIntentRecord)sender; - final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); - synchronized (stats) { - mBatteryStatsService.enforceCallingPermission(); - int MY_UID = Binder.getCallingUid(); - final int uid; - if (sender == null) { - uid = sourceUid; + + if (sourceUid <= 0 && workSource == null) { + // Try and derive a UID to attribute things to based on the caller. + if (sender != null) { + if (!(sender instanceof PendingIntentRecord)) { + return; + } + + final PendingIntentRecord rec = (PendingIntentRecord) sender; + final int callerUid = Binder.getCallingUid(); + sourceUid = rec.uid == callerUid ? SYSTEM_UID : rec.uid; } else { - uid = rec.uid == MY_UID ? SYSTEM_UID : rec.uid; + // TODO(narayan): Should we throw an exception in this case ? It means that we + // haven't been able to derive a UID to attribute things to. + return; } - mBatteryStatsService.noteAlarmFinish(tag, sourceUid >= 0 ? sourceUid : uid); } + + if (DEBUG_POWER) { + Slog.w(TAG, "noteAlarmFinish[sourceUid=" + sourceUid + ", workSource=" + workSource + + ", tag=" + tag + "]"); + } + + mBatteryStatsService.noteAlarmFinish(tag, workSource, sourceUid); } public boolean killPids(int[] pids, String pReason, boolean secure) { diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 0a42aa9cce63..21085fa2f717 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -3116,6 +3116,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Need to make sure the pinned stack exist so we can resize it below... stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP); + // Calculate the target bounds here before the task is reparented back into pinned windowing + // mode (which will reset the saved bounds) + final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio); + try { final TaskRecord task = r.getTask(); // Resize the pinned stack to match the current size of the task the activity we are @@ -3154,11 +3158,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mWindowManager.continueSurfaceLayout(); } - // Calculate the default bounds (don't use existing stack bounds as we may have just created - // the stack, and schedule the start of the animation into PiP (the bounds animator that - // is triggered by this is posted on another thread) - final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio); - stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */, true /* fromFullscreen */); diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 35318f655bc4..430320a58e9c 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -38,6 +38,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManagerInternal; import android.os.WorkSource; +import android.os.WorkSource.WorkChain; import android.os.connectivity.CellularBatteryStats; import android.os.health.HealthStatsParceler; import android.os.health.HealthStatsWriter; @@ -66,6 +67,7 @@ import java.nio.CharBuffer; import java.nio.charset.CharsetDecoder; import java.nio.charset.CodingErrorAction; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutionException; @@ -446,17 +448,24 @@ public final class BatteryStatsService extends IBatteryStats.Stub } } - public void noteAlarmStart(String name, int uid) { + public void noteWakupAlarm(String name, int uid, WorkSource workSource, String tag) { enforceCallingPermission(); synchronized (mStats) { - mStats.noteAlarmStartLocked(name, uid); + mStats.noteWakupAlarmLocked(name, uid, workSource, tag); } } - public void noteAlarmFinish(String name, int uid) { + public void noteAlarmStart(String name, WorkSource workSource, int uid) { enforceCallingPermission(); synchronized (mStats) { - mStats.noteAlarmFinishLocked(name, uid); + mStats.noteAlarmStartLocked(name, workSource, uid); + } + } + + public void noteAlarmFinish(String name, WorkSource workSource, int uid) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteAlarmFinishLocked(name, workSource, uid); } } diff --git a/services/core/java/com/android/server/am/ClientLifecycleManager.java b/services/core/java/com/android/server/am/ClientLifecycleManager.java index 1e7080980d9e..014f7086efa3 100644 --- a/services/core/java/com/android/server/am/ClientLifecycleManager.java +++ b/services/core/java/com/android/server/am/ClientLifecycleManager.java @@ -21,7 +21,6 @@ import android.app.IApplicationThread; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.ClientTransactionItem; import android.app.servertransaction.ActivityLifecycleItem; -import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -44,12 +43,8 @@ class ClientLifecycleManager { */ void scheduleTransaction(ClientTransaction transaction) throws RemoteException { transaction.schedule(); - if (!(transaction.getClient() instanceof Binder)) { - // If client is not an instance of Binder - it's a remote call and at this point it is - // safe to recycle the object. All objects used for local calls will be recycled after - // the transaction is executed on client in ActivityThread. - transaction.recycle(); - } + // TODO: b/70616950 + //transaction.recycle(); } /** diff --git a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java index 3064144072ae..ef5166579cb3 100644 --- a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java +++ b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java @@ -25,6 +25,7 @@ import android.hardware.radio.ITuner; import android.hardware.radio.ITunerCallback; import android.hardware.radio.RadioManager; import android.os.ParcelableException; +import android.util.Slog; import com.android.server.SystemService; @@ -33,6 +34,8 @@ import java.util.Objects; import java.util.OptionalInt; public class BroadcastRadioService extends SystemService { + private static final String TAG = "BcRadioSrv"; + private final ServiceImpl mServiceImpl = new ServiceImpl(); private final com.android.server.broadcastradio.hal1.BroadcastRadioService mHal1 = @@ -84,13 +87,14 @@ public class BroadcastRadioService extends SystemService { @Override public ITuner openTuner(int moduleId, RadioManager.BandConfig bandConfig, boolean withAudio, ITunerCallback callback) { + Slog.i(TAG, "openTuner(" + moduleId + ", _, " + withAudio + ", _)"); enforcePolicyAccess(); if (callback == null) { throw new IllegalArgumentException("Callback must not be empty"); } synchronized (mLock) { if (mHal2.hasModule(moduleId)) { - throw new RuntimeException("Not implemented"); + return mHal2.openSession(moduleId, callback); } else { return mHal1.openTuner(moduleId, bandConfig, withAudio, callback); } diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java index 7629477438e4..413a27ce9af0 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java @@ -17,6 +17,8 @@ package com.android.server.broadcastradio.hal2; import android.annotation.NonNull; +import android.hardware.radio.ITuner; +import android.hardware.radio.ITunerCallback; import android.hardware.radio.RadioManager; import android.hardware.broadcastradio.V2_0.IBroadcastRadio; import android.hidl.manager.V1_0.IServiceManager; @@ -28,6 +30,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; public class BroadcastRadioService { @@ -76,4 +79,15 @@ public class BroadcastRadioService { public boolean hasModule(int id) { return mModules.containsKey(id); } + + public ITuner openSession(int moduleId, @NonNull ITunerCallback callback) { + Objects.requireNonNull(callback); + + RadioModule module = mModules.get(moduleId); + if (module == null) { + throw new IllegalArgumentException("Invalid module ID"); + } + + return module.openSession(callback); + } } diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java index c3394e9e0df5..434e2620a324 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java @@ -18,12 +18,17 @@ package com.android.server.broadcastradio.hal2; import android.annotation.NonNull; import android.annotation.Nullable; +import android.hardware.broadcastradio.V2_0.AmFmBandRange; +import android.hardware.broadcastradio.V2_0.AmFmRegionConfig; import android.hardware.broadcastradio.V2_0.Properties; +import android.hardware.broadcastradio.V2_0.Result; import android.hardware.broadcastradio.V2_0.VendorKeyValue; import android.hardware.radio.ProgramSelector; import android.hardware.radio.RadioManager; +import android.os.ParcelableException; import android.util.Slog; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -35,6 +40,28 @@ import java.util.Set; class Convert { private static final String TAG = "BcRadio2Srv.convert"; + static void throwOnError(String action, int result) { + switch (result) { + case Result.OK: + return; + case Result.UNKNOWN_ERROR: + throw new ParcelableException(new RuntimeException(action + ": UNKNOWN_ERROR")); + case Result.INTERNAL_ERROR: + throw new ParcelableException(new RuntimeException(action + ": INTERNAL_ERROR")); + case Result.INVALID_ARGUMENTS: + throw new IllegalArgumentException(action + ": INVALID_ARGUMENTS"); + case Result.INVALID_STATE: + throw new IllegalStateException(action + ": INVALID_STATE"); + case Result.NOT_SUPPORTED: + throw new UnsupportedOperationException(action + ": NOT_SUPPORTED"); + case Result.TIMEOUT: + throw new ParcelableException(new RuntimeException(action + ": TIMEOUT")); + default: + throw new ParcelableException(new RuntimeException( + action + ": unknown error (" + result + ")")); + } + } + private static @NonNull Map<String, String> vendorInfoFromHal(@Nullable List<VendorKeyValue> info) { if (info == null) return Collections.emptyMap(); @@ -94,13 +121,48 @@ class Convert { return pTypes.stream().mapToInt(Integer::intValue).toArray(); } + private static @NonNull RadioManager.BandDescriptor[] + amfmConfigToBands(@Nullable AmFmRegionConfig config) { + if (config == null) return new RadioManager.BandDescriptor[0]; + + int len = config.ranges.size(); + List<RadioManager.BandDescriptor> bands = new ArrayList<>(len); + + // Just a dummy value. + int region = RadioManager.REGION_ITU_1; + + for (AmFmBandRange range : config.ranges) { + FrequencyBand bandType = Utils.getBand(range.lowerBound); + if (bandType == FrequencyBand.UNKNOWN) { + Slog.e(TAG, "Unknown frequency band at " + range.lowerBound + "kHz"); + continue; + } + if (bandType == FrequencyBand.FM) { + bands.add(new RadioManager.FmBandDescriptor(region, RadioManager.BAND_FM, + range.lowerBound, range.upperBound, range.spacing, + + // TODO(b/69958777): stereo, rds, ta, af, ea + true, true, true, true, true + )); + } else { // AM + bands.add(new RadioManager.AmBandDescriptor(region, RadioManager.BAND_AM, + range.lowerBound, range.upperBound, range.spacing, + + // TODO(b/69958777): stereo + true + )); + } + } + + return bands.toArray(new RadioManager.BandDescriptor[bands.size()]); + } + static @NonNull RadioManager.ModuleProperties - propertiesFromHal(int id, @NonNull String serviceName, Properties prop) { + propertiesFromHal(int id, @NonNull String serviceName, @NonNull Properties prop, + @Nullable AmFmRegionConfig amfmConfig) { + Objects.requireNonNull(serviceName); Objects.requireNonNull(prop); - // TODO(b/69958423): implement region info - RadioManager.BandDescriptor[] bands = new RadioManager.BandDescriptor[0]; - int[] supportedIdentifierTypes = prop.supportedIdentifierTypes.stream(). mapToInt(Integer::intValue).toArray(); int[] supportedProgramTypes = identifierTypesToProgramTypes(supportedIdentifierTypes); @@ -123,7 +185,7 @@ class Convert { 1, // numAudioSources false, // isCaptureSupported - bands, + amfmConfigToBands(amfmConfig), true, // isBgScanSupported is deprecated supportedProgramTypes, supportedIdentifierTypes, diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Mutable.java b/services/core/java/com/android/server/broadcastradio/hal2/Mutable.java new file mode 100644 index 000000000000..a9d80549f963 --- /dev/null +++ b/services/core/java/com/android/server/broadcastradio/hal2/Mutable.java @@ -0,0 +1,46 @@ +/** + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.broadcastradio.hal2; + +/** + * A wrapper class for mutable objects to be used in non-mutable contexts + * (i.e. final variables catched in lambda closures). + * + * @param <E> type of boxed value. + */ +final class Mutable<E> { + /** + * A mutable value. + */ + public E value; + + /** + * Initialize value with null pointer. + */ + public Mutable() { + value = null; + } + + /** + * Initialize value with specific value. + * + * @param value initial value. + */ + public Mutable(E value) { + this.value = value; + } +} diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java index 34c1b0ce7d93..8a7ac7355b7e 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java @@ -18,9 +18,15 @@ package com.android.server.broadcastradio.hal2; import android.annotation.NonNull; import android.annotation.Nullable; +import android.hardware.radio.ITuner; import android.hardware.radio.RadioManager; +import android.hardware.broadcastradio.V2_0.AmFmRegionConfig; import android.hardware.broadcastradio.V2_0.IBroadcastRadio; +import android.hardware.broadcastradio.V2_0.ITunerSession; +import android.hardware.broadcastradio.V2_0.Result; +import android.os.ParcelableException; import android.os.RemoteException; +import android.util.MutableInt; import android.util.Slog; import java.util.Objects; @@ -42,8 +48,13 @@ class RadioModule { IBroadcastRadio service = IBroadcastRadio.getService(); if (service == null) return null; + Mutable<AmFmRegionConfig> amfmConfig = new Mutable<>(); + service.getAmFmRegionConfig(false, (int result, AmFmRegionConfig config) -> { + if (result == Result.OK) amfmConfig.value = config; + }); + RadioManager.ModuleProperties prop = - Convert.propertiesFromHal(idx, fqName, service.getProperties()); + Convert.propertiesFromHal(idx, fqName, service.getProperties(), amfmConfig.value); return new RadioModule(service, prop); } catch (RemoteException ex) { @@ -51,4 +62,44 @@ class RadioModule { return null; } } + + public @NonNull ITuner openSession(@NonNull android.hardware.radio.ITunerCallback userCb) { + TunerCallback cb = new TunerCallback(Objects.requireNonNull(userCb)); + Mutable<ITunerSession> hwSession = new Mutable<>(); + MutableInt halResult = new MutableInt(Result.UNKNOWN_ERROR); + + try { + mService.openSession(cb, (int result, ITunerSession session) -> { + hwSession.value = session; + halResult.value = result; + }); + } catch (RemoteException ex) { + Slog.e(TAG, "failed to open session", ex); + throw new ParcelableException(ex); + } + + Convert.throwOnError("openSession", halResult.value); + Objects.requireNonNull(hwSession.value); + + TunerSession session = new TunerSession(hwSession.value, cb); + + // send out legacy callback about band configuration + RadioManager.BandDescriptor[] bands = mProperties.getBands(); + if (bands != null && bands.length > 0) { + RadioManager.BandDescriptor descr = bands[0]; // just pick first + Mutable<RadioManager.BandConfig> config = new Mutable<>(); + if (descr instanceof RadioManager.FmBandDescriptor) { + config.value = new RadioManager.FmBandConfig((RadioManager.FmBandDescriptor)descr); + } else if (descr instanceof RadioManager.AmBandDescriptor) { + config.value = new RadioManager.AmBandConfig((RadioManager.AmBandDescriptor)descr); + } else { + Slog.w(TAG, "Descriptor is neither AM nor FM"); + } + if (config.value != null) { + TunerCallback.dispatch(() -> userCb.onConfigurationChanged(config.value)); + } + } + + return session; + } } diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerCallback.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerCallback.java new file mode 100644 index 000000000000..5ee6a4c693cd --- /dev/null +++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerCallback.java @@ -0,0 +1,66 @@ +/** + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.broadcastradio.hal2; + +import android.annotation.NonNull; +import android.hardware.broadcastradio.V2_0.ITunerCallback; +import android.hardware.broadcastradio.V2_0.ProgramInfo; +import android.hardware.broadcastradio.V2_0.ProgramListChunk; +import android.hardware.broadcastradio.V2_0.ProgramSelector; +import android.hardware.broadcastradio.V2_0.VendorKeyValue; +import android.os.RemoteException; +import android.util.Slog; + +import java.util.ArrayList; +import java.util.Objects; + +class TunerCallback extends ITunerCallback.Stub { + private static final String TAG = "BcRadio2Srv.cb"; + + final android.hardware.radio.ITunerCallback mCb; + + interface RunnableThrowingRemoteException { + void run() throws RemoteException; + } + + TunerCallback(@NonNull android.hardware.radio.ITunerCallback clientCallback) { + mCb = Objects.requireNonNull(clientCallback); + } + + static void dispatch(RunnableThrowingRemoteException func) { + try { + func.run(); + } catch (RemoteException ex) { + Slog.e(TAG, "callback call failed", ex); + } + } + + @Override + public void onTuneFailed(int result, ProgramSelector selector) {} + + @Override + public void onCurrentProgramInfoChanged(ProgramInfo info) {} + + @Override + public void onProgramListUpdated(ProgramListChunk chunk) {} + + @Override + public void onAntennaStateChange(boolean connected) {} + + @Override + public void onParametersUpdated(ArrayList<VendorKeyValue> parameters) {} +} diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java new file mode 100644 index 000000000000..e8faf3dfa63f --- /dev/null +++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java @@ -0,0 +1,137 @@ +/** + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.broadcastradio.hal2; + +import android.annotation.NonNull; +import android.graphics.Bitmap; +import android.hardware.broadcastradio.V2_0.ITunerSession; +import android.hardware.radio.ITuner; +import android.hardware.radio.ProgramSelector; +import android.hardware.radio.RadioManager; +import android.util.Slog; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +class TunerSession extends ITuner.Stub { + private static final String TAG = "BcRadio2Srv.session"; + + private final Object mLock = new Object(); + + private final ITunerSession mHwSession; + private final TunerCallback mCallback; + private boolean mIsClosed = false; + + TunerSession(@NonNull ITunerSession hwSession, @NonNull TunerCallback callback) { + mHwSession = Objects.requireNonNull(hwSession); + mCallback = Objects.requireNonNull(callback); + } + + @Override + public void close() { + synchronized (mLock) { + if (mIsClosed) return; + mIsClosed = true; + } + } + + @Override + public boolean isClosed() { + return mIsClosed; + } + + private void checkNotClosedLocked() { + if (mIsClosed) { + throw new IllegalStateException("Tuner is closed, no further operations are allowed"); + } + } + + @Override + public void setConfiguration(RadioManager.BandConfig config) {} + + @Override + public RadioManager.BandConfig getConfiguration() { + return null; + } + + @Override + public void setMuted(boolean mute) {} + + @Override + public boolean isMuted() { + return false; + } + + @Override + public void step(boolean directionDown, boolean skipSubChannel) {} + + @Override + public void scan(boolean directionDown, boolean skipSubChannel) {} + + @Override + public void tune(ProgramSelector selector) {} + + @Override + public void cancel() {} + + @Override + public void cancelAnnouncement() {} + + @Override + public RadioManager.ProgramInfo getProgramInformation() { + return null; + } + + @Override + public Bitmap getImage(int id) { + return null; + } + + @Override + public boolean startBackgroundScan() { + return false; + } + + @Override + public List<RadioManager.ProgramInfo> getProgramList(Map vendorFilter) { + return null; + } + + @Override + public boolean isAnalogForced() { + return false; + } + + @Override + public void setAnalogForced(boolean isForced) {} + + @Override + public Map setParameters(Map parameters) { + return null; + } + + @Override + public Map getParameters(List<String> keys) { + return null; + } + + @Override + public boolean isAntennaConnected() { + return true; + } +} diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Utils.java b/services/core/java/com/android/server/broadcastradio/hal2/Utils.java new file mode 100644 index 000000000000..3520f37880c5 --- /dev/null +++ b/services/core/java/com/android/server/broadcastradio/hal2/Utils.java @@ -0,0 +1,40 @@ +/** + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.broadcastradio.hal2; + +enum FrequencyBand { + UNKNOWN, + FM, + AM_LW, + AM_MW, + AM_SW, +}; + +class Utils { + private static final String TAG = "BcRadio2Srv.utils"; + + static FrequencyBand getBand(int freq) { + // keep in sync with hardware/interfaces/broadcastradio/common/utils2x/Utils.cpp + if (freq < 30) return FrequencyBand.UNKNOWN; + if (freq < 500) return FrequencyBand.AM_LW; + if (freq < 1705) return FrequencyBand.AM_MW; + if (freq < 30000) return FrequencyBand.AM_SW; + if (freq < 60000) return FrequencyBand.UNKNOWN; + if (freq < 110000) return FrequencyBand.FM; + return FrequencyBand.UNKNOWN; + } +} diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 7715727f6d73..c7a43153c0aa 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -305,6 +305,7 @@ public class Vpn { } else { for (Network underlying : underlyingNetworks) { final NetworkCapabilities underlyingCaps = cm.getNetworkCapabilities(underlying); + if (underlyingCaps == null) continue; for (int underlyingType : underlyingCaps.getTransportTypes()) { transportTypes = ArrayUtils.appendInt(transportTypes, underlyingType); } diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java index 3b9d40fa0825..0b62907a0d0c 100644 --- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java +++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java @@ -17,6 +17,8 @@ package com.android.server.display; import android.annotation.Nullable; +import android.content.res.Resources; +import android.content.res.TypedArray; import android.hardware.display.BrightnessConfiguration; import android.os.PowerManager; import android.util.MathUtils; @@ -41,11 +43,30 @@ public abstract class BrightnessMappingStrategy { private static final boolean DEBUG = false; @Nullable - public static BrightnessMappingStrategy create( - float[] luxLevels, int[] brightnessLevelsBacklight, float[] brightnessLevelsNits, - float[] nitsRange, int[] backlightRange) { + public static BrightnessMappingStrategy create(Resources resources) { + float[] luxLevels = getLuxLevels(resources.getIntArray( + com.android.internal.R.array.config_autoBrightnessLevels)); + int[] brightnessLevelsBacklight = resources.getIntArray( + com.android.internal.R.array.config_autoBrightnessLcdBacklightValues); + float[] brightnessLevelsNits = getFloatArray(resources.obtainTypedArray( + com.android.internal.R.array.config_autoBrightnessDisplayValuesNits)); + + float[] nitsRange = getFloatArray(resources.obtainTypedArray( + com.android.internal.R.array.config_screenBrightnessNits)); + int[] backlightRange = resources.getIntArray( + com.android.internal.R.array.config_screenBrightnessBacklight); + if (isValidMapping(nitsRange, backlightRange) && isValidMapping(luxLevels, brightnessLevelsNits)) { + int minimumBacklight = resources.getInteger( + com.android.internal.R.integer.config_screenBrightnessSettingMinimum); + int maximumBacklight = resources.getInteger( + com.android.internal.R.integer.config_screenBrightnessSettingMaximum); + if (backlightRange[0] > minimumBacklight + || backlightRange[backlightRange.length - 1] < maximumBacklight) { + Slog.w(TAG, "Screen brightness mapping does not cover whole range of available" + + " backlight values, autobrightness functionality may be impaired."); + } BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); builder.setCurve(luxLevels, brightnessLevelsNits); return new PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange); @@ -56,6 +77,25 @@ public abstract class BrightnessMappingStrategy { } } + private static float[] getLuxLevels(int[] lux) { + // The first control point is implicit and always at 0 lux. + float[] levels = new float[lux.length + 1]; + for (int i = 0; i < lux.length; i++) { + levels[i + 1] = (float) lux[i]; + } + return levels; + } + + private static float[] getFloatArray(TypedArray array) { + final int N = array.length(); + float[] vals = new float[N]; + for (int i = 0; i < N; i++) { + vals[i] = array.getFloat(i, -1.0f); + } + array.recycle(); + return vals; + } + private static boolean isValidMapping(float[] x, float[] y) { if (x == null || y == null || x.length == 0 || y.length == 0) { return false; @@ -124,10 +164,17 @@ public abstract class BrightnessMappingStrategy { * brightness and 0 is the display at minimum brightness. * * @param lux The current ambient brightness in lux. - * @return The desired brightness of the display compressed to the range [0, 1.0]. + * @return The desired brightness of the display normalized to the range [0, 1.0]. */ public abstract float getBrightness(float lux); + /** + * Gets the display's brightness in nits for the given backlight value. + * + * Returns -1.0f if there's no available mapping for the backlight to nits. + */ + public abstract float getNits(int backlight); + public abstract void dump(PrintWriter pw); private static float normalizeAbsoluteBrightness(int brightness) { @@ -186,6 +233,11 @@ public abstract class BrightnessMappingStrategy { } @Override + public float getNits(int backlight) { + return -1.0f; + } + + @Override public void dump(PrintWriter pw) { pw.println("SimpleMappingStrategy"); pw.println(" mSpline=" + mSpline); @@ -209,7 +261,11 @@ public abstract class BrightnessMappingStrategy { // A spline mapping from nits to the corresponding backlight value, normalized to the range // [0, 1.0]. - private final Spline mBacklightSpline; + private final Spline mNitsToBacklightSpline; + + // A spline mapping from the device's backlight value, normalized to the range [0, 1.0], to + // a brightness in nits. + private final Spline mBacklightToNitsSpline; // The default brightness configuration. private final BrightnessConfiguration mDefaultConfig; @@ -227,19 +283,18 @@ public abstract class BrightnessMappingStrategy { // Setup the backlight spline final int N = nits.length; - float[] x = new float[N]; - float[] y = new float[N]; + float[] normalizedBacklight = new float[N]; for (int i = 0; i < N; i++) { - x[i] = nits[i]; - y[i] = normalizeAbsoluteBrightness(backlight[i]); + normalizedBacklight[i] = normalizeAbsoluteBrightness(backlight[i]); } - mBacklightSpline = Spline.createSpline(x, y); + mNitsToBacklightSpline = Spline.createSpline(nits, normalizedBacklight); + mBacklightToNitsSpline = Spline.createSpline(normalizedBacklight, nits); if (DEBUG) { - Slog.d(TAG, "Backlight spline: " + mBacklightSpline); + Slog.d(TAG, "Backlight spline: " + mNitsToBacklightSpline); for (float v = 1f; v < nits[nits.length - 1] * 1.25f; v *= 1.25f) { Slog.d(TAG, String.format( - " %7.1f: %7.1f", v, mBacklightSpline.interpolate(v))); + " %7.1f: %7.1f", v, mNitsToBacklightSpline.interpolate(v))); } } @@ -275,7 +330,12 @@ public abstract class BrightnessMappingStrategy { @Override public float getBrightness(float lux) { - return mBacklightSpline.interpolate(mBrightnessSpline.interpolate(lux)); + return mNitsToBacklightSpline.interpolate(mBrightnessSpline.interpolate(lux)); + } + + @Override + public float getNits(int backlight) { + return mBacklightToNitsSpline.interpolate(normalizeAbsoluteBrightness(backlight)); } @Override @@ -283,7 +343,7 @@ public abstract class BrightnessMappingStrategy { pw.println("PhysicalMappingStrategy"); pw.println(" mConfig=" + mConfig); pw.println(" mBrightnessSpline=" + mBrightnessSpline); - pw.println(" mBacklightSpline=" + mBacklightSpline); + pw.println(" mNitsToBacklightSpline=" + mNitsToBacklightSpline); } } } diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java index 42247f94e69f..cbb1c0139bca 100644 --- a/services/core/java/com/android/server/display/BrightnessTracker.java +++ b/services/core/java/com/android/server/display/BrightnessTracker.java @@ -24,7 +24,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ParceledListSlice; -import android.database.ContentObserver; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; @@ -34,6 +33,8 @@ import android.net.Uri; import android.os.BatteryManager; import android.os.Environment; import android.os.Handler; +import android.os.Looper; +import android.os.Message; import android.os.PowerManager; import android.os.RemoteException; import android.os.SystemClock; @@ -88,7 +89,7 @@ public class BrightnessTracker { private static final String TAG_EVENTS = "events"; private static final String TAG_EVENT = "event"; - private static final String ATTR_BRIGHTNESS = "brightness"; + private static final String ATTR_NITS = "nits"; private static final String ATTR_TIMESTAMP = "timestamp"; private static final String ATTR_PACKAGE_NAME = "packageName"; private static final String ATTR_USER = "user"; @@ -97,7 +98,10 @@ public class BrightnessTracker { private static final String ATTR_BATTERY_LEVEL = "batteryLevel"; private static final String ATTR_NIGHT_MODE = "nightMode"; private static final String ATTR_COLOR_TEMPERATURE = "colorTemperature"; - private static final String ATTR_LAST_BRIGHTNESS = "lastBrightness"; + private static final String ATTR_LAST_NITS = "lastNits"; + + private static final int MSG_BACKGROUND_START = 0; + private static final int MSG_BRIGHTNESS_CHANGED = 1; // Lock held while accessing mEvents, is held while writing events to flash. private final Object mEventsLock = new Object(); @@ -113,9 +117,7 @@ public class BrightnessTracker { private final Context mContext; private final ContentResolver mContentResolver; private Handler mBgHandler; - // mSettingsObserver, mBroadcastReceiver and mSensorListener should only be used on - // the mBgHandler thread. - private SettingsObserver mSettingsObserver; + // mBroadcastReceiver and mSensorListener should only be used on the mBgHandler thread. private BroadcastReceiver mBroadcastReceiver; private SensorListener mSensorListener; @@ -126,9 +128,9 @@ public class BrightnessTracker { @GuardedBy("mDataCollectionLock") private float mLastBatteryLevel = Float.NaN; @GuardedBy("mDataCollectionLock") - private int mIgnoreBrightness = -1; + private float mLastBrightness = -1; @GuardedBy("mDataCollectionLock") - private int mLastBrightness = -1; + private boolean mStarted; private final Injector mInjector; @@ -144,33 +146,31 @@ public class BrightnessTracker { } } - /** Start listening for brightness slider events */ - public void start() { + /** + * Start listening for brightness slider events + * + * @param brightness the initial screen brightness + */ + public void start(float initialBrightness) { if (DEBUG) { Slog.d(TAG, "Start"); } - mBgHandler = mInjector.getBackgroundHandler(); + mBgHandler = new TrackerHandler(mInjector.getBackgroundHandler().getLooper()); mUserManager = mContext.getSystemService(UserManager.class); - mBgHandler.post(() -> backgroundStart()); + mBgHandler.obtainMessage(MSG_BACKGROUND_START, (Float) initialBrightness).sendToTarget(); } - private void backgroundStart() { + private void backgroundStart(float initialBrightness) { readEvents(); - mLastBrightness = mInjector.getSystemIntForUser(mContentResolver, - Settings.System.SCREEN_BRIGHTNESS, -1, - UserHandle.USER_CURRENT); - mSensorListener = new SensorListener(); + if (mInjector.isInteractive(mContext)) { mInjector.registerSensorListener(mContext, mSensorListener, mBgHandler); } - mSettingsObserver = new SettingsObserver(mBgHandler); - mInjector.registerBrightnessObserver(mContentResolver, mSettingsObserver); - final IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_SHUTDOWN); intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); @@ -180,6 +180,10 @@ public class BrightnessTracker { mInjector.registerReceiver(mContext, mBroadcastReceiver, intentFilter); mInjector.scheduleIdleJob(mContext); + synchronized (mDataCollectionLock) { + mLastBrightness = initialBrightness; + mStarted = true; + } } /** Stop listening for events */ @@ -188,10 +192,14 @@ public class BrightnessTracker { if (DEBUG) { Slog.d(TAG, "Stop"); } + mBgHandler.removeMessages(MSG_BACKGROUND_START); mInjector.unregisterSensorListener(mContext, mSensorListener); mInjector.unregisterReceiver(mContext, mBroadcastReceiver); - mInjector.unregisterBrightnessObserver(mContext, mSettingsObserver); mInjector.cancelIdleJob(mContext); + + synchronized (mDataCollectionLock) { + mStarted = false; + } } /** @@ -220,40 +228,45 @@ public class BrightnessTracker { return new ParceledListSlice<>(out); } - /** Sets brightness without logging the brightness change event */ - public void setBrightness(int brightness, int userId) { - synchronized (mDataCollectionLock) { - mIgnoreBrightness = brightness; - } - mInjector.putSystemIntForUser(mContentResolver, Settings.System.SCREEN_BRIGHTNESS, - brightness, userId); - } - public void persistEvents() { scheduleWriteEvents(); } - private void handleBrightnessChanged() { + /** + * Notify the BrightnessTracker that the user has changed the brightness of the display. + */ + public void notifyBrightnessChanged(float brightness, boolean userInitiated) { if (DEBUG) { - Slog.d(TAG, "Brightness change"); + Slog.d(TAG, String.format("notifyBrightnessChanged(brightness=%f, userInitiated=%b)", + brightness, userInitiated)); } - final BrightnessChangeEvent event = new BrightnessChangeEvent(); - event.timeStamp = mInjector.currentTimeMillis(); - - int brightness = mInjector.getSystemIntForUser(mContentResolver, - Settings.System.SCREEN_BRIGHTNESS, -1, - UserHandle.USER_CURRENT); + Message m = mBgHandler.obtainMessage(MSG_BRIGHTNESS_CHANGED, + userInitiated ? 1 : 0, 0 /*unused*/, (Float) brightness); + m.sendToTarget(); + } + private void handleBrightnessChanged(float brightness, boolean userInitiated) { + final BrightnessChangeEvent event; synchronized (mDataCollectionLock) { - int previousBrightness = mLastBrightness; + if (!mStarted) { + // Not currently gathering brightness change information + return; + } + + float previousBrightness = mLastBrightness; mLastBrightness = brightness; - if (brightness == -1 || brightness == mIgnoreBrightness) { - // Notified of brightness change but no setting or self change so ignore. - mIgnoreBrightness = -1; + if (!userInitiated) { + // We want to record what current brightness is so that we know what the user + // changed it from, but if it wasn't user initiated then we don't want to record it + // as a BrightnessChangeEvent. return; } + + event = new BrightnessChangeEvent(); + event.timeStamp = mInjector.currentTimeMillis(); + final int readingCount = mLastSensorReadings.size(); if (readingCount == 0) { // No sensor data so ignore this. @@ -386,7 +399,7 @@ public class BrightnessTracker { if (userSerialNo != -1 && toWrite[i].timeStamp > timeCutOff) { mEvents.append(toWrite[i]); out.startTag(null, TAG_EVENT); - out.attribute(null, ATTR_BRIGHTNESS, Integer.toString(toWrite[i].brightness)); + out.attribute(null, ATTR_NITS, Float.toString(toWrite[i].brightness)); out.attribute(null, ATTR_TIMESTAMP, Long.toString(toWrite[i].timeStamp)); out.attribute(null, ATTR_PACKAGE_NAME, toWrite[i].packageName); out.attribute(null, ATTR_USER, Integer.toString(userSerialNo)); @@ -394,8 +407,8 @@ public class BrightnessTracker { out.attribute(null, ATTR_NIGHT_MODE, Boolean.toString(toWrite[i].nightMode)); out.attribute(null, ATTR_COLOR_TEMPERATURE, Integer.toString( toWrite[i].colorTemperature)); - out.attribute(null, ATTR_LAST_BRIGHTNESS, - Integer.toString(toWrite[i].lastBrightness)); + out.attribute(null, ATTR_LAST_NITS, + Float.toString(toWrite[i].lastBrightness)); StringBuilder luxValues = new StringBuilder(); StringBuilder luxTimestamps = new StringBuilder(); for (int j = 0; j < toWrite[i].luxValues.length; ++j) { @@ -446,8 +459,8 @@ public class BrightnessTracker { if (TAG_EVENT.equals(tag)) { BrightnessChangeEvent event = new BrightnessChangeEvent(); - String brightness = parser.getAttributeValue(null, ATTR_BRIGHTNESS); - event.brightness = Integer.parseInt(brightness); + String brightness = parser.getAttributeValue(null, ATTR_NITS); + event.brightness = Float.parseFloat(brightness); String timestamp = parser.getAttributeValue(null, ATTR_TIMESTAMP); event.timeStamp = Long.parseLong(timestamp); event.packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME); @@ -460,8 +473,8 @@ public class BrightnessTracker { String colorTemperature = parser.getAttributeValue(null, ATTR_COLOR_TEMPERATURE); event.colorTemperature = Integer.parseInt(colorTemperature); - String lastBrightness = parser.getAttributeValue(null, ATTR_LAST_BRIGHTNESS); - event.lastBrightness = Integer.parseInt(lastBrightness); + String lastBrightness = parser.getAttributeValue(null, ATTR_LAST_NITS); + event.lastBrightness = Float.parseFloat(lastBrightness); String luxValue = parser.getAttributeValue(null, ATTR_LUX); String luxTimestamp = parser.getAttributeValue(null, ATTR_LUX_TIMESTAMPS); @@ -582,22 +595,6 @@ public class BrightnessTracker { } } - private final class SettingsObserver extends ContentObserver { - public SettingsObserver(Handler handler) { - super(handler); - } - - @Override - public void onChange(boolean selfChange, Uri uri) { - if (DEBUG) { - Slog.v(TAG, "settings change " + uri); - } - // Self change is based on observer passed to notifyObserver, SettingsProvider - // passes null so no changes are self changes. - handleBrightnessChanged(); - } - } - private final class Receiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { @@ -623,6 +620,24 @@ public class BrightnessTracker { } } + private final class TrackerHandler extends Handler { + public TrackerHandler(Looper looper) { + super(looper, null, true /*async*/); + } + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_BACKGROUND_START: + backgroundStart((float)msg.obj /*initial brightness*/); + break; + case MSG_BRIGHTNESS_CHANGED: + float newBrightness = (float) msg.obj; + boolean userInitiatedChange = (msg.arg1 == 1); + handleBrightnessChanged(newBrightness, userInitiatedChange); + break; + } + } + } + @VisibleForTesting static class Injector { public void registerSensorListener(Context context, @@ -638,18 +653,6 @@ public class BrightnessTracker { sensorManager.unregisterListener(sensorListener); } - public void registerBrightnessObserver(ContentResolver resolver, - ContentObserver settingsObserver) { - resolver.registerContentObserver(Settings.System.getUriFor( - Settings.System.SCREEN_BRIGHTNESS), - false, settingsObserver, UserHandle.USER_ALL); - } - - public void unregisterBrightnessObserver(Context context, - ContentObserver settingsObserver) { - context.getContentResolver().unregisterContentObserver(settingsObserver); - } - public void registerReceiver(Context context, BroadcastReceiver receiver, IntentFilter filter) { context.registerReceiver(receiver, filter); @@ -664,16 +667,6 @@ public class BrightnessTracker { return BackgroundThread.getHandler(); } - public int getSystemIntForUser(ContentResolver resolver, String setting, int defaultValue, - int userId) { - return Settings.System.getIntForUser(resolver, setting, defaultValue, userId); - } - - public void putSystemIntForUser(ContentResolver resolver, String setting, int value, - int userId) { - Settings.System.putIntForUser(resolver, setting, value, userId); - } - public int getSecureIntForUser(ContentResolver resolver, String setting, int defaultValue, int userId) { return Settings.Secure.getIntForUser(resolver, setting, defaultValue, userId); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 9b97934cfc3b..02e4fe00f893 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -270,8 +270,6 @@ public final class DisplayManagerService extends SystemService { private final Injector mInjector; - private final BrightnessTracker mBrightnessTracker; - public DisplayManagerService(Context context) { this(context, new Injector()); } @@ -290,7 +288,6 @@ public final class DisplayManagerService extends SystemService { PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting(); - mBrightnessTracker = new BrightnessTracker(context, null); mCurrentUserId = UserHandle.USER_SYSTEM; } @@ -1339,9 +1336,6 @@ public final class DisplayManagerService extends SystemService { pw.println(); mPersistentDataStore.dump(pw); - - pw.println(); - mBrightnessTracker.dump(pw); } } @@ -1418,10 +1412,6 @@ public final class DisplayManagerService extends SystemService { break; } - case MSG_REGISTER_BRIGHTNESS_TRACKER: - mBrightnessTracker.start(); - break; - case MSG_LOAD_BRIGHTNESS_CONFIGURATION: loadBrightnessConfiguration(); break; @@ -1833,22 +1823,9 @@ public final class DisplayManagerService extends SystemService { final int userId = UserHandle.getUserId(callingUid); final long token = Binder.clearCallingIdentity(); try { - return mBrightnessTracker.getEvents(userId, hasUsageStats); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override // Binder call - public void setBrightness(int brightness) { - // STOPSHIP - remove when adaptive brightness controller accepts curves. - mContext.enforceCallingOrSelfPermission( - Manifest.permission.BRIGHTNESS_SLIDER_USAGE, - "Permission to set brightness."); - int userId = UserHandle.getUserId(Binder.getCallingUid()); - final long token = Binder.clearCallingIdentity(); - try { - mBrightnessTracker.setBrightness(brightness, userId); + synchronized (mSyncRoot) { + return mDisplayPowerController.getBrightnessEvents(userId, hasUsageStats); + } } finally { Binder.restoreCallingIdentity(token); } @@ -2028,7 +2005,9 @@ public final class DisplayManagerService extends SystemService { @Override public void persistBrightnessSliderEvents() { - mBrightnessTracker.persistEvents(); + synchronized (mSyncRoot) { + mDisplayPowerController.persistBrightnessSliderEvents(); + } } } } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index a2d954822e64..e5a4b0a78a65 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -24,13 +24,16 @@ import com.android.server.policy.WindowManagerPolicy; import android.animation.Animator; import android.animation.ObjectAnimator; +import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.content.Context; +import android.content.pm.ParceledListSlice; import android.content.res.Resources; -import android.content.res.TypedArray; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; +import android.hardware.display.BrightnessChangeEvent; import android.hardware.display.BrightnessConfiguration; import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks; import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; @@ -96,7 +99,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static final int MSG_SCREEN_ON_UNBLOCKED = 3; private static final int MSG_SCREEN_OFF_UNBLOCKED = 4; private static final int MSG_CONFIGURE_BRIGHTNESS = 5; - private static final int MSG_USER_SWITCH = 6; private static final int PROXIMITY_UNKNOWN = -1; private static final int PROXIMITY_NEGATIVE = 0; @@ -151,9 +153,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // The dim screen brightness. private final int mScreenBrightnessDimConfig; - // The minimum screen brightness to use in a very dark room. - private final int mScreenBrightnessDarkConfig; - // The minimum allowed brightness. private final int mScreenBrightnessRangeMinimum; @@ -261,6 +260,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private long mScreenOnBlockStartRealTime; private long mScreenOffBlockStartRealTime; + // The last brightness that was set by the user and not temporary. Set to -1 when a brightness + // has yet to be recorded. + private int mLastBrightness; + // The last auto brightness adjustment that was set by the user and not temporary. Set to + // Float.NaN when an auto-brightness adjustment hasn't been recorded yet. + private float mLastAutoBrightnessAdjustment; + // Screen state we reported to policy. Must be one of REPORTED_TO_POLICY_SCREEN_* fields. private int mReportedScreenStateToPolicy; @@ -289,6 +295,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // The controller for the automatic brightness level. private AutomaticBrightnessController mAutomaticBrightnessController; + // The mapper between ambient lux, display backlight values, and display brightness. + @Nullable + private BrightnessMappingStrategy mBrightnessMapper; + // The default brightness configuration. Used for whenever we don't have a valid brightness // configuration set. This is typically seen with users that don't have a brightness // configuration that's different from the default. @@ -302,6 +312,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private ObjectAnimator mColorFadeOffAnimator; private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator; + // Tracker for brightness changes + private final BrightnessTracker mBrightnessTracker; + /** * Creates the display power controller. */ @@ -309,6 +322,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call DisplayPowerCallbacks callbacks, Handler handler, SensorManager sensorManager, DisplayBlanker blanker) { mHandler = new DisplayControllerHandler(handler.getLooper()); + mBrightnessTracker = new BrightnessTracker(context, null); mCallbacks = callbacks; mBatteryStats = BatteryStatsService.getService(); @@ -327,23 +341,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mScreenBrightnessDimConfig = clampAbsoluteBrightness(resources.getInteger( com.android.internal.R.integer.config_screenBrightnessDim)); - mScreenBrightnessDarkConfig = clampAbsoluteBrightness(resources.getInteger( - com.android.internal.R.integer.config_screenBrightnessDark)); - if (mScreenBrightnessDarkConfig > mScreenBrightnessDimConfig) { - Slog.w(TAG, "Expected config_screenBrightnessDark (" - + mScreenBrightnessDarkConfig + ") to be less than or equal to " - + "config_screenBrightnessDim (" + mScreenBrightnessDimConfig + ")."); - } - if (mScreenBrightnessDarkConfig > screenBrightnessSettingMinimum) { - Slog.w(TAG, "Expected config_screenBrightnessDark (" - + mScreenBrightnessDarkConfig + ") to be less than or equal to " - + "config_screenBrightnessSettingMinimum (" - + screenBrightnessSettingMinimum + ")."); - } - - int screenBrightnessRangeMinimum = Math.min(Math.min( - screenBrightnessSettingMinimum, mScreenBrightnessDimConfig), - mScreenBrightnessDarkConfig); + mScreenBrightnessRangeMinimum = + Math.min(screenBrightnessSettingMinimum, mScreenBrightnessDimConfig); mScreenBrightnessRangeMaximum = clampAbsoluteBrightness(resources.getInteger( com.android.internal.R.integer.config_screenBrightnessSettingMaximum)); @@ -362,18 +361,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call com.android.internal.R.bool.config_skipScreenOnBrightnessRamp); if (mUseSoftwareAutoBrightnessConfig) { - float[] luxLevels = getLuxLevels(resources.getIntArray( - com.android.internal.R.array.config_autoBrightnessLevels)); - int[] backlightValues = resources.getIntArray( - com.android.internal.R.array.config_autoBrightnessLcdBacklightValues); - float[] brightnessValuesNits = getFloatArray(resources.obtainTypedArray( - com.android.internal.R.array.config_autoBrightnessDisplayValuesNits)); - - final float screenMinimumNits = resources.getFloat( - com.android.internal.R.dimen.config_screenBrightnessMinimumNits); - final float screenMaximumNits = resources.getFloat( - com.android.internal.R.dimen.config_screenBrightnessMaximumNits); - final float dozeScaleFactor = resources.getFraction( com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor, 1, 1); @@ -413,31 +400,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call + "config_autoBrightnessLightSensorRate (" + lightSensorRate + ")."); } - if (backlightValues != null && backlightValues.length > 0) { - final int bottom = backlightValues[0]; - if (mScreenBrightnessDarkConfig > bottom) { - Slog.w(TAG, "config_screenBrightnessDark (" + mScreenBrightnessDarkConfig - + ") should be less than or equal to the first value of " - + "config_autoBrightnessLcdBacklightValues (" - + bottom + ")."); - } - if (bottom < screenBrightnessRangeMinimum) { - screenBrightnessRangeMinimum = bottom; - } - } - - float[] nitsRange = { screenMinimumNits, screenMaximumNits }; - int[] backlightRange = { screenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum }; - - BrightnessMappingStrategy mapper = BrightnessMappingStrategy.create( - luxLevels, backlightValues, brightnessValuesNits, - nitsRange, backlightRange); - if (mapper != null) { + mBrightnessMapper = BrightnessMappingStrategy.create(resources); + if (mBrightnessMapper != null) { mAutomaticBrightnessController = new AutomaticBrightnessController(this, - handler.getLooper(), sensorManager, mapper, lightSensorWarmUpTimeConfig, - screenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum, - dozeScaleFactor, lightSensorRate, initialLightSensorRate, - brighteningLightDebounce, darkeningLightDebounce, + handler.getLooper(), sensorManager, mBrightnessMapper, + lightSensorWarmUpTimeConfig, mScreenBrightnessRangeMinimum, + mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate, + initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp, ambientLightHorizon, autoBrightnessAdjustmentMaxGamma, dynamicHysteresis); } else { @@ -445,9 +414,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } - mScreenBrightnessRangeMinimum = screenBrightnessRangeMinimum; - - mColorFadeEnabled = !ActivityManager.isLowRamDeviceStatic(); mColorFadeFadesConfig = resources.getBoolean( com.android.internal.R.bool.config_animateScreenLights); @@ -466,32 +432,32 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } + mLastBrightness = -1; + mLastAutoBrightnessAdjustment = Float.NaN; } - private static float[] getLuxLevels(int[] lux) { - // The first control point is implicit and always at 0 lux. - float[] levels = new float[lux.length + 1]; - for (int i = 0; i < lux.length; i++) { - levels[i + 1] = (float) lux[i]; - } - return levels; + /** + * Returns true if the proximity sensor screen-off function is available. + */ + public boolean isProximitySensorAvailable() { + return mProximitySensor != null; } - private static float[] getFloatArray(TypedArray array) { - final int N = array.length(); - float[] vals = new float[N]; - for (int i = 0; i < N; i++) { - vals[i] = array.getFloat(i, -1.0f); - } - array.recycle(); - return vals; + /** + * Get the {@link BrightnessChangeEvent}s for the specified user. + * @param userId userId to fetch data for + * @param includePackage if false will null out the package name in events + */ + public ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents( + @UserIdInt int userId, boolean includePackage) { + return mBrightnessTracker.getEvents(userId, includePackage); } /** - * Returns true if the proximity sensor screen-off function is available. + * Persist the brightness slider events to disk. */ - public boolean isProximitySensorAvailable() { - return mProximitySensor != null; + public void persistBrightnessSliderEvents() { + mBrightnessTracker.persistEvents(); } /** @@ -588,6 +554,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } catch (RemoteException ex) { // same process } + + // Initialize all of the brightness tracking state + final float brightness = getNits(mPowerState.getScreenBrightness()); + if (brightness >= 0.0f) { + mBrightnessTracker.start(brightness); + } } private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() { @@ -722,16 +694,32 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call brightness = PowerManager.BRIGHTNESS_OFF; } + + final boolean autoBrightnessEnabledInDoze = + mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state); + final boolean autoBrightnessEnabled = mPowerRequest.useAutoBrightness + && (state == Display.STATE_ON || autoBrightnessEnabledInDoze) + && brightness < 0 + && mAutomaticBrightnessController != null; + final boolean brightnessAdjustmentChanged = + !Float.isNaN(mLastAutoBrightnessAdjustment) + && mPowerRequest.screenAutoBrightnessAdjustment != mLastAutoBrightnessAdjustment; + final boolean brightnessChanged = mLastBrightness >= 0 + && mPowerRequest.screenBrightness != mLastBrightness; + + // Update the last set brightness values. + final boolean userInitiatedChange; + if (mPowerRequest.brightnessSetByUser && !mPowerRequest.brightnessIsTemporary) { + userInitiatedChange = autoBrightnessEnabled && brightnessAdjustmentChanged + || !autoBrightnessEnabled && brightnessChanged; + mLastBrightness = mPowerRequest.screenBrightness; + mLastAutoBrightnessAdjustment = mPowerRequest.screenAutoBrightnessAdjustment; + } else { + userInitiatedChange = false; + } + // Configure auto-brightness. - boolean autoBrightnessEnabled = false; if (mAutomaticBrightnessController != null) { - final boolean autoBrightnessEnabledInDoze = - mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state); - autoBrightnessEnabled = mPowerRequest.useAutoBrightness - && (state == Display.STATE_ON || autoBrightnessEnabledInDoze) - && brightness < 0; - final boolean userInitiatedChange = autoBrightnessAdjustmentChanged - && mPowerRequest.brightnessSetByUser; mAutomaticBrightnessController.configure(autoBrightnessEnabled, mBrightnessConfiguration, mPowerRequest.screenAutoBrightnessAdjustment, state != Display.STATE_ON, userInitiatedChange); @@ -854,6 +842,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call animateScreenBrightness(brightness, slowChange ? mBrightnessRampRateSlow : mBrightnessRampRateFast); } + + final float brightnessInNits = getNits(brightness); + if (!mPowerRequest.brightnessIsTemporary && brightnessInNits >= 0.0f) { + // We only want to track changes made by the user and on devices that can actually + // map the display backlight values into a physical brightness unit. + mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiatedChange); + } } // Determine whether the display is ready for use in the newly requested state. @@ -1312,6 +1307,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mHandler.post(mOnStateChangedRunnable); } + private float getNits(int backlight) { + if (mBrightnessMapper != null) { + return mBrightnessMapper.getNits(backlight); + } else { + return -1.0f; + } + } + private final Runnable mOnStateChangedRunnable = new Runnable() { @Override public void run() { @@ -1362,7 +1365,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println("Display Power Controller Configuration:"); pw.println(" mScreenBrightnessDozeConfig=" + mScreenBrightnessDozeConfig); pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig); - pw.println(" mScreenBrightnessDarkConfig=" + mScreenBrightnessDarkConfig); pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum); pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum); pw.println(" mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig); @@ -1383,7 +1385,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println("Display Power Controller Thread State:"); pw.println(" mPowerRequest=" + mPowerRequest); pw.println(" mWaitingForNegativeProximity=" + mWaitingForNegativeProximity); - pw.println(" mProximitySensor=" + mProximitySensor); pw.println(" mProximitySensorEnabled=" + mProximitySensorEnabled); pw.println(" mProximityThreshold=" + mProximityThreshold); @@ -1392,6 +1393,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println(" mPendingProximityDebounceTime=" + TimeUtils.formatUptime(mPendingProximityDebounceTime)); pw.println(" mScreenOffBecauseOfProximity=" + mScreenOffBecauseOfProximity); + pw.println(" mLastBrightness=" + mLastBrightness); + pw.println(" mLastAutoBrightnessAdjustment=" + mLastAutoBrightnessAdjustment); pw.println(" mAppliedAutoBrightness=" + mAppliedAutoBrightness); pw.println(" mAppliedDimming=" + mAppliedDimming); pw.println(" mAppliedLowPower=" + mAppliedLowPower); @@ -1420,6 +1423,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAutomaticBrightnessController.dump(pw); } + if (mBrightnessTracker != null) { + pw.println(); + mBrightnessTracker.dump(pw); + } } private static String proximityToString(int state) { diff --git a/services/core/java/com/android/server/location/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/ContextHubServiceUtil.java index c356b639deba..033437a53891 100644 --- a/services/core/java/com/android/server/location/ContextHubServiceUtil.java +++ b/services/core/java/com/android/server/location/ContextHubServiceUtil.java @@ -221,7 +221,7 @@ import java.util.ArrayList; case Result.NOT_INIT: return ContextHubTransaction.RESULT_FAILED_UNINITIALIZED; case Result.TRANSACTION_PENDING: - return ContextHubTransaction.RESULT_FAILED_PENDING; + return ContextHubTransaction.RESULT_FAILED_BUSY; case Result.TRANSACTION_FAILED: case Result.UNKNOWN_FAILURE: default: /* fall through */ diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 482acefac3a5..02218ffc14ea 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -2026,8 +2026,9 @@ public class LockSettingsService extends ILockSettings.Stub { } @Override - public Map<String, byte[]> recoverKeys(@NonNull String sessionId, @NonNull byte[] recoveryKeyBlob, - @NonNull List<KeyEntryRecoveryData> applicationKeys, @UserIdInt int userId) + public Map<String, byte[]> recoverKeys(@NonNull String sessionId, + @NonNull byte[] recoveryKeyBlob, @NonNull List<KeyEntryRecoveryData> applicationKeys, + @UserIdInt int userId) throws RemoteException { return mRecoverableKeyStoreManager.recoverKeys( sessionId, recoveryKeyBlob, applicationKeys, userId); diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java index bc080be70bcb..e851d8cf21b3 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java @@ -298,8 +298,8 @@ public class KeySyncUtils { .order(ByteOrder.LITTLE_ENDIAN) .put(SecureBox.encodePublicKey(thmPublicKey)) .putLong(counterId) - .putInt(maxAttempts) .putLong(deviceId) + .putInt(maxAttempts) .array(); } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java index 95f5cb7ae112..a8b8361b4f03 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java @@ -88,7 +88,8 @@ public class PlatformKeyManager { * * @hide */ - public static PlatformKeyManager getInstance(Context context, RecoverableKeyStoreDb database, int userId) + public static PlatformKeyManager getInstance(Context context, RecoverableKeyStoreDb database, + int userId) throws KeyStoreException, NoSuchAlgorithmException, InsecureUserException { context = context.getApplicationContext(); PlatformKeyManager keyManager = new PlatformKeyManager( @@ -115,16 +116,12 @@ public class PlatformKeyManager { /** * Returns the current generation ID of the platform key. This increments whenever a platform * key has to be replaced. (e.g., because the user has removed and then re-added their lock - * screen). + * screen). Returns -1 if no key has been generated yet. * * @hide */ public int getGenerationId() { - int generationId = mDatabase.getPlatformKeyGenerationId(mUserId); - if (generationId == -1) { - return 1; - } - return generationId; + return mDatabase.getPlatformKeyGenerationId(mUserId); } /** @@ -149,7 +146,6 @@ public class PlatformKeyManager { public void regenerate() throws NoSuchAlgorithmException, KeyStoreException { int nextId = getGenerationId() + 1; generateAndLoadKey(nextId); - setGenerationId(nextId); } /** @@ -207,13 +203,20 @@ public class PlatformKeyManager { Locale.US, "Platform key generation %d exists already.", generationId)); return; } - if (generationId == 1) { + if (generationId == -1) { Log.i(TAG, "Generating initial platform ID."); } else { Log.w(TAG, String.format(Locale.US, "Platform generation ID was %d but no " + "entry was present in AndroidKeyStore. Generating fresh key.", generationId)); } + if (generationId == -1) { + generationId = 1; + } else { + // Had to generate a fresh key, bump the generation id + generationId++; + } + generateAndLoadKey(generationId); } @@ -296,6 +299,8 @@ public class PlatformKeyManager { .setBoundToSpecificSecureUserId(mUserId) .build()); + setGenerationId(generationId); + try { secretKey.destroy(); } catch (DestroyFailedException e) { diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java index fe1cad4b18ac..eccf241dd47f 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java @@ -216,7 +216,6 @@ public class RecoverableKeyStoreManager { // Any application should be able to check status for its own keys. // If caller is a recovery agent it can check statuses for other packages, but // only for recoverable keys it manages. - checkRecoverKeyStorePermission(); return mDatabase.getStatusForAllKeys(Binder.getCallingUid()); } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java index 838311e185e8..5ca5da4ead56 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java @@ -590,6 +590,7 @@ public class RecoverableKeyStoreDb { * * @hide */ + @Nullable public Long getServerParameters(int userId, int uid) { SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase(); diff --git a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java index 171703ac8933..f35e6ec92dae 100644 --- a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java +++ b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java @@ -33,6 +33,7 @@ import android.text.TextUtils; import android.util.Slog; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.internal.net.INetworkWatchlistManager; @@ -92,6 +93,7 @@ public class NetworkWatchlistService extends INetworkWatchlistManager.Stub { } } + @GuardedBy("mLoggingSwitchLock") private volatile boolean mIsLoggingEnabled = false; private final Object mLoggingSwitchLock = new Object(); @@ -220,36 +222,11 @@ public class NetworkWatchlistService extends INetworkWatchlistManager.Stub { } } - /** - * Set a new network watchlist. - * This method should be called by ConfigUpdater only. - * - * @return True if network watchlist is updated. - */ - public boolean setNetworkSecurityWatchlist(List<byte[]> domainsCrc32Digests, - List<byte[]> domainsSha256Digests, - List<byte[]> ipAddressesCrc32Digests, - List<byte[]> ipAddressesSha256Digests) { - Slog.i(TAG, "Setting network watchlist"); - if (domainsCrc32Digests == null || domainsSha256Digests == null - || ipAddressesCrc32Digests == null || ipAddressesSha256Digests == null) { - Slog.e(TAG, "Parameters cannot be null"); - return false; - } - if (domainsCrc32Digests.size() != domainsSha256Digests.size() - || ipAddressesCrc32Digests.size() != ipAddressesSha256Digests.size()) { - Slog.e(TAG, "Must need to have the same number of CRC32 and SHA256 digests"); - return false; - } - if (domainsSha256Digests.size() + ipAddressesSha256Digests.size() - > MAX_NUM_OF_WATCHLIST_DIGESTS) { - Slog.e(TAG, "Total watchlist size cannot exceed " + MAX_NUM_OF_WATCHLIST_DIGESTS); - return false; - } - mSettings.writeSettingsToDisk(domainsCrc32Digests, domainsSha256Digests, - ipAddressesCrc32Digests, ipAddressesSha256Digests); - Slog.i(TAG, "Set network watchlist: Success"); - return true; + @Override + public void reloadWatchlist() throws RemoteException { + enforceWatchlistLoggingPermission(); + Slog.i(TAG, "Reloading watchlist"); + mSettings.reloadSettings(); } @Override diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java index f48463f5ae63..838aa53938fa 100644 --- a/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java +++ b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java @@ -21,10 +21,12 @@ import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; +import android.os.Environment; import android.util.Pair; import com.android.internal.util.HexDump; +import java.io.File; import java.util.ArrayList; import java.util.GregorianCalendar; import java.util.HashMap; @@ -83,9 +85,12 @@ class WatchlistReportDbHelper extends SQLiteOpenHelper { HashMap<String, String> appDigestCNCList; } + static File getSystemWatchlistDbFile() { + return new File(Environment.getDataSystemDirectory(), NAME); + } + private WatchlistReportDbHelper(Context context) { - super(context, WatchlistSettings.getSystemWatchlistFile(NAME).getAbsolutePath(), - null, VERSION); + super(context, getSystemWatchlistDbFile().getAbsolutePath(), null, VERSION); // Memory optimization - close idle connections after 30s of inactivity setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS); } diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java b/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java index c50f0d56c992..70002ea21aff 100644 --- a/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java +++ b/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java @@ -19,8 +19,10 @@ package com.android.server.net.watchlist; import android.os.Environment; import android.util.AtomicFile; import android.util.Log; +import android.util.Slog; import android.util.Xml; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.HexDump; @@ -51,10 +53,9 @@ import java.util.zip.CRC32; class WatchlistSettings { private static final String TAG = "WatchlistSettings"; - // Settings xml will be stored in /data/system/network_watchlist/watchlist_settings.xml - static final String SYSTEM_WATCHLIST_DIR = "network_watchlist"; - - private static final String WATCHLIST_XML_FILE = "watchlist_settings.xml"; + // Watchlist config that pushed by ConfigUpdater. + private static final String NETWORK_WATCHLIST_DB_PATH = + "/data/misc/network_watchlist/network_watchlist.xml"; private static class XmlTags { private static final String WATCHLIST_SETTINGS = "watchlist-settings"; @@ -65,86 +66,74 @@ class WatchlistSettings { private static final String HASH = "hash"; } - private static WatchlistSettings sInstance = new WatchlistSettings(); + private static class CrcShaDigests { + final HarmfulDigests crc32Digests; + final HarmfulDigests sha256Digests; + + public CrcShaDigests(HarmfulDigests crc32Digests, HarmfulDigests sha256Digests) { + this.crc32Digests = crc32Digests; + this.sha256Digests = sha256Digests; + } + } + + private final static WatchlistSettings sInstance = new WatchlistSettings(); private final AtomicFile mXmlFile; - private final Object mLock = new Object(); - private HarmfulDigests mCrc32DomainDigests = new HarmfulDigests(new ArrayList<>()); - private HarmfulDigests mSha256DomainDigests = new HarmfulDigests(new ArrayList<>()); - private HarmfulDigests mCrc32IpDigests = new HarmfulDigests(new ArrayList<>()); - private HarmfulDigests mSha256IpDigests = new HarmfulDigests(new ArrayList<>()); - public static synchronized WatchlistSettings getInstance() { + private volatile CrcShaDigests mDomainDigests; + private volatile CrcShaDigests mIpDigests; + + public static WatchlistSettings getInstance() { return sInstance; } private WatchlistSettings() { - this(getSystemWatchlistFile(WATCHLIST_XML_FILE)); + this(new File(NETWORK_WATCHLIST_DB_PATH)); } @VisibleForTesting protected WatchlistSettings(File xmlFile) { mXmlFile = new AtomicFile(xmlFile); - readSettingsLocked(); - } - - static File getSystemWatchlistFile(String filename) { - final File dataSystemDir = Environment.getDataSystemDirectory(); - final File systemWatchlistDir = new File(dataSystemDir, SYSTEM_WATCHLIST_DIR); - systemWatchlistDir.mkdirs(); - return new File(systemWatchlistDir, filename); + reloadSettings(); } - private void readSettingsLocked() { - synchronized (mLock) { - FileInputStream stream; - try { - stream = mXmlFile.openRead(); - } catch (FileNotFoundException e) { - Log.i(TAG, "No watchlist settings: " + mXmlFile.getBaseFile().getAbsolutePath()); - return; - } + public void reloadSettings() { + try (FileInputStream stream = mXmlFile.openRead()){ final List<byte[]> crc32DomainList = new ArrayList<>(); final List<byte[]> sha256DomainList = new ArrayList<>(); final List<byte[]> crc32IpList = new ArrayList<>(); final List<byte[]> sha256IpList = new ArrayList<>(); - try { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(stream, StandardCharsets.UTF_8.name()); - parser.nextTag(); - parser.require(XmlPullParser.START_TAG, null, XmlTags.WATCHLIST_SETTINGS); - while (parser.nextTag() == XmlPullParser.START_TAG) { - String tagName = parser.getName(); - switch (tagName) { - case XmlTags.CRC32_DOMAIN: - parseHash(parser, tagName, crc32DomainList); - break; - case XmlTags.CRC32_IP: - parseHash(parser, tagName, crc32IpList); - break; - case XmlTags.SHA256_DOMAIN: - parseHash(parser, tagName, sha256DomainList); - break; - case XmlTags.SHA256_IP: - parseHash(parser, tagName, sha256IpList); - break; - default: - Log.w(TAG, "Unknown element: " + parser.getName()); - XmlUtils.skipCurrentTag(parser); - } - } - parser.require(XmlPullParser.END_TAG, null, XmlTags.WATCHLIST_SETTINGS); - writeSettingsToMemory(crc32DomainList, sha256DomainList, crc32IpList, sha256IpList); - } catch (IllegalStateException | NullPointerException | NumberFormatException | - XmlPullParserException | IOException | IndexOutOfBoundsException e) { - Log.w(TAG, "Failed parsing " + e); - } finally { - try { - stream.close(); - } catch (IOException e) { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(stream, StandardCharsets.UTF_8.name()); + parser.nextTag(); + parser.require(XmlPullParser.START_TAG, null, XmlTags.WATCHLIST_SETTINGS); + while (parser.nextTag() == XmlPullParser.START_TAG) { + String tagName = parser.getName(); + switch (tagName) { + case XmlTags.CRC32_DOMAIN: + parseHash(parser, tagName, crc32DomainList); + break; + case XmlTags.CRC32_IP: + parseHash(parser, tagName, crc32IpList); + break; + case XmlTags.SHA256_DOMAIN: + parseHash(parser, tagName, sha256DomainList); + break; + case XmlTags.SHA256_IP: + parseHash(parser, tagName, sha256IpList); + break; + default: + Log.w(TAG, "Unknown element: " + parser.getName()); + XmlUtils.skipCurrentTag(parser); } } + parser.require(XmlPullParser.END_TAG, null, XmlTags.WATCHLIST_SETTINGS); + writeSettingsToMemory(crc32DomainList, sha256DomainList, crc32IpList, sha256IpList); + Log.i(TAG, "Reload watchlist done"); + } catch (IllegalStateException | NullPointerException | NumberFormatException | + XmlPullParserException | IOException | IndexOutOfBoundsException e) { + Slog.e(TAG, "Failed parsing xml", e); } } @@ -161,101 +150,61 @@ class WatchlistSettings { } /** - * Write network watchlist settings to disk. - * Adb should not use it, should use writeSettingsToMemory directly instead. - */ - public void writeSettingsToDisk(List<byte[]> newCrc32DomainList, - List<byte[]> newSha256DomainList, - List<byte[]> newCrc32IpList, - List<byte[]> newSha256IpList) { - synchronized (mLock) { - FileOutputStream stream; - try { - stream = mXmlFile.startWrite(); - } catch (IOException e) { - Log.w(TAG, "Failed to write display settings: " + e); - return; - } - - try { - XmlSerializer out = new FastXmlSerializer(); - out.setOutput(stream, StandardCharsets.UTF_8.name()); - out.startDocument(null, true); - out.startTag(null, XmlTags.WATCHLIST_SETTINGS); - - writeHashSetToXml(out, XmlTags.SHA256_DOMAIN, newSha256DomainList); - writeHashSetToXml(out, XmlTags.SHA256_IP, newSha256IpList); - writeHashSetToXml(out, XmlTags.CRC32_DOMAIN, newCrc32DomainList); - writeHashSetToXml(out, XmlTags.CRC32_IP, newCrc32IpList); - - out.endTag(null, XmlTags.WATCHLIST_SETTINGS); - out.endDocument(); - mXmlFile.finishWrite(stream); - writeSettingsToMemory(newCrc32DomainList, newSha256DomainList, newCrc32IpList, - newSha256IpList); - } catch (IOException e) { - Log.w(TAG, "Failed to write display settings, restoring backup.", e); - mXmlFile.failWrite(stream); - } - } - } - - /** * Write network watchlist settings to memory. */ public void writeSettingsToMemory(List<byte[]> newCrc32DomainList, List<byte[]> newSha256DomainList, List<byte[]> newCrc32IpList, List<byte[]> newSha256IpList) { - synchronized (mLock) { - mCrc32DomainDigests = new HarmfulDigests(newCrc32DomainList); - mCrc32IpDigests = new HarmfulDigests(newCrc32IpList); - mSha256DomainDigests = new HarmfulDigests(newSha256DomainList); - mSha256IpDigests = new HarmfulDigests(newSha256IpList); - } - } - - private static void writeHashSetToXml(XmlSerializer out, String tagName, List<byte[]> hashSet) - throws IOException { - out.startTag(null, tagName); - for (byte[] hash : hashSet) { - out.startTag(null, XmlTags.HASH); - out.text(HexDump.toHexString(hash)); - out.endTag(null, XmlTags.HASH); - } - out.endTag(null, tagName); + mDomainDigests = new CrcShaDigests(new HarmfulDigests(newCrc32DomainList), + new HarmfulDigests(newSha256DomainList)); + mIpDigests = new CrcShaDigests(new HarmfulDigests(newCrc32IpList), + new HarmfulDigests(newSha256IpList)); } public boolean containsDomain(String domain) { + final CrcShaDigests domainDigests = mDomainDigests; + if (domainDigests == null) { + Slog.wtf(TAG, "domainDigests should not be null"); + return false; + } // First it does a quick CRC32 check. final byte[] crc32 = getCrc32(domain); - if (!mCrc32DomainDigests.contains(crc32)) { + if (!domainDigests.crc32Digests.contains(crc32)) { return false; } // Now we do a slow SHA256 check. final byte[] sha256 = getSha256(domain); - return mSha256DomainDigests.contains(sha256); + return domainDigests.sha256Digests.contains(sha256); } public boolean containsIp(String ip) { + final CrcShaDigests ipDigests = mIpDigests; + if (ipDigests == null) { + Slog.wtf(TAG, "ipDigests should not be null"); + return false; + } // First it does a quick CRC32 check. final byte[] crc32 = getCrc32(ip); - if (!mCrc32IpDigests.contains(crc32)) { + if (!ipDigests.crc32Digests.contains(crc32)) { return false; } // Now we do a slow SHA256 check. final byte[] sha256 = getSha256(ip); - return mSha256IpDigests.contains(sha256); + return ipDigests.sha256Digests.contains(sha256); } - /** Get CRC32 of a string */ + /** Get CRC32 of a string + * + * TODO: Review if we should use CRC32 or other algorithms + */ private byte[] getCrc32(String str) { final CRC32 crc = new CRC32(); crc.update(str.getBytes()); final long tmp = crc.getValue(); - return new byte[]{(byte)(tmp >> 24 & 255), (byte)(tmp >> 16 & 255), - (byte)(tmp >> 8 & 255), (byte)(tmp & 255)}; + return new byte[]{(byte) (tmp >> 24 & 255), (byte) (tmp >> 16 & 255), + (byte) (tmp >> 8 & 255), (byte) (tmp & 255)}; } /** Get SHA256 of a string */ @@ -273,12 +222,12 @@ class WatchlistSettings { public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("Domain CRC32 digest list:"); - mCrc32DomainDigests.dump(fd, pw, args); + mDomainDigests.crc32Digests.dump(fd, pw, args); pw.println("Domain SHA256 digest list:"); - mSha256DomainDigests.dump(fd, pw, args); + mDomainDigests.sha256Digests.dump(fd, pw, args); pw.println("Ip CRC32 digest list:"); - mCrc32IpDigests.dump(fd, pw, args); + mIpDigests.crc32Digests.dump(fd, pw, args); pw.println("Ip SHA256 digest list:"); - mSha256IpDigests.dump(fd, pw, args); + mIpDigests.sha256Digests.dump(fd, pw, args); } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 696d89575e2d..2d55698a22fc 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -163,10 +163,12 @@ import android.content.pm.PackageCleanItem; import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageInstaller; +import android.content.pm.PackageList; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManager.LegacyPackageDeleteObserver; import android.content.pm.PackageManager.PackageInfoFlags; +import android.content.pm.PackageManagerInternal.PackageListObserver; import android.content.pm.PackageParser; import android.content.pm.PackageParser.ActivityIntentInfo; import android.content.pm.PackageParser.Package; @@ -757,6 +759,9 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mPackages") final SparseArray<Map<String, Integer>> mChangedPackagesSequenceNumbers = new SparseArray<>(); + @GuardedBy("mPackages") + final private ArraySet<PackageListObserver> mPackageListObservers = new ArraySet<>(); + class PackageParserCallback implements PackageParser.Callback { @Override public final boolean hasFeature(String feature) { return PackageManagerService.this.hasSystemFeature(feature, 0); @@ -2095,6 +2100,10 @@ public class PackageManagerService extends IPackageManager.Stub } } + if (allNewUsers && !update) { + notifyPackageAdded(packageName); + } + // Log current value of "unknown sources" setting EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED, getUnknownSourcesSettings()); @@ -12983,6 +12992,34 @@ public class PackageManagerService extends IPackageManager.Stub }); } + @Override + public void notifyPackageAdded(String packageName) { + final PackageListObserver[] observers; + synchronized (mPackages) { + if (mPackageListObservers.size() == 0) { + return; + } + observers = (PackageListObserver[]) mPackageListObservers.toArray(); + } + for (int i = observers.length - 1; i >= 0; --i) { + observers[i].onPackageAdded(packageName); + } + } + + @Override + public void notifyPackageRemoved(String packageName) { + final PackageListObserver[] observers; + synchronized (mPackages) { + if (mPackageListObservers.size() == 0) { + return; + } + observers = (PackageListObserver[]) mPackageListObservers.toArray(); + } + for (int i = observers.length - 1; i >= 0; --i) { + observers[i].onPackageRemoved(packageName); + } + } + /** * Sends a broadcast for the given action. * <p>If {@code isInstantApp} is {@code true}, then the broadcast is protected with @@ -17640,6 +17677,7 @@ public class PackageManagerService extends IPackageManager.Stub removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null, null, broadcastUsers, instantUserIds); + packageSender.notifyPackageRemoved(removedPackage); } } if (removedAppId >= 0) { @@ -20395,10 +20433,9 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } } sUserManager.systemReady(); - // If we upgraded grant all default permissions before kicking off. for (int userId : grantPermissionsUserIds) { - mDefaultPermissionPolicy.grantDefaultPermissions(mPackages.values(), userId); + mDefaultPermissionPolicy.grantDefaultPermissions(userId); } if (grantPermissionsUserIds == EMPTY_INT_ARRAY) { @@ -22445,8 +22482,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } void onNewUserCreated(final int userId) { + mDefaultPermissionPolicy.grantDefaultPermissions(userId); synchronized(mPackages) { - mDefaultPermissionPolicy.grantDefaultPermissions(mPackages.values(), userId); // If permission review for legacy apps is required, we represent // dagerous permissions for such apps as always granted runtime // permissions to keep per user flag state whether review is needed. @@ -22933,6 +22970,29 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } @Override + public PackageList getPackageList(PackageListObserver observer) { + synchronized (mPackages) { + final int N = mPackages.size(); + final ArrayList<String> list = new ArrayList<>(N); + for (int i = 0; i < N; i++) { + list.add(mPackages.keyAt(i)); + } + final PackageList packageList = new PackageList(list, observer); + if (observer != null) { + mPackageListObservers.add(packageList); + } + return packageList; + } + } + + @Override + public void removePackageListObserver(PackageListObserver observer) { + synchronized (mPackages) { + mPackageListObservers.remove(observer); + } + } + + @Override public PackageParser.Package getDisabledPackage(String packageName) { synchronized (mPackages) { final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName); @@ -22989,6 +23049,11 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } @Override + public void setUseOpenWifiAppPackagesProvider(PackagesProvider provider) { + mDefaultPermissionPolicy.setUseOpenWifiAppPackagesProvider(provider); + } + + @Override public void setSyncAdapterPackagesprovider(SyncAdapterPackagesProvider provider) { mDefaultPermissionPolicy.setSyncAdapterPackagesProvider(provider); } @@ -23013,6 +23078,12 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } @Override + public void grantDefaultPermissionsToDefaultUseOpenWifiApp(String packageName, int userId) { + mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultUseOpenWifiApp( + packageName, userId); + } + + @Override public void setKeepUninstalledPackages(final List<String> packageList) { Preconditions.checkNotNull(packageList); List<String> removedFromList = null; @@ -23594,4 +23665,6 @@ interface PackageSender { final IIntentReceiver finishedReceiver, final int[] userIds, int[] instantUserIds); void sendPackageAddedForNewUsers(String packageName, boolean sendBootCompleted, boolean includeStopped, int appId, int[] userIds, int[] instantUserIds); + void notifyPackageAdded(String packageName); + void notifyPackageRemoved(String packageName); } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 648f847ac3c5..4cf18149d853 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -3122,7 +3122,7 @@ public final class Settings { ATTR_VOLUME_UUID); final VersionInfo ver = findOrCreateVersion(volumeUuid); ver.sdkVersion = XmlUtils.readIntAttribute(parser, ATTR_SDK_VERSION); - ver.databaseVersion = XmlUtils.readIntAttribute(parser, ATTR_SDK_VERSION); + ver.databaseVersion = XmlUtils.readIntAttribute(parser, ATTR_DATABASE_VERSION); ver.fingerprint = XmlUtils.readStringAttribute(parser, ATTR_FINGERPRINT); } else { Slog.w(PackageManagerService.TAG, "Unknown element under <packages>: " diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 768eb8f37549..c3dce3133026 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -27,7 +27,6 @@ import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.ActivityManagerNative; -import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.IStopUserCallback; import android.app.KeyguardManager; @@ -795,12 +794,7 @@ public class UserManagerService extends IUserManager.Stub { "target should only be specified when we are disabling quiet mode."); } - if (!isAllowedToSetWorkMode(callingPackage, Binder.getCallingUid())) { - throw new SecurityException("Not allowed to call trySetQuietModeEnabled, " - + "caller is foreground default launcher " - + "nor with MANAGE_USERS/MODIFY_QUIET_MODE permission"); - } - + ensureCanModifyQuietMode(callingPackage, Binder.getCallingUid(), target != null); final long identity = Binder.clearCallingIdentity(); try { if (enableQuietMode) { @@ -824,35 +818,44 @@ public class UserManagerService extends IUserManager.Stub { } /** - * An app can modify quiet mode if the caller meets one of the condition: + * The caller can modify quiet mode if it meets one of these conditions: * <ul> * <li>Has system UID or root UID</li> * <li>Has {@link Manifest.permission#MODIFY_QUIET_MODE}</li> * <li>Has {@link Manifest.permission#MANAGE_USERS}</li> * </ul> + * <p> + * If caller wants to start an intent after disabling the quiet mode, it must has + * {@link Manifest.permission#MANAGE_USERS}. */ - private boolean isAllowedToSetWorkMode(String callingPackage, int callingUid) { + private void ensureCanModifyQuietMode(String callingPackage, int callingUid, + boolean startIntent) { if (hasManageUsersPermission()) { - return true; + return; + } + if (startIntent) { + throw new SecurityException("MANAGE_USERS permission is required to start intent " + + "after disabling quiet mode."); } - final boolean hasModifyQuietModePermission = ActivityManager.checkComponentPermission( Manifest.permission.MODIFY_QUIET_MODE, callingUid, -1, true) == PackageManager.PERMISSION_GRANTED; if (hasModifyQuietModePermission) { - return true; + return; } + verifyCallingPackage(callingPackage, callingUid); final ShortcutServiceInternal shortcutInternal = LocalServices.getService(ShortcutServiceInternal.class); if (shortcutInternal != null) { boolean isForegroundLauncher = shortcutInternal.isForegroundDefaultLauncher(callingPackage, callingUid); if (isForegroundLauncher) { - return true; + return; } } - return false; + throw new SecurityException("Can't modify quiet mode, caller is neither foreground " + + "default launcher nor has MANAGE_USERS/MODIFY_QUIET_MODE permission"); } private void setQuietModeEnabled( @@ -3932,4 +3935,16 @@ public class UserManagerService extends IUserManager.Stub { return false; } } + + /** + * Check if the calling package name matches with the calling UID, throw + * {@link SecurityException} if not. + */ + private void verifyCallingPackage(String callingPackage, int callingUid) { + int packageUid = mPm.getPackageUid(callingPackage, 0, UserHandle.getUserId(callingUid)); + if (packageUid != callingUid) { + throw new SecurityException("Specified package " + callingPackage + + " does not match the calling uid " + callingUid); + } + } } diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 01f3c576f72b..34c3ce359e86 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -30,6 +30,7 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; +import android.content.pm.PackageList; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageParser; @@ -134,6 +135,11 @@ public final class DefaultPermissionGrantPolicy { LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION); } + private static final Set<String> COARSE_LOCATION_PERMISSIONS = new ArraySet<>(); + static { + COARSE_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION); + } + private static final Set<String> CALENDAR_PERMISSIONS = new ArraySet<>(); static { CALENDAR_PERMISSIONS.add(Manifest.permission.READ_CALENDAR); @@ -182,6 +188,7 @@ public final class DefaultPermissionGrantPolicy { private PackagesProvider mSmsAppPackagesProvider; private PackagesProvider mDialerAppPackagesProvider; private PackagesProvider mSimCallManagerPackagesProvider; + private PackagesProvider mUseOpenWifiAppPackagesProvider; private SyncAdapterPackagesProvider mSyncAdapterPackagesProvider; private ArrayMap<String, List<DefaultPermissionGrant>> mGrantExceptions; @@ -246,17 +253,23 @@ public final class DefaultPermissionGrantPolicy { } } + public void setUseOpenWifiAppPackagesProvider(PackagesProvider provider) { + synchronized (mLock) { + mUseOpenWifiAppPackagesProvider = provider; + } + } + public void setSyncAdapterPackagesProvider(SyncAdapterPackagesProvider provider) { synchronized (mLock) { mSyncAdapterPackagesProvider = provider; } } - public void grantDefaultPermissions(Collection<PackageParser.Package> packages, int userId) { + public void grantDefaultPermissions(int userId) { if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) { - grantAllRuntimePermissions(packages, userId); + grantAllRuntimePermissions(userId); } else { - grantPermissionsToSysComponentsAndPrivApps(packages, userId); + grantPermissionsToSysComponentsAndPrivApps(userId); grantDefaultSystemHandlerPermissions(userId); grantDefaultPermissionExceptions(userId); } @@ -278,10 +291,14 @@ public final class DefaultPermissionGrantPolicy { } } - private void grantAllRuntimePermissions( - Collection<PackageParser.Package> packages, int userId) { + private void grantAllRuntimePermissions(int userId) { Log.i(TAG, "Granting all runtime permissions for user " + userId); - for (PackageParser.Package pkg : packages) { + final PackageList packageList = mServiceInternal.getPackageList(); + for (String packageName : packageList.getPackageNames()) { + final PackageParser.Package pkg = mServiceInternal.getPackage(packageName); + if (pkg == null) { + continue; + } grantRuntimePermissionsForPackage(userId, pkg); } } @@ -290,10 +307,14 @@ public final class DefaultPermissionGrantPolicy { mHandler.sendEmptyMessage(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS); } - private void grantPermissionsToSysComponentsAndPrivApps( - Collection<PackageParser.Package> packages, int userId) { + private void grantPermissionsToSysComponentsAndPrivApps(int userId) { Log.i(TAG, "Granting permissions to platform components for user " + userId); - for (PackageParser.Package pkg : packages) { + final PackageList packageList = mServiceInternal.getPackageList(); + for (String packageName : packageList.getPackageNames()) { + final PackageParser.Package pkg = mServiceInternal.getPackage(packageName); + if (pkg == null) { + continue; + } if (!isSysComponentOrPersistentPlatformSignedPrivApp(pkg) || !doesPackageSupportRuntimePermissions(pkg) || pkg.requestedPermissions.isEmpty()) { @@ -311,6 +332,7 @@ public final class DefaultPermissionGrantPolicy { final PackagesProvider smsAppPackagesProvider; final PackagesProvider dialerAppPackagesProvider; final PackagesProvider simCallManagerPackagesProvider; + final PackagesProvider useOpenWifiAppPackagesProvider; final SyncAdapterPackagesProvider syncAdapterPackagesProvider; synchronized (mLock) { @@ -319,6 +341,7 @@ public final class DefaultPermissionGrantPolicy { smsAppPackagesProvider = mSmsAppPackagesProvider; dialerAppPackagesProvider = mDialerAppPackagesProvider; simCallManagerPackagesProvider = mSimCallManagerPackagesProvider; + useOpenWifiAppPackagesProvider = mUseOpenWifiAppPackagesProvider; syncAdapterPackagesProvider = mSyncAdapterPackagesProvider; } @@ -332,6 +355,8 @@ public final class DefaultPermissionGrantPolicy { ? dialerAppPackagesProvider.getPackages(userId) : null; String[] simCallManagerPackageNames = (simCallManagerPackagesProvider != null) ? simCallManagerPackagesProvider.getPackages(userId) : null; + String[] useOpenWifiAppPackageNames = (useOpenWifiAppPackagesProvider != null) + ? useOpenWifiAppPackagesProvider.getPackages(userId) : null; String[] contactsSyncAdapterPackages = (syncAdapterPackagesProvider != null) ? syncAdapterPackagesProvider.getPackages(ContactsContract.AUTHORITY, userId) : null; String[] calendarSyncAdapterPackages = (syncAdapterPackagesProvider != null) ? @@ -449,6 +474,18 @@ public final class DefaultPermissionGrantPolicy { } } + // Use Open Wifi + if (useOpenWifiAppPackageNames != null) { + for (String useOpenWifiPackageName : useOpenWifiAppPackageNames) { + PackageParser.Package useOpenWifiPackage = + getSystemPackage(useOpenWifiPackageName); + if (useOpenWifiPackage != null) { + grantDefaultPermissionsToDefaultSystemUseOpenWifiApp(useOpenWifiPackage, + userId); + } + } + } + // SMS if (smsAppPackageNames == null) { Intent smsIntent = new Intent(Intent.ACTION_MAIN); @@ -818,6 +855,13 @@ public final class DefaultPermissionGrantPolicy { } } + private void grantDefaultPermissionsToDefaultSystemUseOpenWifiApp( + PackageParser.Package useOpenWifiPackage, int userId) { + if (doesPackageSupportRuntimePermissions(useOpenWifiPackage)) { + grantRuntimePermissions(useOpenWifiPackage, COARSE_LOCATION_PERMISSIONS, userId); + } + } + public void grantDefaultPermissionsToDefaultSmsApp(String packageName, int userId) { Log.i(TAG, "Granting permissions to default sms app for user:" + userId); if (packageName == null) { @@ -850,6 +894,19 @@ public final class DefaultPermissionGrantPolicy { } } + public void grantDefaultPermissionsToDefaultUseOpenWifiApp(String packageName, int userId) { + Log.i(TAG, "Granting permissions to default Use Open WiFi app for user:" + userId); + if (packageName == null) { + return; + } + PackageParser.Package useOpenWifiPackage = getPackage(packageName); + if (useOpenWifiPackage != null + && doesPackageSupportRuntimePermissions(useOpenWifiPackage)) { + grantRuntimePermissions( + useOpenWifiPackage, COARSE_LOCATION_PERMISSIONS, false, true, userId); + } + } + private void grantDefaultPermissionsToDefaultSimCallManager( PackageParser.Package simCallManagerPackage, int userId) { Log.i(TAG, "Granting permissions to sim call manager for user:" + userId); @@ -1005,7 +1062,7 @@ public final class DefaultPermissionGrantPolicy { } private void grantRuntimePermissions(PackageParser.Package pkg, Set<String> permissions, - boolean systemFixed, boolean isDefaultPhoneOrSms, int userId) { + boolean systemFixed, boolean ignoreSystemPackage, int userId) { if (pkg.requestedPermissions.isEmpty()) { return; } @@ -1013,13 +1070,13 @@ public final class DefaultPermissionGrantPolicy { List<String> requestedPermissions = pkg.requestedPermissions; Set<String> grantablePermissions = null; - // If this is the default Phone or SMS app we grant permissions regardless - // whether the version on the system image declares the permission as used since - // selecting the app as the default Phone or SMS the user makes a deliberate + // In some cases, like for the Phone or SMS app, we grant permissions regardless + // of if the version on the system image declares the permission as used since + // selecting the app as the default for that function the user makes a deliberate // choice to grant this app the permissions needed to function. For all other // apps, (default grants on first boot and user creation) we don't grant default // permissions if the version on the system image does not declare them. - if (!isDefaultPhoneOrSms && pkg.isUpdatedSystemApp()) { + if (!ignoreSystemPackage && pkg.isUpdatedSystemApp()) { final PackageParser.Package disabledPkg = mServiceInternal.getDisabledPackage(pkg.packageName); if (disabledPkg != null) { @@ -1053,7 +1110,7 @@ public final class DefaultPermissionGrantPolicy { // Unless the caller wants to override user choices. The override is // to make sure we can grant the needed permission to the default // sms and phone apps after the user chooses this in the UI. - if (flags == 0 || isDefaultPhoneOrSms) { + if (flags == 0 || ignoreSystemPackage) { // Never clobber policy or system. final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED | PackageManager.FLAG_PERMISSION_POLICY_FIXED; diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 02c8f681e7c4..86b22bb9c219 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -57,6 +57,7 @@ import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.os.WorkSource; +import android.os.WorkSource.WorkChain; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.service.dreams.DreamManagerInternal; @@ -1976,6 +1977,16 @@ public final class PowerManagerService extends SystemService return true; } } + + final ArrayList<WorkChain> workChains = wakeLock.mWorkSource.getWorkChains(); + if (workChains != null) { + for (int k = 0; k < workChains.size(); k++) { + final int uid = workChains.get(k).getAttributionUid(); + if (userId == UserHandle.getUserId(uid)) { + return true; + } + } + } } return userId == UserHandle.getUserId(wakeLock.mOwnerUid); } @@ -2441,6 +2452,7 @@ public final class PowerManagerService extends SystemService float screenAutoBrightnessAdjustment = 0.0f; boolean autoBrightness = (mScreenBrightnessModeSetting == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + boolean brightnessIsTemporary = false; if (!mBootCompleted) { // Keep the brightness steady during boot. This requires the // bootloader brightness and the default brightness to be identical. @@ -2455,6 +2467,7 @@ public final class PowerManagerService extends SystemService brightnessSetByUser = false; } else if (isValidBrightness(mTemporaryScreenBrightnessSettingOverride)) { screenBrightness = mTemporaryScreenBrightnessSettingOverride; + brightnessIsTemporary = true; } else if (isValidBrightness(mScreenBrightnessSetting)) { screenBrightness = mScreenBrightnessSetting; } @@ -2464,6 +2477,7 @@ public final class PowerManagerService extends SystemService mTemporaryScreenAutoBrightnessAdjustmentSettingOverride)) { screenAutoBrightnessAdjustment = mTemporaryScreenAutoBrightnessAdjustmentSettingOverride; + brightnessIsTemporary = true; } else if (isValidAutoBrightnessAdjustment( mScreenAutoBrightnessAdjustmentSetting)) { screenAutoBrightnessAdjustment = mScreenAutoBrightnessAdjustmentSetting; @@ -2479,6 +2493,7 @@ public final class PowerManagerService extends SystemService mDisplayPowerRequest.screenAutoBrightnessAdjustment = screenAutoBrightnessAdjustment; mDisplayPowerRequest.brightnessSetByUser = brightnessSetByUser; + mDisplayPowerRequest.brightnessIsTemporary = brightnessIsTemporary; mDisplayPowerRequest.useAutoBrightness = autoBrightness; mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked(); mDisplayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness(); diff --git a/services/core/java/com/android/server/updates/NetworkWatchlistInstallReceiver.java b/services/core/java/com/android/server/updates/NetworkWatchlistInstallReceiver.java new file mode 100644 index 000000000000..3b7ddc2e803f --- /dev/null +++ b/services/core/java/com/android/server/updates/NetworkWatchlistInstallReceiver.java @@ -0,0 +1,40 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.updates; + +import android.content.Context; +import android.content.Intent; +import android.net.NetworkWatchlistManager; +import android.os.RemoteException; +import android.util.Slog; + +public class NetworkWatchlistInstallReceiver extends ConfigUpdateInstallReceiver { + + public NetworkWatchlistInstallReceiver() { + super("/data/misc/network_watchlist/", "network_watchlist.xml", "metadata/", "version"); + } + + @Override + protected void postInstall(Context context, Intent intent) { + try { + context.getSystemService(NetworkWatchlistManager.class).reloadWatchlist(); + } catch (Exception e) { + // Network Watchlist is not available + Slog.wtf("NetworkWatchlistInstallReceiver", "Unable to reload watchlist"); + } + } +} diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java index b86cd50dc922..c16a5315060f 100644 --- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java +++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java @@ -139,7 +139,7 @@ class AppWindowThumbnail implements Animatable { @Override public Builder makeAnimationLeash() { - return mAppToken.makeSurface().setParent(mAppToken.getAppAnimationLayer()); + return mAppToken.makeSurface(); } @Override @@ -148,6 +148,11 @@ class AppWindowThumbnail implements Animatable { } @Override + public SurfaceControl getAnimationLeashParent() { + return mAppToken.getAppAnimationLayer(); + } + + @Override public SurfaceControl getParentSurfaceControl() { return mAppToken.getParentSurfaceControl(); } diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 44d7948b12b6..dcd88dd074d1 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -16,6 +16,9 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; @@ -53,6 +56,7 @@ import static com.android.server.wm.proto.AppWindowTokenProto.WINDOW_TOKEN; import android.annotation.CallSuper; import android.app.Activity; +import android.app.WindowConfiguration.WindowingMode; import android.content.res.Configuration; import android.graphics.GraphicBuffer; import android.graphics.Point; @@ -1211,6 +1215,30 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } @Override + public void onConfigurationChanged(Configuration newParentConfig) { + final int prevWinMode = getWindowingMode(); + super.onConfigurationChanged(newParentConfig); + final int winMode = getWindowingMode(); + + if (prevWinMode == winMode) { + return; + } + + if (prevWinMode != WINDOWING_MODE_UNDEFINED && winMode == WINDOWING_MODE_PINNED) { + // Entering PiP from fullscreen, reset the snap fraction + mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(this); + } else if (prevWinMode == WINDOWING_MODE_PINNED && winMode != WINDOWING_MODE_UNDEFINED) { + // Leaving PiP to fullscreen, save the snap fraction based on the pre-animation bounds + // for the next re-entry into PiP (assuming the activity is not hidden or destroyed) + final TaskStack pinnedStack = mDisplayContent.getPinnedStack(); + if (pinnedStack != null) { + mDisplayContent.mPinnedStackControllerLocked.saveReentrySnapFraction(this, + pinnedStack.mPreAnimationBounds); + } + } + } + + @Override void checkAppWindowsReadyToShow() { if (allDrawn == mLastAllDrawn) { return; @@ -1512,9 +1540,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } @Override - public SurfaceControl.Builder makeAnimationLeash() { - return super.makeAnimationLeash() - .setParent(getAppAnimationLayer()); + public SurfaceControl getAnimationLeashParent() { + return getAppAnimationLayer(); } boolean applyAnimationLocked(WindowManager.LayoutParams lp, int transit, boolean enter, @@ -1541,6 +1568,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (stack != null) { stack.getRelativePosition(mTmpPoint); stack.getBounds(mTmpRect); + mTmpRect.offsetTo(0, 0); } final AnimationAdapter adapter = new LocalAnimationAdapter( new WindowAnimationSpec(a, mTmpPoint, mTmpRect, @@ -1837,6 +1865,11 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree @Override void setHidden(boolean hidden) { super.setHidden(hidden); + + if (hidden) { + // Once the app window is hidden, reset the last saved PiP snap fraction + mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(this); + } scheduleAnimation(); } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index d053015cea82..63dfbc2c25b6 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1923,6 +1923,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mService.unregisterPointerEventListener(mService.mMousePositionTracker); } } + mService.mAnimator.removeDisplayLocked(mDisplayId); + // The pending transaction won't be applied so we should // just clean up any surfaces pending destruction. onPendingTransactionApplied(); diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java index d8726bfc2c20..69cbe4607cf1 100644 --- a/services/core/java/com/android/server/wm/PinnedStackController.java +++ b/services/core/java/com/android/server/wm/PinnedStackController.java @@ -48,6 +48,7 @@ import com.android.internal.policy.PipSnapAlgorithm; import com.android.server.UiThread; import java.io.PrintWriter; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; @@ -71,6 +72,7 @@ class PinnedStackController { private static final String TAG = TAG_WITH_CLASS_NAME ? "PinnedStackController" : TAG_WM; + public static final float INVALID_SNAP_FRACTION = -1f; private final WindowManagerService mService; private final DisplayContent mDisplayContent; private final Handler mHandler = UiThread.getHandler(); @@ -101,6 +103,8 @@ class PinnedStackController { private float mDefaultAspectRatio; private Point mScreenEdgeInsets; private int mCurrentMinSize; + private float mReentrySnapFraction = INVALID_SNAP_FRACTION; + private WeakReference<AppWindowToken> mLastPipActivity = null; // The aspect ratio bounds of the PIP. private float mMinAspectRatio; @@ -113,6 +117,7 @@ class PinnedStackController { private final Rect mTmpAnimatingBoundsRect = new Rect(); private final Point mTmpDisplaySize = new Point(); + /** * The callback object passed to listeners for them to notify the controller of state changes. */ @@ -250,9 +255,35 @@ class PinnedStackController { } /** + * Saves the current snap fraction for re-entry of the current activity into PiP. + */ + void saveReentrySnapFraction(final AppWindowToken token, final Rect stackBounds) { + mReentrySnapFraction = getSnapFraction(stackBounds); + mLastPipActivity = new WeakReference<>(token); + } + + /** + * Resets the last saved snap fraction so that the default bounds will be returned. + */ + void resetReentrySnapFraction(AppWindowToken token) { + if (mLastPipActivity != null && mLastPipActivity.get() == token) { + mReentrySnapFraction = INVALID_SNAP_FRACTION; + mLastPipActivity = null; + } + } + + /** * @return the default bounds to show the PIP when there is no active PIP. */ - Rect getDefaultBounds() { + Rect getDefaultOrLastSavedBounds() { + return getDefaultBounds(mReentrySnapFraction); + } + + /** + * @return the default bounds to show the PIP, if a {@param snapFraction} is provided, then it + * will apply the default bounds to the provided snap fraction. + */ + Rect getDefaultBounds(float snapFraction) { synchronized (mService.mWindowMap) { final Rect insetBounds = new Rect(); getInsetBounds(insetBounds); @@ -260,8 +291,14 @@ class PinnedStackController { final Rect defaultBounds = new Rect(); final Size size = mSnapAlgorithm.getSizeForAspectRatio(mDefaultAspectRatio, mDefaultMinSize, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); - Gravity.apply(mDefaultStackGravity, size.getWidth(), size.getHeight(), insetBounds, - 0, mIsImeShowing ? mImeHeight : 0, defaultBounds); + if (snapFraction != INVALID_SNAP_FRACTION) { + defaultBounds.set(0, 0, size.getWidth(), size.getHeight()); + final Rect movementBounds = getMovementBounds(defaultBounds); + mSnapAlgorithm.applySnapFraction(defaultBounds, movementBounds, snapFraction); + } else { + Gravity.apply(mDefaultStackGravity, size.getWidth(), size.getHeight(), insetBounds, + 0, mIsImeShowing ? mImeHeight : 0, defaultBounds); + } return defaultBounds; } } @@ -299,9 +336,7 @@ class PinnedStackController { final Rect postChangeStackBounds = mTmpRect; // Calculate the snap fraction of the current stack along the old movement bounds - final Rect preChangeMovementBounds = getMovementBounds(postChangeStackBounds); - final float snapFraction = mSnapAlgorithm.getSnapFraction(postChangeStackBounds, - preChangeMovementBounds); + final float snapFraction = getSnapFraction(postChangeStackBounds); mDisplayInfo.copyFrom(displayInfo); // Calculate the stack bounds in the new orientation to the same same fraction along the @@ -414,7 +449,7 @@ class PinnedStackController { try { final Rect insetBounds = new Rect(); getInsetBounds(insetBounds); - final Rect normalBounds = getDefaultBounds(); + final Rect normalBounds = getDefaultBounds(INVALID_SNAP_FRACTION); if (isValidPictureInPictureAspectRatio(mAspectRatio)) { transformBoundsToAspectRatio(normalBounds, mAspectRatio, false /* useCurrentMinEdgeSize */); @@ -486,6 +521,14 @@ class PinnedStackController { } /** + * @return the default snap fraction to apply instead of the default gravity when calculating + * the default stack bounds when first entering PiP. + */ + private float getSnapFraction(Rect stackBounds) { + return mSnapAlgorithm.getSnapFraction(stackBounds, getMovementBounds(stackBounds)); + } + + /** * @return the pixels for a given dp value. */ private int dpToPx(float dpValue, DisplayMetrics dm) { @@ -494,7 +537,8 @@ class PinnedStackController { void dump(String prefix, PrintWriter pw) { pw.println(prefix + "PinnedStackController"); - pw.print(prefix + " defaultBounds="); getDefaultBounds().printShortString(pw); + pw.print(prefix + " defaultBounds="); + getDefaultBounds(INVALID_SNAP_FRACTION).printShortString(pw); pw.println(); mService.getStackBounds(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mTmpRect); pw.print(prefix + " movementBounds="); getMovementBounds(mTmpRect).printShortString(pw); @@ -516,7 +560,7 @@ class PinnedStackController { void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); - getDefaultBounds().writeToProto(proto, DEFAULT_BOUNDS); + getDefaultBounds(INVALID_SNAP_FRACTION).writeToProto(proto, DEFAULT_BOUNDS); mService.getStackBounds(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mTmpRect); getMovementBounds(mTmpRect).writeToProto(proto, MOVEMENT_BOUNDS); proto.end(token); diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java index b021a7223e2e..02fbfba9d332 100644 --- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java +++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java @@ -61,7 +61,7 @@ public class PinnedStackWindowController extends StackWindowController { displayContent.getPinnedStackController(); if (stackBounds == null) { // Calculate the aspect ratio bounds from the default bounds - stackBounds = pinnedStackController.getDefaultBounds(); + stackBounds = pinnedStackController.getDefaultOrLastSavedBounds(); } if (pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)) { @@ -173,7 +173,7 @@ public class PinnedStackWindowController extends StackWindowController { * from fullscreen to non-fullscreen bounds. */ public boolean deferScheduleMultiWindowModeChanged() { - synchronized(mWindowMap) { + synchronized (mWindowMap) { return mContainer.deferScheduleMultiWindowModeChanged(); } } diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java index bda5bc954103..a32e711df534 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimator.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java @@ -72,23 +72,29 @@ class SurfaceAnimator { target.mInnerAnimationFinishedCallback.onAnimationFinished(anim); return; } - if (anim != mAnimation) { - // Callback was from another animation - ignore. - return; - } - final Transaction t = new Transaction(); - SurfaceControl.openTransaction(); - try { - reset(t, true /* destroyLeash */); - animationFinishedCallback.run(); - } finally { - // TODO: This should use pendingTransaction eventually, but right now things - // happening on the animation finished callback are happening on the global - // transaction. - SurfaceControl.mergeToGlobalTransaction(t); - SurfaceControl.closeTransaction(); - } + // TODO: This should use pendingTransaction eventually, but right now things + // happening on the animation finished callback are happening on the global + // transaction. + // For now we need to run this after it's guaranteed that the transaction that + // reparents the surface onto the leash is executed already. Otherwise this may be + // executed first, leading to surface loss, as the reparent operations wouldn't + // be in order. + mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> { + if (anim != mAnimation) { + // Callback was from another animation - ignore. + return; + } + final Transaction t = new Transaction(); + SurfaceControl.openTransaction(); + try { + reset(t, true /* destroyLeash */); + animationFinishedCallback.run(); + } finally { + SurfaceControl.mergeToGlobalTransaction(t); + SurfaceControl.closeTransaction(); + } + }); } }; } @@ -213,7 +219,7 @@ class SurfaceAnimator { return; } final SurfaceControl surface = mAnimatable.getSurfaceControl(); - final SurfaceControl parent = mAnimatable.getParentSurfaceControl(); + final SurfaceControl parent = mAnimatable.getAnimationLeashParent(); if (surface == null || parent == null) { Slog.w(TAG, "Unable to transfer animation, surface or parent is null"); cancelAnimation(); @@ -287,6 +293,7 @@ class SurfaceAnimator { int height, boolean hidden) { if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash"); final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash() + .setParent(mAnimatable.getAnimationLeashParent()) .setName(surface + " - animation-leash") .setSize(width, height); final SurfaceControl leash = builder.build(); @@ -355,6 +362,11 @@ class SurfaceAnimator { SurfaceControl.Builder makeAnimationLeash(); /** + * @return The parent that should be used for the animation leash. + */ + @Nullable SurfaceControl getAnimationLeashParent(); + + /** * @return The surface of the object to be animated. */ @Nullable SurfaceControl getSurfaceControl(); diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 3ffc7fae5d0a..eb8eae1a95d7 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -44,6 +44,7 @@ import static com.android.server.wm.proto.StackProto.WINDOW_CONTAINER; import android.annotation.CallSuper; import android.content.res.Configuration; +import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; import android.os.RemoteException; @@ -145,6 +146,7 @@ public class TaskStack extends WindowContainer<Task> implements * For {@link #prepareSurfaces}. */ final Rect mTmpDimBoundsRect = new Rect(); + private final Point mLastSurfaceSize = new Point(); TaskStack(WindowManagerService service, int stackId, StackWindowController controller) { super(service); @@ -743,7 +745,13 @@ public class TaskStack extends WindowContainer<Task> implements } final Rect stackBounds = getBounds(); - transaction.setSize(mSurfaceControl, stackBounds.width(), stackBounds.height()); + final int width = stackBounds.width(); + final int height = stackBounds.height(); + if (width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) { + return; + } + transaction.setSize(mSurfaceControl, width, height); + mLastSurfaceSize.set(width, height); } @Override diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java index 986529344a78..031b57b73b05 100644 --- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java +++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java @@ -92,8 +92,8 @@ public class WindowAnimationSpec implements AnimationSpec { t.setFinalCrop(leash, mStackBounds); t.setWindowCrop(leash, tmp.transformation.getClipRect()); } else { - mTmpRect.set(tmp.transformation.getClipRect()); - mTmpRect.intersect(mStackBounds); + mTmpRect.set(mStackBounds); + mTmpRect.intersect(tmp.transformation.getClipRect()); t.setWindowCrop(leash, mTmpRect); } } diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index 8bceb640aa50..729587375421 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -35,6 +35,7 @@ import com.android.server.AnimationThread; import com.android.server.policy.WindowManagerPolicy; import java.io.PrintWriter; +import java.util.ArrayList; /** * Singleton class that carries out the animations and Surface operations in a separate task @@ -87,6 +88,12 @@ public class WindowAnimator { */ private boolean mAnimationFrameCallbackScheduled; + /** + * A list of runnable that need to be run after {@link WindowContainer#prepareSurfaces} is + * executed and the corresponding transaction is closed and applied. + */ + private final ArrayList<Runnable> mAfterPrepareSurfacesRunnables = new ArrayList<>(); + WindowAnimator(final WindowManagerService service) { mService = service; mContext = service.mContext; @@ -262,6 +269,7 @@ public class WindowAnimator { mService.destroyPreservedSurfaceLocked(); mService.mWindowPlacerLocked.destroyPendingSurfaces(); + executeAfterPrepareSurfacesRunnables(); if (DEBUG_WINDOW_TRACE) { Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating @@ -425,4 +433,23 @@ public class WindowAnimator { void orAnimating(boolean animating) { mAnimating |= animating; } + + /** + * Adds a runnable to be executed after {@link WindowContainer#prepareSurfaces} is called and + * the corresponding transaction is closed and applied. + */ + void addAfterPrepareSurfacesRunnable(Runnable r) { + mAfterPrepareSurfacesRunnables.add(r); + scheduleAnimation(); + } + + private void executeAfterPrepareSurfacesRunnables() { + + // Traverse in order they were added. + final int size = mAfterPrepareSurfacesRunnables.size(); + for (int i = 0; i < size; i++) { + mAfterPrepareSurfacesRunnables.get(i).run(); + } + mAfterPrepareSurfacesRunnables.clear(); + } } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index b251b53bc051..af314101cd2b 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -95,6 +95,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< protected final WindowManagerService mService; private final Point mTmpPos = new Point(); + protected final Point mLastSurfacePosition = new Point(); /** Total number of elements in this subtree, including our own hierarchy element. */ private int mTreeWeight = 1; @@ -1088,6 +1089,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return makeSurface(); } + @Override + public SurfaceControl getAnimationLeashParent() { + return getParentSurfaceControl(); + } + /** * @return The layer on which all app animations are happening. */ @@ -1172,7 +1178,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } getRelativePosition(mTmpPos); + if (mTmpPos.equals(mLastSurfacePosition)) { + return; + } + transaction.setPosition(mSurfaceControl, mTmpPos.x, mTmpPos.y); + mLastSurfacePosition.set(mTmpPos.x, mTmpPos.y); for (int i = mChildren.size() - 1; i >= 0; i--) { mChildren.get(i).updateSurfacePosition(transaction); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index dfb385bddcdf..fcbc80234947 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -48,8 +48,8 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPL import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; @@ -112,15 +112,36 @@ import static com.android.server.wm.proto.WindowStateProto.CHILD_WINDOWS; import static com.android.server.wm.proto.WindowStateProto.CONTAINING_FRAME; import static com.android.server.wm.proto.WindowStateProto.CONTENT_FRAME; import static com.android.server.wm.proto.WindowStateProto.CONTENT_INSETS; +import static com.android.server.wm.proto.WindowStateProto.CUTOUT; +import static com.android.server.wm.proto.WindowStateProto.DECOR_FRAME; +import static com.android.server.wm.proto.WindowStateProto.DESTROYING; +import static com.android.server.wm.proto.WindowStateProto.DISPLAY_FRAME; import static com.android.server.wm.proto.WindowStateProto.DISPLAY_ID; import static com.android.server.wm.proto.WindowStateProto.FRAME; import static com.android.server.wm.proto.WindowStateProto.GIVEN_CONTENT_INSETS; +import static com.android.server.wm.proto.WindowStateProto.HAS_SURFACE; import static com.android.server.wm.proto.WindowStateProto.IDENTIFIER; +import static com.android.server.wm.proto.WindowStateProto.IS_ON_SCREEN; +import static com.android.server.wm.proto.WindowStateProto.IS_READY_FOR_DISPLAY; +import static com.android.server.wm.proto.WindowStateProto.IS_VISIBLE; +import static com.android.server.wm.proto.WindowStateProto.OUTSETS; +import static com.android.server.wm.proto.WindowStateProto.OUTSET_FRAME; +import static com.android.server.wm.proto.WindowStateProto.OVERSCAN_FRAME; +import static com.android.server.wm.proto.WindowStateProto.OVERSCAN_INSETS; import static com.android.server.wm.proto.WindowStateProto.PARENT_FRAME; -import static com.android.server.wm.proto.WindowStateProto.STACK_ID; +import static com.android.server.wm.proto.WindowStateProto.REMOVED; +import static com.android.server.wm.proto.WindowStateProto.REMOVE_ON_EXIT; +import static com.android.server.wm.proto.WindowStateProto.REQUESTED_HEIGHT; +import static com.android.server.wm.proto.WindowStateProto.REQUESTED_WIDTH; import static com.android.server.wm.proto.WindowStateProto.SHOWN_POSITION; +import static com.android.server.wm.proto.WindowStateProto.STABLE_INSETS; +import static com.android.server.wm.proto.WindowStateProto.STACK_ID; import static com.android.server.wm.proto.WindowStateProto.SURFACE_INSETS; import static com.android.server.wm.proto.WindowStateProto.SURFACE_POSITION; +import static com.android.server.wm.proto.WindowStateProto.SYSTEM_UI_VISIBILITY; +import static com.android.server.wm.proto.WindowStateProto.VIEW_VISIBILITY; +import static com.android.server.wm.proto.WindowStateProto.VISIBLE_FRAME; +import static com.android.server.wm.proto.WindowStateProto.VISIBLE_INSETS; import static com.android.server.wm.proto.WindowStateProto.WINDOW_CONTAINER; import android.annotation.CallSuper; @@ -142,6 +163,7 @@ import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; import android.os.WorkSource; +import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.MergedConfiguration; import android.util.Slog; @@ -2222,12 +2244,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWinAnimator + ": " + mPolicyVisibilityAfterAnim); } mPolicyVisibility = mPolicyVisibilityAfterAnim; - setDisplayLayoutNeeded(); if (!mPolicyVisibility) { + mWinAnimator.hide("checkPolicyVisibilityChange"); if (mService.mCurrentFocus == this) { if (DEBUG_FOCUS_LIGHT) Slog.i(TAG, "setAnimationLocked: setting mFocusMayChange true"); mService.mFocusMayChange = true; + setDisplayLayoutNeeded(); } // Window is no longer visible -- make sure if we were waiting // for it to be displayed before enabling the display, that @@ -3083,6 +3106,27 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP for (int i = 0; i < mChildren.size(); i++) { mChildren.get(i).writeToProto(proto, CHILD_WINDOWS, trim); } + proto.write(REQUESTED_WIDTH, mRequestedWidth); + proto.write(REQUESTED_HEIGHT, mRequestedHeight); + proto.write(VIEW_VISIBILITY, mViewVisibility); + proto.write(SYSTEM_UI_VISIBILITY, mSystemUiVisibility); + proto.write(HAS_SURFACE, mHasSurface); + proto.write(IS_READY_FOR_DISPLAY, isReadyForDisplay()); + mDisplayFrame.writeToProto(proto, DISPLAY_FRAME); + mOverscanFrame.writeToProto(proto, OVERSCAN_FRAME); + mVisibleFrame.writeToProto(proto, VISIBLE_FRAME); + mDecorFrame.writeToProto(proto, DECOR_FRAME); + mOutsetFrame.writeToProto(proto, OUTSET_FRAME); + mOverscanInsets.writeToProto(proto, OVERSCAN_INSETS); + mVisibleInsets.writeToProto(proto, VISIBLE_INSETS); + mStableInsets.writeToProto(proto, STABLE_INSETS); + mOutsets.writeToProto(proto, OUTSETS); + mDisplayCutout.writeToProto(proto, CUTOUT); + proto.write(REMOVE_ON_EXIT, mRemoveOnExit); + proto.write(DESTROYING, mDestroying); + proto.write(REMOVED, mRemoved); + proto.write(IS_ON_SCREEN, isOnScreen()); + proto.write(IS_VISIBLE, isVisible()); proto.end(token); } @@ -3675,6 +3719,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP windowInfo.activityToken = mAppToken.appToken.asBinder(); } windowInfo.title = mAttrs.accessibilityTitle; + // Panel windows have no public way to set the a11y title directly. Use the + // regular title as a fallback. + if (TextUtils.isEmpty(windowInfo.title) + && (mAttrs.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW) + && (mAttrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW)) { + windowInfo.title = mAttrs.getTitle(); + } windowInfo.accessibilityIdOfAnchor = mAttrs.accessibilityIdOfAnchor; windowInfo.focused = isFocused(); Task task = getTask(); @@ -4291,8 +4342,22 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP float9[Matrix.MSKEW_Y] = mWinAnimator.mDtDx; float9[Matrix.MSKEW_X] = mWinAnimator.mDtDy; float9[Matrix.MSCALE_Y] = mWinAnimator.mDsDy; - float9[Matrix.MTRANS_X] = mSurfacePosition.x + mShownPosition.x; - float9[Matrix.MTRANS_Y] = mSurfacePosition.y + mShownPosition.y; + int x = mSurfacePosition.x + mShownPosition.x; + int y = mSurfacePosition.y + mShownPosition.y; + + // If changed, also adjust transformFrameToSurfacePosition + final WindowContainer parent = getParent(); + if (isChildWindow()) { + final WindowState parentWindow = getParentWindow(); + x += parentWindow.mFrame.left - parentWindow.mAttrs.surfaceInsets.left; + y += parentWindow.mFrame.top - parentWindow.mAttrs.surfaceInsets.top; + } else if (parent != null) { + final Rect parentBounds = parent.getBounds(); + x += parentBounds.left; + y += parentBounds.top; + } + float9[Matrix.MTRANS_X] = x; + float9[Matrix.MTRANS_Y] = y; float9[Matrix.MPERSP_0] = 0; float9[Matrix.MPERSP_1] = 0; float9[Matrix.MPERSP_2] = 1; @@ -4417,6 +4482,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // Leash is now responsible for position, so set our position to 0. t.setPosition(mSurfaceControl, 0, 0); + mLastSurfacePosition.set(0, 0); } @Override @@ -4432,13 +4498,16 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } transformFrameToSurfacePosition(mFrame.left, mFrame.top, mSurfacePosition); - if (!mSurfaceAnimator.hasLeash()) { + if (!mSurfaceAnimator.hasLeash() && !mLastSurfacePosition.equals(mSurfacePosition)) { t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y); + mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y); } } private void transformFrameToSurfacePosition(int left, int top, Point outPoint) { outPoint.set(left, top); + + // If changed, also adjust getTransformationMatrix final WindowContainer parentWindowContainer = getParent(); if (isChildWindow()) { // TODO: This probably falls apart at some point and we should diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index d2247ac2c973..96b0bd5f7b2f 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -42,8 +42,10 @@ import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER; import static com.android.server.wm.WindowManagerService.logWithStack; import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE; import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN; +import static com.android.server.wm.proto.WindowStateAnimatorProto.DRAW_STATE; import static com.android.server.wm.proto.WindowStateAnimatorProto.LAST_CLIP_RECT; import static com.android.server.wm.proto.WindowStateAnimatorProto.SURFACE; +import static com.android.server.wm.proto.WindowStateAnimatorProto.SYSTEM_DECOR_RECT; import android.content.Context; import android.graphics.Matrix; @@ -1381,6 +1383,8 @@ class WindowStateAnimator { if (mSurfaceController != null) { mSurfaceController.writeToProto(proto, SURFACE); } + proto.write(DRAW_STATE, mDrawState); + mSystemDecorRect.writeToProto(proto, SYSTEM_DECOR_RECT); proto.end(token); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index e55d4ea35739..c1e95ebeddf2 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -67,7 +67,8 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { public void transferOwner(ComponentName admin, ComponentName target, PersistableBundle bundle) {} public boolean generateKeyPair(ComponentName who, String callerPackage, String algorithm, - ParcelableKeyGenParameterSpec keySpec, KeymasterCertificateChain attestationChain) { + ParcelableKeyGenParameterSpec keySpec, int idAttestationFlags, + KeymasterCertificateChain attestationChain) { return false; } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index e5351b48e14d..11fce4d7101c 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -45,6 +45,10 @@ import static android.app.admin.DevicePolicyManager.DELEGATION_INSTALL_EXISTING_ import static android.app.admin.DevicePolicyManager.DELEGATION_KEEP_UNINSTALLED_PACKAGES; import static android.app.admin.DevicePolicyManager.DELEGATION_PACKAGE_ACCESS; import static android.app.admin.DevicePolicyManager.DELEGATION_PERMISSION_GRANT; +import static android.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO; +import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI; +import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID; +import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL; import static android.app.admin.DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; @@ -217,8 +221,10 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Map.Entry; +import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -5052,17 +5058,82 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } + private void enforceIsDeviceOwnerOrCertInstallerOfDeviceOwner( + ComponentName who, String callerPackage, int callerUid) throws SecurityException { + if (who == null) { + if (!mOwners.hasDeviceOwner()) { + throw new SecurityException("Not in Device Owner mode."); + } + if (UserHandle.getUserId(callerUid) != mOwners.getDeviceOwnerUserId()) { + throw new SecurityException("Caller not from device owner user"); + } + if (!isCallerDelegate(callerPackage, DELEGATION_CERT_INSTALL)) { + throw new SecurityException("Caller with uid " + mInjector.binderGetCallingUid() + + "has no permission to generate keys."); + } + } else { + // Caller provided - check it is the device owner. + synchronized (this) { + getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); + } + } + } + + @VisibleForTesting + public static int[] translateIdAttestationFlags( + int idAttestationFlags) { + Map<Integer, Integer> idTypeToAttestationFlag = new HashMap(); + idTypeToAttestationFlag.put(ID_TYPE_SERIAL, AttestationUtils.ID_TYPE_SERIAL); + idTypeToAttestationFlag.put(ID_TYPE_IMEI, AttestationUtils.ID_TYPE_IMEI); + idTypeToAttestationFlag.put(ID_TYPE_MEID, AttestationUtils.ID_TYPE_MEID); + + int numFlagsSet = Integer.bitCount(idAttestationFlags); + // No flags are set - return null to indicate no device ID attestation information should + // be included in the attestation record. + if (numFlagsSet == 0) { + return null; + } + + // If the ID_TYPE_BASE_INFO is set, make sure that a non-null array is returned, even if + // no other flag is set. That will lead to inclusion of general device make data in the + // attestation record, but no specific device identifiers. + if ((idAttestationFlags & ID_TYPE_BASE_INFO) != 0) { + numFlagsSet -= 1; + idAttestationFlags = idAttestationFlags & (~ID_TYPE_BASE_INFO); + } + + int[] attestationUtilsFlags = new int[numFlagsSet]; + int i = 0; + for (Integer idType: idTypeToAttestationFlag.keySet()) { + if ((idType & idAttestationFlags) != 0) { + attestationUtilsFlags[i++] = idTypeToAttestationFlag.get(idType); + } + } + + return attestationUtilsFlags; + } + @Override public boolean generateKeyPair(ComponentName who, String callerPackage, String algorithm, ParcelableKeyGenParameterSpec parcelableKeySpec, + int idAttestationFlags, KeymasterCertificateChain attestationChain) { - enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, - DELEGATION_CERT_INSTALL); + // Get attestation flags, if any. + final int[] attestationUtilsFlags = translateIdAttestationFlags(idAttestationFlags); + final boolean deviceIdAttestationRequired = attestationUtilsFlags != null; + final int callingUid = mInjector.binderGetCallingUid(); + + if (deviceIdAttestationRequired && attestationUtilsFlags.length > 0) { + enforceIsDeviceOwnerOrCertInstallerOfDeviceOwner(who, callerPackage, callingUid); + } else { + enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, + DELEGATION_CERT_INSTALL); + } final KeyGenParameterSpec keySpec = parcelableKeySpec.getSpec(); - if (TextUtils.isEmpty(keySpec.getKeystoreAlias())) { + final String alias = keySpec.getKeystoreAlias(); + if (TextUtils.isEmpty(alias)) { throw new IllegalArgumentException("Empty alias provided."); } - final String alias = keySpec.getKeystoreAlias(); // As the caller will be granted access to the key, ensure no UID was specified, as // it will not have the desired effect. if (keySpec.getUid() != KeyStore.UID_SELF) { @@ -5070,7 +5141,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } - final int callingUid = mInjector.binderGetCallingUid(); + if (deviceIdAttestationRequired && (keySpec.getAttestationChallenge() == null)) { + throw new IllegalArgumentException( + "Requested Device ID attestation but challenge is empty."); + } final UserHandle userHandle = mInjector.binderGetCallingUserHandle(); final long id = mInjector.binderClearCallingIdentity(); @@ -5103,7 +5177,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final byte[] attestationChallenge = keySpec.getAttestationChallenge(); if (attestationChallenge != null) { final boolean attestationResult = keyChain.attestKey( - alias, attestationChallenge, attestationChain); + alias, attestationChallenge, attestationUtilsFlags, attestationChain); if (!attestationResult) { Log.e(LOG_TAG, String.format( "Attestation for %s failed, deleting key.", alias)); @@ -11802,10 +11876,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final long id = mInjector.binderClearCallingIdentity(); try { - //STOPSHIP add support for COMP, DO, edge cases when device is rebooted/work mode off, + //STOPSHIP add support for COMP, edge cases when device is rebooted/work mode off, //transfer callbacks and broadcast - if (isProfileOwner(admin, callingUserId)) { - transferProfileOwner(admin, target, callingUserId); + synchronized (this) { + if (isProfileOwner(admin, callingUserId)) { + transferProfileOwnerLocked(admin, target, callingUserId); + } else if (isDeviceOwner(admin, callingUserId)) { + transferDeviceOwnerLocked(admin, target, callingUserId); + } } } finally { mInjector.binderRestoreCallingIdentity(id); @@ -11815,15 +11893,25 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { /** * Transfers the profile owner for user with id profileOwnerUserId from admin to target. */ - private void transferProfileOwner(ComponentName admin, ComponentName target, + private void transferProfileOwnerLocked(ComponentName admin, ComponentName target, int profileOwnerUserId) { - synchronized (this) { - transferActiveAdminUncheckedLocked(target, admin, profileOwnerUserId); - mOwners.transferProfileOwner(target, profileOwnerUserId); - Slog.i(LOG_TAG, "Profile owner set: " + target + " on user " + profileOwnerUserId); - mOwners.writeProfileOwner(profileOwnerUserId); - mDeviceAdminServiceController.startServiceForOwner( - target.getPackageName(), profileOwnerUserId, "transfer-profile-owner"); - } + transferActiveAdminUncheckedLocked(target, admin, profileOwnerUserId); + mOwners.transferProfileOwner(target, profileOwnerUserId); + Slog.i(LOG_TAG, "Profile owner set: " + target + " on user " + profileOwnerUserId); + mOwners.writeProfileOwner(profileOwnerUserId); + mDeviceAdminServiceController.startServiceForOwner( + target.getPackageName(), profileOwnerUserId, "transfer-profile-owner"); + } + + /** + * Transfers the device owner for user with id userId from admin to target. + */ + private void transferDeviceOwnerLocked(ComponentName admin, ComponentName target, int userId) { + transferActiveAdminUncheckedLocked(target, admin, userId); + mOwners.transferDeviceOwner(target); + Slog.i(LOG_TAG, "Device owner set: " + target + " on user " + userId); + mOwners.writeDeviceOwner(); + mDeviceAdminServiceController.startServiceForOwner( + target.getPackageName(), userId, "transfer-device-owner"); } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java index 9042a8d8b305..2a23888b875c 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java @@ -288,6 +288,17 @@ class Owners { } } + void transferDeviceOwner(ComponentName target) { + synchronized (mLock) { + // We don't set a name because it's not used anyway. + // See DevicePolicyManagerService#getDeviceOwnerName + mDeviceOwner = new OwnerInfo(null, target, + mDeviceOwner.userRestrictionsMigrated, mDeviceOwner.remoteBugreportUri, + mDeviceOwner.remoteBugreportHash); + pushToPackageManagerLocked(); + } + } + ComponentName getProfileOwnerComponent(int userId) { synchronized (mLock) { OwnerInfo profileOwner = mProfileOwners.get(userId); diff --git a/services/robotests/src/com/android/server/backup/BackupManagerConstantsTest.java b/services/robotests/src/com/android/server/backup/BackupManagerConstantsTest.java index c20c37678b24..a473bc61941c 100644 --- a/services/robotests/src/com/android/server/backup/BackupManagerConstantsTest.java +++ b/services/robotests/src/com/android/server/backup/BackupManagerConstantsTest.java @@ -16,19 +16,17 @@ package com.android.server.backup; +import static com.google.common.truth.Truth.assertThat; + import android.app.AlarmManager; import android.content.Context; import android.os.Handler; import android.platform.test.annotations.Presubmit; import android.provider.Settings; -import com.android.server.backup.testing.ShadowBackupTransportStub; -import com.android.server.backup.testing.ShadowContextImplForBackup; -import com.android.server.backup.testing.ShadowPackageManagerForBackup; import com.android.server.testing.FrameworkRobolectricTestRunner; import com.android.server.testing.SystemLoaderClasses; -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -36,19 +34,9 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; -import static com.google.common.truth.Truth.assertThat; - @RunWith(FrameworkRobolectricTestRunner.class) -@Config( - manifest = Config.NONE, - sdk = 26, - shadows = { - ShadowContextImplForBackup.class, - ShadowBackupTransportStub.class, - ShadowPackageManagerForBackup.class - } -) -@SystemLoaderClasses({TransportManager.class}) +@Config(manifest = Config.NONE, sdk = 26) +@SystemLoaderClasses({BackupManagerConstants.class}) @Presubmit public class BackupManagerConstantsTest { private static final String PACKAGE_NAME = "some.package.name"; @@ -59,17 +47,13 @@ public class BackupManagerConstantsTest { MockitoAnnotations.initMocks(this); } - @After - public void tearDown() throws Exception { - } - @Test public void testDefaultValues() throws Exception { final Context context = RuntimeEnvironment.application.getApplicationContext(); final Handler handler = new Handler(); - Settings.Secure.putString(context.getContentResolver(), - Settings.Secure.BACKUP_MANAGER_CONSTANTS, null); + Settings.Secure.putString( + context.getContentResolver(), Settings.Secure.BACKUP_MANAGER_CONSTANTS, null); final BackupManagerConstants constants = new BackupManagerConstants(handler, context.getContentResolver()); @@ -93,17 +77,21 @@ public class BackupManagerConstantsTest { final Context context = RuntimeEnvironment.application.getApplicationContext(); final Handler handler = new Handler(); - final String recievers_setting = "backup_finished_notification_receivers=" + - PACKAGE_NAME + ':' + ANOTHER_PACKAGE_NAME; - Settings.Secure.putString(context.getContentResolver(), - Settings.Secure.BACKUP_MANAGER_CONSTANTS, recievers_setting); + final String recieversSetting = + "backup_finished_notification_receivers=" + + PACKAGE_NAME + + ':' + + ANOTHER_PACKAGE_NAME; + Settings.Secure.putString( + context.getContentResolver(), + Settings.Secure.BACKUP_MANAGER_CONSTANTS, + recieversSetting); final BackupManagerConstants constants = new BackupManagerConstants(handler, context.getContentResolver()); constants.start(); - assertThat(constants.getBackupFinishedNotificationReceivers()).isEqualTo(new String[] { - PACKAGE_NAME, - ANOTHER_PACKAGE_NAME}); + assertThat(constants.getBackupFinishedNotificationReceivers()) + .isEqualTo(new String[] {PACKAGE_NAME, ANOTHER_PACKAGE_NAME}); } } diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java index 3b7db9f73010..4176d2a7d9d5 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java @@ -24,12 +24,15 @@ import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyListOf; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -42,7 +45,9 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.content.res.Resources; +import android.database.ContentObserver; import android.net.INetworkRecommendationProvider; import android.net.INetworkScoreCache; import android.net.NetworkKey; @@ -63,6 +68,7 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.UserHandle; +import android.provider.Settings; import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; import android.support.test.runner.AndroidJUnit4; @@ -128,7 +134,9 @@ public class NetworkScoreServiceTest { @Mock private UnaryOperator<List<ScoredNetwork>> mScanResultsFilter; @Mock private WifiInfo mWifiInfo; @Mock private NetworkScoreService.ScoringServiceConnection mServiceConnection; + @Mock private PackageManagerInternal mPackageManagerInternal; @Captor private ArgumentCaptor<List<ScoredNetwork>> mScoredNetworkCaptor; + @Captor private ArgumentCaptor<PackageManagerInternal.PackagesProvider> mPackagesProviderCaptor; private ContentResolver mContentResolver; private NetworkScoreService mNetworkScoreService; @@ -156,6 +164,7 @@ public class NetworkScoreServiceTest { when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER); mHandlerThread = new HandlerThread("NetworkScoreServiceTest"); mHandlerThread.start(); + LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal); mNetworkScoreService = new NetworkScoreService(mContext, mNetworkScorerAppManager, networkScorerAppData -> mServiceConnection, mHandlerThread.getLooper()); WifiConfiguration configuration = new WifiConfiguration(); @@ -181,6 +190,29 @@ public class NetworkScoreServiceTest { @After public void tearDown() throws Exception { mHandlerThread.quitSafely(); + LocalServices.removeServiceForTest(PackageManagerInternal.class); + } + + @Test + public void testConstructor_setsUseOpenWifiPackagesProvider() { + Settings.Global.putString(mContentResolver, + Settings.Global.USE_OPEN_WIFI_PACKAGE, "com.some.app"); + + verify(mPackageManagerInternal) + .setUseOpenWifiAppPackagesProvider(mPackagesProviderCaptor.capture()); + + String[] packages = mPackagesProviderCaptor.getValue().getPackages(0); + assertEquals(1, packages.length); + assertEquals("com.some.app", packages[0]); + } + + @Test + public void testConstructor_registersUseOpenWifiPackageContentObserver() { + Settings.Global.putString(mContentResolver, + Settings.Global.USE_OPEN_WIFI_PACKAGE, "com.some.other.app"); + + verify(mPackageManagerInternal, timeout(500)) + .grantDefaultPermissionsToDefaultUseOpenWifiApp("com.some.other.app", 0); } @Test @@ -947,8 +979,8 @@ public class NetworkScoreServiceTest { } private static class CountDownHandler extends Handler { - CountDownLatch latch = new CountDownLatch(1); - int receivedWhat; + final CountDownLatch latch = new CountDownLatch(1); + volatile int receivedWhat; CountDownHandler(Looper looper) { super(looper); @@ -956,8 +988,8 @@ public class NetworkScoreServiceTest { @Override public void handleMessage(Message msg) { - latch.countDown(); receivedWhat = msg.what; + latch.countDown(); } } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java index 8a54c4e7f9c0..8d5556eac447 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java @@ -32,6 +32,7 @@ import static org.mockito.Mockito.verify; import android.annotation.NonNull; import android.content.Context; +import android.os.Message; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import android.util.DebugUtils; @@ -48,6 +49,34 @@ import org.junit.runner.RunWith; import java.util.function.IntConsumer; +/** + * Tests the state transitions of {@link MagnificationGestureHandler} + * + * Here's a dot graph describing the transitions being tested: + * {@code + * digraph { + * IDLE -> SHORTCUT_TRIGGERED [label="a11y\nbtn"] + * SHORTCUT_TRIGGERED -> IDLE [label="a11y\nbtn"] + * IDLE -> DOUBLE_TAP [label="2tap"] + * DOUBLE_TAP -> IDLE [label="timeout"] + * DOUBLE_TAP -> TRIPLE_TAP_AND_HOLD [label="down"] + * SHORTCUT_TRIGGERED -> TRIPLE_TAP_AND_HOLD [label="down"] + * TRIPLE_TAP_AND_HOLD -> ZOOMED [label="up"] + * TRIPLE_TAP_AND_HOLD -> DRAGGING_TMP [label="hold/\nswipe"] + * DRAGGING_TMP -> IDLE [label="release"] + * ZOOMED -> ZOOMED_DOUBLE_TAP [label="2tap"] + * ZOOMED_DOUBLE_TAP -> ZOOMED [label="timeout"] + * ZOOMED_DOUBLE_TAP -> DRAGGING [label="hold"] + * ZOOMED_DOUBLE_TAP -> IDLE [label="tap"] + * DRAGGING -> ZOOMED [label="release"] + * ZOOMED -> IDLE [label="a11y\nbtn"] + * ZOOMED -> PANNING [label="2hold"] + * PANNING -> PANNING_SCALING [label="pinch"] + * PANNING_SCALING -> ZOOMED [label="release"] + * PANNING -> ZOOMED [label="release"] + * } + * } + */ @RunWith(AndroidJUnit4.class) public class MagnificationGestureHandlerTest { @@ -76,6 +105,8 @@ public class MagnificationGestureHandlerTest { private MagnificationGestureHandler mMgh; private TestHandler mHandler; + private long mLastDownTime = Integer.MIN_VALUE; + @Before public void setUp() { mContext = InstrumentationRegistry.getContext(); @@ -104,7 +135,13 @@ public class MagnificationGestureHandlerTest { MagnificationGestureHandler h = new MagnificationGestureHandler( mContext, mMagnificationController, detectTripleTap, detectShortcutTrigger); - mHandler = new TestHandler(h.mDetectingState, mClock); + mHandler = new TestHandler(h.mDetectingState, mClock) { + @Override + protected String messageToString(Message m) { + return DebugUtils.valueToString( + MagnificationGestureHandler.DetectingState.class, "MESSAGE_", m.what); + } + }; h.mDetectingState.mHandler = mHandler; h.setNext(strictMock(EventStreamTransformation.class)); return h; @@ -184,11 +221,11 @@ public class MagnificationGestureHandlerTest { fastForward1sec(); }, STATE_ZOOMED); - // tap+tap+swipe gets delegated - assertTransition(STATE_2TAPS, () -> { - allowEventDelegation(); - swipe(); - }, STATE_IDLE); + // tap+tap+swipe doesn't get delegated + assertTransition(STATE_2TAPS, () -> swipe(), STATE_IDLE); + + // tap+tap+swipe initiates viewport dragging immediately + assertTransition(STATE_2TAPS, () -> swipeAndHold(), STATE_DRAGGING_TMP); } @Test @@ -439,23 +476,24 @@ public class MagnificationGestureHandlerTest { } private void tap() { - MotionEvent downEvent = downEvent(); - send(downEvent); - send(upEvent(downEvent.getDownTime())); + send(downEvent()); + send(upEvent()); } private void swipe() { - MotionEvent downEvent = downEvent(); - send(downEvent); + swipeAndHold(); + send(upEvent()); + } + + private void swipeAndHold() { + send(downEvent()); send(moveEvent(DEFAULT_X * 2, DEFAULT_Y * 2)); - send(upEvent(downEvent.getDownTime())); } private void longTap() { - MotionEvent downEvent = downEvent(); - send(downEvent); + send(downEvent()); fastForward(2000); - send(upEvent(downEvent.getDownTime())); + send(upEvent()); } private void triggerShortcut() { @@ -473,16 +511,17 @@ public class MagnificationGestureHandlerTest { } private MotionEvent moveEvent(float x, float y) { - return MotionEvent.obtain(defaultDownTime(), mClock.now(), ACTION_MOVE, x, y, 0); + return MotionEvent.obtain(mLastDownTime, mClock.now(), ACTION_MOVE, x, y, 0); } private MotionEvent downEvent() { - return MotionEvent.obtain(mClock.now(), mClock.now(), + mLastDownTime = mClock.now(); + return MotionEvent.obtain(mLastDownTime, mLastDownTime, ACTION_DOWN, DEFAULT_X, DEFAULT_Y, 0); } private MotionEvent upEvent() { - return upEvent(defaultDownTime()); + return upEvent(mLastDownTime); } private MotionEvent upEvent(long downTime) { @@ -490,11 +529,6 @@ public class MagnificationGestureHandlerTest { MotionEvent.ACTION_UP, DEFAULT_X, DEFAULT_Y, 0); } - private long defaultDownTime() { - MotionEvent lastDown = mMgh.mDetectingState.mLastDown; - return lastDown == null ? mClock.now() - 1 : lastDown.getDownTime(); - } - private MotionEvent pointerEvent(int action, float x, float y) { MotionEvent.PointerProperties defPointerProperties = new MotionEvent.PointerProperties(); defPointerProperties.id = 0; diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 60783db8bb3c..58ac7d28eade 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -17,6 +17,10 @@ package com.android.server.devicepolicy; import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS; import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL; +import static android.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO; +import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI; +import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID; +import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL; import static android.app.admin.DevicePolicyManager.WIPE_EUICC; import static android.os.UserManagerInternal.CAMERA_DISABLED_GLOBALLY; import static android.os.UserManagerInternal.CAMERA_DISABLED_LOCALLY; @@ -71,6 +75,7 @@ import android.os.UserManager; import android.platform.test.annotations.Presubmit; import android.provider.Settings; import android.security.KeyChain; +import android.security.keystore.AttestationUtils; import android.telephony.TelephonyManager; import android.test.MoreAsserts; import android.test.suitebuilder.annotation.SmallTest; @@ -4459,6 +4464,47 @@ public class DevicePolicyManagerTest extends DpmTestBase { }); } + private void assertAttestationFlags(int attestationFlags, int[] expectedFlags) { + int[] gotFlags = DevicePolicyManagerService.translateIdAttestationFlags(attestationFlags); + Arrays.sort(gotFlags); + Arrays.sort(expectedFlags); + assertTrue(Arrays.equals(expectedFlags, gotFlags)); + } + + public void testTranslationOfIdAttestationFlag() { + int[] allIdTypes = new int[]{ID_TYPE_SERIAL, ID_TYPE_IMEI, ID_TYPE_MEID}; + int[] correspondingAttUtilsTypes = new int[]{ + AttestationUtils.ID_TYPE_SERIAL, AttestationUtils.ID_TYPE_IMEI, + AttestationUtils.ID_TYPE_MEID}; + + // Test translation of zero flags + assertNull(DevicePolicyManagerService.translateIdAttestationFlags(0)); + + // Test translation of the ID_TYPE_BASE_INFO flag, which should yield an empty, but + // non-null array + assertAttestationFlags(ID_TYPE_BASE_INFO, new int[] {}); + + // Test translation of a single flag + assertAttestationFlags(ID_TYPE_BASE_INFO | ID_TYPE_SERIAL, + new int[] {AttestationUtils.ID_TYPE_SERIAL}); + assertAttestationFlags(ID_TYPE_SERIAL, new int[] {AttestationUtils.ID_TYPE_SERIAL}); + + // Test translation of two flags + assertAttestationFlags(ID_TYPE_SERIAL | ID_TYPE_IMEI, + new int[] {AttestationUtils.ID_TYPE_IMEI, AttestationUtils.ID_TYPE_SERIAL}); + assertAttestationFlags(ID_TYPE_BASE_INFO | ID_TYPE_MEID | ID_TYPE_SERIAL, + new int[] {AttestationUtils.ID_TYPE_MEID, AttestationUtils.ID_TYPE_SERIAL}); + + // Test translation of all three flags + assertAttestationFlags(ID_TYPE_SERIAL | ID_TYPE_IMEI | ID_TYPE_MEID, + new int[] {AttestationUtils.ID_TYPE_IMEI, AttestationUtils.ID_TYPE_SERIAL, + AttestationUtils.ID_TYPE_MEID}); + // Test translation of all three flags + assertAttestationFlags(ID_TYPE_SERIAL | ID_TYPE_IMEI | ID_TYPE_MEID | ID_TYPE_BASE_INFO, + new int[] {AttestationUtils.ID_TYPE_IMEI, AttestationUtils.ID_TYPE_SERIAL, + AttestationUtils.ID_TYPE_MEID}); + } + private void setUserSetupCompleteForUser(boolean isUserSetupComplete, int userhandle) { when(getServices().settings.settingsSecureGetIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, userhandle)).thenReturn(isUserSetupComplete ? 1 : 0); diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java index 2629b12375a4..5105f4e2a9c9 100644 --- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java +++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java @@ -20,7 +20,13 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import android.content.res.Resources; +import android.content.res.TypedArray; import android.os.PowerManager; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -35,18 +41,18 @@ import java.util.Arrays; @RunWith(AndroidJUnit4.class) public class BrightnessMappingStrategyTest { - private static final float[] LUX_LEVELS = { - 0f, - 5f, - 20f, - 40f, - 100f, - 325f, - 600f, - 1250f, - 2200f, - 4000f, - 5000f + private static final int[] LUX_LEVELS = { + 0, + 5, + 20, + 40, + 100, + 325, + 600, + 1250, + 2200, + 4000, + 5000 }; private static final float[] DISPLAY_LEVELS_NITS = { @@ -80,11 +86,13 @@ public class BrightnessMappingStrategyTest { private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f }; private static final int[] BACKLIGHT_RANGE = { 1, 255 }; + private static final float[] EMPTY_FLOAT_ARRAY = new float[0]; + private static final int[] EMPTY_INT_ARRAY = new int[0]; + @Test public void testSimpleStrategyMappingAtControlPoints() { - BrightnessMappingStrategy simple = BrightnessMappingStrategy.create( - LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT, - null /*brightnessLevelsNits*/, null /*nitsRange*/, null /*backlightRange*/); + Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); + BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res); assertNotNull("BrightnessMappingStrategy should not be null", simple); for (int i = 0; i < LUX_LEVELS.length; i++) { final float expectedLevel = @@ -96,9 +104,8 @@ public class BrightnessMappingStrategyTest { @Test public void testSimpleStrategyMappingBetweenControlPoints() { - BrightnessMappingStrategy simple = BrightnessMappingStrategy.create( - LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT, - null /*brightnessLevelsNits*/, null /*nitsRange*/, null /*backlightRange*/); + Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); + BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res); assertNotNull("BrightnessMappingStrategy should not be null", simple); for (int i = 1; i < LUX_LEVELS.length; i++) { final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2; @@ -111,9 +118,9 @@ public class BrightnessMappingStrategyTest { @Test public void testPhysicalStrategyMappingAtControlPoints() { - BrightnessMappingStrategy physical = BrightnessMappingStrategy.create( - LUX_LEVELS, null /*brightnessLevelsBacklight*/, - DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); + Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, + DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); + BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res); assertNotNull("BrightnessMappingStrategy should not be null", physical); for (int i = 0; i < LUX_LEVELS.length; i++) { final float expectedLevel = DISPLAY_LEVELS_NITS[i] / DISPLAY_RANGE_NITS[1]; @@ -124,9 +131,9 @@ public class BrightnessMappingStrategyTest { @Test public void testPhysicalStrategyMappingBetweenControlPoints() { - BrightnessMappingStrategy physical = BrightnessMappingStrategy.create( - LUX_LEVELS, null /*brightnessLevelsBacklight*/, - DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); + Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, + DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); + BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res); assertNotNull("BrightnessMappingStrategy should not be null", physical); Spline backlightToBrightness = Spline.createSpline(toFloatArray(BACKLIGHT_RANGE), DISPLAY_RANGE_NITS); @@ -141,82 +148,79 @@ public class BrightnessMappingStrategyTest { @Test public void testDefaultStrategyIsPhysical() { - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create( - LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT, + Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res); assertTrue(strategy instanceof BrightnessMappingStrategy.PhysicalMappingStrategy); } @Test public void testNonStrictlyIncreasingLuxLevelsFails() { - final float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length); + final int[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length); final int idx = lux.length / 2; - float tmp = lux[idx]; + int tmp = lux[idx]; lux[idx] = lux[idx+1]; lux[idx+1] = tmp; - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create( - lux, null /*brightnessLevelsBacklight*/, - DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); + Resources res = createResources(lux, DISPLAY_LEVELS_NITS, + DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res); assertNull(strategy); // And make sure we get the same result even if it's monotone but not increasing. lux[idx] = lux[idx+1]; - strategy = BrightnessMappingStrategy.create( - lux, null /*brightnessLevelsBacklight*/, - DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); + res = createResources(lux, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); + strategy = BrightnessMappingStrategy.create(res); assertNull(strategy); } @Test public void testDifferentNumberOfControlPointValuesFails() { //Extra lux level - final float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length+1); + final int[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length+1); // Make sure it's strictly increasing so that the only failure is the differing array // lengths lux[lux.length - 1] = lux[lux.length - 2] + 1; - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create( - lux, null /*brightnessLevelsBacklight*/, - DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); + Resources res = createResources(lux, DISPLAY_LEVELS_NITS, + DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res); assertNull(strategy); - strategy = BrightnessMappingStrategy.create( - lux, DISPLAY_LEVELS_BACKLIGHT, - null /*brightnessLevelsNits*/, null /*nitsRange*/, null /*backlightRange*/); + res = createResources(lux, DISPLAY_LEVELS_BACKLIGHT); + strategy = BrightnessMappingStrategy.create(res); assertNull(strategy); // Extra backlight level final int[] backlight = Arrays.copyOf( DISPLAY_LEVELS_BACKLIGHT, DISPLAY_LEVELS_BACKLIGHT.length+1); backlight[backlight.length - 1] = backlight[backlight.length - 2] + 1; - strategy = BrightnessMappingStrategy.create( - LUX_LEVELS, backlight, - null /*brightnessLevelsNits*/, null /*nitsRange*/, null /*backlightRange*/); + res = createResources(LUX_LEVELS, backlight); + strategy = BrightnessMappingStrategy.create(res); assertNull(strategy); // Extra nits level final float[] nits = Arrays.copyOf(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_NITS.length+1); nits[nits.length - 1] = nits[nits.length - 2] + 1; - strategy = BrightnessMappingStrategy.create( - LUX_LEVELS, null /*brightnessLevelsBacklight*/, - nits, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); + res = createResources(LUX_LEVELS, nits, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); + strategy = BrightnessMappingStrategy.create(res); assertNull(strategy); } @Test public void testPhysicalStrategyRequiresNitsMapping() { - BrightnessMappingStrategy physical = BrightnessMappingStrategy.create( - LUX_LEVELS, null /*brightnessLevelsBacklight*/, - DISPLAY_LEVELS_NITS, null, BACKLIGHT_RANGE); + Resources res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, + DISPLAY_LEVELS_NITS, EMPTY_FLOAT_ARRAY /*nitsRange*/, BACKLIGHT_RANGE); + BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res); assertNull(physical); - physical = BrightnessMappingStrategy.create( - LUX_LEVELS, null /*brightnessLevelsBacklight*/, - DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, null); + res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, + DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, EMPTY_INT_ARRAY /*backlightRange*/); + physical = BrightnessMappingStrategy.create(res); assertNull(physical); - physical = BrightnessMappingStrategy.create( - LUX_LEVELS, null /*brightnessLevelsBacklight*/, - DISPLAY_LEVELS_NITS, null, null); + res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, + DISPLAY_LEVELS_NITS, EMPTY_FLOAT_ARRAY /*nitsRange*/, + EMPTY_INT_ARRAY /*backlightRange*/); + physical = BrightnessMappingStrategy.create(res); assertNull(physical); } @@ -227,4 +231,73 @@ public class BrightnessMappingStrategyTest { } return newVals; } + + private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight) { + return createResources(luxLevels, brightnessLevelsBacklight, + EMPTY_FLOAT_ARRAY /*brightnessLevelsNits*/, EMPTY_FLOAT_ARRAY /*nitsRange*/, + EMPTY_INT_ARRAY /*backlightRange*/); + } + + private Resources createResources(int[] luxLevels, float[] brightnessLevelsNits, + float[] nitsRange, int[] backlightRange) { + return createResources(luxLevels, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, + brightnessLevelsNits, nitsRange, backlightRange); + } + + private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight, + float[] brightnessLevelsNits, float[] nitsRange, int[] backlightRange) { + Resources mockResources = mock(Resources.class); + // For historical reasons, the lux levels resource implicitly defines the first point as 0, + // so we need to chop it off of the array the mock resource object returns. + int[] luxLevelsResource = Arrays.copyOfRange(luxLevels, 1, luxLevels.length); + when(mockResources.getIntArray(com.android.internal.R.array.config_autoBrightnessLevels)) + .thenReturn(luxLevelsResource); + + when(mockResources.getIntArray( + com.android.internal.R.array.config_autoBrightnessLcdBacklightValues)) + .thenReturn(brightnessLevelsBacklight); + + TypedArray mockBrightnessLevelNits = createFloatTypedArray(brightnessLevelsNits); + when(mockResources.obtainTypedArray( + com.android.internal.R.array.config_autoBrightnessDisplayValuesNits)) + .thenReturn(mockBrightnessLevelNits); + + TypedArray mockNitsRange = createFloatTypedArray(nitsRange); + when(mockResources.obtainTypedArray( + com.android.internal.R.array.config_screenBrightnessNits)) + .thenReturn(mockNitsRange); + + when(mockResources.getIntArray( + com.android.internal.R.array.config_screenBrightnessBacklight)) + .thenReturn(backlightRange); + + when(mockResources.getInteger( + com.android.internal.R.integer.config_screenBrightnessSettingMinimum)) + .thenReturn(1); + when(mockResources.getInteger( + com.android.internal.R.integer.config_screenBrightnessSettingMaximum)) + .thenReturn(255); + return mockResources; + } + + private TypedArray createFloatTypedArray(float[] vals) { + TypedArray mockArray = mock(TypedArray.class); + when(mockArray.length()).thenAnswer(invocation -> { + return vals.length; + }); + when(mockArray.getFloat(anyInt(), anyFloat())).thenAnswer(invocation -> { + final float def = (float) invocation.getArguments()[1]; + if (vals == null) { + return def; + } + int idx = (int) invocation.getArguments()[0]; + if (idx >= 0 && idx < vals.length) { + return vals[idx]; + } else { + return def; + } + }); + return mockArray; + } + } diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java index 926009ebfbae..08edd52c7112 100644 --- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java @@ -66,6 +66,8 @@ import java.util.concurrent.TimeUnit; @SmallTest @RunWith(AndroidJUnit4.class) public class BrightnessTrackerTest { + private static final float DEFAULT_INITIAL_BRIGHTNESS = 2.5f; + private static final float FLOAT_DELTA = 0.01f; private BrightnessTracker mTracker; private TestInjector mInjector; @@ -98,7 +100,6 @@ public class BrightnessTrackerTest { mInjector.mInteractive = false; startTracker(mTracker); assertNull(mInjector.mSensorListener); - assertNotNull(mInjector.mSettingsObserver); assertNotNull(mInjector.mBroadcastReceiver); assertTrue(mInjector.mIdleScheduled); Intent onIntent = new Intent(); @@ -119,7 +120,6 @@ public class BrightnessTrackerTest { mTracker.stop(); assertNull(mInjector.mSensorListener); - assertNull(mInjector.mSettingsObserver); assertNull(mInjector.mBroadcastReceiver); assertFalse(mInjector.mIdleScheduled); } @@ -128,12 +128,10 @@ public class BrightnessTrackerTest { public void testBrightnessEvent() { final int brightness = 20; - mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, brightness); startTracker(mTracker); mInjector.mSensorListener.onSensorChanged(createSensorEvent(1.0f)); mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2)); - mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor( - Settings.System.SCREEN_BRIGHTNESS)); + notifyBrightnessChanged(mTracker, brightness); List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList(); mTracker.stop(); @@ -141,10 +139,11 @@ public class BrightnessTrackerTest { BrightnessChangeEvent event = events.get(0); assertEquals(mInjector.currentTimeMillis(), event.timeStamp); assertEquals(1, event.luxValues.length); - assertEquals(1.0f, event.luxValues[0], 0.1f); + assertEquals(1.0f, event.luxValues[0], FLOAT_DELTA); assertEquals(mInjector.currentTimeMillis() - TimeUnit.SECONDS.toMillis(2), event.luxTimestamps[0]); - assertEquals(brightness, event.brightness); + assertEquals(brightness, event.brightness, FLOAT_DELTA); + assertEquals(DEFAULT_INITIAL_BRIGHTNESS, event.lastBrightness, FLOAT_DELTA); // System had no data so these should all be at defaults. assertEquals(Float.NaN, event.batteryLevel, 0.0); @@ -154,21 +153,18 @@ public class BrightnessTrackerTest { @Test public void testBrightnessFullPopulatedEvent() { - final int lastBrightness = 230; + final int initialBrightness = 230; final int brightness = 130; - mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, lastBrightness); mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1); mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3333); - startTracker(mTracker); - mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, brightness); + startTracker(mTracker, initialBrightness); mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(), batteryChangeEvent(30, 60)); mInjector.mSensorListener.onSensorChanged(createSensorEvent(1000.0f)); final long sensorTime = mInjector.currentTimeMillis(); - mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor( - Settings.System.SCREEN_BRIGHTNESS)); + notifyBrightnessChanged(mTracker, brightness); List<BrightnessChangeEvent> eventsNoPackage = mTracker.getEvents(0, false).getList(); List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList(); @@ -179,9 +175,9 @@ public class BrightnessTrackerTest { assertEquals(event.timeStamp, mInjector.currentTimeMillis()); assertArrayEquals(new float[] {1000.0f}, event.luxValues, 0.01f); assertArrayEquals(new long[] {sensorTime}, event.luxTimestamps); - assertEquals(brightness, event.brightness); - assertEquals(lastBrightness, event.lastBrightness); - assertEquals(0.5, event.batteryLevel, 0.01); + assertEquals(brightness, event.brightness, FLOAT_DELTA); + assertEquals(initialBrightness, event.lastBrightness, FLOAT_DELTA); + assertEquals(0.5, event.batteryLevel, FLOAT_DELTA); assertTrue(event.nightMode); assertEquals(3333, event.colorTemperature); assertEquals("a.package", event.packageName); @@ -192,45 +188,34 @@ public class BrightnessTrackerTest { } @Test - public void testIgnoreSelfChange() { + public void testIgnoreAutomaticBrightnessChange() { final int initialBrightness = 30; - mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, initialBrightness); - startTracker(mTracker); + startTracker(mTracker, initialBrightness); mInjector.mSensorListener.onSensorChanged(createSensorEvent(1.0f)); mInjector.incrementTime(TimeUnit.SECONDS.toMillis(1)); final int systemUpdatedBrightness = 20; - mTracker.setBrightness(systemUpdatedBrightness, 0); - assertEquals(systemUpdatedBrightness, - (int) mInjector.mSystemIntSettings.get(Settings.System.SCREEN_BRIGHTNESS)); - mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor( - Settings.System.SCREEN_BRIGHTNESS)); + notifyBrightnessChanged(mTracker, systemUpdatedBrightness, false /*userInitiated*/); List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList(); // No events because we filtered out our change. assertEquals(0, events.size()); final int firstUserUpdateBrightness = 20; // Then change comes from somewhere else so we shouldn't filter. - mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, - firstUserUpdateBrightness); - mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor( - Settings.System.SCREEN_BRIGHTNESS)); + notifyBrightnessChanged(mTracker, firstUserUpdateBrightness); // and with a different brightness value. final int secondUserUpdateBrightness = 34; - mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, - secondUserUpdateBrightness); - mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor( - Settings.System.SCREEN_BRIGHTNESS)); + notifyBrightnessChanged(mTracker, secondUserUpdateBrightness); events = mTracker.getEvents(0, true).getList(); assertEquals(2, events.size()); // First event is change from system update (20) to first user update (20) - assertEquals(systemUpdatedBrightness, events.get(0).lastBrightness); - assertEquals(firstUserUpdateBrightness, events.get(0).brightness); + assertEquals(systemUpdatedBrightness, events.get(0).lastBrightness, FLOAT_DELTA); + assertEquals(firstUserUpdateBrightness, events.get(0).brightness, FLOAT_DELTA); // Second event is from first to second user update. - assertEquals(firstUserUpdateBrightness, events.get(1).lastBrightness); - assertEquals(secondUserUpdateBrightness, events.get(1).brightness); + assertEquals(firstUserUpdateBrightness, events.get(1).lastBrightness, FLOAT_DELTA); + assertEquals(secondUserUpdateBrightness, events.get(1).brightness, FLOAT_DELTA); mTracker.stop(); } @@ -243,9 +228,7 @@ public class BrightnessTrackerTest { for (int brightness = 0; brightness <= 255; ++brightness) { mInjector.mSensorListener.onSensorChanged(createSensorEvent(1.0f)); mInjector.incrementTime(TimeUnit.SECONDS.toNanos(1)); - mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, brightness); - mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor( - Settings.System.SCREEN_BRIGHTNESS)); + notifyBrightnessChanged(mTracker, brightness); } List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList(); mTracker.stop(); @@ -254,14 +237,13 @@ public class BrightnessTrackerTest { assertEquals(100, events.size()); for (int i = 0; i < events.size(); i++) { BrightnessChangeEvent event = events.get(i); - assertEquals(156 + i, event.brightness); + assertEquals(156 + i, event.brightness, FLOAT_DELTA); } } @Test public void testLimitedSensorEvents() { final int brightness = 20; - mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, brightness); startTracker(mTracker); // 20 Sensor events 1 second apart. @@ -269,8 +251,7 @@ public class BrightnessTrackerTest { mInjector.incrementTime(TimeUnit.SECONDS.toMillis(1)); mInjector.mSensorListener.onSensorChanged(createSensorEvent(i + 1.0f)); } - mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor( - Settings.System.SCREEN_BRIGHTNESS)); + notifyBrightnessChanged(mTracker, 20); List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList(); mTracker.stop(); @@ -284,7 +265,7 @@ public class BrightnessTrackerTest { assertEquals(event.luxTimestamps[11 - i], mInjector.currentTimeMillis() - i * TimeUnit.SECONDS.toMillis(1)); } - assertEquals(brightness, event.brightness); + assertEquals(brightness, event.brightness, FLOAT_DELTA); } @Test @@ -298,25 +279,25 @@ public class BrightnessTrackerTest { String eventFile = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + "<events>\n" - + "<event brightness=\"194\" timestamp=\"" + + "<event nits=\"194.2\" timestamp=\"" + Long.toString(someTimeAgo) + "\" packageName=\"" + "com.example.app\" user=\"10\" " - + "lastBrightness=\"32\" " + + "lastNits=\"32.333\" " + "batteryLevel=\"1.0\" nightMode=\"false\" colorTemperature=\"0\"\n" + "lux=\"32.2,31.1\" luxTimestamps=\"" + Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\"/>" - + "<event brightness=\"71\" timestamp=\"" + + "<event nits=\"71\" timestamp=\"" + Long.toString(someTimeAgo) + "\" packageName=\"" + "com.android.anapp\" user=\"11\" " - + "lastBrightness=\"32\" " + + "lastNits=\"32\" " + "batteryLevel=\"0.5\" nightMode=\"true\" colorTemperature=\"3235\"\n" + "lux=\"132.2,131.1\" luxTimestamps=\"" + Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\"/>" // Event that is too old so shouldn't show up. - + "<event brightness=\"142\" timestamp=\"" + + "<event nits=\"142\" timestamp=\"" + Long.toString(twoMonthsAgo) + "\" packageName=\"" + "com.example.app\" user=\"10\" " - + "lastBrightness=\"32\" " + + "lastNits=\"32\" " + "batteryLevel=\"1.0\" nightMode=\"false\" colorTemperature=\"0\"\n" + "lux=\"32.2,31.1\" luxTimestamps=\"" + Long.toString(twoMonthsAgo) + "," + Long.toString(twoMonthsAgo) + "\"/>" @@ -326,27 +307,27 @@ public class BrightnessTrackerTest { assertEquals(1, events.size()); BrightnessChangeEvent event = events.get(0); assertEquals(someTimeAgo, event.timeStamp); - assertEquals(194, event.brightness); - assertArrayEquals(new float[] {32.2f, 31.1f}, event.luxValues, 0.01f); + assertEquals(194.2, event.brightness, FLOAT_DELTA); + assertArrayEquals(new float[] {32.2f, 31.1f}, event.luxValues, FLOAT_DELTA); assertArrayEquals(new long[] {someTimeAgo, someTimeAgo}, event.luxTimestamps); - assertEquals(32, event.lastBrightness); + assertEquals(32.333, event.lastBrightness, FLOAT_DELTA); assertEquals(0, event.userId); assertFalse(event.nightMode); - assertEquals(1.0f, event.batteryLevel, 0.01); + assertEquals(1.0f, event.batteryLevel, FLOAT_DELTA); assertEquals("com.example.app", event.packageName); events = tracker.getEvents(1, true).getList(); assertEquals(1, events.size()); event = events.get(0); assertEquals(someTimeAgo, event.timeStamp); - assertEquals(71, event.brightness); - assertArrayEquals(new float[] {132.2f, 131.1f}, event.luxValues, 0.01f); + assertEquals(71, event.brightness, FLOAT_DELTA); + assertArrayEquals(new float[] {132.2f, 131.1f}, event.luxValues, FLOAT_DELTA); assertArrayEquals(new long[] {someTimeAgo, someTimeAgo}, event.luxTimestamps); - assertEquals(32, event.lastBrightness); + assertEquals(32, event.lastBrightness, FLOAT_DELTA); assertEquals(1, event.userId); assertTrue(event.nightMode); assertEquals(3235, event.colorTemperature); - assertEquals(0.5f, event.batteryLevel, 0.01); + assertEquals(0.5f, event.batteryLevel, FLOAT_DELTA); assertEquals("com.android.anapp", event.packageName); } @@ -370,7 +351,7 @@ public class BrightnessTrackerTest { eventFile = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + "<events>\n" - + "<event brightness=\"194\" timestamp=\"" + someTimeAgo + "\" packageName=\"" + + "<event nits=\"194\" timestamp=\"" + someTimeAgo + "\" packageName=\"" + "com.example.app\" user=\"10\" " + "batteryLevel=\"0.7\" nightMode=\"false\" colorTemperature=\"0\" />\n" + "</events>"; @@ -386,7 +367,6 @@ public class BrightnessTrackerTest { public void testWriteThenRead() throws Exception { final int brightness = 20; - mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, brightness); mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1); mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3339); @@ -399,8 +379,7 @@ public class BrightnessTrackerTest { mInjector.mSensorListener.onSensorChanged(createSensorEvent(3000.0f)); final long secondSensorTime = mInjector.currentTimeMillis(); mInjector.incrementTime(TimeUnit.SECONDS.toMillis(3)); - mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor( - Settings.System.SCREEN_BRIGHTNESS)); + notifyBrightnessChanged(mTracker, brightness); ByteArrayOutputStream baos = new ByteArrayOutputStream(); mTracker.writeEventsLocked(baos); mTracker.stop(); @@ -414,10 +393,10 @@ public class BrightnessTrackerTest { assertEquals(1, events.size()); BrightnessChangeEvent event = events.get(0); - assertArrayEquals(new float[] {2000.0f, 3000.0f}, event.luxValues, 0.01f); + assertArrayEquals(new float[] {2000.0f, 3000.0f}, event.luxValues, FLOAT_DELTA); assertArrayEquals(new long[] {firstSensorTime, secondSensorTime}, event.luxTimestamps); - assertEquals(brightness, event.brightness); - assertEquals(0.3, event.batteryLevel, 0.01f); + assertEquals(brightness, event.brightness, FLOAT_DELTA); + assertEquals(0.3, event.batteryLevel, FLOAT_DELTA); assertTrue(event.nightMode); assertEquals(3339, event.colorTemperature); } @@ -426,7 +405,6 @@ public class BrightnessTrackerTest { public void testWritePrunesOldEvents() throws Exception { final int brightness = 20; - mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, brightness); mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1); mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3339); @@ -437,14 +415,12 @@ public class BrightnessTrackerTest { mInjector.incrementTime(TimeUnit.SECONDS.toMillis(1)); mInjector.mSensorListener.onSensorChanged(createSensorEvent(2000.0f)); final long sensorTime = mInjector.currentTimeMillis(); - mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor( - Settings.System.SCREEN_BRIGHTNESS)); + notifyBrightnessChanged(mTracker, brightness); // 31 days later mInjector.incrementTime(TimeUnit.DAYS.toMillis(31)); mInjector.mSensorListener.onSensorChanged(createSensorEvent(3000.0f)); - mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor( - Settings.System.SCREEN_BRIGHTNESS)); + notifyBrightnessChanged(mTracker, brightness); final long eventTime = mInjector.currentTimeMillis(); List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList(); @@ -460,10 +436,10 @@ public class BrightnessTrackerTest { assertEquals(eventTime, event.timeStamp); // We will keep one of the old sensor events because we keep 1 event outside the window. - assertArrayEquals(new float[] {2000.0f, 3000.0f}, event.luxValues, 0.01f); + assertArrayEquals(new float[] {2000.0f, 3000.0f}, event.luxValues, FLOAT_DELTA); assertArrayEquals(new long[] {sensorTime, eventTime}, event.luxTimestamps); - assertEquals(brightness, event.brightness); - assertEquals(0.3, event.batteryLevel, 0.01f); + assertEquals(brightness, event.brightness, FLOAT_DELTA); + assertEquals(0.3, event.batteryLevel, FLOAT_DELTA); assertTrue(event.nightMode); assertEquals(3339, event.colorTemperature); } @@ -472,7 +448,7 @@ public class BrightnessTrackerTest { public void testParcelUnParcel() { Parcel parcel = Parcel.obtain(); BrightnessChangeEvent event = new BrightnessChangeEvent(); - event.brightness = 23; + event.brightness = 23f; event.timeStamp = 345L; event.packageName = "com.example"; event.userId = 12; @@ -485,7 +461,7 @@ public class BrightnessTrackerTest { event.batteryLevel = 0.7f; event.nightMode = false; event.colorTemperature = 345; - event.lastBrightness = 50; + event.lastBrightness = 50f; event.writeToParcel(parcel, 0); byte[] parceled = parcel.marshall(); @@ -497,16 +473,16 @@ public class BrightnessTrackerTest { BrightnessChangeEvent event2 = BrightnessChangeEvent.CREATOR.createFromParcel(parcel); parcel.recycle(); - assertEquals(event.brightness, event2.brightness); + assertEquals(event.brightness, event2.brightness, FLOAT_DELTA); assertEquals(event.timeStamp, event2.timeStamp); assertEquals(event.packageName, event2.packageName); assertEquals(event.userId, event2.userId); - assertArrayEquals(event.luxValues, event2.luxValues, 0.01f); + assertArrayEquals(event.luxValues, event2.luxValues, FLOAT_DELTA); assertArrayEquals(event.luxTimestamps, event2.luxTimestamps); - assertEquals(event.batteryLevel, event2.batteryLevel, 0.01f); + assertEquals(event.batteryLevel, event2.batteryLevel, FLOAT_DELTA); assertEquals(event.nightMode, event2.nightMode); assertEquals(event.colorTemperature, event2.colorTemperature); - assertEquals(event.lastBrightness, event2.lastBrightness); + assertEquals(event.lastBrightness, event2.lastBrightness, FLOAT_DELTA); parcel = Parcel.obtain(); event.batteryLevel = Float.NaN; @@ -518,7 +494,7 @@ public class BrightnessTrackerTest { parcel.unmarshall(parceled, 0, parceled.length); parcel.setDataPosition(0); event2 = BrightnessChangeEvent.CREATOR.createFromParcel(parcel); - assertEquals(event.batteryLevel, event2.batteryLevel, 0.01f); + assertEquals(event.batteryLevel, event2.batteryLevel, FLOAT_DELTA); } private InputStream getInputStream(String data) { @@ -550,7 +526,21 @@ public class BrightnessTrackerTest { } private void startTracker(BrightnessTracker tracker) { - tracker.start(); + startTracker(tracker, DEFAULT_INITIAL_BRIGHTNESS); + } + + private void startTracker(BrightnessTracker tracker, float initialBrightness) { + tracker.start(initialBrightness); + mInjector.waitForHandler(); + } + + private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness) { + notifyBrightnessChanged(tracker, brightness, true /*userInitiated*/); + } + + private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness, + boolean userInitiated) { + tracker.notifyBrightnessChanged(brightness, userInitiated); mInjector.waitForHandler(); } @@ -578,7 +568,6 @@ public class BrightnessTrackerTest { private class TestInjector extends BrightnessTracker.Injector { SensorEventListener mSensorListener; - ContentObserver mSettingsObserver; BroadcastReceiver mBroadcastReceiver; Map<String, Integer> mSystemIntSettings = new HashMap<>(); Map<String, Integer> mSecureIntSettings = new HashMap<>(); @@ -610,18 +599,6 @@ public class BrightnessTrackerTest { } @Override - public void registerBrightnessObserver(ContentResolver resolver, - ContentObserver settingsObserver) { - mSettingsObserver = settingsObserver; - } - - @Override - public void unregisterBrightnessObserver(Context context, - ContentObserver settingsObserver) { - mSettingsObserver = null; - } - - @Override public void registerReceiver(Context context, BroadcastReceiver shutdownReceiver, IntentFilter shutdownFilter) { mBroadcastReceiver = shutdownReceiver; @@ -647,23 +624,6 @@ public class BrightnessTrackerTest { } @Override - public int getSystemIntForUser(ContentResolver resolver, String setting, int defaultValue, - int userId) { - Integer value = mSystemIntSettings.get(setting); - if (value == null) { - return defaultValue; - } else { - return value; - } - } - - @Override - public void putSystemIntForUser(ContentResolver resolver, String setting, int value, - int userId) { - mSystemIntSettings.put(setting, value); - } - - @Override public int getSecureIntForUser(ContentResolver resolver, String setting, int defaultValue, int userId) { Integer value = mSecureIntSettings.get(setting); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java index ba40c67cb35d..114da1aaebb5 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java @@ -386,37 +386,38 @@ public class KeySyncUtilsTest { } @Test - public void packVaultParams_encodesMaxAttemptsAsThirdParam() throws Exception { - int maxAttempts = 10; + public void packVaultParams_encodesDeviceIdAsThirdParam() throws Exception { + long deviceId = 102942158152L; byte[] packedForm = KeySyncUtils.packVaultParams( SecureBox.genKeyPair().getPublic(), - /*counterId=*/ 1001L, - maxAttempts, - /*deviceId=*/ 1L); + /*counterId=*/ 10021L, + /*maxAttempts=*/ 10, + deviceId); ByteBuffer byteBuffer = ByteBuffer.wrap(packedForm) .order(ByteOrder.LITTLE_ENDIAN); byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES + Long.BYTES); - assertEquals(maxAttempts, byteBuffer.getInt()); + assertEquals(deviceId, byteBuffer.getLong()); } @Test - public void packVaultParams_encodesDeviceIdAsLastParam() throws Exception { - long deviceId = 102942158152L; + public void packVaultParams_encodesMaxAttemptsAsLastParam() throws Exception { + int maxAttempts = 10; byte[] packedForm = KeySyncUtils.packVaultParams( SecureBox.genKeyPair().getPublic(), - /*counterId=*/ 10021L, - /*maxAttempts=*/ 10, - deviceId); + /*counterId=*/ 1001L, + maxAttempts, + /*deviceId=*/ 1L); ByteBuffer byteBuffer = ByteBuffer.wrap(packedForm) .order(ByteOrder.LITTLE_ENDIAN); - byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES + Long.BYTES + Integer.BYTES); - assertEquals(deviceId, byteBuffer.getLong()); + byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES + 2 * Long.BYTES); + assertEquals(maxAttempts, byteBuffer.getInt()); } + private static byte[] randomBytes(int n) { byte[] bytes = new byte[n]; new Random().nextBytes(bytes); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java index 6f13a9873909..97fbca2403bf 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java @@ -205,6 +205,14 @@ public class PlatformKeyManagerTest { } @Test + public void init_savesGenerationIdToDatabase() throws Exception { + mPlatformKeyManager.init(); + + assertEquals(1, + mRecoverableKeyStoreDb.getPlatformKeyGenerationId(USER_ID_FIXTURE)); + } + + @Test public void init_setsGenerationIdTo1() throws Exception { mPlatformKeyManager.init(); @@ -212,7 +220,38 @@ public class PlatformKeyManagerTest { } @Test + public void init_incrementsGenerationIdIfKeyIsUnavailable() throws Exception { + mPlatformKeyManager.init(); + + mPlatformKeyManager.init(); + + assertEquals(2, mPlatformKeyManager.getGenerationId()); + } + + @Test + public void init_doesNotIncrementGenerationIdIfKeyAvailable() throws Exception { + mPlatformKeyManager.init(); + when(mKeyStoreProxy + .containsAlias("com.android.server.locksettings.recoverablekeystore/" + + "platform/42/1/decrypt")).thenReturn(true); + when(mKeyStoreProxy + .containsAlias("com.android.server.locksettings.recoverablekeystore/" + + "platform/42/1/encrypt")).thenReturn(true); + + mPlatformKeyManager.init(); + + assertEquals(1, mPlatformKeyManager.getGenerationId()); + } + + @Test + public void getGenerationId_returnsMinusOneIfNotInitialized() throws Exception { + assertEquals(-1, mPlatformKeyManager.getGenerationId()); + } + + @Test public void getDecryptKey_getsDecryptKeyWithCorrectAlias() throws Exception { + mPlatformKeyManager.init(); + mPlatformKeyManager.getDecryptKey(); verify(mKeyStoreProxy).getKey( @@ -222,6 +261,8 @@ public class PlatformKeyManagerTest { @Test public void getEncryptKey_getsDecryptKeyWithCorrectAlias() throws Exception { + mPlatformKeyManager.init(); + mPlatformKeyManager.getEncryptKey(); verify(mKeyStoreProxy).getKey( diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java index f3cb98078602..212d25d42420 100644 --- a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java @@ -95,41 +95,6 @@ public class WatchlistSettingsTests { } @Test - public void testWatchlistSettings_writeSettingsToDisk() throws Exception { - copyWatchlistSettingsXml(mContext, TEST_XML_1, mTestXmlFile); - WatchlistSettings settings = new WatchlistSettings(mTestXmlFile); - settings.writeSettingsToDisk(Arrays.asList(TEST_NEW_CC_DOMAIN_CRC32), - Arrays.asList(TEST_NEW_CC_DOMAIN_SHA256), Arrays.asList(TEST_NEW_CC_IP_CRC32), - Arrays.asList(TEST_NEW_CC_IP_SHA256)); - // Ensure old watchlist is not in memory - assertFalse(settings.containsDomain(TEST_CC_DOMAIN)); - assertFalse(settings.containsIp(TEST_CC_IP)); - assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN)); - assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP)); - assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN)); - assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP)); - assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN)); - assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP)); - // Ensure new watchlist is in memory - assertTrue(settings.containsDomain(TEST_NEW_CC_DOMAIN)); - assertTrue(settings.containsIp(TEST_NEW_CC_IP)); - // Reload settings from disk and test again - settings = new WatchlistSettings(mTestXmlFile); - // Ensure old watchlist is not in memory - assertFalse(settings.containsDomain(TEST_CC_DOMAIN)); - assertFalse(settings.containsIp(TEST_CC_IP)); - assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN)); - assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP)); - assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN)); - assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP)); - assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN)); - assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP)); - // Ensure new watchlist is in memory - assertTrue(settings.containsDomain(TEST_NEW_CC_DOMAIN)); - assertTrue(settings.containsIp(TEST_NEW_CC_IP)); - } - - @Test public void testWatchlistSettings_writeSettingsToMemory() throws Exception { copyWatchlistSettingsXml(mContext, TEST_XML_1, mTestXmlFile); WatchlistSettings settings = new WatchlistSettings(mTestXmlFile); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java index e12a8da805f6..cdac516c9577 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java @@ -17,75 +17,93 @@ package com.android.server.pm; import android.content.IIntentReceiver; - import android.os.Bundle; +import android.support.test.runner.AndroidJUnit4; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; import java.io.File; // runtest -c com.android.server.pm.PackageManagerServiceTest frameworks-services - -@SmallTest -public class PackageManagerServiceTest extends AndroidTestCase { - @Override - protected void setUp() throws Exception { - super.setUp(); +// bit FrameworksServicesTests:com.android.server.pm.PackageManagerServiceTest +@RunWith(AndroidJUnit4.class) +public class PackageManagerServiceTest { + @Before + public void setUp() throws Exception { } - @Override - protected void tearDown() throws Exception { - super.tearDown(); + @After + public void tearDown() throws Exception { } + @Test public void testPackageRemoval() throws Exception { - class PackageSenderImpl implements PackageSender { - public void sendPackageBroadcast(final String action, final String pkg, - final Bundle extras, final int flags, final String targetPkg, - final IIntentReceiver finishedReceiver, final int[] userIds, - int[] instantUserIds) { + class PackageSenderImpl implements PackageSender { + public void sendPackageBroadcast(final String action, final String pkg, + final Bundle extras, final int flags, final String targetPkg, + final IIntentReceiver finishedReceiver, final int[] userIds, + int[] instantUserIds) { + } + + public void sendPackageAddedForNewUsers(String packageName, + boolean sendBootComplete, boolean includeStopped, int appId, + int[] userIds, int[] instantUserIds) { + } + + @Override + public void notifyPackageAdded(String packageName) { + } + + @Override + public void notifyPackageRemoved(String packageName) { + } } - public void sendPackageAddedForNewUsers(String packageName, - boolean sendBootComplete, boolean includeStopped, int appId, - int[] userIds, int[] instantUserIds) { - } - } - - PackageSenderImpl sender = new PackageSenderImpl(); - PackageSetting setting = null; - PackageManagerService.PackageRemovedInfo pri = - new PackageManagerService.PackageRemovedInfo(sender); - - // Initial conditions: nothing there - assertNull(pri.removedUsers); - assertNull(pri.broadcastUsers); - - // populateUsers with nothing leaves nothing - pri.populateUsers(null, setting); - assertNull(pri.broadcastUsers); - - // Create a real (non-null) PackageSetting and confirm that the removed - // users are copied properly - setting = new PackageSetting("name", "realName", new File("codePath"), - new File("resourcePath"), "legacyNativeLibraryPathString", - "primaryCpuAbiString", "secondaryCpuAbiString", - "cpuAbiOverrideString", 0, 0, 0, "parentPackageName", null, 0, - null, null); - pri.populateUsers(new int[] {1, 2, 3, 4, 5}, setting); - assertNotNull(pri.broadcastUsers); - assertEquals(5, pri.broadcastUsers.length); - - // Exclude a user - pri.broadcastUsers = null; - final int EXCLUDED_USER_ID = 4; - setting.setInstantApp(true, EXCLUDED_USER_ID); - pri.populateUsers(new int[] {1, 2, 3, EXCLUDED_USER_ID, 5}, setting); - assertNotNull(pri.broadcastUsers); - assertEquals(5 - 1, pri.broadcastUsers.length); - - // TODO: test that sendApplicationHiddenForUser() actually fills in - // broadcastUsers + PackageSenderImpl sender = new PackageSenderImpl(); + PackageSetting setting = null; + PackageManagerService.PackageRemovedInfo pri = + new PackageManagerService.PackageRemovedInfo(sender); + + // Initial conditions: nothing there + Assert.assertNull(pri.removedUsers); + Assert.assertNull(pri.broadcastUsers); + + // populateUsers with nothing leaves nothing + pri.populateUsers(null, setting); + Assert.assertNull(pri.broadcastUsers); + + // Create a real (non-null) PackageSetting and confirm that the removed + // users are copied properly + setting = new PackageSetting("name", "realName", new File("codePath"), + new File("resourcePath"), "legacyNativeLibraryPathString", + "primaryCpuAbiString", "secondaryCpuAbiString", + "cpuAbiOverrideString", 0, 0, 0, "parentPackageName", null, 0, + null, null); + pri.populateUsers(new int[] { + 1, 2, 3, 4, 5 + }, setting); + Assert.assertNotNull(pri.broadcastUsers); + Assert.assertEquals(5, pri.broadcastUsers.length); + Assert.assertNotNull(pri.instantUserIds); + Assert.assertEquals(0, pri.instantUserIds.length); + + // Exclude a user + pri.broadcastUsers = null; + final int EXCLUDED_USER_ID = 4; + setting.setInstantApp(true, EXCLUDED_USER_ID); + pri.populateUsers(new int[] { + 1, 2, 3, EXCLUDED_USER_ID, 5 + }, setting); + Assert.assertNotNull(pri.broadcastUsers); + Assert.assertEquals(4, pri.broadcastUsers.length); + Assert.assertNotNull(pri.instantUserIds); + Assert.assertEquals(1, pri.instantUserIds.length); + + // TODO: test that sendApplicationHiddenForUser() actually fills in + // broadcastUsers } } diff --git a/services/tests/servicestests/src/com/android/server/testutils/TestHandler.java b/services/tests/servicestests/src/com/android/server/testutils/TestHandler.java index 2d4bc0f8b7d0..029d9f1a8262 100644 --- a/services/tests/servicestests/src/com/android/server/testutils/TestHandler.java +++ b/services/tests/servicestests/src/com/android/server/testutils/TestHandler.java @@ -104,6 +104,15 @@ public class TestHandler extends Handler { return new PriorityQueue<>(mMessages); } + /** + * Optionally-overridable to allow deciphering message types + * + * @see android.util.DebugUtils#valueToString - a handy utility to use when overriding this + */ + protected String messageToString(Message message) { + return message.toString(); + } + private void dispatch(MsgInfo msg) { int msgId = msg.message.what; @@ -148,7 +157,7 @@ public class TestHandler extends Handler { @Override public String toString() { return "MsgInfo{" + - "message=" + message + + "message=" + messageToString(message) + ", sendTime=" + sendTime + '}'; } diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java index 96ff46195002..6e57f47997fc 100644 --- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java @@ -45,6 +45,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; /** * Test class for {@link SurfaceAnimatorTest}. @@ -82,6 +83,7 @@ public class SurfaceAnimatorTest extends WindowTestsBase { verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture()); callbackCaptor.getValue().onAnimationFinished(mSpec); + waitUntilPrepareSurfaces(); assertNotAnimating(mAnimatable); assertTrue(mAnimatable.mFinishedCallbackCalled); assertTrue(mAnimatable.mPendingDestroySurfaces.contains(mAnimatable.mLeash)); @@ -104,11 +106,13 @@ public class SurfaceAnimatorTest extends WindowTestsBase { // First animation was finished, but this shouldn't cancel the second animation callbackCaptor.getValue().onAnimationFinished(mSpec); + waitUntilPrepareSurfaces(); assertTrue(mAnimatable.mSurfaceAnimator.isAnimating()); // Second animation was finished verify(mSpec2).startAnimation(any(), any(), callbackCaptor.capture()); callbackCaptor.getValue().onAnimationFinished(mSpec2); + waitUntilPrepareSurfaces(); assertNotAnimating(mAnimatable); assertTrue(mAnimatable.mFinishedCallbackCalled); } @@ -160,6 +164,7 @@ public class SurfaceAnimatorTest extends WindowTestsBase { assertEquals(leash, mAnimatable2.mSurfaceAnimator.mLeash); assertFalse(mAnimatable.mPendingDestroySurfaces.contains(leash)); callbackCaptor.getValue().onAnimationFinished(mSpec); + waitUntilPrepareSurfaces(); assertNotAnimating(mAnimatable2); assertTrue(mAnimatable2.mFinishedCallbackCalled); assertTrue(mAnimatable2.mPendingDestroySurfaces.contains(leash)); @@ -175,6 +180,14 @@ public class SurfaceAnimatorTest extends WindowTestsBase { assertNull(animatable.mSurfaceAnimator.getAnimation()); } + private void waitUntilPrepareSurfaces() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + synchronized (sWm.mWindowMap) { + sWm.mAnimator.addAfterPrepareSurfacesRunnable(latch::countDown); + } + latch.await(); + } + private class MyAnimatable implements Animatable { final SurfaceControl mParent; @@ -233,6 +246,11 @@ public class SurfaceAnimatorTest extends WindowTestsBase { } @Override + public SurfaceControl getAnimationLeashParent() { + return mParent; + } + + @Override public SurfaceControl getSurfaceControl() { return mSurface; } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java new file mode 100644 index 000000000000..9cdef16194ff --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.wm; + +import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM; +import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM; +import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE; + +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.view.SurfaceControl; +import android.view.animation.Animation; +import android.view.animation.ClipRectAnimation; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for the {@link WindowAnimationSpec} class. + * + * atest FrameworksServicesTests:com.android.server.wm.WindowAnimationSpecTest + */ +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class WindowAnimationSpecTest { + private final SurfaceControl mSurfaceControl = mock(SurfaceControl.class); + private final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class); + private final Animation mAnimation = mock(Animation.class); + private final Rect mStackBounds = new Rect(0, 0, 10, 10); + + @Test + public void testApply_clipNone() { + Rect windowCrop = new Rect(0, 0, 20, 20); + Animation a = new ClipRectAnimation(windowCrop, windowCrop); + WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null, + mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_NONE); + windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0); + verify(mTransaction).setWindowCrop(eq(mSurfaceControl), + argThat(rect -> rect.equals(windowCrop))); + } + + @Test + public void testApply_clipAfter() { + WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation, null, + mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_AFTER_ANIM); + windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0); + verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty)); + verify(mTransaction).setFinalCrop(eq(mSurfaceControl), + argThat(rect -> rect.equals(mStackBounds))); + } + + @Test + public void testApply_clipBeforeNoAnimationBounds() { + // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 0, 0) + WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation, null, + mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM); + windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0); + verify(mTransaction).setWindowCrop(eq(mSurfaceControl), + argThat(rect -> rect.equals(mStackBounds))); + } + + @Test + public void testApply_clipBeforeNoStackBounds() { + // Stack bounds is (0, 0, 0, 0) animation clip is (0, 0, 20, 20) + Rect windowCrop = new Rect(0, 0, 20, 20); + Animation a = new ClipRectAnimation(windowCrop, windowCrop); + WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null, + null, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM); + windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0); + verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty)); + } + + @Test + public void testApply_clipBeforeSmallerAnimationClip() { + // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 5, 5) + Rect windowCrop = new Rect(0, 0, 5, 5); + Animation a = new ClipRectAnimation(windowCrop, windowCrop); + WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null, + mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM); + windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0); + verify(mTransaction).setWindowCrop(eq(mSurfaceControl), + argThat(rect -> rect.equals(windowCrop))); + } + + @Test + public void testApply_clipBeforeSmallerStackClip() { + // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 20, 20) + Rect windowCrop = new Rect(0, 0, 20, 20); + Animation a = new ClipRectAnimation(windowCrop, windowCrop); + WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null, + mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM); + windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0); + verify(mTransaction).setWindowCrop(eq(mSurfaceControl), + argThat(rect -> rect.equals(mStackBounds))); + } +} diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java index c699a94db279..ff840f3aeea9 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java @@ -103,6 +103,7 @@ class WindowTestsBase { context.getDisplay().getDisplayInfo(mDisplayInfo); mDisplayContent = createNewDisplay(); + sWm.mAnimator.mInitialized = true; sWm.mDisplayEnabled = true; sWm.mDisplayReady = true; diff --git a/telephony/java/android/telephony/Telephony.java b/telephony/java/android/provider/Telephony.java index 942ea009f684..942ea009f684 100644 --- a/telephony/java/android/telephony/Telephony.java +++ b/telephony/java/android/provider/Telephony.java diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java index 973df316d280..176057ddc23e 100644 --- a/telephony/java/android/telephony/euicc/EuiccManager.java +++ b/telephony/java/android/telephony/euicc/EuiccManager.java @@ -15,8 +15,10 @@ */ package android.telephony.euicc; +import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.SdkConstant; +import android.annotation.SystemApi; import android.app.Activity; import android.app.PendingIntent; import android.content.Context; @@ -29,6 +31,9 @@ import android.os.ServiceManager; import com.android.internal.telephony.euicc.IEuiccController; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * EuiccManager is the application interface to eUICCs, or eSIMs/embedded SIMs. * @@ -167,6 +172,35 @@ public class EuiccManager { */ public static final String META_DATA_CARRIER_ICON = "android.telephony.euicc.carriericon"; + /** + * Euicc OTA update status which can be got by {@link #getOtaStatus} + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"EUICC_OTA_"}, value = { + EUICC_OTA_IN_PROGRESS, + EUICC_OTA_FAILED, + EUICC_OTA_SUCCEEDED, + EUICC_OTA_NOT_NEEDED, + EUICC_OTA_STATUS_UNAVAILABLE + + }) + public @interface OtaStatus{} + + /** + * An OTA is in progress. During this time, the eUICC is not available and the user may lose + * network access. + */ + public static final int EUICC_OTA_IN_PROGRESS = 1; + /** The OTA update failed. */ + public static final int EUICC_OTA_FAILED = 2; + /** The OTA update finished successfully. */ + public static final int EUICC_OTA_SUCCEEDED = 3; + /** The OTA update not needed since current eUICC OS is latest. */ + public static final int EUICC_OTA_NOT_NEEDED = 4; + /** The OTA status is unavailable since eUICC service is unavailable. */ + public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5; + private final Context mContext; /** @hide */ @@ -211,6 +245,26 @@ public class EuiccManager { } /** + * Returns the current status of eUICC OTA. + * + * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. + * + * @return the status of eUICC OTA. If {@link #isEnabled()} is false or the eUICC is not ready, + * {@link OtaStatus#EUICC_OTA_STATUS_UNAVAILABLE} will be returned. + */ + @SystemApi + public int getOtaStatus() { + if (!isEnabled()) { + return EUICC_OTA_STATUS_UNAVAILABLE; + } + try { + return getIEuiccController().getOtaStatus(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Attempt to download the given {@link DownloadableSubscription}. * * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission, diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index e2d25b8e352c..f804cb068b31 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -417,6 +417,8 @@ cat include/telephony/ril.h | \ int RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION = 141; int RIL_REQUEST_START_NETWORK_SCAN = 142; int RIL_REQUEST_STOP_NETWORK_SCAN = 143; + int RIL_REQUEST_GET_SLOT_STATUS = 144; + int RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING = 145; int RIL_RESPONSE_ACKNOWLEDGEMENT = 800; @@ -471,4 +473,5 @@ cat include/telephony/ril.h | \ int RIL_UNSOL_MODEM_RESTART = 1047; int RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION = 1048; int RIL_UNSOL_NETWORK_SCAN_RESULT = 1049; + int RIL_UNSOL_ICC_SLOT_STATUS = 1050; } diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl index b3fc90db75d3..0a0ad90b5954 100644 --- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl +++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl @@ -30,6 +30,7 @@ interface IEuiccController { oneway void getDefaultDownloadableSubscriptionList( String callingPackage, in PendingIntent callbackIntent); String getEid(); + int getOtaStatus(); oneway void downloadSubscription(in DownloadableSubscription subscription, boolean switchAfterDownload, String callingPackage, in PendingIntent callbackIntent); EuiccInfo getEuiccInfo(); diff --git a/tests/DexLoggerIntegrationTests/Android.mk b/tests/DexLoggerIntegrationTests/Android.mk new file mode 100644 index 000000000000..7187a3795433 --- /dev/null +++ b/tests/DexLoggerIntegrationTests/Android.mk @@ -0,0 +1,49 @@ +# +# Copyright 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. +# + +LOCAL_PATH:= $(call my-dir) + +# Build a tiny library that the test app can dynamically load + +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests +LOCAL_MODULE := DexLoggerTestLibrary +LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/dcl) + +include $(BUILD_JAVA_LIBRARY) + +dexloggertest_jar := $(LOCAL_BUILT_MODULE) + + +# Build the test app itself + +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests +LOCAL_PACKAGE_NAME := DexLoggerIntegrationTests +LOCAL_COMPATIBILITY_SUITE := device-tests +LOCAL_CERTIFICATE := platform +LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/server/pm) + +LOCAL_STATIC_JAVA_LIBRARIES := \ + android-support-test \ + truth-prebuilt \ + +# This gets us the javalib.jar built by DexLoggerTestLibrary above. +LOCAL_JAVA_RESOURCE_FILES := $(dexloggertest_jar) + +include $(BUILD_PACKAGE) diff --git a/tests/DexLoggerIntegrationTests/AndroidManifest.xml b/tests/DexLoggerIntegrationTests/AndroidManifest.xml new file mode 100644 index 000000000000..a847e8f3b921 --- /dev/null +++ b/tests/DexLoggerIntegrationTests/AndroidManifest.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.dexloggertest"> + + <!-- Tests feature introduced in P (27) --> + <uses-sdk + android:minSdkVersion="27" + android:targetSdkVersion="27" /> + + <uses-permission android:name="android.permission.READ_LOGS" /> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="android.support.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.frameworks.dexloggertest" + android:label="Integration test for DexLogger" /> +</manifest> diff --git a/tests/DexLoggerIntegrationTests/AndroidTest.xml b/tests/DexLoggerIntegrationTests/AndroidTest.xml new file mode 100644 index 000000000000..8ed19f893476 --- /dev/null +++ b/tests/DexLoggerIntegrationTests/AndroidTest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 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. +--> +<configuration description="Runs DexLogger Integration Tests"> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="DexLoggerIntegrationTests.apk"/> + <option name="cleanup-apks" value="true"/> + </target_preparer> + + <option name="test-suite-tag" value="apct"/> + <option name="test-tag" value="DexLoggerIntegrationTests"/> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.frameworks.dexloggertest"/> + <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/> + </test> +</configuration> diff --git a/tests/DexLoggerIntegrationTests/src/com/android/dcl/Simple.java b/tests/DexLoggerIntegrationTests/src/com/android/dcl/Simple.java new file mode 100644 index 000000000000..e995a26ea5c9 --- /dev/null +++ b/tests/DexLoggerIntegrationTests/src/com/android/dcl/Simple.java @@ -0,0 +1,22 @@ +/* + * Copyright 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.dcl; + +/** Dummy class which is built into a jar purely so we can pass it to DexClassLoader. */ +public final class Simple { + public Simple() {} +} diff --git a/tests/DexLoggerIntegrationTests/src/com/android/server/pm/DexLoggerIntegrationTests.java b/tests/DexLoggerIntegrationTests/src/com/android/server/pm/DexLoggerIntegrationTests.java new file mode 100644 index 000000000000..d9f34d589c41 --- /dev/null +++ b/tests/DexLoggerIntegrationTests/src/com/android/server/pm/DexLoggerIntegrationTests.java @@ -0,0 +1,151 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.util.EventLog; + +import dalvik.system.DexClassLoader; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.Formatter; +import java.util.List; + +/** + * Integration tests for {@link com.android.server.pm.dex.DexLogger}. + * + * The setup for the test dynamically loads code in a jar extracted + * from our assets (a secondary dex file). + * + * We then use adb to trigger secondary dex file reconcilation (and + * wait for it to complete). As a side-effect of this DexLogger should + * be notified of the file and should log the hash of the file's name + * and content. We verify that this message appears in the event log. + * + * Run with "atest DexLoggerIntegrationTests". + */ +@RunWith(JUnit4.class) +public final class DexLoggerIntegrationTests { + + private static final String TAG = DexLoggerIntegrationTests.class.getSimpleName(); + + private static final String PACKAGE_NAME = "com.android.frameworks.dexloggertest"; + + private static final int SNET_TAG = 0x534e4554; + private static final String DCL_SUBTAG = "dcl"; + + // Obtained via "echo -n copied.jar | sha256sum" + private static final String EXPECTED_NAME_HASH = + "1B6C71DB26F36582867432CCA12FB6A517470C9F9AABE9198DD4C5C030D6DC0C"; + + private static String expectedContentHash; + + @BeforeClass + public static void setUpAll() throws Exception { + Context context = InstrumentationRegistry.getTargetContext(); + MessageDigest hasher = MessageDigest.getInstance("SHA-256"); + + // Copy the jar from our Java resources to a private data directory + File privateCopy = new File(context.getDir("jars", Context.MODE_PRIVATE), "copied.jar"); + try (InputStream input = DexLoggerIntegrationTests.class.getResourceAsStream("/javalib.jar"); + OutputStream output = new FileOutputStream(privateCopy)) { + byte[] buffer = new byte[1024]; + while (true) { + int numRead = input.read(buffer); + if (numRead < 0) { + break; + } + output.write(buffer, 0, numRead); + hasher.update(buffer, 0, numRead); + } + } + + // Remember the SHA-256 of the file content to check that it is the same as + // the value we see logged. + Formatter formatter = new Formatter(); + for (byte b : hasher.digest()) { + formatter.format("%02X", b); + } + expectedContentHash = formatter.toString(); + + // Feed the jar to a class loader and make sure it contains what we expect. + ClassLoader loader = + new DexClassLoader( + privateCopy.toString(), null, null, context.getClass().getClassLoader()); + loader.loadClass("com.android.dcl.Simple"); + } + + @Test + public void testDexLoggerReconcileGeneratesEvents() throws Exception { + int[] tagList = new int[] { SNET_TAG }; + List<EventLog.Event> events = new ArrayList<>(); + + // There may already be events in the event log - figure out the most recent one + EventLog.readEvents(tagList, events); + long previousEventNanos = events.isEmpty() ? 0 : events.get(events.size() - 1).getTimeNanos(); + events.clear(); + + Process process = Runtime.getRuntime().exec( + "cmd package reconcile-secondary-dex-files " + PACKAGE_NAME); + int exitCode = process.waitFor(); + assertThat(exitCode).isEqualTo(0); + + int myUid = android.os.Process.myUid(); + String expectedMessage = EXPECTED_NAME_HASH + " " + expectedContentHash; + + EventLog.readEvents(tagList, events); + boolean found = false; + for (EventLog.Event event : events) { + if (event.getTimeNanos() <= previousEventNanos) { + continue; + } + Object[] data = (Object[]) event.getData(); + + // We only care about DCL events that we generated. + String subTag = (String) data[0]; + if (!DCL_SUBTAG.equals(subTag)) { + continue; + } + int uid = (int) data[1]; + if (uid != myUid) { + continue; + } + + String message = (String) data[2]; + assertThat(message).isEqualTo(expectedMessage); + found = true; + } + + assertThat(found).isTrue(); + } +} diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp index 765079507513..102bbf9d07d7 100644 --- a/tools/incident_section_gen/main.cpp +++ b/tools/incident_section_gen/main.cpp @@ -20,6 +20,7 @@ #include <map> #include <set> #include <string> +#include <sstream> using namespace android; using namespace android::os; @@ -482,9 +483,44 @@ static bool generateSectionListCpp(Descriptor const* descriptor) { } // ================================================================================ +static string replace_string(const string& str, const char replace, const char with) +{ + string result(str); + const int N = result.size(); + for (int i=0; i<N; i++) { + if (result[i] == replace) { + result[i] = with; + } + } + return result; +} + +static void generateCsv(Descriptor const* descriptor, const string& indent, set<string>* parents) { + DebugStringOptions options; + options.include_comments = true; + for (int i=0; i<descriptor->field_count(); i++) { + const FieldDescriptor* field = descriptor->field(i); + stringstream text; + if (field->type() == FieldDescriptor::TYPE_MESSAGE) { + text << field->message_type()->name(); + } else { + text << field->type_name(); + } + text << " " << field->name(); + printf("%s%s,\n", indent.c_str(), replace_string(text.str(), '\n', ' ').c_str()); + if (field->type() == FieldDescriptor::TYPE_MESSAGE && + parents->find(field->message_type()->full_name()) == parents->end()) { + parents->insert(field->message_type()->full_name()); + generateCsv(field->message_type(), indent + ",", parents); + parents->erase(field->message_type()->full_name()); + } + } +} + +// ================================================================================ int main(int argc, char const *argv[]) { - if (argc != 2) return 1; + if (argc < 2) return 1; const char* module = argv[1]; Descriptor const* descriptor = IncidentProto::descriptor(); @@ -495,7 +531,23 @@ int main(int argc, char const *argv[]) if (strcmp(module, "incidentd") == 0 ) { return !generateSectionListCpp(descriptor); } - - // return failure if not called by the whitelisted modules + // Generates Csv Format of proto definition for each section. + if (strcmp(module, "csv") == 0 && argc > 2) { + int sectionId = atoi(argv[2]); + for (int i=0; i<descriptor->field_count(); i++) { + const FieldDescriptor* field = descriptor->field(i); + if (strcmp(field->name().c_str(), argv[2]) == 0 + || field->number() == sectionId) { + set<string> parents; + printf("%s\n", field->name().c_str()); + generateCsv(field->message_type(), "", &parents); + break; + } + } + // Returns failure if csv is enabled to prevent Android building with it. + // It doesn't matter if this command runs manually. + return 1; + } + // Returns failure if not called by the whitelisted modules return 1; } diff --git a/tools/sdkparcelables/Android.bp b/tools/sdkparcelables/Android.bp new file mode 100644 index 000000000000..00fb8aa20b18 --- /dev/null +++ b/tools/sdkparcelables/Android.bp @@ -0,0 +1,22 @@ +java_binary_host { + name: "sdkparcelables", + manifest: "manifest.txt", + srcs: [ + "src/**/*.kt", + ], + static_libs: [ + "asm-6.0", + ], +} + +java_library_host { + name: "sdkparcelables_test", + manifest: "manifest.txt", + srcs: [ + "tests/**/*.kt", + ], + static_libs: [ + "sdkparcelables", + "junit", + ], +} diff --git a/tools/sdkparcelables/manifest.txt b/tools/sdkparcelables/manifest.txt new file mode 100644 index 000000000000..cd5420ce0bf4 --- /dev/null +++ b/tools/sdkparcelables/manifest.txt @@ -0,0 +1 @@ +Main-class: com.android.sdkparcelables.MainKt diff --git a/tools/sdkparcelables/src/com/android/sdkparcelables/AncestorCollector.kt b/tools/sdkparcelables/src/com/android/sdkparcelables/AncestorCollector.kt new file mode 100644 index 000000000000..f278aec8eb6f --- /dev/null +++ b/tools/sdkparcelables/src/com/android/sdkparcelables/AncestorCollector.kt @@ -0,0 +1,28 @@ +package com.android.sdkparcelables + +import org.objectweb.asm.ClassVisitor +import java.util.* + +data class Ancestors(val superName: String?, val interfaces: List<String>?) + +/** A class that implements an ASM ClassVisitor that collects super class and + * implemented interfaces for each class that it visits. + */ +class AncestorCollector(api: Int, dest: ClassVisitor?) : ClassVisitor(api, dest) { + private val _ancestors = LinkedHashMap<String, Ancestors>() + + val ancestors: Map<String, Ancestors> + get() = _ancestors + + override fun visit(version: Int, access: Int, name: String?, signature: String?, + superName: String?, interfaces: Array<out String>?) { + name!! + + val old = _ancestors.put(name, Ancestors(superName, interfaces?.toList())) + if (old != null) { + throw RuntimeException("class $name already found") + } + + super.visit(version, access, name, signature, superName, interfaces) + } +}
\ No newline at end of file diff --git a/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt new file mode 100644 index 000000000000..3e9d92cd978f --- /dev/null +++ b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt @@ -0,0 +1,56 @@ +package com.android.sdkparcelables + +import org.objectweb.asm.ClassReader +import org.objectweb.asm.Opcodes +import java.io.File +import java.io.IOException +import java.util.zip.ZipFile + +fun main(args: Array<String>) { + if (args.size != 2) { + usage() + } + + val zipFileName = args[0] + val aidlFileName = args[1] + + val zipFile: ZipFile + + try { + zipFile = ZipFile(zipFileName) + } catch (e: IOException) { + System.err.println("error reading input jar: ${e.message}") + kotlin.system.exitProcess(2) + } + + val ancestorCollector = AncestorCollector(Opcodes.ASM6, null) + + for (entry in zipFile.entries()) { + if (entry.name.endsWith(".class")) { + val reader = ClassReader(zipFile.getInputStream(entry)) + reader.accept(ancestorCollector, + ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES) + } + } + + val parcelables = ParcelableDetector.ancestorsToParcelables(ancestorCollector.ancestors) + + try { + val outFile = File(aidlFileName) + val outWriter = outFile.bufferedWriter() + for (parcelable in parcelables) { + outWriter.write("parcelable ") + outWriter.write(parcelable.replace('/', '.').replace('$', '.')) + outWriter.write(";\n") + } + outWriter.flush() + } catch (e: IOException) { + System.err.println("error writing output aidl: ${e.message}") + kotlin.system.exitProcess(2) + } +} + +fun usage() { + System.err.println("Usage: <input jar> <output aidl>") + kotlin.system.exitProcess(1) +}
\ No newline at end of file diff --git a/tools/sdkparcelables/src/com/android/sdkparcelables/ParcelableDetector.kt b/tools/sdkparcelables/src/com/android/sdkparcelables/ParcelableDetector.kt new file mode 100644 index 000000000000..620f798daf48 --- /dev/null +++ b/tools/sdkparcelables/src/com/android/sdkparcelables/ParcelableDetector.kt @@ -0,0 +1,52 @@ +package com.android.sdkparcelables + +/** A class that uses an ancestor map to find all classes that + * implement android.os.Parcelable, including indirectly through + * super classes or super interfaces. + */ +class ParcelableDetector { + companion object { + fun ancestorsToParcelables(ancestors: Map<String, Ancestors>): List<String> { + val impl = Impl(ancestors) + impl.build() + return impl.parcelables + } + } + + private class Impl(val ancestors: Map<String, Ancestors>) { + val isParcelableCache = HashMap<String, Boolean>() + val parcelables = ArrayList<String>() + + fun build() { + val classList = ancestors.keys + classList.filterTo(parcelables, this::isParcelable) + parcelables.sort() + } + + private fun isParcelable(c: String?): Boolean { + if (c == null) { + return false + } + + if (c == "android/os/Parcelable") { + return true + } + + val old = isParcelableCache[c] + if (old != null) { + return old + } + + val cAncestors = ancestors[c] ?: + throw RuntimeException("class $c missing ancestor information") + + val seq = (cAncestors.interfaces?.asSequence() ?: emptySequence()) + + cAncestors.superName + + val ancestorIsParcelable = seq.any(this::isParcelable) + + isParcelableCache[c] = ancestorIsParcelable + return ancestorIsParcelable + } + } +} diff --git a/tools/sdkparcelables/tests/com/android/sdkparcelables/ParcelableDetectorTest.kt b/tools/sdkparcelables/tests/com/android/sdkparcelables/ParcelableDetectorTest.kt new file mode 100644 index 000000000000..edfc8259a738 --- /dev/null +++ b/tools/sdkparcelables/tests/com/android/sdkparcelables/ParcelableDetectorTest.kt @@ -0,0 +1,57 @@ +package com.android.sdkparcelables + +import junit.framework.TestCase.assertEquals +import org.junit.Test + +class ParcelableDetectorTest { + @Test + fun `detect implements`() { + val ancestorMap = mapOf( + testAncestors("android/test/Parcelable",null, "android/os/Parcelable"), + testAncestors("android/os/Parcelable", null)) + + val parcelables = ParcelableDetector.ancestorsToParcelables(ancestorMap) + + assertEquals(parcelables, listOf("android/os/Parcelable", "android/test/Parcelable")) + } + + @Test + fun `detect implements in reverse order`() { + val ancestorMap = mapOf( + testAncestors("android/os/Parcelable", null), + testAncestors("android/test/Parcelable",null, "android/os/Parcelable")) + + val parcelables = ParcelableDetector.ancestorsToParcelables(ancestorMap) + + assertEquals(parcelables, listOf("android/os/Parcelable", "android/test/Parcelable")) + } + + @Test + fun `detect super implements`() { + val ancestorMap = mapOf( + testAncestors("android/test/SuperParcelable",null, "android/os/Parcelable"), + testAncestors("android/test/Parcelable","android/test/SuperParcelable"), + testAncestors("android/os/Parcelable", null)) + + val parcelables = ParcelableDetector.ancestorsToParcelables(ancestorMap) + + assertEquals(parcelables, listOf("android/os/Parcelable", "android/test/Parcelable", "android/test/SuperParcelable")) + } + + @Test + fun `detect super interface`() { + val ancestorMap = mapOf( + testAncestors("android/test/IParcelable",null, "android/os/Parcelable"), + testAncestors("android/test/Parcelable",null, "android/test/IParcelable"), + testAncestors("android/os/Parcelable", null)) + + val parcelables = ParcelableDetector.ancestorsToParcelables(ancestorMap) + + assertEquals(parcelables, listOf("android/os/Parcelable", "android/test/IParcelable", "android/test/Parcelable")) + } + +} + +private fun testAncestors(name: String, superName: String?, vararg interfaces: String): Pair<String, Ancestors> { + return Pair(name, Ancestors(superName, interfaces.toList())) +} diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java index e3752ac77a5e..928a1da8b51c 100644 --- a/wifi/java/android/net/wifi/WifiScanner.java +++ b/wifi/java/android/net/wifi/WifiScanner.java @@ -160,6 +160,24 @@ public class WifiScanner { */ public static final int REPORT_EVENT_NO_BATCH = (1 << 2); + /** + * This is used to indicate the purpose of the scan to the wifi chip in + * {@link ScanSettings#type}. + * On devices with multiple hardware radio chains (and hence different modes of scan), + * this type serves as an indication to the hardware on what mode of scan to perform. + * Only apps holding android.Manifest.permission.NETWORK_STACK permission can set this value. + * + * Note: This serves as an intent and not as a stipulation, the wifi chip + * might honor or ignore the indication based on the current radio conditions. Always + * use the {@link ScanResult#radioChainInfos} to figure out the radio chain configuration used + * to receive the corresponding scan result. + */ + /** {@hide} */ + public static final int TYPE_LOW_LATENCY = 0; + /** {@hide} */ + public static final int TYPE_LOW_POWER = 1; + /** {@hide} */ + public static final int TYPE_HIGH_ACCURACY = 2; /** {@hide} */ public static final String SCAN_PARAMS_SCAN_SETTINGS_KEY = "ScanSettings"; @@ -193,7 +211,8 @@ public class WifiScanner { * list of hidden networks to scan for. Explicit probe requests are sent out for such * networks during scan. Only valid for single scan requests. * {@hide} - * */ + */ + @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public HiddenNetwork[] hiddenNetworks; /** period of background scan; in millisecond, 0 => single shot scan */ public int periodInMs; @@ -223,6 +242,13 @@ public class WifiScanner { * {@hide} */ public boolean isPnoScan; + /** + * Indicate the type of scan to be performed by the wifi chip. + * Default value: {@link #TYPE_LOW_LATENCY}. + * {@hide} + */ + @RequiresPermission(android.Manifest.permission.NETWORK_STACK) + public int type = TYPE_LOW_LATENCY; /** Implement the Parcelable interface {@hide} */ public int describeContents() { @@ -239,6 +265,7 @@ public class WifiScanner { dest.writeInt(maxPeriodInMs); dest.writeInt(stepCount); dest.writeInt(isPnoScan ? 1 : 0); + dest.writeInt(type); if (channels != null) { dest.writeInt(channels.length); for (int i = 0; i < channels.length; i++) { @@ -272,6 +299,7 @@ public class WifiScanner { settings.maxPeriodInMs = in.readInt(); settings.stepCount = in.readInt(); settings.isPnoScan = in.readInt() == 1; + settings.type = in.readInt(); int num_channels = in.readInt(); settings.channels = new ChannelSpec[num_channels]; for (int i = 0; i < num_channels; i++) { diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java index d57d1524d083..2f0c3163aa0c 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java @@ -269,6 +269,10 @@ public class WifiAwareManager { + identityChangedListener); } + if (attachCallback == null) { + throw new IllegalArgumentException("Null callback provided"); + } + synchronized (mLock) { Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper(); @@ -300,6 +304,10 @@ public class WifiAwareManager { DiscoverySessionCallback callback) { if (VDBG) Log.v(TAG, "publish(): clientId=" + clientId + ", config=" + publishConfig); + if (callback == null) { + throw new IllegalArgumentException("Null callback provided"); + } + try { mService.publish(mContext.getOpPackageName(), clientId, publishConfig, new WifiAwareDiscoverySessionCallbackProxy(this, looper, true, callback, @@ -333,6 +341,10 @@ public class WifiAwareManager { } } + if (callback == null) { + throw new IllegalArgumentException("Null callback provided"); + } + try { mService.subscribe(mContext.getOpPackageName(), clientId, subscribeConfig, new WifiAwareDiscoverySessionCallbackProxy(this, looper, false, callback, diff --git a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java index 8b86cdde4a9e..2ea6e797ec93 100644 --- a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java +++ b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java @@ -26,13 +26,49 @@ import android.os.Handler; */ public abstract class ProvisioningCallback { - /** + /** * The reason code for Provisioning Failure due to connection failure to OSU AP. * @hide */ public static final int OSU_FAILURE_AP_CONNECTION = 1; /** + * The reason code for Provisioning Failure due to connection failure to OSU AP. + * @hide + */ + public static final int OSU_FAILURE_SERVER_URL_INVALID = 2; + + /** + * The reason code for Provisioning Failure due to connection failure to OSU AP. + * @hide + */ + public static final int OSU_FAILURE_SERVER_CONNECTION = 3; + + /** + * The reason code for Provisioning Failure due to connection failure to OSU AP. + * @hide + */ + public static final int OSU_FAILURE_SERVER_VALIDATION = 4; + + /** + * The reason code for Provisioning Failure due to connection failure to OSU AP. + * @hide + */ + public static final int OSU_FAILURE_PROVIDER_VERIFICATION = 5; + + /** + * The reason code for Provisioning Failure when a provisioning flow is aborted. + * @hide + */ + public static final int OSU_FAILURE_PROVISIONING_ABORTED = 6; + + /** + * The reason code for Provisioning Failure when a provisioning flow is aborted. + * @hide + */ + public static final int OSU_FAILURE_PROVISIONING_NOT_AVAILABLE = 7; + + /** * The status code for Provisioning flow to indicate connecting to OSU AP * @hide */ @@ -45,6 +81,24 @@ public abstract class ProvisioningCallback { public static final int OSU_STATUS_AP_CONNECTED = 2; /** + * The status code for Provisioning flow to indicate connecting to OSU AP + * @hide + */ + public static final int OSU_STATUS_SERVER_CONNECTED = 3; + + /** + * The status code for Provisioning flow to indicate connecting to OSU AP + * @hide + */ + public static final int OSU_STATUS_SERVER_VALIDATED = 4; + + /** + * The status code for Provisioning flow to indicate connecting to OSU AP + * @hide + */ + public static final int OSU_STATUS_PROVIDER_VERIFIED = 5; + + /** * Provisioning status for OSU failure * @param status indicates error condition */ diff --git a/wifi/java/android/net/wifi/rtt/ResponderConfig.java b/wifi/java/android/net/wifi/rtt/ResponderConfig.java index 8be7782d5664..c3e10074c56c 100644 --- a/wifi/java/android/net/wifi/rtt/ResponderConfig.java +++ b/wifi/java/android/net/wifi/rtt/ResponderConfig.java @@ -37,7 +37,7 @@ import java.util.Objects; * * @hide (@SystemApi) */ -public class ResponderConfig implements Parcelable { +public final class ResponderConfig implements Parcelable { private static final int AWARE_BAND_2_DISCOVERY_CHANNEL = 2437; /** @hide */ @@ -122,15 +122,14 @@ public class ResponderConfig implements Parcelable { /** * The MAC address of the Responder. Will be null if a Wi-Fi Aware peer identifier (the * peerHandle field) ise used to identify the Responder. - * TODO: convert to MacAddress */ - public MacAddress macAddress; + public final MacAddress macAddress; /** * The peer identifier of a Wi-Fi Aware Responder. Will be null if a MAC Address (the macAddress * field) is used to identify the Responder. */ - public PeerHandle peerHandle; + public final PeerHandle peerHandle; /** * The device type of the Responder. @@ -171,7 +170,7 @@ public class ResponderConfig implements Parcelable { public final int preamble; /** - * Constructs Responder configuration. + * Constructs Responder configuration, using a MAC address to identify the Responder. * * @param macAddress The MAC address of the Responder. * @param responderType The type of the responder device, specified using @@ -210,7 +209,7 @@ public class ResponderConfig implements Parcelable { } /** - * Constructs Responder configuration. + * Constructs Responder configuration, using a Wi-Fi Aware PeerHandle to identify the Responder. * * @param peerHandle The Wi-Fi Aware peer identifier of the Responder. * @param responderType The type of the responder device, specified using @@ -245,6 +244,45 @@ public class ResponderConfig implements Parcelable { } /** + * Constructs Responder configuration. This is an internal-only constructor which specifies both + * a MAC address and a Wi-Fi PeerHandle to identify the Responder. + * + * @param macAddress The MAC address of the Responder. + * @param peerHandle The Wi-Fi Aware peer identifier of the Responder. + * @param responderType The type of the responder device, specified using + * {@link ResponderType}. + * @param supports80211mc Indicates whether the responder supports IEEE 802.11mc. + * @param channelWidth Responder channel bandwidth, specified using {@link ChannelWidth}. + * @param frequency The primary 20 MHz frequency (in MHz) of the channel of the Responder. + * @param centerFreq0 Not used if the {@code channelWidth} is 20 MHz. If the Responder uses + * 40, 80 or 160 MHz, this is the center frequency (in MHz), if the + * Responder uses 80 + 80 MHz, this is the center frequency of the first + * segment (in MHz). + * @param centerFreq1 Only used if the {@code channelWidth} is 80 + 80 MHz. If the + * Responder + * uses 80 + 80 MHz, this is the center frequency of the second segment + * (in + * MHz). + * @param preamble The preamble used by the Responder, specified using + * {@link PreambleType}. + * @hide + */ + public ResponderConfig(@NonNull MacAddress macAddress, @NonNull PeerHandle peerHandle, + @ResponderType int responderType, boolean supports80211mc, + @ChannelWidth int channelWidth, int frequency, int centerFreq0, int centerFreq1, + @PreambleType int preamble) { + this.macAddress = macAddress; + this.peerHandle = peerHandle; + this.responderType = responderType; + this.supports80211mc = supports80211mc; + this.channelWidth = channelWidth; + this.frequency = frequency; + this.centerFreq0 = centerFreq0; + this.centerFreq1 = centerFreq1; + this.preamble = preamble; + } + + /** * Creates a Responder configuration from a {@link ScanResult} corresponding to an Access * Point (AP), which can be obtained from {@link android.net.wifi.WifiManager#getScanResults()}. */ diff --git a/wifi/java/android/net/wifi/rtt/WifiRttManager.java b/wifi/java/android/net/wifi/rtt/WifiRttManager.java index b4c690f4840d..240b3c1e3b51 100644 --- a/wifi/java/android/net/wifi/rtt/WifiRttManager.java +++ b/wifi/java/android/net/wifi/rtt/WifiRttManager.java @@ -123,6 +123,10 @@ public class WifiRttManager { + ", callback=" + callback + ", handler=" + handler); } + if (callback == null) { + throw new IllegalArgumentException("Null callback provided"); + } + Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper(); Binder binder = new Binder(); try { diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java index e542789e01e3..a4366d454a67 100644 --- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java @@ -16,19 +16,23 @@ package android.net.wifi; +import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.validateMockitoUsage; import static org.mockito.Mockito.when; import android.content.Context; import android.os.Handler; +import android.os.Parcel; import android.os.test.TestLooper; import android.test.suitebuilder.annotation.SmallTest; +import android.net.wifi.WifiScanner.ScanSettings; import com.android.internal.util.test.BidirectionalAsyncChannelServer; import org.junit.After; import org.junit.Before; +import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -70,4 +74,50 @@ public class WifiScannerTest { validateMockitoUsage(); } + /** + * Verify parcel read/write for ScanSettings. + */ + @Test + public void verifyScanSettingsParcelWithBand() throws Exception { + ScanSettings writeSettings = new ScanSettings(); + writeSettings.type = WifiScanner.TYPE_LOW_POWER; + writeSettings.band = WifiScanner.WIFI_BAND_BOTH_WITH_DFS; + + ScanSettings readSettings = parcelWriteRead(writeSettings); + assertEquals(readSettings.type, writeSettings.type); + assertEquals(readSettings.band, writeSettings.band); + assertEquals(0, readSettings.channels.length); + } + + /** + * Verify parcel read/write for ScanSettings. + */ + @Test + public void verifyScanSettingsParcelWithChannels() throws Exception { + ScanSettings writeSettings = new ScanSettings(); + writeSettings.type = WifiScanner.TYPE_HIGH_ACCURACY; + writeSettings.band = WifiScanner.WIFI_BAND_UNSPECIFIED; + writeSettings.channels = new WifiScanner.ChannelSpec[] { + new WifiScanner.ChannelSpec(5), + new WifiScanner.ChannelSpec(7) + }; + + ScanSettings readSettings = parcelWriteRead(writeSettings); + assertEquals(writeSettings.type, readSettings.type); + assertEquals(writeSettings.band, readSettings.band); + assertEquals(2, readSettings.channels.length); + assertEquals(5, readSettings.channels[0].frequency); + assertEquals(7, readSettings.channels[1].frequency); + } + + /** + * Write the provided {@link ScanSettings} to a parcel and deserialize it. + */ + private static ScanSettings parcelWriteRead(ScanSettings writeSettings) throws Exception { + Parcel parcel = Parcel.obtain(); + writeSettings.writeToParcel(parcel, 0); + parcel.setDataPosition(0); // Rewind data position back to the beginning for read. + return ScanSettings.CREATOR.createFromParcel(parcel); + } + } |