Merge "Remove minLayer and maxLayer from display screenshot."
diff --git a/Android.bp b/Android.bp
index ca1c614..415eff3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -368,6 +368,7 @@
"core/java/com/android/internal/app/IAppOpsService.aidl",
"core/java/com/android/internal/app/IBatteryStats.aidl",
"core/java/com/android/internal/app/ISoundTriggerService.aidl",
+ "core/java/com/android/internal/app/IVoiceActionCheckCallback.aidl",
"core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl",
"core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl",
"core/java/com/android/internal/app/IVoiceInteractionSessionShowCallback.aidl",
@@ -1166,7 +1167,7 @@
previous_api: ":last-released-public-api",
merge_annotations_dirs: [
"metalava-manual",
- "ojluni-annotated-stubs",
+ "ojluni-annotated-sdk-stubs",
],
}
@@ -1228,7 +1229,12 @@
previous_api: ":last-released-public-api",
merge_annotations_dirs: [
"metalava-manual",
- "ojluni-annotated-stubs",
+ "ojluni-annotated-sdk-stubs",
+ ],
+ api_levels_annotations_enabled: true,
+ api_levels_annotations_dirs: [
+ "sdk-dir",
+ "api-versions-jars-dir",
],
}
@@ -1499,7 +1505,7 @@
previous_api: ":last-released-public-api",
merge_annotations_dirs: [
"metalava-manual",
- "ojluni-annotated-stubs",
+ "ojluni-annotated-sdk-stubs",
],
args: " --show-annotation android.annotation.SystemApi",
}
@@ -1585,6 +1591,7 @@
removed_api_file: "api/removed.txt",
},
},
+ jdiff_enabled: true,
}
droidstubs {
@@ -1609,6 +1616,7 @@
removed_api_file: "api/system-removed.txt",
},
},
+ jdiff_enabled: true,
}
droidstubs {
diff --git a/Android.mk b/Android.mk
index 5c4c237..d333074 100644
--- a/Android.mk
+++ b/Android.mk
@@ -53,265 +53,16 @@
cat $^ | sort -u > $@.tmp
$(call commit-change-for-toc,$@)
-# the documentation
-# ============================================================
-
-# TODO: deal with com/google/android/googleapps
-packages_to_document := \
- android \
- javax/microedition/khronos \
- org/apache/http/conn \
- org/apache/http/params \
-
-# include definition of libcore_to_document
-include libcore/Docs.mk
-
-non_base_dirs := \
- ../opt/telephony/src/java/android/telephony \
- ../opt/telephony/src/java/android/telephony/gsm \
- ../opt/net/voip/src/java/android/net/rtp \
- ../opt/net/voip/src/java/android/net/sip \
-
-# Find all files in specific directories (relative to frameworks/base)
-# to document and check apis
-files_to_check_apis := \
- $(call find-other-java-files, \
- $(non_base_dirs) \
- )
-
-# Find all files in specific packages that were used to compile
-# framework.jar to document and check apis
-files_to_check_apis += \
- $(addprefix ../../,\
- $(filter \
- $(foreach dir,$(FRAMEWORKS_BASE_JAVA_SRC_DIRS),\
- $(foreach package,$(packages_to_document),\
- $(dir)/$(package)/%.java)),\
- $(SOONG_FRAMEWORK_SRCS)))
-
-# Find all generated files that were used to compile framework.jar
-files_to_check_apis_generated := \
- $(filter $(OUT_DIR)/%,\
- $(SOONG_FRAMEWORK_SRCS))
-
-# These are relative to frameworks/base
-# FRAMEWORKS_BASE_SUBDIRS comes from build/core/pathmap.mk
-files_to_document := \
- $(files_to_check_apis) \
- $(call find-other-java-files,\
- test-base/src \
- test-mock/src \
- test-runner/src)
-
-# These are relative to frameworks/base
-html_dirs := \
- $(FRAMEWORKS_BASE_SUBDIRS) \
- $(non_base_dirs) \
-
-# Common sources for doc check and api check
-common_src_files := \
- $(call find-other-html-files, $(html_dirs)) \
- $(addprefix ../../, $(libcore_to_document)) \
-
-# These are relative to frameworks/base
-framework_docs_LOCAL_SRC_FILES := \
- $(files_to_document) \
- $(common_src_files) \
-
-# These are relative to frameworks/base
-framework_docs_LOCAL_API_CHECK_SRC_FILES := \
- $(files_to_check_apis) \
- $(common_src_files) \
-
# This is used by ide.mk as the list of source files that are
# always included.
INTERNAL_SDK_SOURCE_DIRS := $(addprefix $(LOCAL_PATH)/,$(dirs_to_document))
-framework_docs_LOCAL_DROIDDOC_SOURCE_PATH := \
- $(FRAMEWORKS_BASE_JAVA_SRC_DIRS)
-
-framework_docs_LOCAL_SRCJARS := $(SOONG_FRAMEWORK_SRCJARS)
-
-framework_docs_LOCAL_GENERATED_SOURCES := \
- $(libcore_to_document_generated) \
- $(files_to_check_apis_generated) \
-
-framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES := \
- core-oj \
- core-libart \
- conscrypt \
- bouncycastle \
- okhttp \
- ext \
- framework \
- voip-common \
-
-# Platform docs can refer to Support Library APIs, but we don't actually build
-# them as part of the docs target, so we need to include them on the classpath.
-framework_docs_LOCAL_JAVA_LIBRARIES := \
- $(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES) \
- $(FRAMEWORKS_SUPPORT_JAVA_LIBRARIES)
-
-framework_docs_LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-framework_docs_LOCAL_DROIDDOC_HTML_DIR := docs/html
-# The since flag (-since N.xml API_LEVEL) is used to add API Level information
-# to the reference documentation. Must be in order of oldest to newest.
-#
-# Conscrypt (com.android.org.conscrypt) is an implementation detail and should
-# not be referenced in the documentation.
-framework_docs_LOCAL_DROIDDOC_OPTIONS := \
- -android \
- -knowntags ./frameworks/base/docs/knowntags.txt \
- -knowntags ./libcore/known_oj_tags.txt \
- -manifest ./frameworks/base/core/res/AndroidManifest.xml \
- -hidePackage com.android.internal \
- -hidePackage com.android.internal.util \
- -hidePackage com.android.okhttp \
- -hidePackage com.android.org.conscrypt \
- -hidePackage com.android.server
-
-# Convert an sdk level to a "since" argument.
-since-arg = -since $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/$(1)/public/api/android.$(2)) $(1)
-
-finalized_xml_sdks := $(call numerically_sort,\
- $(patsubst $(HISTORICAL_SDK_VERSIONS_ROOT)/%/public/api/android.xml,%,\
- $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/public/api/android.xml)))
-finalized_txt_sdks := $(call numerically_sort,\
- $(patsubst $(HISTORICAL_SDK_VERSIONS_ROOT)/%/public/api/android.txt,%,\
- $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/public/api/android.txt)))
-
-framework_docs_LOCAL_DROIDDOC_OPTIONS += $(foreach sdk,$(finalized_xml_sdks),$(call since-arg,$(sdk),xml))
-framework_docs_LOCAL_DROIDDOC_OPTIONS += $(foreach sdk,$(finalized_txt_sdks),$(call since-arg,$(sdk),txt))
-ifneq ($(PLATFORM_VERSION_CODENAME),REL)
- framework_docs_LOCAL_DROIDDOC_OPTIONS += \
- -since ./frameworks/base/api/current.txt $(PLATFORM_VERSION_CODENAME)
-endif
-framework_docs_LOCAL_DROIDDOC_OPTIONS += \
- -werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 \
- -overview $(LOCAL_PATH)/core/java/overview.html
-
-framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR:= \
- $(call intermediates-dir-for,JAVA_LIBRARIES,framework,,COMMON)
-
-framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:= \
- $(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
-
-framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES := \
- frameworks/base/docs/knowntags.txt \
- $(libcore_to_document_generated)
-
-samples_dir := development/samples/browseable
-
-# Whitelist of valid groups, used for default TOC grouping. Each sample must
-# belong to one (and only one) group. Assign samples to groups by setting
-# a sample.group var to one of these groups in the sample's _index.jd.
-sample_groups := -samplegroup Admin \
- -samplegroup Background \
- -samplegroup Connectivity \
- -samplegroup Content \
- -samplegroup Input \
- -samplegroup Media \
- -samplegroup Notification \
- -samplegroup RenderScript \
- -samplegroup Security \
- -samplegroup Sensors \
- -samplegroup System \
- -samplegroup Testing \
- -samplegroup UI \
- -samplegroup Views \
- -samplegroup Wearable
-
-## SDK version identifiers used in the published docs
- # major[.minor] version for current SDK. (full releases only)
-framework_docs_SDK_VERSION:=7.0
- # release version (ie "Release x") (full releases only)
-framework_docs_SDK_REL_ID:=1
-
-framework_docs_LOCAL_DROIDDOC_OPTIONS += \
- -hdf dac true \
- -hdf sdk.codename O \
- -hdf sdk.preview.version 1 \
- -hdf sdk.version $(framework_docs_SDK_VERSION) \
- -hdf sdk.rel.id $(framework_docs_SDK_REL_ID) \
- -hdf sdk.preview 0 \
- -resourcesdir $(LOCAL_PATH)/docs/html/reference/images/ \
- -resourcesoutdir reference/android/images/
-
-# Federate Support Library references against local API file.
-framework_docs_LOCAL_DROIDDOC_OPTIONS += \
- -federate SupportLib https://developer.android.com \
- -federationapi SupportLib prebuilts/sdk/current/support-api.txt
-
-# Federate AndroidX references against local API file.
-framework_docs_LOCAL_DROIDDOC_OPTIONS += \
- -federate AndroidX https://developer.android.com \
- -federationapi AndroidX prebuilts/sdk/current/androidx-api.txt
-
-# Get the highest numbered api txt for the given api level.
-# $(1): the api level (e.g. public, system)
-define highest_sdk_txt
-$(HISTORICAL_SDK_VERSIONS_ROOT)/$(lastword $(call numerically_sort, \
- $(patsubst \
- $(HISTORICAL_SDK_VERSIONS_ROOT)/%,\
- %,\
- $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/$(1)/api/android.txt)\
- ) \
-))
-endef
-
-# ==== Public API diff ===========================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(framework_docs_LOCAL_API_CHECK_SRC_FILES)
-LOCAL_GENERATED_SOURCES := $(framework_docs_LOCAL_GENERATED_SOURCES)
-LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
-LOCAL_JAVA_LIBRARIES := $(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES)
-LOCAL_MODULE_CLASS := $(framework_docs_LOCAL_MODULE_CLASS)
-LOCAL_ADDITIONAL_JAVA_DIR := $(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
-LOCAL_ADDITIONAL_DEPENDENCIES := \
- $(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) \
- $(INTERNAL_PLATFORM_API_FILE)
-
-LOCAL_MODULE := offline-sdk-referenceonly
-
-# Basename, because apidiff adds .txt internally.
-LOCAL_APIDIFF_OLDAPI := $(basename $(call highest_sdk_txt,public))
-LOCAL_APIDIFF_NEWAPI := $(LOCAL_PATH)/../../$(basename $(INTERNAL_PLATFORM_API_FILE))
-
-include $(BUILD_APIDIFF)
-
-# Hack to get diffs included in docs output
-out_zip := $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip
-$(out_zip): $(full_target)
-
-# ==== System API diff ===========================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(framework_docs_LOCAL_API_CHECK_SRC_FILES)
-LOCAL_GENERATED_SOURCES := $(framework_docs_LOCAL_GENERATED_SOURCES)
-LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
-LOCAL_JAVA_LIBRARIES := $(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES)
-LOCAL_MODULE_CLASS := $(framework_docs_LOCAL_MODULE_CLASS)
-LOCAL_ADDITIONAL_JAVA_DIR := $(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
-LOCAL_ADDITIONAL_DEPENDENCIES := \
- $(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) \
- $(INTERNAL_PLATFORM_SYSTEM_API_FILE)
-
-LOCAL_MODULE := offline-system-sdk-referenceonly
-
-# Basename, because apidiff adds .txt internally.
-LOCAL_APIDIFF_OLDAPI := $(basename $(call highest_sdk_txt,system))
-LOCAL_APIDIFF_NEWAPI := $(LOCAL_PATH)/../../$(basename $(INTERNAL_PLATFORM_SYSTEM_API_FILE))
-
-include $(BUILD_APIDIFF)
-
-# Hack to get diffs included in docs output
-out_zip := $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip
-$(out_zip): $(full_target)
-
$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE))
$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE))
+$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE):apistubs/android/public/api/android.txt)
+$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE):apistubs/android/system/api/android.txt)
+$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE):apistubs/android/test/api/android.txt)
# sdk.atree needs to copy the whole dir: $(OUT_DOCS)/offline-sdk to the final zip.
# So keep offline-sdk-timestamp target here, and unzip offline-sdk-docs.zip to
diff --git a/api/current.txt b/api/current.txt
index c1a1c52..6042b2f 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -5581,14 +5581,14 @@
ctor public Notification.WearableExtender(android.app.Notification);
method public android.app.Notification.WearableExtender addAction(android.app.Notification.Action);
method public android.app.Notification.WearableExtender addActions(java.util.List<android.app.Notification.Action>);
- method public android.app.Notification.WearableExtender addPage(android.app.Notification);
- method public android.app.Notification.WearableExtender addPages(java.util.List<android.app.Notification>);
+ method public deprecated android.app.Notification.WearableExtender addPage(android.app.Notification);
+ method public deprecated android.app.Notification.WearableExtender addPages(java.util.List<android.app.Notification>);
method public android.app.Notification.WearableExtender clearActions();
- method public android.app.Notification.WearableExtender clearPages();
+ method public deprecated android.app.Notification.WearableExtender clearPages();
method public android.app.Notification.WearableExtender clone();
method public android.app.Notification.Builder extend(android.app.Notification.Builder);
method public java.util.List<android.app.Notification.Action> getActions();
- method public android.graphics.Bitmap getBackground();
+ method public deprecated android.graphics.Bitmap getBackground();
method public java.lang.String getBridgeTag();
method public int getContentAction();
method public deprecated int getContentIcon();
@@ -5597,17 +5597,17 @@
method public deprecated int getCustomContentHeight();
method public deprecated int getCustomSizePreset();
method public java.lang.String getDismissalId();
- method public android.app.PendingIntent getDisplayIntent();
+ method public deprecated android.app.PendingIntent getDisplayIntent();
method public deprecated int getGravity();
- method public boolean getHintAmbientBigPicture();
+ method public deprecated boolean getHintAmbientBigPicture();
method public deprecated boolean getHintAvoidBackgroundClipping();
method public boolean getHintContentIntentLaunchesActivity();
method public deprecated boolean getHintHideIcon();
method public deprecated int getHintScreenTimeout();
method public deprecated boolean getHintShowBackgroundOnly();
- method public java.util.List<android.app.Notification> getPages();
+ method public deprecated java.util.List<android.app.Notification> getPages();
method public boolean getStartScrollBottom();
- method public android.app.Notification.WearableExtender setBackground(android.graphics.Bitmap);
+ method public deprecated android.app.Notification.WearableExtender setBackground(android.graphics.Bitmap);
method public android.app.Notification.WearableExtender setBridgeTag(java.lang.String);
method public android.app.Notification.WearableExtender setContentAction(int);
method public deprecated android.app.Notification.WearableExtender setContentIcon(int);
@@ -5616,23 +5616,23 @@
method public deprecated android.app.Notification.WearableExtender setCustomContentHeight(int);
method public deprecated android.app.Notification.WearableExtender setCustomSizePreset(int);
method public android.app.Notification.WearableExtender setDismissalId(java.lang.String);
- method public android.app.Notification.WearableExtender setDisplayIntent(android.app.PendingIntent);
+ method public deprecated android.app.Notification.WearableExtender setDisplayIntent(android.app.PendingIntent);
method public deprecated android.app.Notification.WearableExtender setGravity(int);
- method public android.app.Notification.WearableExtender setHintAmbientBigPicture(boolean);
+ method public deprecated android.app.Notification.WearableExtender setHintAmbientBigPicture(boolean);
method public deprecated android.app.Notification.WearableExtender setHintAvoidBackgroundClipping(boolean);
method public android.app.Notification.WearableExtender setHintContentIntentLaunchesActivity(boolean);
method public deprecated android.app.Notification.WearableExtender setHintHideIcon(boolean);
method public deprecated android.app.Notification.WearableExtender setHintScreenTimeout(int);
method public deprecated android.app.Notification.WearableExtender setHintShowBackgroundOnly(boolean);
method public android.app.Notification.WearableExtender setStartScrollBottom(boolean);
- field public static final int SCREEN_TIMEOUT_LONG = -1; // 0xffffffff
- field public static final int SCREEN_TIMEOUT_SHORT = 0; // 0x0
- field public static final int SIZE_DEFAULT = 0; // 0x0
- field public static final int SIZE_FULL_SCREEN = 5; // 0x5
- field public static final int SIZE_LARGE = 4; // 0x4
- field public static final int SIZE_MEDIUM = 3; // 0x3
- field public static final int SIZE_SMALL = 2; // 0x2
- field public static final int SIZE_XSMALL = 1; // 0x1
+ field public static final deprecated int SCREEN_TIMEOUT_LONG = -1; // 0xffffffff
+ field public static final deprecated int SCREEN_TIMEOUT_SHORT = 0; // 0x0
+ field public static final deprecated int SIZE_DEFAULT = 0; // 0x0
+ field public static final deprecated int SIZE_FULL_SCREEN = 5; // 0x5
+ field public static final deprecated int SIZE_LARGE = 4; // 0x4
+ field public static final deprecated int SIZE_MEDIUM = 3; // 0x3
+ field public static final deprecated int SIZE_SMALL = 2; // 0x2
+ field public static final deprecated int SIZE_XSMALL = 1; // 0x1
field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
}
@@ -7734,7 +7734,9 @@
method public boolean isMultipleAdvertisementSupported();
method public boolean isOffloadedFilteringSupported();
method public boolean isOffloadedScanBatchingSupported();
+ method public android.bluetooth.BluetoothServerSocket listenUsingInsecureL2capChannel() throws java.io.IOException;
method public android.bluetooth.BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(java.lang.String, java.util.UUID) throws java.io.IOException;
+ method public android.bluetooth.BluetoothServerSocket listenUsingL2capChannel() throws java.io.IOException;
method public android.bluetooth.BluetoothServerSocket listenUsingRfcommWithServiceRecord(java.lang.String, java.util.UUID) throws java.io.IOException;
method public boolean setName(java.lang.String);
method public boolean startDiscovery();
@@ -8102,7 +8104,9 @@
method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int, int);
method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int, int, android.os.Handler);
method public boolean createBond();
+ method public android.bluetooth.BluetoothSocket createInsecureL2capChannel(int) throws java.io.IOException;
method public android.bluetooth.BluetoothSocket createInsecureRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
+ method public android.bluetooth.BluetoothSocket createL2capChannel(int) throws java.io.IOException;
method public android.bluetooth.BluetoothSocket createRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
method public int describeContents();
method public boolean fetchUuidsWithSdp();
@@ -8518,6 +8522,7 @@
method public android.bluetooth.BluetoothSocket accept() throws java.io.IOException;
method public android.bluetooth.BluetoothSocket accept(int) throws java.io.IOException;
method public void close() throws java.io.IOException;
+ method public int getPsm();
}
public final class BluetoothSocket implements java.io.Closeable {
@@ -14010,11 +14015,15 @@
method public android.graphics.Paint.Style getStyle();
method public android.graphics.Paint.Align getTextAlign();
method public void getTextBounds(java.lang.String, int, int, android.graphics.Rect);
+ method public void getTextBounds(java.lang.CharSequence, int, int, android.graphics.Rect);
method public void getTextBounds(char[], int, int, android.graphics.Rect);
method public java.util.Locale getTextLocale();
method public android.os.LocaleList getTextLocales();
method public void getTextPath(char[], int, int, float, float, android.graphics.Path);
method public void getTextPath(java.lang.String, int, int, float, float, android.graphics.Path);
+ method public float getTextRunAdvances(char[], int, int, int, int, boolean, float[], int);
+ method public int getTextRunCursor(char[], int, int, boolean, int, int);
+ method public int getTextRunCursor(java.lang.CharSequence, int, int, boolean, int, int);
method public float getTextScaleX();
method public float getTextSize();
method public float getTextSkewX();
@@ -14081,6 +14090,11 @@
method public void setWordSpacing(float);
method public android.graphics.Xfermode setXfermode(android.graphics.Xfermode);
field public static final int ANTI_ALIAS_FLAG = 1; // 0x1
+ field public static final int CURSOR_AFTER = 0; // 0x0
+ field public static final int CURSOR_AT = 4; // 0x4
+ field public static final int CURSOR_AT_OR_AFTER = 1; // 0x1
+ field public static final int CURSOR_AT_OR_BEFORE = 3; // 0x3
+ field public static final int CURSOR_BEFORE = 2; // 0x2
field public static final int DEV_KERN_TEXT_FLAG = 256; // 0x100
field public static final int DITHER_FLAG = 4; // 0x4
field public static final int EMBEDDED_BITMAP_TEXT_FLAG = 1024; // 0x400
@@ -18676,6 +18690,37 @@
field public static final int WORD_NUMBER_LIMIT = 200; // 0xc8
}
+ public abstract class CaseMap {
+ method public static android.icu.text.CaseMap.Fold fold();
+ method public abstract android.icu.text.CaseMap omitUnchangedText();
+ method public static android.icu.text.CaseMap.Lower toLower();
+ method public static android.icu.text.CaseMap.Title toTitle();
+ method public static android.icu.text.CaseMap.Upper toUpper();
+ }
+
+ public static final class CaseMap.Fold extends android.icu.text.CaseMap {
+ method public <A extends java.lang.Appendable> A apply(java.lang.CharSequence, A, android.icu.text.Edits);
+ method public android.icu.text.CaseMap.Fold omitUnchangedText();
+ method public android.icu.text.CaseMap.Fold turkic();
+ }
+
+ public static final class CaseMap.Lower extends android.icu.text.CaseMap {
+ method public <A extends java.lang.Appendable> A apply(java.util.Locale, java.lang.CharSequence, A, android.icu.text.Edits);
+ method public android.icu.text.CaseMap.Lower omitUnchangedText();
+ }
+
+ public static final class CaseMap.Title extends android.icu.text.CaseMap {
+ method public <A extends java.lang.Appendable> A apply(java.util.Locale, android.icu.text.BreakIterator, java.lang.CharSequence, A, android.icu.text.Edits);
+ method public android.icu.text.CaseMap.Title noBreakAdjustment();
+ method public android.icu.text.CaseMap.Title noLowercase();
+ method public android.icu.text.CaseMap.Title omitUnchangedText();
+ }
+
+ public static final class CaseMap.Upper extends android.icu.text.CaseMap {
+ method public <A extends java.lang.Appendable> A apply(java.util.Locale, java.lang.CharSequence, A, android.icu.text.Edits);
+ method public android.icu.text.CaseMap.Upper omitUnchangedText();
+ }
+
public final class CollationElementIterator {
method public int getMaxExpansion(int);
method public int getOffset();
@@ -19341,6 +19386,30 @@
enum_constant public static final android.icu.text.DisplayContext.Type SUBSTITUTE_HANDLING;
}
+ public final class Edits {
+ ctor public Edits();
+ method public void addReplace(int, int);
+ method public void addUnchanged(int);
+ method public android.icu.text.Edits.Iterator getCoarseChangesIterator();
+ method public android.icu.text.Edits.Iterator getCoarseIterator();
+ method public android.icu.text.Edits.Iterator getFineChangesIterator();
+ method public android.icu.text.Edits.Iterator getFineIterator();
+ method public boolean hasChanges();
+ method public int lengthDelta();
+ method public void reset();
+ }
+
+ public static final class Edits.Iterator {
+ method public int destinationIndex();
+ method public boolean findSourceIndex(int);
+ method public boolean hasChange();
+ method public int newLength();
+ method public boolean next();
+ method public int oldLength();
+ method public int replacementIndex();
+ method public int sourceIndex();
+ }
+
public abstract class IDNA {
method public static android.icu.text.IDNA getUTS46Instance(int);
method public abstract java.lang.StringBuilder labelToASCII(java.lang.CharSequence, java.lang.StringBuilder, android.icu.text.IDNA.Info);
@@ -39859,6 +39928,7 @@
method public int getDisabledShowContext();
method public static boolean isActiveService(android.content.Context, android.content.ComponentName);
method public android.os.IBinder onBind(android.content.Intent);
+ method public java.util.Set<java.lang.String> onGetSupportedVoiceActions(java.util.Set<java.lang.String>);
method public void onLaunchVoiceAssistFromKeyguard();
method public void onReady();
method public void onShutdown();
@@ -49697,6 +49767,7 @@
method public boolean isScrollable();
method public boolean isSelected();
method public boolean isShowingHintText();
+ method public boolean isTextEntryKey();
method public boolean isVisibleToUser();
method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View);
method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View, int);
@@ -49758,6 +49829,7 @@
method public void setSource(android.view.View);
method public void setSource(android.view.View, int);
method public void setText(java.lang.CharSequence);
+ method public void setTextEntryKey(boolean);
method public void setTextSelection(int, int);
method public void setTooltipText(java.lang.CharSequence);
method public void setTraversalAfter(android.view.View);
diff --git a/api/system-current.txt b/api/system-current.txt
index d29b216..8f7606a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -91,6 +91,7 @@
field public static final java.lang.String KILL_UID = "android.permission.KILL_UID";
field public static final java.lang.String LOCAL_MAC_ADDRESS = "android.permission.LOCAL_MAC_ADDRESS";
field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
+ field public static final java.lang.String LOCK_DEVICE = "android.permission.LOCK_DEVICE";
field public static final java.lang.String LOOP_RADIO = "android.permission.LOOP_RADIO";
field public static final java.lang.String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
field public static final java.lang.String MANAGE_APP_OPS_RESTRICTIONS = "android.permission.MANAGE_APP_OPS_RESTRICTIONS";
@@ -155,6 +156,7 @@
field public static final java.lang.String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER";
field public static final java.lang.String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION";
field public static final java.lang.String REMOVE_DRM_CERTIFICATES = "android.permission.REMOVE_DRM_CERTIFICATES";
+ field public static final java.lang.String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
field public static final java.lang.String RESTRICTED_VR_ACCESS = "android.permission.RESTRICTED_VR_ACCESS";
field public static final java.lang.String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
field public static final java.lang.String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS";
@@ -217,6 +219,14 @@
field public static final int config_sendPackageName = 17891328; // 0x1110000
}
+ public static final class R.dimen {
+ field public static final int config_restricted_icon_size = 17104903; // 0x1050007
+ }
+
+ public static final class R.drawable {
+ field public static final int ic_info = 17301684; // 0x10800b4
+ }
+
public static final class R.raw {
field public static final int loaderror = 17825792; // 0x1100000
field public static final int nodomain = 17825793; // 0x1100001
@@ -355,11 +365,6 @@
field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.PackageOps> CREATOR;
}
- public final class AutomaticZenRule implements android.os.Parcelable {
- ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean, long);
- ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean, long, android.service.notification.ZenPolicy);
- }
-
public class BroadcastOptions {
method public static android.app.BroadcastOptions makeBasic();
method public void setDontSendToRestrictedApps(boolean);
@@ -1113,6 +1118,7 @@
public class PackageItemInfo {
method public deprecated java.lang.CharSequence loadSafeLabel(android.content.pm.PackageManager);
method public java.lang.CharSequence loadSafeLabel(android.content.pm.PackageManager, float, int);
+ method public static void setForceSafeLabels(boolean);
field public static final int SAFE_LABEL_FLAG_FIRST_LINE = 4; // 0x4
field public static final int SAFE_LABEL_FLAG_SINGLE_LINE = 2; // 0x2
field public static final int SAFE_LABEL_FLAG_TRIM = 1; // 0x1
@@ -4193,6 +4199,7 @@
method public final void attachBaseContext(android.content.Context);
method public final android.os.IBinder onBind(android.content.Intent);
method public abstract java.util.List<android.content.pm.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(java.lang.String);
+ method public abstract void onRevokeRuntimePermission(java.lang.String, java.lang.String);
field public static final java.lang.String SERVICE_INTERFACE = "android.permissionpresenterservice.RuntimePermissionPresenterService";
}
@@ -5398,7 +5405,6 @@
}
public class TelephonyManager {
- method public deprecated void answerRingingCall();
method public deprecated void call(java.lang.String, java.lang.String);
method public int checkCarrierPrivilegesForPackage(java.lang.String);
method public int checkCarrierPrivilegesForPackageAnyPhone(java.lang.String);
@@ -5406,7 +5412,6 @@
method public boolean disableDataConnectivity();
method public boolean enableDataConnectivity();
method public void enableVideoCalling(boolean);
- method public deprecated boolean endCall();
method public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
@@ -6588,6 +6593,10 @@
package android.view {
+ public abstract class Window {
+ method public void addPrivateFlags(int);
+ }
+
public abstract interface WindowManager implements android.view.ViewManager {
method public abstract android.graphics.Region getCurrentImeTouchRegion();
}
@@ -6595,6 +6604,7 @@
public static class WindowManager.LayoutParams extends android.view.ViewGroup.LayoutParams implements android.os.Parcelable {
method public final long getUserActivityTimeout();
method public final void setUserActivityTimeout(long);
+ field public static final int PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 524288; // 0x80000
}
}
diff --git a/api/system-removed.txt b/api/system-removed.txt
index b88c760..9012c33 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -143,3 +143,12 @@
}
+package android.telephony {
+
+ public class TelephonyManager {
+ method public deprecated void answerRingingCall();
+ method public deprecated boolean endCall();
+ }
+
+}
+
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 36e51b9..12fb4a3 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -470,7 +470,8 @@
onExecute(provider);
} finally {
if (provider != null) {
- activityManager.removeContentProviderExternal(providerName, token);
+ activityManager.removeContentProviderExternalAsUser(
+ providerName, token, mUserId);
}
}
} catch (Exception e) {
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 87799b3..cd48af9 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -407,7 +407,19 @@
WorkerThreadSection::~WorkerThreadSection() {}
+void sigpipe_handler(int signum) {
+ if (signum == SIGPIPE) {
+ ALOGE("Wrote to a broken pipe\n");
+ } else {
+ ALOGE("Received unexpected signal: %d\n", signum);
+ }
+}
+
static void* worker_thread_func(void* cookie) {
+ // Don't crash the service if we write to a closed pipe (which can happen if
+ // dumping times out).
+ signal(SIGPIPE, sigpipe_handler);
+
WorkerThreadData* data = (WorkerThreadData*)cookie;
status_t err = data->section->BlockingCall(data->pipe.writeFd().get());
@@ -486,6 +498,7 @@
}
}
}
+
write_section_stats(requests->sectionStats(this->id), buffer);
if (timedOut || buffer.timedOut()) {
ALOGW("[%s] timed out", this->name.string());
@@ -773,7 +786,10 @@
}
}
gLastLogsRetrieved[mLogID] = lastTimestamp;
- proto.flush(pipeWriteFd);
+ if (!proto.flush(pipeWriteFd) && errno == EPIPE) {
+ ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
+ return EPIPE;
+ }
return NO_ERROR;
}
@@ -875,7 +891,7 @@
break;
}
if (cStatus != NO_ERROR) {
- ALOGE("TombstoneSection '%s' child had an issue: %s\n", this->name.string(), strerror(-cStatus));
+ ALOGE("[%s] child had an issue: %s\n", this->name.string(), strerror(-cStatus));
}
auto dump = std::make_unique<char[]>(buffer.size());
@@ -894,7 +910,13 @@
dumpPipe.readFd().reset();
}
- proto.flush(pipeWriteFd);
+ if (!proto.flush(pipeWriteFd) && errno == EPIPE) {
+ ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
+ if (err != NO_ERROR) {
+ return EPIPE;
+ }
+ }
+
return err;
}
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 11029e7..d334f96 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -206,7 +206,14 @@
result = outBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &base);
- if (base == NULL) {
+ if (base == nullptr || result != NO_ERROR) {
+ String8 reason;
+ if (base == nullptr) {
+ reason = "Failed to write to buffer";
+ } else {
+ reason.appendFormat("Error Code: %d", result);
+ }
+ fprintf(stderr, "Failed to take screenshot (%s)\n", reason.c_str());
close(fd);
return 1;
}
diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp
index 7b6d29b..fc1a61c 100644
--- a/cmds/statsd/src/FieldValue.cpp
+++ b/cmds/statsd/src/FieldValue.cpp
@@ -147,6 +147,9 @@
case STRING:
str_value = from.str_value;
break;
+ case STORAGE:
+ storage_value = from.storage_value;
+ break;
default:
break;
}
@@ -164,6 +167,8 @@
return std::to_string(double_value) + "[D]";
case STRING:
return str_value + "[S]";
+ case STORAGE:
+ return "bytes of size " + std::to_string(storage_value.size()) + "[ST]";
default:
return "[UNKNOWN]";
}
@@ -183,6 +188,8 @@
return double_value == that.double_value;
case STRING:
return str_value == that.str_value;
+ case STORAGE:
+ return storage_value == that.storage_value;
default:
return false;
}
@@ -201,6 +208,8 @@
return double_value != that.double_value;
case STRING:
return str_value != that.str_value;
+ case STORAGE:
+ return storage_value != that.storage_value;
default:
return false;
}
@@ -220,6 +229,8 @@
return double_value < that.double_value;
case STRING:
return str_value < that.str_value;
+ case STORAGE:
+ return storage_value < that.storage_value;
default:
return false;
}
@@ -239,6 +250,8 @@
return double_value > that.double_value;
case STRING:
return str_value > that.str_value;
+ case STORAGE:
+ return storage_value > that.storage_value;
default:
return false;
}
@@ -258,6 +271,8 @@
return double_value >= that.double_value;
case STRING:
return str_value >= that.str_value;
+ case STORAGE:
+ return storage_value >= that.storage_value;
default:
return false;
}
@@ -274,6 +289,11 @@
return v;
}
+ if (type == STORAGE) {
+ ALOGE("Can't operate on storage value type");
+ return v;
+ }
+
switch (type) {
case INT:
v.setInt(int_value - that.int_value);
@@ -311,6 +331,9 @@
case STRING:
str_value = that.str_value;
break;
+ case STORAGE:
+ storage_value = that.storage_value;
+ break;
default:
break;
}
@@ -326,6 +349,10 @@
ALOGE("Can't operate on string value type");
return *this;
}
+ if (type == STORAGE) {
+ ALOGE("Can't operate on storage value type");
+ return *this;
+ }
switch (type) {
case INT:
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index b1b885e..77163f9 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -32,7 +32,7 @@
const int32_t kClearLastBitDeco = 0x7f;
const int32_t kClearAllPositionMatcherMask = 0xffff00ff;
-enum Type { UNKNOWN, INT, LONG, FLOAT, DOUBLE, STRING };
+enum Type { UNKNOWN, INT, LONG, FLOAT, DOUBLE, STRING, STORAGE };
int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth);
@@ -293,6 +293,11 @@
type = STRING;
}
+ Value(const std::vector<uint8_t>& v) {
+ storage_value = v;
+ type = STORAGE;
+ }
+
void setInt(int32_t v) {
int_value = v;
type = INT;
@@ -320,6 +325,7 @@
double double_value;
};
std::string str_value;
+ std::vector<uint8_t> storage_value;
Type type;
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 6ab4dd9..b8f19ee 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -133,6 +133,9 @@
VibratorStateChanged vibrator_state_changed = 84;
DeferredJobStatsReported deferred_job_stats_reported = 85;
ThermalThrottlingStateChanged thermal_throttling = 86;
+ FingerprintAcquired fingerprint_acquired = 87;
+ FingerprintAuthenticated fingerprint_authenticated = 88;
+ FingerprintErrorOccurred fingerprint_error_occurred = 89;
}
// Pulled events will start at field 10000.
@@ -167,7 +170,9 @@
DirectoryUsage directory_usage = 10026;
AppSize app_size = 10027;
CategorySize category_size = 10028;
- android.service.procstats.ProcessStatsSectionProto proc_stats = 10029;
+ BatteryVoltage battery_voltage = 10030;
+ NumFingerprints num_fingerprints = 10031;
+ ProcStats proc_stats = 10029;
}
// DO NOT USE field numbers above 100,000 in AOSP. Field numbers above
@@ -431,9 +436,9 @@
* Logs reporting of a ble scan finding results.
*
* Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ * packages/apps/Bluetooth/src/com/android/bluetooth/gatt/AppScanStats.java
*/
-// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats).
+// TODO: Consider also tracking per-scanner-id.
message BleScanResultReceived {
repeated AttributionNode attribution_node = 1;
@@ -1831,6 +1836,60 @@
optional android.os.statsd.EventType event_id = 2;
}
+/**
+ * Logs when a fingerprint acquire event occurs.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+ */
+message FingerprintAcquired {
+ // The associated user. Eg: 0 for owners, 10+ for others.
+ // Defined in android/os/UserHandle.java
+ optional int32 user = 1;
+ // If this acquire is for a crypto fingerprint.
+ // e.g. Secure purchases, unlock password storage.
+ optional bool is_crypto = 2;
+}
+
+/**
+ * Logs when a fingerprint authentication event occurs.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+ */
+message FingerprintAuthenticated {
+ // The associated user. Eg: 0 for owners, 10+ for others.
+ // Defined in android/os/UserHandle.java
+ optional int32 user = 1;
+ // If this authentication is for a crypto fingerprint.
+ // e.g. Secure purchases, unlock password storage.
+ optional bool is_crypto = 2;
+ // Whether or not this authentication was successful.
+ optional bool is_authenticated = 3;
+}
+
+/**
+ * Logs when a fingerprint error occurs.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+ */
+message FingerprintErrorOccurred {
+ // The associated user. Eg: 0 for owners, 10+ for others.
+ // Defined in android/os/UserHandle.java
+ optional int32 user = 1;
+ // If this error is for a crypto fingerprint.
+ // e.g. Secure purchases, unlock password storage.
+ optional bool is_crypto = 2;
+
+ enum Error {
+ UNKNOWN = 0;
+ LOCKOUT = 1;
+ PERMANENT_LOCKOUT = 2;
+ }
+ // The type of error.
+ optional Error error = 3;
+}
//////////////////////////////////////////////////////////////////////
// Pulled atoms below this line //
//////////////////////////////////////////////////////////////////////
@@ -2176,6 +2235,16 @@
}
/**
+ * Pulls battery voltage.
+ * Pulled from:
+ * frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+ */
+message BatteryVoltage {
+ // The voltage of the battery, in millivolts.
+ optional int32 voltage_mV = 1;
+}
+
+/**
* Pulls the temperature of various parts of the device.
* The units are tenths of a degree Celsius. Eg: 30.3C is reported as 303.
*
@@ -2261,6 +2330,14 @@
optional int64 exception_count = 2;
}
+/**
+ * Pulls the statistics of message dispatching on HandlerThreads.
+ *
+ * Looper stats will be reset every time the data is pulled. It means it can only be pulled by one
+ * config on the device.
+ *
+ * Next tag: 11
+ */
message LooperStats {
// Currently not collected and always set to 0.
optional int32 uid = 1 [(is_uid) = true];
@@ -2304,8 +2381,11 @@
// Total CPU usage of all processed message.
// Average can be computed using recorded_total_cpu_micros /
// recorded_message_count. Total can be computed using
- // recorded_total_cpu_micros / recorded_message_count * call_count.
+ // recorded_total_cpu_micros / recorded_message_count * message_count.
optional int64 recorded_total_cpu_micros = 9;
+
+ // True if the screen was interactive PowerManager#isInteractive at the end of the call.
+ optional bool screen_interactive = 10;
}
/**
@@ -2386,3 +2466,23 @@
// Uses System.currentTimeMillis(), which is wall clock time.
optional int64 cache_time_millis = 3;
}
+
+/**
+ * Pulls the number of fingerprints for each user.
+ *
+ * Pulled from StatsCompanionService, which queries FingerprintManager.
+ */
+message NumFingerprints {
+ // The associated user. Eg: 0 for owners, 10+ for others.
+ // Defined in android/os/UserHandle.java
+ optional int32 user = 1;
+ // Number of fingerprints registered to that user.
+ optional int32 num_fingerprints = 2;
+}
+
+/**
+ * Pulled from ProcessStatsService.java
+ */
+message ProcStats {
+ optional android.service.procstats.ProcessStatsSectionProto proc_stats_section = 1;
+}
diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
index ae97d7a..ae2cf74 100644
--- a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
@@ -67,6 +67,7 @@
data->clear();
bool result_success = true;
+ // Get the data from the Health HAL (hardware/interfaces/health/1.0/types.hal).
Return<void> ret = gHealthHal->getHealthInfo([&](Result r, HealthInfo v) {
if (r != Result::SUCCESS) {
result_success = false;
@@ -84,6 +85,12 @@
ptr->write(v.legacy.batteryFullCharge);
ptr->init();
data->push_back(ptr);
+ } else if (mTagId == android::util::BATTERY_VOLTAGE) {
+ auto ptr = make_shared<LogEvent>(android::util::BATTERY_VOLTAGE,
+ wallClockTimestampNs, elapsedTimestampNs);
+ ptr->write(v.legacy.batteryVoltage);
+ ptr->init();
+ data->push_back(ptr);
} else {
ALOGE("Unsupported tag in ResourceHealthManagerPuller: %d", mTagId);
}
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
index d953f50..6d7bba0 100644
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
+++ b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
@@ -36,8 +36,6 @@
namespace os {
namespace statsd {
-const int kLogMsgHeaderSize = 28;
-
// The reading and parsing are implemented in Java. It is not difficult to port over. But for now
// let StatsCompanionService handle that and send the data back.
StatsCompanionServicePuller::StatsCompanionServicePuller(int tagId) : StatsPuller(tagId) {
@@ -56,20 +54,12 @@
vector<StatsLogEventWrapper> returned_value;
Status status = statsCompanionServiceCopy->pullData(mTagId, &returned_value);
if (!status.isOk()) {
- ALOGW("error pulling for %d", mTagId);
+ ALOGW("StatsCompanionServicePuller::pull failed to pull for %d", mTagId);
return false;
}
data->clear();
- int32_t timestampSec = getWallClockSec();
for (const StatsLogEventWrapper& it : returned_value) {
- log_msg tmp;
- tmp.entry_v1.len = it.bytes.size();
- // Manually set the header size to 28 bytes to match the pushed log events.
- tmp.entry.hdr_size = kLogMsgHeaderSize;
- tmp.entry_v1.sec = timestampSec;
- // And set the received bytes starting after the 28 bytes reserved for header.
- std::copy(it.bytes.begin(), it.bytes.end(), tmp.buf + kLogMsgHeaderSize);
- data->push_back(make_shared<LogEvent>(tmp));
+ data->push_back(make_shared<LogEvent>(it));
}
VLOG("StatsCompanionServicePuller::pull succeeded for %d", mTagId);
return true;
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index f6ba0b6..5a0172b 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -161,6 +161,12 @@
{},
1 * NS_PER_SEC,
new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
+ // battery_voltage
+ {android::util::BATTERY_VOLTAGE,
+ {{},
+ {},
+ 1 * NS_PER_SEC,
+ new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
// process_memory_state
{android::util::PROCESS_MEMORY_STATE,
{{4, 5, 6, 7, 8},
@@ -184,33 +190,30 @@
// looper_stats
{android::util::LOOPER_STATS,
{{5, 6, 7, 8, 9},
- {2, 3, 4},
+ {2, 3, 4, 10},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::LOOPER_STATS)}},
// Disk Stats
{android::util::DISK_STATS,
- {{},
- {},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::DISK_STATS)}},
+ {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_STATS)}},
// Directory usage
{android::util::DIRECTORY_USAGE,
- {{},
- {},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
+ {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
// Size of app's code, data, and cache
{android::util::APP_SIZE,
- {{},
- {},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::APP_SIZE)}},
+ {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::APP_SIZE)}},
// Size of specific categories of files. Eg. Music.
{android::util::CATEGORY_SIZE,
{{},
{},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
+ // Number of fingerprints registered to each user.
+ {android::util::NUM_FINGERPRINTS,
+ {{},
+ {},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS)}},
};
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index cf04ee3..f9f1b38 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -41,6 +41,44 @@
}
}
+LogEvent::LogEvent(const StatsLogEventWrapper& statsLogEventWrapper) {
+ mTagId = statsLogEventWrapper.getTagId();
+ mLogdTimestampNs = statsLogEventWrapper.getWallClockTimeNs();
+ mElapsedTimestampNs = statsLogEventWrapper.getElapsedRealTimeNs();
+ mLogUid = 0;
+ for (int i = 0; i < (int)statsLogEventWrapper.getElements().size(); i++) {
+ Field field(statsLogEventWrapper.getTagId(), getSimpleField(i + 1));
+ switch (statsLogEventWrapper.getElements()[i].type) {
+ case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::INT:
+ mValues.push_back(
+ FieldValue(field, Value(statsLogEventWrapper.getElements()[i].int_value)));
+ break;
+ case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::LONG:
+ mValues.push_back(
+ FieldValue(field, Value(statsLogEventWrapper.getElements()[i].long_value)));
+ break;
+ case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::FLOAT:
+ mValues.push_back(FieldValue(
+ field, Value(statsLogEventWrapper.getElements()[i].float_value)));
+ break;
+ case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::DOUBLE:
+ mValues.push_back(FieldValue(
+ field, Value(statsLogEventWrapper.getElements()[i].double_value)));
+ break;
+ case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::STRING:
+ mValues.push_back(
+ FieldValue(field, Value(statsLogEventWrapper.getElements()[i].str_value)));
+ break;
+ case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::STORAGE:
+ mValues.push_back(FieldValue(
+ field, Value(statsLogEventWrapper.getElements()[i].storage_value)));
+ break;
+ default:
+ break;
+ }
+ }
+}
+
LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs) {
mLogdTimestampNs = wallClockTimestampNs;
mTagId = tagId;
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 2ee6bdf..9ef0bf4 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -18,6 +18,7 @@
#include "FieldValue.h"
+#include <android/os/StatsLogEventWrapper.h>
#include <android/util/ProtoOutputStream.h>
#include <log/log_event_list.h>
#include <log/log_read.h>
@@ -61,6 +62,8 @@
*/
explicit LogEvent(log_msg& msg);
+ explicit LogEvent(const StatsLogEventWrapper& statsLogEventWrapper);
+
/**
* Constructs a LogEvent with synthetic data for testing. Must call init() before reading.
*/
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index a0ab3e4..805e583 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -322,6 +322,11 @@
case STRING:
protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value);
break;
+ case STORAGE:
+ protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum,
+ (const char*)dim.mValue.storage_value.data(),
+ dim.mValue.storage_value.size());
+ break;
default:
break;
}
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
index 950a258..455e4bb 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
@@ -84,7 +84,8 @@
cursor.close();
}
if (provider != null) {
- activityManager.removeContentProviderExternal(providerName, token);
+ activityManager.removeContentProviderExternalAsUser(providerName, token,
+ UserHandle.USER_SYSTEM);
}
}
} catch (RemoteException e) {
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index b683138..be9ccec 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -11067,6 +11067,7 @@
HPLjava/lang/Long;->valueOf(Ljava/lang/String;I)Ljava/lang/Long;
HPLjava/lang/Math;->addExact(JJ)J
HPLjava/lang/Math;->scalb(FI)F
+HPLjava/lang/Object;->wait()V
HPLjava/lang/StackTraceElement;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V
HPLjava/lang/String;->copyValueOf([C)Ljava/lang/String;
HPLjava/lang/String;->join(Ljava/lang/CharSequence;[Ljava/lang/CharSequence;)Ljava/lang/String;
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 8c347fc..0472461 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -2134,6 +2134,7 @@
Lcom/android/internal/telephony/SmsHeader$ConcatRef;-><init>()V
Lcom/android/internal/telephony/SmsHeader$PortAddrs;-><init>()V
Lcom/android/internal/telephony/SmsMessageBase;-><init>()V
+Lcom/android/internal/telephony/uicc/IccUtils;->bytesToHexString([B)Ljava/lang/String;
Lcom/android/internal/textservice/ITextServicesManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Lcom/android/internal/util/HexDump;->toHexString([BZ)Ljava/lang/String;
Lcom/android/internal/view/BaseIWindow;-><init>()V
@@ -2226,6 +2227,7 @@
Lcom/android/org/conscrypt/OpenSSLKey;->fromPrivateKey(Ljava/security/PrivateKey;)Lcom/android/org/conscrypt/OpenSSLKey;
Lcom/android/org/conscrypt/OpenSSLKey;->getNativeRef()Lcom/android/org/conscrypt/NativeRef$EVP_PKEY;
Lcom/android/org/conscrypt/OpenSSLKey;->getPublicKey()Ljava/security/PublicKey;
+Lcom/android/org/conscrypt/OpenSSLProvider;-><init>()V
Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getAlpnSelectedProtocol()[B
Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getChannelId()[B
Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getHostname()Ljava/lang/String;
@@ -2247,6 +2249,108 @@
Lcom/android/org/conscrypt/TrustedCertificateStore;->getCertificateChain(Ljava/security/cert/X509Certificate;)Ljava/util/List;
Lcom/android/org/conscrypt/TrustManagerImpl;-><init>(Ljava/security/KeyStore;)V
Lcom/android/org/conscrypt/TrustManagerImpl;->checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljava/lang/String;)Ljava/util/List;
+Lcom/google/android/mms/ContentType;->getAudioTypes()Ljava/util/ArrayList;
+Lcom/google/android/mms/ContentType;->getImageTypes()Ljava/util/ArrayList;
+Lcom/google/android/mms/ContentType;->getVideoTypes()Ljava/util/ArrayList;
+Lcom/google/android/mms/ContentType;->isAudioType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isDrmType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isImageType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isTextType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isVideoType(Ljava/lang/String;)Z
+Lcom/google/android/mms/MmsException;-><init>()V
+Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/String;)V
+Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/Throwable;)V
+Lcom/google/android/mms/pdu/AcknowledgeInd;-><init>(I[B)V
+Lcom/google/android/mms/pdu/CharacterSets;->getMimeName(I)Ljava/lang/String;
+Lcom/google/android/mms/pdu/DeliveryInd;->getMessageId()[B
+Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(I[B)V
+Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(Ljava/lang/String;)V
+Lcom/google/android/mms/pdu/EncodedStringValue;-><init>([B)V
+Lcom/google/android/mms/pdu/EncodedStringValue;->concat([Lcom/google/android/mms/pdu/EncodedStringValue;)Ljava/lang/String;
+Lcom/google/android/mms/pdu/EncodedStringValue;->encodeStrings([Ljava/lang/String;)[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/EncodedStringValue;->getString()Ljava/lang/String;
+Lcom/google/android/mms/pdu/GenericPdu;->getMessageType()I
+Lcom/google/android/mms/pdu/GenericPdu;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getBody()Lcom/google/android/mms/pdu/PduBody;
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getDate()J
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getPriority()I
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getSubject()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getTo()[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setBody(Lcom/google/android/mms/pdu/PduBody;)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setDate(J)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setPriority(I)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setSubject(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/NotificationInd;->getContentLocation()[B
+Lcom/google/android/mms/pdu/NotificationInd;->getExpiry()J
+Lcom/google/android/mms/pdu/NotificationInd;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/NotificationInd;->getMessageClass()[B
+Lcom/google/android/mms/pdu/NotificationInd;->getMessageSize()J
+Lcom/google/android/mms/pdu/NotificationInd;->getSubject()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/NotificationInd;->getTransactionId()[B
+Lcom/google/android/mms/pdu/NotificationInd;->setContentLocation([B)V
+Lcom/google/android/mms/pdu/NotifyRespInd;-><init>(I[BI)V
+Lcom/google/android/mms/pdu/PduBody;-><init>()V
+Lcom/google/android/mms/pdu/PduBody;->addPart(ILcom/google/android/mms/pdu/PduPart;)V
+Lcom/google/android/mms/pdu/PduBody;->addPart(Lcom/google/android/mms/pdu/PduPart;)Z
+Lcom/google/android/mms/pdu/PduBody;->getPart(I)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByContentId(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByContentLocation(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByFileName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartsNum()I
+Lcom/google/android/mms/pdu/PduComposer;-><init>(Landroid/content/Context;Lcom/google/android/mms/pdu/GenericPdu;)V
+Lcom/google/android/mms/pdu/PduComposer;->make()[B
+Lcom/google/android/mms/pdu/PduParser;->parse()Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/pdu/PduPart;-><init>()V
+Lcom/google/android/mms/pdu/PduPart;->generateLocation()Ljava/lang/String;
+Lcom/google/android/mms/pdu/PduPart;->getCharset()I
+Lcom/google/android/mms/pdu/PduPart;->getContentLocation()[B
+Lcom/google/android/mms/pdu/PduPart;->getContentType()[B
+Lcom/google/android/mms/pdu/PduPart;->getData()[B
+Lcom/google/android/mms/pdu/PduPart;->getDataUri()Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPart;->getFilename()[B
+Lcom/google/android/mms/pdu/PduPart;->getName()[B
+Lcom/google/android/mms/pdu/PduPart;->setCharset(I)V
+Lcom/google/android/mms/pdu/PduPart;->setContentId([B)V
+Lcom/google/android/mms/pdu/PduPart;->setContentLocation([B)V
+Lcom/google/android/mms/pdu/PduPart;->setContentType([B)V
+Lcom/google/android/mms/pdu/PduPart;->setData([B)V
+Lcom/google/android/mms/pdu/PduPart;->setDataUri(Landroid/net/Uri;)V
+Lcom/google/android/mms/pdu/PduPersister;->getBytes(Ljava/lang/String;)[B
+Lcom/google/android/mms/pdu/PduPersister;->getPduPersister(Landroid/content/Context;)Lcom/google/android/mms/pdu/PduPersister;
+Lcom/google/android/mms/pdu/PduPersister;->getPendingMessages(J)Landroid/database/Cursor;
+Lcom/google/android/mms/pdu/PduPersister;->load(Landroid/net/Uri;)Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/pdu/PduPersister;->move(Landroid/net/Uri;Landroid/net/Uri;)Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPersister;->persist(Lcom/google/android/mms/pdu/GenericPdu;Landroid/net/Uri;ZZLjava/util/HashMap;)Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPersister;->persistPart(Lcom/google/android/mms/pdu/PduPart;JLjava/util/HashMap;)Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPersister;->toIsoString([B)Ljava/lang/String;
+Lcom/google/android/mms/pdu/PduPersister;->updateHeaders(Landroid/net/Uri;Lcom/google/android/mms/pdu/SendReq;)V
+Lcom/google/android/mms/pdu/PduPersister;->updateParts(Landroid/net/Uri;Lcom/google/android/mms/pdu/PduBody;Ljava/util/HashMap;)V
+Lcom/google/android/mms/pdu/ReadOrigInd;->getMessageId()[B
+Lcom/google/android/mms/pdu/ReadRecInd;-><init>(Lcom/google/android/mms/pdu/EncodedStringValue;[BII[Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/ReadRecInd;->setDate(J)V
+Lcom/google/android/mms/pdu/RetrieveConf;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/RetrieveConf;->getMessageId()[B
+Lcom/google/android/mms/pdu/RetrieveConf;->getTransactionId()[B
+Lcom/google/android/mms/pdu/SendConf;->getMessageId()[B
+Lcom/google/android/mms/pdu/SendConf;->getResponseStatus()I
+Lcom/google/android/mms/pdu/SendConf;->getTransactionId()[B
+Lcom/google/android/mms/pdu/SendReq;-><init>()V
+Lcom/google/android/mms/pdu/SendReq;->getBcc()[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/SendReq;->getTransactionId()[B
+Lcom/google/android/mms/pdu/SendReq;->setDeliveryReport(I)V
+Lcom/google/android/mms/pdu/SendReq;->setExpiry(J)V
+Lcom/google/android/mms/pdu/SendReq;->setMessageClass([B)V
+Lcom/google/android/mms/pdu/SendReq;->setMessageSize(J)V
+Lcom/google/android/mms/pdu/SendReq;->setReadReport(I)V
+Lcom/google/android/mms/pdu/SendReq;->setTo([Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/util/AbstractCache;->get(Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/google/android/mms/util/PduCache;->getInstance()Lcom/google/android/mms/util/PduCache;
+Lcom/google/android/mms/util/PduCache;->isUpdating(Landroid/net/Uri;)Z
+Lcom/google/android/mms/util/PduCache;->purge(Landroid/net/Uri;)Lcom/google/android/mms/util/PduCacheEntry;
+Lcom/google/android/mms/util/PduCache;->purgeAll()V
+Lcom/google/android/mms/util/PduCacheEntry;->getPdu()Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/util/SqliteWrapper;->insert(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri;
Ldalvik/system/BaseDexClassLoader;-><init>(Ljava/lang/String;Ljava/io/File;Ljava/lang/String;Ljava/lang/ClassLoader;Z)V
Ldalvik/system/BaseDexClassLoader;->addDexPath(Ljava/lang/String;)V
Ldalvik/system/BaseDexClassLoader;->addDexPath(Ljava/lang/String;Z)V
@@ -2553,6 +2657,7 @@
Ljava/net/SocketException;-><init>(Ljava/lang/String;Ljava/lang/Throwable;)V
Ljava/net/SocketImpl;->serverSocket:Ljava/net/ServerSocket;
Ljava/net/SocketImpl;->socket:Ljava/net/Socket;
+Ljava/net/SocksSocketImpl;-><init>()V
Ljava/net/URI;->fragment:Ljava/lang/String;
Ljava/net/URI;->host:Ljava/lang/String;
Ljava/net/URI;->port:I
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 50e97c5..63c583f 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -1436,6 +1436,12 @@
android.hardware.usb.UsbDeviceConnection
android.hardware.usb.UsbManager
android.hardware.usb.UsbRequest
+android.hidl.base.V1_0.DebugInfo
+android.hidl.base.V1_0.IBase
+android.hidl.manager.V1_0.IServiceManager
+android.hidl.manager.V1_0.IServiceManager$Proxy
+android.hidl.manager.V1_0.IServiceNotification
+android.hidl.manager.V1_0.IServiceNotification$Stub
android.icu.impl.BMPSet
android.icu.impl.CacheBase
android.icu.impl.CacheValue
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 5499d59..2ee266d 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -545,12 +545,12 @@
* <a name="SavingPersistentState"></a>
* <h3>Saving Persistent State</h3>
*
- * <p>There are generally two kinds of persistent state than an activity
+ * <p>There are generally two kinds of persistent state that an activity
* will deal with: shared document-like data (typically stored in a SQLite
* database using a {@linkplain android.content.ContentProvider content provider})
* and internal state such as user preferences.</p>
*
- * <p>For content provider data, we suggest that activities use a
+ * <p>For content provider data, we suggest that activities use an
* "edit in place" user model. That is, any edits a user makes are effectively
* made immediately without requiring an additional confirmation step.
* Supporting this model is generally a simple matter of following two rules:</p>
@@ -1383,6 +1383,7 @@
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this);
getApplication().dispatchActivityResumed(this);
mActivityTransitionState.onResume(this, isTopOfTask());
+ enableAutofillCompatibilityIfNeeded();
if (mAutoFillResetNeeded) {
if (!mAutoFillIgnoreFirstResumePause) {
View focus = getCurrentFocus();
@@ -7165,7 +7166,6 @@
mWindow.setColorMode(info.colorMode);
setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled());
- enableAutofillCompatibilityIfNeeded();
}
private void enableAutofillCompatibilityIfNeeded() {
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 16360b3..2490cae 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -263,7 +263,9 @@
void killAllBackgroundProcesses();
ContentProviderHolder getContentProviderExternal(in String name, int userId,
in IBinder token, String tag);
+ /** @deprecated - Use {@link #removeContentProviderExternalAsUser} which takes a user ID. */
void removeContentProviderExternal(in String name, in IBinder token);
+ void removeContentProviderExternalAsUser(in String name, in IBinder token, int userId);
// Get memory information about the calling process.
void getMyMemoryState(out ActivityManager.RunningAppProcessInfo outInfo);
boolean killProcessesBelowForeground(in String reason);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 217225e..3638bc4 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -8253,7 +8253,10 @@
* <p>For custom display notifications created using {@link #setDisplayIntent},
* the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
* on their content.
+ *
+ * @deprecated Display intents are no longer supported.
*/
+ @Deprecated
public static final int SIZE_DEFAULT = 0;
/**
@@ -8261,7 +8264,10 @@
* with an extra small size.
* <p>This value is only applicable for custom display notifications created using
* {@link #setDisplayIntent}.
+ *
+ * @deprecated Display intents are no longer supported.
*/
+ @Deprecated
public static final int SIZE_XSMALL = 1;
/**
@@ -8269,7 +8275,10 @@
* with a small size.
* <p>This value is only applicable for custom display notifications created using
* {@link #setDisplayIntent}.
+ *
+ * @deprecated Display intents are no longer supported.
*/
+ @Deprecated
public static final int SIZE_SMALL = 2;
/**
@@ -8277,7 +8286,10 @@
* with a medium size.
* <p>This value is only applicable for custom display notifications created using
* {@link #setDisplayIntent}.
+ *
+ * @deprecated Display intents are no longer supported.
*/
+ @Deprecated
public static final int SIZE_MEDIUM = 3;
/**
@@ -8285,7 +8297,10 @@
* with a large size.
* <p>This value is only applicable for custom display notifications created using
* {@link #setDisplayIntent}.
+ *
+ * @deprecated Display intents are no longer supported.
*/
+ @Deprecated
public static final int SIZE_LARGE = 4;
/**
@@ -8293,20 +8308,29 @@
* full screen.
* <p>This value is only applicable for custom display notifications created using
* {@link #setDisplayIntent}.
+ *
+ * @deprecated Display intents are no longer supported.
*/
+ @Deprecated
public static final int SIZE_FULL_SCREEN = 5;
/**
* Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
* short amount of time when this notification is displayed on the screen. This
* is the default value.
+ *
+ * @deprecated This feature is no longer supported.
*/
+ @Deprecated
public static final int SCREEN_TIMEOUT_SHORT = 0;
/**
* Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
* for a longer amount of time when this notification is displayed on the screen.
+ *
+ * @deprecated This feature is no longer supported.
*/
+ @Deprecated
public static final int SCREEN_TIMEOUT_LONG = -1;
/** Notification extra which contains wearable extensions */
@@ -8556,7 +8580,9 @@
* @param intent the {@link PendingIntent} for an activity
* @return this object for method chaining
* @see android.app.Notification.WearableExtender#getDisplayIntent
+ * @deprecated Display intents are no longer supported.
*/
+ @Deprecated
public WearableExtender setDisplayIntent(PendingIntent intent) {
mDisplayIntent = intent;
return this;
@@ -8565,7 +8591,10 @@
/**
* Get the intent to launch inside of an activity view when displaying this
* notification. This {@code PendingIntent} should be for an activity.
+ *
+ * @deprecated Display intents are no longer supported.
*/
+ @Deprecated
public PendingIntent getDisplayIntent() {
return mDisplayIntent;
}
@@ -8579,7 +8608,9 @@
* @param page the notification to add as another page
* @return this object for method chaining
* @see android.app.Notification.WearableExtender#getPages
+ * @deprecated Multiple content pages are no longer supported.
*/
+ @Deprecated
public WearableExtender addPage(Notification page) {
mPages.add(page);
return this;
@@ -8594,7 +8625,9 @@
* @param pages a list of notifications
* @return this object for method chaining
* @see android.app.Notification.WearableExtender#getPages
+ * @deprecated Multiple content pages are no longer supported.
*/
+ @Deprecated
public WearableExtender addPages(List<Notification> pages) {
mPages.addAll(pages);
return this;
@@ -8604,7 +8637,9 @@
* Clear all additional pages present on this builder.
* @return this object for method chaining.
* @see #addPage
+ * @deprecated Multiple content pages are no longer supported.
*/
+ @Deprecated
public WearableExtender clearPages() {
mPages.clear();
return this;
@@ -8616,7 +8651,9 @@
* subsequent pages. This field can be used to separate a notification into multiple
* sections.
* @return the pages for this notification
+ * @deprecated Multiple content pages are no longer supported.
*/
+ @Deprecated
public List<Notification> getPages() {
return mPages;
}
@@ -8629,7 +8666,9 @@
* @param background the background bitmap
* @return this object for method chaining
* @see android.app.Notification.WearableExtender#getBackground
+ * @deprecated Background images are no longer supported.
*/
+ @Deprecated
public WearableExtender setBackground(Bitmap background) {
mBackground = background;
return this;
@@ -8642,7 +8681,9 @@
*
* @return the background image
* @see android.app.Notification.WearableExtender#setBackground
+ * @deprecated Background images are no longer supported.
*/
+ @Deprecated
public Bitmap getBackground() {
return mBackground;
}
@@ -8688,15 +8729,11 @@
}
/**
- * Set an action from this notification's actions to be clickable with the content of
- * this notification. This action will no longer display separately from the
- * notification's content.
+ * Set an action from this notification's actions as the primary action. If the action has a
+ * {@link RemoteInput} associated with it, shortcuts to the options for that input are shown
+ * directly on the notification.
*
- * <p>For notifications with multiple pages, child pages can also have content actions
- * set, although the list of available actions comes from the main notification and not
- * from the child page's notification.
- *
- * @param actionIndex The index of the action to hoist onto the current notification page.
+ * @param actionIndex The index of the primary action.
* If wearable actions were added to the main notification, this index
* will apply to that list, otherwise it will apply to the regular
* actions list.
@@ -8707,13 +8744,8 @@
}
/**
- * Get the index of the notification action, if any, that was specified as being clickable
- * with the content of this notification. This action will no longer display separately
- * from the notification's content.
- *
- * <p>For notifications with multiple pages, child pages can also have content actions
- * set, although the list of available actions comes from the main notification and not
- * from the child page's notification.
+ * Get the index of the notification action, if any, that was specified as the primary
+ * action.
*
* <p>If wearable specific actions were added to the main notification, this index will
* apply to that list, otherwise it will apply to the regular actions list.
@@ -8938,7 +8970,9 @@
* qr codes, as well as other simple black-and-white tickets.
* @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
* @return this object for method chaining
+ * @deprecated This feature is no longer supported.
*/
+ @Deprecated
public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
return this;
@@ -8950,7 +8984,9 @@
* qr codes, as well as other simple black-and-white tickets.
* @return {@code true} if it should be displayed in ambient, false otherwise
* otherwise. The default value is {@code false} if this was never set.
+ * @deprecated This feature is no longer supported.
*/
+ @Deprecated
public boolean getHintAmbientBigPicture() {
return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
}
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 4f172a4..e532ece 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -25,7 +25,6 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
-import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -214,7 +213,7 @@
}
/**
- * Sets the night mode.
+ * Sets the system-wide night mode.
* <p>
* The mode can be one of:
* <ul>
@@ -231,6 +230,12 @@
* are only effective when the {@link Configuration#UI_MODE_TYPE_CAR car}
* or {@link Configuration#UI_MODE_TYPE_DESK desk} mode is enabled on a
* device. Starting in API 23, changes to night mode are always effective.
+ * <p>
+ * Changes to night mode take effect globally and will result in a configuration change
+ * (and potentially an Activity lifecycle event) being applied to all running apps.
+ * Developers interested in an app-local implementation of night mode should consider using
+ * {@link androidx.appcompat.app.AppCompatDelegate#setDefaultNightMode(int)} to manage the
+ * -night qualifier locally.
*
* @param mode the night mode to set
* @see #getNightMode()
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index bd7a2dd..fc67c10 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3450,15 +3450,14 @@
* @param flags Bit mask of additional options: currently supported flags are
* {@link #WIPE_EXTERNAL_STORAGE} and {@link #WIPE_RESET_PROTECTION_DATA}.
* @param reason a string that contains the reason for wiping data, which can be
- * presented to the user.
+ * presented to the user. If the string is null or empty, user won't be notified.
* @throws SecurityException if the calling application does not own an active administrator
* that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}
* @throws IllegalArgumentException if the input reason string is null or empty.
*/
- public void wipeData(int flags, @NonNull CharSequence reason) {
+ public void wipeData(int flags, CharSequence reason) {
throwIfParentInstance("wipeData");
- Preconditions.checkNotNull(reason, "CharSequence is null");
- wipeDataInternal(flags, reason.toString());
+ wipeDataInternal(flags, reason != null ? reason.toString() : null);
}
/**
@@ -3469,7 +3468,7 @@
* @see #wipeData(int, CharSequence)
* @hide
*/
- private void wipeDataInternal(int flags, @NonNull String wipeReasonForUser) {
+ private void wipeDataInternal(int flags, String wipeReasonForUser) {
if (mService != null) {
try {
mService.wipeDataWithReason(flags, wipeReasonForUser);
diff --git a/core/java/android/app/backup/package.html b/core/java/android/app/backup/package.html
index 8b5e3ba..dd6c254 100644
--- a/core/java/android/app/backup/package.html
+++ b/core/java/android/app/backup/package.html
@@ -11,7 +11,7 @@
<p>All backup and restore operations are controlled by the {@link
android.app.backup.BackupManager}. Each application that would
-like to enable backup and preserve its data on remote strage must implement a
+like to enable backup and preserve its data on remote storage must implement a
backup agent. A backup agent can be built by extending either {@link android.app.backup.BackupAgent}
or {@link android.app.backup.BackupAgentHelper}. The {@link
android.app.backup.BackupAgentHelper} class provides a wrapper around {@link
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 4c655b5..654bfaf 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -81,7 +81,8 @@
* {@link #getBondedDevices()}; start device discovery with
* {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to
* listen for incoming RFComm connection requests with {@link
- * #listenUsingRfcommWithServiceRecord(String, UUID)}; or start a scan for
+ * #listenUsingRfcommWithServiceRecord(String, UUID)}; listen for incoming L2CAP Connection-oriented
+ * Channels (CoC) connection requests with {@link #listenUsingL2capChannel()}; or start a scan for
* Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}.
* </p>
* <p>This class is thread safe.</p>
@@ -2967,7 +2968,7 @@
/**
* Create a secure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and
* assign a dynamic protocol/service multiplexer (PSM) value. This socket can be used to listen
- * for incoming connections.
+ * for incoming connections. The supported Bluetooth transport is LE only.
* <p>A remote device connecting to this socket will be authenticated and communication on this
* socket will be encrypted.
* <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
@@ -2977,21 +2978,16 @@
* closed, Bluetooth is turned off, or the application exits unexpectedly.
* <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
* defined and performed by the application.
- * <p>Use {@link BluetoothDevice#createL2capCocSocket(int, int)} to connect to this server
+ * <p>Use {@link BluetoothDevice#createL2capChannel(int)} to connect to this server
* socket from another Android device that is given the PSM value.
*
- * @param transport Bluetooth transport to use, must be {@link BluetoothDevice#TRANSPORT_LE}
* @return an L2CAP CoC BluetoothServerSocket
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions, or unable to start this CoC
- * @hide
*/
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public BluetoothServerSocket listenUsingL2capCoc(int transport)
+ public BluetoothServerSocket listenUsingL2capChannel()
throws IOException {
- if (transport != BluetoothDevice.TRANSPORT_LE) {
- throw new IllegalArgumentException("Unsupported transport: " + transport);
- }
BluetoothServerSocket socket =
new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, true, true,
SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false);
@@ -3005,7 +3001,7 @@
throw new IOException("Error: Unable to assign PSM value");
}
if (DBG) {
- Log.d(TAG, "listenUsingL2capCoc: set assigned PSM to "
+ Log.d(TAG, "listenUsingL2capChannel: set assigned PSM to "
+ assignedPsm);
}
socket.setChannel(assignedPsm);
@@ -3014,10 +3010,23 @@
}
/**
+ * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new
+ * API name, listenUsingL2capChannel.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public BluetoothServerSocket listenUsingL2capCoc(int transport)
+ throws IOException {
+ Log.e(TAG, "listenUsingL2capCoc: PLEASE USE THE OFFICIAL API, listenUsingL2capChannel");
+ return listenUsingL2capChannel();
+ }
+
+ /**
* Create an insecure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and
- * assign a dynamic PSM value. This socket can be used to listen for incoming connections.
+ * assign a dynamic PSM value. This socket can be used to listen for incoming connections. The
+ * supported Bluetooth transport is LE only.
* <p>The link key is not required to be authenticated, i.e the communication may be vulnerable
- * to man-in-the-middle attacks. Use {@link #listenUsingL2capCoc}, if an encrypted and
+ * to man-in-the-middle attacks. Use {@link #listenUsingL2capChannel}, if an encrypted and
* authenticated communication channel is desired.
* <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
* {@link BluetoothServerSocket}.
@@ -3027,21 +3036,16 @@
* unexpectedly.
* <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
* defined and performed by the application.
- * <p>Use {@link BluetoothDevice#createInsecureL2capCocSocket(int, int)} to connect to this
- * server socket from another Android device that is given the PSM value.
+ * <p>Use {@link BluetoothDevice#createInsecureL2capChannel(int)} to connect to this server
+ * socket from another Android device that is given the PSM value.
*
- * @param transport Bluetooth transport to use, must be {@link BluetoothDevice#TRANSPORT_LE}
* @return an L2CAP CoC BluetoothServerSocket
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions, or unable to start this CoC
- * @hide
*/
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public BluetoothServerSocket listenUsingInsecureL2capCoc(int transport)
+ public BluetoothServerSocket listenUsingInsecureL2capChannel()
throws IOException {
- if (transport != BluetoothDevice.TRANSPORT_LE) {
- throw new IllegalArgumentException("Unsupported transport: " + transport);
- }
BluetoothServerSocket socket =
new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, false, false,
SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false);
@@ -3055,11 +3059,24 @@
throw new IOException("Error: Unable to assign PSM value");
}
if (DBG) {
- Log.d(TAG, "listenUsingInsecureL2capOn: set assigned PSM to "
+ Log.d(TAG, "listenUsingInsecureL2capChannel: set assigned PSM to "
+ assignedPsm);
}
socket.setChannel(assignedPsm);
return socket;
}
+
+ /**
+ * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new
+ * API name, listenUsingInsecureL2capChannel.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public BluetoothServerSocket listenUsingInsecureL2capCoc(int transport)
+ throws IOException {
+ Log.e(TAG, "listenUsingInsecureL2capCoc: PLEASE USE THE OFFICIAL API, "
+ + "listenUsingInsecureL2capChannel");
+ return listenUsingInsecureL2capChannel();
+ }
}
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 818a749..73e98cd 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1963,8 +1963,8 @@
/**
* Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can
* be used to start a secure outgoing connection to the remote device with the same dynamic
- * protocol/service multiplexer (PSM) value.
- * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingL2capCoc(int)} for
+ * protocol/service multiplexer (PSM) value. The supported Bluetooth transport is LE only.
+ * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingL2capChannel()} for
* peer-peer Bluetooth applications.
* <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
* <p>Application using this API is responsible for obtaining PSM value from remote device.
@@ -1975,59 +1975,71 @@
* secure socket connection is not possible, use {#link createInsecureLeL2capCocSocket(int,
* int)}.
*
- * @param transport Bluetooth transport to use, must be {@link #TRANSPORT_LE}
* @param psm dynamic PSM value from remote device
* @return a CoC #BluetoothSocket ready for an outgoing connection
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions
- * @hide
*/
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public BluetoothSocket createL2capCocSocket(int transport, int psm) throws IOException {
+ public BluetoothSocket createL2capChannel(int psm) throws IOException {
if (!isBluetoothEnabled()) {
- Log.e(TAG, "createL2capCocSocket: Bluetooth is not enabled");
+ Log.e(TAG, "createL2capChannel: Bluetooth is not enabled");
throw new IOException();
}
- if (transport != BluetoothDevice.TRANSPORT_LE) {
- throw new IllegalArgumentException("Unsupported transport: " + transport);
- }
- if (DBG) Log.d(TAG, "createL2capCocSocket: transport=" + transport + ", psm=" + psm);
+ if (DBG) Log.d(TAG, "createL2capChannel: psm=" + psm);
return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, true, true, this, psm,
null);
}
/**
+ * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new
+ * API name, createL2capChannel.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public BluetoothSocket createL2capCocSocket(int transport, int psm) throws IOException {
+ Log.e(TAG, "createL2capCocSocket: PLEASE USE THE OFFICIAL API, createL2capChannel");
+ return createL2capChannel(psm);
+ }
+
+ /**
* Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can
* be used to start a secure outgoing connection to the remote device with the same dynamic
- * protocol/service multiplexer (PSM) value.
- * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingInsecureL2capCoc(int)}
- * for peer-peer Bluetooth applications.
+ * protocol/service multiplexer (PSM) value. The supported Bluetooth transport is LE only.
+ * <p>This is designed to be used with {@link
+ * BluetoothAdapter#listenUsingInsecureL2capChannel()} for peer-peer Bluetooth applications.
* <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
* <p>Application using this API is responsible for obtaining PSM value from remote device.
* <p> The communication channel may not have an authenticated link key, i.e. it may be subject
- * to man-in-the-middle attacks. Use {@link #createL2capCocSocket(int, int)} if an encrypted and
+ * to man-in-the-middle attacks. Use {@link #createL2capChannel(int)} if an encrypted and
* authenticated communication channel is possible.
*
- * @param transport Bluetooth transport to use, must be {@link #TRANSPORT_LE}
* @param psm dynamic PSM value from remote device
* @return a CoC #BluetoothSocket ready for an outgoing connection
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public BluetoothSocket createInsecureL2capChannel(int psm) throws IOException {
+ if (!isBluetoothEnabled()) {
+ Log.e(TAG, "createInsecureL2capChannel: Bluetooth is not enabled");
+ throw new IOException();
+ }
+ if (DBG) {
+ Log.d(TAG, "createInsecureL2capChannel: psm=" + psm);
+ }
+ return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, false, false, this, psm,
+ null);
+ }
+
+ /**
+ * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new
+ * API name, createInsecureL2capChannel.
* @hide
*/
@RequiresPermission(Manifest.permission.BLUETOOTH)
public BluetoothSocket createInsecureL2capCocSocket(int transport, int psm) throws IOException {
- if (!isBluetoothEnabled()) {
- Log.e(TAG, "createInsecureL2capCocSocket: Bluetooth is not enabled");
- throw new IOException();
- }
- if (transport != BluetoothDevice.TRANSPORT_LE) {
- throw new IllegalArgumentException("Unsupported transport: " + transport);
- }
- if (DBG) {
- Log.d(TAG, "createInsecureL2capCocSocket: transport=" + transport + ", psm=" + psm);
- }
- return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, false, false, this, psm,
- null);
+ Log.e(TAG, "createL2capCocSocket: PLEASE USE THE OFFICIAL API, createInsecureL2capChannel");
+ return createInsecureL2capChannel(psm);
}
}
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index ba4b5a5..5fc344a 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -203,12 +203,11 @@
/**
* Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP
* Connection-oriented Channel (CoC) server socket. This server socket must be returned by the
- * {#link BluetoothAdapter.listenUsingL2capCoc(int)} or {#link
- * BluetoothAdapter.listenUsingInsecureL2capCoc(int)}. The returned value is undefined if this
+ * {#link BluetoothAdapter.listenUsingL2capChannel()} or {#link
+ * BluetoothAdapter.listenUsingInsecureL2capChannel()}. The returned value is undefined if this
* method is called on non-L2CAP server sockets.
*
* @return the assigned PSM or LE_PSM value depending on transport
- * @hide
*/
public int getPsm() {
return mChannel;
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 780f896..3a1e2f5 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -667,6 +667,10 @@
* @return one of {@link #TYPE_RFCOMM}, {@link #TYPE_SCO} or {@link #TYPE_L2CAP}
*/
public int getConnectionType() {
+ if (mType == TYPE_L2CAP_LE) {
+ // Treat the LE CoC to be the same type as L2CAP.
+ return TYPE_L2CAP;
+ }
return mType;
}
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
index 3fe17840..8760efe 100644
--- a/core/java/android/content/ClipboardManager.java
+++ b/core/java/android/content/ClipboardManager.java
@@ -95,9 +95,6 @@
* Sets the current primary clip on the clipboard. This is the clip that
* is involved in normal cut and paste operations.
*
- * <em>If the application is not the default IME or does not have input focus this will have
- * no effect.</em>
- *
* @param clip The clipped data item to set.
* @see #getPrimaryClip()
* @see #clearPrimaryClip()
@@ -115,9 +112,6 @@
/**
* Clears any current primary clip on the clipboard.
*
- * <em>If the application is not the default IME or does not have input focus this will have
- * no effect.</em>
- *
* @see #setPrimaryClip(ClipData)
*/
public void clearPrimaryClip() {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2ae3ae6..d88f6e3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3167,11 +3167,11 @@
*
* <p>Note: Instant apps, for which {@link PackageManager#isInstantApp()} returns true,
* don't have access to the following system services: {@link #DEVICE_POLICY_SERVICE},
- * {@link #FINGERPRINT_SERVICE}, {@link #SHORTCUT_SERVICE}, {@link #USB_SERVICE},
- * {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE}, {@link #WIFI_SERVICE},
- * {@link #WIFI_AWARE_SERVICE}. For these services this method will return <code>null</code>.
- * Generally, if you are running as an instant app you should always check whether the result
- * of this method is null.
+ * {@link #FINGERPRINT_SERVICE}, {@link #KEYGUARD_SERVICE}, {@link #SHORTCUT_SERVICE},
+ * {@link #USB_SERVICE}, {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE},
+ * {@link #WIFI_SERVICE}, {@link #WIFI_AWARE_SERVICE}. For these services this method will
+ * return <code>null</code>. Generally, if you are running as an instant app you should always
+ * check whether the result of this method is null.
*
* @param name The name of the desired service.
*
@@ -3258,11 +3258,11 @@
*
* <p>Note: Instant apps, for which {@link PackageManager#isInstantApp()} returns true,
* don't have access to the following system services: {@link #DEVICE_POLICY_SERVICE},
- * {@link #FINGERPRINT_SERVICE}, {@link #SHORTCUT_SERVICE}, {@link #USB_SERVICE},
- * {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE}, {@link #WIFI_SERVICE},
- * {@link #WIFI_AWARE_SERVICE}. For these services this method will return <code>null</code>.
- * Generally, if you are running as an instant app you should always check whether the result
- * of this method is null.
+ * {@link #FINGERPRINT_SERVICE}, {@link #KEYGUARD_SERVICE}, {@link #SHORTCUT_SERVICE},
+ * {@link #USB_SERVICE}, {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE},
+ * {@link #WIFI_SERVICE}, {@link #WIFI_AWARE_SERVICE}. For these services this method will
+ * return <code>null</code>. Generally, if you are running as an instant app you should always
+ * check whether the result of this method is null.
*
* @param serviceClass The class of the desired service.
* @return The service or null if the class is not a supported system service.
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 0dd6186..0469a90 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -125,7 +125,7 @@
*
* <p><strong>Data Authority</strong> matches if any of the given values match
* the Intent's data authority <em>and</em> one of the data schemes in the filter
- * has matched the Intent, <em>or</em> no authories were supplied in the filter.
+ * has matched the Intent, <em>or</em> no authorities were supplied in the filter.
* The Intent authority is determined by calling
* {@link Intent#getData} and {@link android.net.Uri#getAuthority} on that URI.
* <em>Note that authority matching here is <b>case sensitive</b>, unlike
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index 0c70a3d..19af609 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -22,7 +22,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -103,8 +102,14 @@
private static volatile boolean sForceSafeLabels = false;
- /** {@hide} */
- @UnsupportedAppUsage
+ /**
+ * Always use {@link #loadSafeLabel safe labels} when calling {@link #loadLabel}.
+ *
+ * @param forceSafeLabels {@code true} to enforce safe labels
+ *
+ * @hide
+ */
+ @SystemApi
public static void setForceSafeLabels(boolean forceSafeLabels) {
sForceSafeLabels = forceSafeLabels;
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 1b4878c..a15711f5 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1979,6 +1979,8 @@
* </ul>
* A version of 1.1.0 or higher also indicates:
* <ul>
+ * <li>The {@code VK_ANDROID_external_memory_android_hardware_buffer} extension is
+ * supported.</li>
* <li>{@code SYNC_FD} external semaphore and fence handles are supported.</li>
* <li>{@code VkPhysicalDeviceSamplerYcbcrConversionFeatures::samplerYcbcrConversion} is
* supported.</li>
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
index dbb2527..c604ff1 100644
--- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -202,31 +202,6 @@
}
/**
- * @param error
- * @param vendorCode
- * @return the error string associated with this error
- */
- default String getErrorString(int error, int vendorCode) {
- throw new UnsupportedOperationException("Stub!");
- }
-
- /**
- * @param acquireInfo
- * @param vendorCode
- * @return the help string associated with this code
- */
- default String getAcquiredString(int acquireInfo, int vendorCode) {
- throw new UnsupportedOperationException("Stub!");
- }
-
- /**
- * @return one of {@link #TYPE_FINGERPRINT} {@link #TYPE_IRIS} or {@link #TYPE_FACE}
- */
- default int getType() {
- throw new UnsupportedOperationException("Stub!");
- }
-
- /**
* This call warms up the hardware and starts scanning for valid biometrics. It terminates
* when {@link AuthenticationCallback#onAuthenticationError(int,
* CharSequence)} is called or when {@link AuthenticationCallback#onAuthenticationSucceeded(
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 0f83c8b..20e0116 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -168,10 +168,11 @@
} catch (RemoteException e) {
Log.w(TAG, "Remote exception while authenticating: ", e);
if (callback != null) {
- // Though this may not be a hardware issue, it will cause apps to give up or try
- // again later.
+ // Though this may not be a hardware issue, it will cause apps to give up or
+ // try again later.
callback.onAuthenticationError(FACE_ERROR_HW_UNAVAILABLE,
- getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+ getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */));
}
}
}
@@ -232,10 +233,11 @@
} catch (RemoteException e) {
Log.w(TAG, "Remote exception in enroll: ", e);
if (callback != null) {
- // Though this may not be a hardware issue, it will cause apps to give up or try
- // again later.
+ // Though this may not be a hardware issue, it will cause apps to give up or
+ // try again later.
callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
- getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+ getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */));
}
}
}
@@ -315,7 +317,8 @@
Log.w(TAG, "Remote exception in remove: ", e);
if (callback != null) {
callback.onRemovalError(face, FACE_ERROR_HW_UNAVAILABLE,
- getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+ getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */));
}
}
}
@@ -513,32 +516,34 @@
}
}
- @Override
- public String getErrorString(int errMsg, int vendorCode) {
+ /**
+ * @hide
+ */
+ public static String getErrorString(Context context, int errMsg, int vendorCode) {
switch (errMsg) {
case FACE_ERROR_HW_UNAVAILABLE:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.face_error_hw_not_available);
case FACE_ERROR_UNABLE_TO_PROCESS:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.face_error_unable_to_process);
case FACE_ERROR_TIMEOUT:
- return mContext.getString(com.android.internal.R.string.face_error_timeout);
+ return context.getString(com.android.internal.R.string.face_error_timeout);
case FACE_ERROR_NO_SPACE:
- return mContext.getString(com.android.internal.R.string.face_error_no_space);
+ return context.getString(com.android.internal.R.string.face_error_no_space);
case FACE_ERROR_CANCELED:
- return mContext.getString(com.android.internal.R.string.face_error_canceled);
+ return context.getString(com.android.internal.R.string.face_error_canceled);
case FACE_ERROR_LOCKOUT:
- return mContext.getString(com.android.internal.R.string.face_error_lockout);
+ return context.getString(com.android.internal.R.string.face_error_lockout);
case FACE_ERROR_LOCKOUT_PERMANENT:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.face_error_lockout_permanent);
case FACE_ERROR_NOT_ENROLLED:
- return mContext.getString(com.android.internal.R.string.face_error_not_enrolled);
+ return context.getString(com.android.internal.R.string.face_error_not_enrolled);
case FACE_ERROR_HW_NOT_PRESENT:
- return mContext.getString(com.android.internal.R.string.face_error_hw_not_present);
+ return context.getString(com.android.internal.R.string.face_error_hw_not_present);
case FACE_ERROR_VENDOR: {
- String[] msgArray = mContext.getResources().getStringArray(
+ String[] msgArray = context.getResources().getStringArray(
com.android.internal.R.array.face_error_vendor);
if (vendorCode < msgArray.length) {
return msgArray[vendorCode];
@@ -552,35 +557,34 @@
/**
* @hide
*/
- @Override
- public String getAcquiredString(int acquireInfo, int vendorCode) {
+ public static String getAcquiredString(Context context, int acquireInfo, int vendorCode) {
switch (acquireInfo) {
case FACE_ACQUIRED_GOOD:
return null;
case FACE_ACQUIRED_INSUFFICIENT:
- return mContext.getString(R.string.face_acquired_insufficient);
+ return context.getString(R.string.face_acquired_insufficient);
case FACE_ACQUIRED_TOO_BRIGHT:
- return mContext.getString(R.string.face_acquired_too_bright);
+ return context.getString(R.string.face_acquired_too_bright);
case FACE_ACQUIRED_TOO_DARK:
- return mContext.getString(R.string.face_acquired_too_dark);
+ return context.getString(R.string.face_acquired_too_dark);
case FACE_ACQUIRED_TOO_CLOSE:
- return mContext.getString(R.string.face_acquired_too_close);
+ return context.getString(R.string.face_acquired_too_close);
case FACE_ACQUIRED_TOO_FAR:
- return mContext.getString(R.string.face_acquired_too_far);
+ return context.getString(R.string.face_acquired_too_far);
case FACE_ACQUIRED_TOO_HIGH:
- return mContext.getString(R.string.face_acquired_too_high);
+ return context.getString(R.string.face_acquired_too_high);
case FACE_ACQUIRED_TOO_LOW:
- return mContext.getString(R.string.face_acquired_too_low);
+ return context.getString(R.string.face_acquired_too_low);
case FACE_ACQUIRED_TOO_RIGHT:
- return mContext.getString(R.string.face_acquired_too_right);
+ return context.getString(R.string.face_acquired_too_right);
case FACE_ACQUIRED_TOO_LEFT:
- return mContext.getString(R.string.face_acquired_too_left);
+ return context.getString(R.string.face_acquired_too_left);
case FACE_ACQUIRED_POOR_GAZE:
- return mContext.getString(R.string.face_acquired_poor_gaze);
+ return context.getString(R.string.face_acquired_poor_gaze);
case FACE_ACQUIRED_NOT_DETECTED:
- return mContext.getString(R.string.face_acquired_not_detected);
+ return context.getString(R.string.face_acquired_not_detected);
case FACE_ACQUIRED_VENDOR: {
- String[] msgArray = mContext.getResources().getStringArray(
+ String[] msgArray = context.getResources().getStringArray(
R.array.face_acquired_vendor);
if (vendorCode < msgArray.length) {
return msgArray[vendorCode];
@@ -592,18 +596,10 @@
}
/**
- * @hide
- */
- @Override
- public int getType() {
- return TYPE_FACE;
- }
-
- /**
* Used so BiometricPrompt can map the face ones onto existing public constants.
* @hide
*/
- public int getMappedAcquiredInfo(int acquireInfo, int vendorCode) {
+ public static int getMappedAcquiredInfo(int acquireInfo, int vendorCode) {
switch (acquireInfo) {
case FACE_ACQUIRED_GOOD:
return BiometricConstants.BIOMETRIC_ACQUIRED_GOOD;
@@ -898,13 +894,13 @@
? (vendorCode + FACE_ERROR_VENDOR_BASE) : errMsgId;
if (mEnrollmentCallback != null) {
mEnrollmentCallback.onEnrollmentError(clientErrMsgId,
- getErrorString(errMsgId, vendorCode));
+ getErrorString(mContext, errMsgId, vendorCode));
} else if (mAuthenticationCallback != null) {
mAuthenticationCallback.onAuthenticationError(clientErrMsgId,
- getErrorString(errMsgId, vendorCode));
+ getErrorString(mContext, errMsgId, vendorCode));
} else if (mRemovalCallback != null) {
mRemovalCallback.onRemovalError(mRemovalFace, clientErrMsgId,
- getErrorString(errMsgId, vendorCode));
+ getErrorString(mContext, errMsgId, vendorCode));
}
}
@@ -932,7 +928,7 @@
if (mAuthenticationCallback != null) {
mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
}
- final String msg = getAcquiredString(acquireInfo, vendorCode);
+ final String msg = getAcquiredString(mContext, acquireInfo, vendorCode);
if (msg == null) {
return;
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index b380a2e..a4f3ce1 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -426,7 +426,8 @@
// Though this may not be a hardware issue, it will cause apps to give up or try
// again later.
callback.onAuthenticationError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
- getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+ getErrorString(mContext, FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */));
}
}
}
@@ -476,7 +477,8 @@
// Though this may not be a hardware issue, it will cause apps to give up or try
// again later.
callback.onEnrollmentError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
- getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+ getErrorString(mContext, FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */));
}
}
}
@@ -546,7 +548,8 @@
Slog.w(TAG, "Remote exception in remove: ", e);
if (callback != null) {
callback.onRemovalError(fp, FINGERPRINT_ERROR_HW_UNAVAILABLE,
- getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+ getErrorString(mContext, FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */));
}
}
}
@@ -568,7 +571,8 @@
Slog.w(TAG, "Remote exception in enumerate: ", e);
if (callback != null) {
callback.onEnumerateError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
- getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+ getErrorString(mContext, FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */));
}
}
}
@@ -862,7 +866,7 @@
if (mAuthenticationCallback != null) {
mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
}
- final String msg = getAcquiredString(acquireInfo, vendorCode);
+ final String msg = getAcquiredString(mContext, acquireInfo, vendorCode);
if (msg == null) {
return;
}
@@ -882,16 +886,16 @@
? (vendorCode + FINGERPRINT_ERROR_VENDOR_BASE) : errMsgId;
if (mEnrollmentCallback != null) {
mEnrollmentCallback.onEnrollmentError(clientErrMsgId,
- getErrorString(errMsgId, vendorCode));
+ getErrorString(mContext, errMsgId, vendorCode));
} else if (mAuthenticationCallback != null) {
mAuthenticationCallback.onAuthenticationError(clientErrMsgId,
- getErrorString(errMsgId, vendorCode));
+ getErrorString(mContext, errMsgId, vendorCode));
} else if (mRemovalCallback != null) {
mRemovalCallback.onRemovalError(mRemovalFingerprint, clientErrMsgId,
- getErrorString(errMsgId, vendorCode));
+ getErrorString(mContext, errMsgId, vendorCode));
} else if (mEnumerateCallback != null) {
mEnumerateCallback.onEnumerateError(clientErrMsgId,
- getErrorString(errMsgId, vendorCode));
+ getErrorString(mContext, errMsgId, vendorCode));
}
}
@@ -934,38 +938,37 @@
/**
* @hide
*/
- @Override
- public String getErrorString(int errMsg, int vendorCode) {
+ public static String getErrorString(Context context, int errMsg, int vendorCode) {
switch (errMsg) {
case FINGERPRINT_ERROR_UNABLE_TO_PROCESS:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_error_unable_to_process);
case FINGERPRINT_ERROR_HW_UNAVAILABLE:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_error_hw_not_available);
case FINGERPRINT_ERROR_NO_SPACE:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_error_no_space);
case FINGERPRINT_ERROR_TIMEOUT:
- return mContext.getString(com.android.internal.R.string.fingerprint_error_timeout);
+ return context.getString(com.android.internal.R.string.fingerprint_error_timeout);
case FINGERPRINT_ERROR_CANCELED:
- return mContext.getString(com.android.internal.R.string.fingerprint_error_canceled);
+ return context.getString(com.android.internal.R.string.fingerprint_error_canceled);
case FINGERPRINT_ERROR_LOCKOUT:
- return mContext.getString(com.android.internal.R.string.fingerprint_error_lockout);
+ return context.getString(com.android.internal.R.string.fingerprint_error_lockout);
case FINGERPRINT_ERROR_LOCKOUT_PERMANENT:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_error_lockout_permanent);
case FINGERPRINT_ERROR_USER_CANCELED:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_error_user_canceled);
case FINGERPRINT_ERROR_NO_FINGERPRINTS:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_error_no_fingerprints);
case FINGERPRINT_ERROR_HW_NOT_PRESENT:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_error_hw_not_present);
case FINGERPRINT_ERROR_VENDOR: {
- String[] msgArray = mContext.getResources().getStringArray(
+ String[] msgArray = context.getResources().getStringArray(
com.android.internal.R.array.fingerprint_error_vendor);
if (vendorCode < msgArray.length) {
return msgArray[vendorCode];
@@ -979,28 +982,27 @@
/**
* @hide
*/
- @Override
- public String getAcquiredString(int acquireInfo, int vendorCode) {
+ public static String getAcquiredString(Context context, int acquireInfo, int vendorCode) {
switch (acquireInfo) {
case FINGERPRINT_ACQUIRED_GOOD:
return null;
case FINGERPRINT_ACQUIRED_PARTIAL:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_acquired_partial);
case FINGERPRINT_ACQUIRED_INSUFFICIENT:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_acquired_insufficient);
case FINGERPRINT_ACQUIRED_IMAGER_DIRTY:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_acquired_imager_dirty);
case FINGERPRINT_ACQUIRED_TOO_SLOW:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_acquired_too_slow);
case FINGERPRINT_ACQUIRED_TOO_FAST:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_acquired_too_fast);
case FINGERPRINT_ACQUIRED_VENDOR: {
- String[] msgArray = mContext.getResources().getStringArray(
+ String[] msgArray = context.getResources().getStringArray(
com.android.internal.R.array.fingerprint_acquired_vendor);
if (vendorCode < msgArray.length) {
return msgArray[vendorCode];
@@ -1011,14 +1013,6 @@
return null;
}
- /**
- * @hide
- */
- @Override
- public int getType() {
- return TYPE_FINGERPRINT;
- }
-
private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() {
@Override // binder call
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 34fa5b6..b948402 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -85,6 +85,7 @@
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -465,7 +466,7 @@
public final void initializeInternal(IBinder token, int displayId,
IInputMethodPrivilegedOperations privilegedOperations) {
mPrivOps.set(privilegedOperations);
- mImm.registerInputMethodPrivOps(token, mPrivOps);
+ InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps);
updateInputMethodDisplay(displayId);
attachToken(token);
}
@@ -1031,7 +1032,7 @@
if (mToken != null) {
// This is completely optional, but allows us to show more explicit error messages
// when IME developers are doing something unsupported.
- mImm.unregisterInputMethodPrivOps(mToken);
+ InputMethodPrivilegedOperationsRegistry.remove(mToken);
}
}
diff --git a/core/java/android/nfc/tech/MifareClassic.java b/core/java/android/nfc/tech/MifareClassic.java
index 02819a6..080e058 100644
--- a/core/java/android/nfc/tech/MifareClassic.java
+++ b/core/java/android/nfc/tech/MifareClassic.java
@@ -38,7 +38,7 @@
* <li>MIFARE Classic Mini are 320 bytes ({@link #SIZE_MINI}), with 5 sectors each of 4 blocks.
* <li>MIFARE Classic 1k are 1024 bytes ({@link #SIZE_1K}), with 16 sectors each of 4 blocks.
* <li>MIFARE Classic 2k are 2048 bytes ({@link #SIZE_2K}), with 32 sectors each of 4 blocks.
- * <li>MIFARE Classic 4k} are 4096 bytes ({@link #SIZE_4K}). The first 32 sectors contain 4 blocks
+ * <li>MIFARE Classic 4k are 4096 bytes ({@link #SIZE_4K}). The first 32 sectors contain 4 blocks
* and the last 8 sectors contain 16 blocks.
* </ul>
*
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index b13bcac..051ab75 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -15,8 +15,10 @@
*/
package android.os;
-import java.io.ByteArrayOutputStream;
-import java.nio.charset.StandardCharsets;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
/**
* Wrapper class for sending data from Android OS to StatsD.
@@ -24,39 +26,28 @@
* @hide
*/
public final class StatsLogEventWrapper implements Parcelable {
- private ByteArrayOutputStream mStorage = new ByteArrayOutputStream();
+ static final boolean DEBUG = false;
+ static final String TAG = "StatsLogEventWrapper";
- // Below are constants copied from log/log.h
- private static final int EVENT_TYPE_INT = 0; /* int32_t */
- private static final int EVENT_TYPE_LONG = 1; /* int64_t */
- private static final int EVENT_TYPE_STRING = 2;
- private static final int EVENT_TYPE_LIST = 3;
- private static final int EVENT_TYPE_FLOAT = 4;
+ // Keep in sync with FieldValue.h enums
+ private static final int EVENT_TYPE_UNKNOWN = 0;
+ private static final int EVENT_TYPE_INT = 1; /* int32_t */
+ private static final int EVENT_TYPE_LONG = 2; /* int64_t */
+ private static final int EVENT_TYPE_FLOAT = 3;
+ private static final int EVENT_TYPE_DOUBLE = 4;
+ private static final int EVENT_TYPE_STRING = 5;
+ private static final int EVENT_TYPE_STORAGE = 6;
- // Keep this in sync with system/core/logcat/event.logtags
- private static final int STATS_BUFFER_TAG_ID = 1937006964;
- /**
- * Creates a log_event that is binary-encoded as implemented in
- * system/core/liblog/log_event_list.c; this allows us to use the same parsing logic in statsd
- * for pushed and pulled data. The write* methods must be called in the same order as their
- * field number. There is no checking that the correct number of write* methods is called.
- * We also write an END_LIST character before beginning to write to parcel, but this END_LIST
- * may be unnecessary.
- *
- * @param tag The integer representing the tag for this event.
- * @param fields The number of fields specified in this event.
- */
- public StatsLogEventWrapper(long elapsedNanos, int tag, int fields) {
- // Write four bytes from tag, starting with least-significant bit.
- // For pulled data, this tag number is not really used. We use the same tag number as
- // pushed ones to be consistent.
- write4Bytes(STATS_BUFFER_TAG_ID);
- mStorage.write(EVENT_TYPE_LIST); // This is required to start the log entry.
- mStorage.write(fields + 2); // Indicate number of elements in this list. +1 for the tag
- // The first element is the elapsed realtime.
- writeLong(elapsedNanos);
- // The second element is the real atom tag number
- writeInt(tag);
+ List<Integer> mTypes = new ArrayList<>();
+ List<Object> mValues = new ArrayList<>();
+ int mTag;
+ long mElapsedTimeNs;
+ long mWallClockTimeNs;
+
+ public StatsLogEventWrapper(int tag, long elapsedTimeNs, long wallClockTimeNs) {
+ this.mTag = tag;
+ this.mElapsedTimeNs = elapsedTimeNs;
+ this.mWallClockTimeNs = wallClockTimeNs;
}
/**
@@ -79,69 +70,80 @@
}
};
- private void write4Bytes(int val) {
- mStorage.write(val);
- mStorage.write(val >>> 8);
- mStorage.write(val >>> 16);
- mStorage.write(val >>> 24);
- }
-
- private void write8Bytes(long val) {
- write4Bytes((int) (val & 0xFFFFFFFF)); // keep the lowe 32-bits
- write4Bytes((int) (val >>> 32)); // Write the high 32-bits.
- }
-
- /**
- * Adds 32-bit integer to output.
- */
public void writeInt(int val) {
- mStorage.write(EVENT_TYPE_INT);
- write4Bytes(val);
+ mTypes.add(EVENT_TYPE_INT);
+ mValues.add(val);
}
- /**
- * Adds 64-bit long to output.
- */
public void writeLong(long val) {
- mStorage.write(EVENT_TYPE_LONG);
- write8Bytes(val);
+ mTypes.add(EVENT_TYPE_LONG);
+ mValues.add(val);
}
/**
- * Adds a 4-byte floating point value to output.
- */
- public void writeFloat(float val) {
- int v = Float.floatToIntBits(val);
- mStorage.write(EVENT_TYPE_FLOAT);
- write4Bytes(v);
- }
-
- /**
- * Adds a string to the output.
+ * Write a string value.
*/
public void writeString(String val) {
- mStorage.write(EVENT_TYPE_STRING);
- write4Bytes(val.length());
- byte[] bytes = val.getBytes(StandardCharsets.UTF_8);
- mStorage.write(bytes, 0, bytes.length);
+ mTypes.add(EVENT_TYPE_STRING);
+ // use empty string for null
+ mValues.add(val == null ? "" : val);
+ }
+
+ public void writeFloat(float val) {
+ mTypes.add(EVENT_TYPE_FLOAT);
+ mValues.add(val);
}
/**
- * Adds a boolean by adding either a 1 or 0 to the output.
+ * Write a storage value.
*/
+ public void writeStorage(byte[] val) {
+ mTypes.add(EVENT_TYPE_STORAGE);
+ mValues.add(val);
+ }
+
public void writeBoolean(boolean val) {
- int toWrite = val ? 1 : 0;
- mStorage.write(EVENT_TYPE_INT);
- write4Bytes(toWrite);
+ mTypes.add(EVENT_TYPE_INT);
+ mValues.add(val ? 1 : 0);
}
/**
* Writes the stored fields to a byte array. Will first write a new-line character to denote
* END_LIST before writing contents to byte array.
*/
+
public void writeToParcel(Parcel out, int flags) {
- mStorage.write(10); // new-line character is same as END_LIST
- out.writeByteArray(mStorage.toByteArray());
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Writing " + mTag + " " + mElapsedTimeNs + " " + mWallClockTimeNs + " and "
+ + mTypes.size() + " elements.");
+ }
+ out.writeInt(mTag);
+ out.writeLong(mElapsedTimeNs);
+ out.writeLong(mWallClockTimeNs);
+ out.writeInt(mTypes.size());
+ for (int i = 0; i < mTypes.size(); i++) {
+ out.writeInt(mTypes.get(i));
+ switch (mTypes.get(i)) {
+ case EVENT_TYPE_INT:
+ out.writeInt((int) mValues.get(i));
+ break;
+ case EVENT_TYPE_LONG:
+ out.writeLong((long) mValues.get(i));
+ break;
+ case EVENT_TYPE_FLOAT:
+ out.writeFloat((float) mValues.get(i));
+ break;
+ case EVENT_TYPE_STRING:
+ out.writeString((String) mValues.get(i));
+ break;
+ case EVENT_TYPE_STORAGE:
+ out.writeByteArray((byte[]) mValues.get(i));
+ break;
+ default:
+ break;
+ }
+ }
}
/**
diff --git a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
index e0bffae..18aea03 100644
--- a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
+++ b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
@@ -16,8 +16,8 @@
package android.permissionpresenterservice;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
@@ -74,15 +74,13 @@
public abstract List<RuntimePermissionPresentationInfo> onGetAppPermissions(String packageName);
/**
- * Revoke the permission {@code permissionName} for app {@code packageName}
+ * Revokes the permission {@code permissionName} for app {@code packageName}
*
* @param packageName The package for which to revoke
* @param permissionName The permission to revoke
- *
- * @hide
*/
- @UnsupportedAppUsage
- public abstract void onRevokeRuntimePermission(String packageName, String permissionName);
+ public abstract void onRevokeRuntimePermission(@NonNull String packageName,
+ @NonNull String permissionName);
@Override
public final IBinder onBind(Intent intent) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4c7f0f3..aa178fb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11537,14 +11537,6 @@
public static final String EMERGENCY_AFFORDANCE_NEEDED = "emergency_affordance_needed";
/**
- * Enable faster emergency phone call feature.
- * The value is a boolean (1 or 0).
- * @hide
- */
- public static final String FASTER_EMERGENCY_PHONE_CALL_ENABLED =
- "faster_emergency_phone_call_enabled";
-
- /**
* See RIL_PreferredNetworkType in ril.h
* @hide
*/
diff --git a/core/java/android/service/autofill/FillCallback.java b/core/java/android/service/autofill/FillCallback.java
index 1695c13..3893f2a 100644
--- a/core/java/android/service/autofill/FillCallback.java
+++ b/core/java/android/service/autofill/FillCallback.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.app.Activity;
import android.os.RemoteException;
+import android.util.Log;
/**
* <p><code>FillCallback</code> handles autofill requests from the {@link AutofillService} into
@@ -28,6 +29,9 @@
* <a href="/guide/topics/text/autofill-services">Build autofill services</a>.
*/
public final class FillCallback {
+
+ private static final String TAG = "FillCallback";
+
private final IFillCallback mCallback;
private final int mRequestId;
private boolean mCalled;
@@ -39,13 +43,20 @@
}
/**
- * Notifies the Android System that an
- * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
- * FillCallback)} was successfully fulfilled by the service.
+ * Notifies the Android System that a fill request
+ * ({@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
+ * FillCallback)}) was successfully fulfilled by the service.
*
- * @param response autofill information for that activity, or {@code null} when the activity
- * cannot be autofilled (for example, if it only contains read-only fields). See
- * {@link FillResponse} for examples.
+ * <p>This method should always be called, even if the service doesn't have the heuristics to
+ * fulfill the request (in which case it should be called with {@code null}).
+ *
+ * <p>See the main {@link AutofillService} documentation for more details and examples.
+ *
+ * @param response autofill information for that activity, or {@code null} when the service
+ * cannot autofill the activity.
+ *
+ * @throws IllegalStateException if this method or {@link #onFailure(CharSequence)} was already
+ * called.
*/
public void onSuccess(@Nullable FillResponse response) {
assertNotCalled();
@@ -63,13 +74,28 @@
}
/**
- * Notifies the Android System that an
+ * Notifies the Android System that a fill request (
* {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
- * FillCallback)} could not be fulfilled by the service.
+ * FillCallback)}) could not be fulfilled by the service (for example, because the user data was
+ * not available yet), so the request could be retried later.
*
- * @param message error message to be displayed to the user.
+ * <p><b>Note: </b>this method should not be used when the service didn't have the heursitics to
+ * fulfill the request; in this case, the service should call {@link #onSuccess(FillResponse)
+ * onSuccess(null)} instead.
+ *
+ * <p><b>Note: </b>on Android versions up to {@link android.os.Build.VERSION_CODES#P}, this
+ * method is not working as intended, and the service should call
+ * {@link #onSuccess(FillResponse) onSuccess(null)} instead.
+ *
+ * @param message error message to be displayed to the user. <b>Note: </b> this message is
+ * displayed on {@code logcat} logs and should not contain PII (Personally Identifiable
+ * Information, such as username or email address).
+ *
+ * @throws IllegalStateException if this method or {@link #onSuccess(FillResponse)} was already
+ * called.
*/
public void onFailure(@Nullable CharSequence message) {
+ Log.w(TAG, "onFailure(): " + (message == null ? null : message.length() + "_chars"));
assertNotCalled();
mCalled = true;
try {
diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java
index 855981a..0625095 100644
--- a/core/java/android/service/autofill/SaveCallback.java
+++ b/core/java/android/service/autofill/SaveCallback.java
@@ -21,6 +21,7 @@
import android.app.Activity;
import android.content.IntentSender;
import android.os.RemoteException;
+import android.util.Log;
import com.android.internal.util.Preconditions;
@@ -29,6 +30,9 @@
* autofilled.
*/
public final class SaveCallback {
+
+ private static final String TAG = "SaveCallback";
+
private final ISaveCallback mCallback;
private boolean mCalled;
@@ -41,6 +45,9 @@
* Notifies the Android System that an
* {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} was successfully handled
* by the service.
+ *
+ * @throws IllegalStateException if this method, {@link #onSuccess(IntentSender)}, or
+ * {@link #onFailure(CharSequence)} was already called.
*/
public void onSuccess() {
onSuccessInternal(null);
@@ -58,6 +65,9 @@
*
* @param intentSender intent that will be launched from the context of activity being
* autofilled.
+ *
+ * @throws IllegalStateException if this method, {@link #onSuccess()},
+ * or {@link #onFailure(CharSequence)} was already called.
*/
public void onSuccess(@NonNull IntentSender intentSender) {
onSuccessInternal(Preconditions.checkNotNull(intentSender));
@@ -86,9 +96,15 @@
* you prefer to show your own message, call {@link #onSuccess()} or
* {@link #onSuccess(IntentSender)} instead.
*
- * @param message error message to be displayed to the user.
+ * @param message error message to be displayed to the user. <b>Note: </b> this message is
+ * displayed on {@code logcat} logs and should not contain PII (Personally Identifiable
+ * Information, such as username or email address).
+ *
+ * @throws IllegalStateException if this method, {@link #onSuccess()},
+ * or {@link #onSuccess(IntentSender)} was already called.
*/
public void onFailure(CharSequence message) {
+ Log.w(TAG, "onFailure(): " + (message == null ? null : message.length() + "_chars"));
assertNotCalled();
mCalled = true;
try {
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index f571956..dfaf49a 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -235,12 +235,17 @@
*/
public static final int FLAG_DONT_SAVE_ON_FINISH = 0x2;
+
/**
- * Don't trigger the autofill save UI when the autofill context associated with the response
- * associated with this {@link SaveInfo} is {@link AutofillManager#commit() committed},
- * but keep its {@link FillContext} so it's delivered in a future
- * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback) save request} of an
- * activity belonging to the same task.
+ * Postpone the autofill save UI.
+ *
+ * <p>If flag is set, the autofill save UI is not triggered when the
+ * autofill context associated with the response associated with this {@link SaveInfo} is
+ * committed (with {@link AutofillManager#commit()}). Instead, the {@link FillContext}
+ * is delivered in future fill requests (with {@link
+ * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)})
+ * and save request (with {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)})
+ * of an activity belonging to the same task.
*
* <p>This flag should be used when the service detects that the application uses
* multiple screens to implement an autofillable workflow (for example, one screen for the
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 26240c5..c1a3c2b 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -283,6 +283,9 @@
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
throw ex.rethrowFromSystemServer();
+ } catch (SecurityException e) {
+ // app cannot catch and recover from this, so do on their behalf
+ Log.w(TAG, "Enqueue adjustment failed; no longer connected", e);
}
}
break;
diff --git a/core/java/android/service/voice/IVoiceInteractionService.aidl b/core/java/android/service/voice/IVoiceInteractionService.aidl
index e3d68a6..24819a6 100644
--- a/core/java/android/service/voice/IVoiceInteractionService.aidl
+++ b/core/java/android/service/voice/IVoiceInteractionService.aidl
@@ -16,6 +16,8 @@
package android.service.voice;
+import com.android.internal.app.IVoiceActionCheckCallback;
+
/**
* @hide
*/
@@ -24,4 +26,6 @@
void soundModelsChanged();
void shutdown();
void launchVoiceAssistFromKeyguard();
+ void getActiveServiceSupportedActions(in List<String> voiceActions,
+ in IVoiceActionCheckCallback callback);
}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 0bbc07e..e105fdf 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -16,6 +16,8 @@
package android.service.voice;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.UnsupportedAppUsage;
import android.app.Service;
@@ -26,17 +28,22 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
+import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IVoiceActionCheckCallback;
import com.android.internal.app.IVoiceInteractionManagerService;
+import com.android.internal.util.function.pooled.PooledLambda;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Locale;
+import java.util.Set;
/**
* Top-level service of the current global voice interactor, which is providing
@@ -71,23 +78,43 @@
public static final String SERVICE_META_DATA = "android.voice_interaction";
IVoiceInteractionService mInterface = new IVoiceInteractionService.Stub() {
- @Override public void ready() {
- mHandler.sendEmptyMessage(MSG_READY);
- }
- @Override public void shutdown() {
- mHandler.sendEmptyMessage(MSG_SHUTDOWN);
- }
- @Override public void soundModelsChanged() {
- mHandler.sendEmptyMessage(MSG_SOUND_MODELS_CHANGED);
- }
@Override
- public void launchVoiceAssistFromKeyguard() throws RemoteException {
- mHandler.sendEmptyMessage(MSG_LAUNCH_VOICE_ASSIST_FROM_KEYGUARD);
+ public void ready() {
+ Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+ VoiceInteractionService::onReady, VoiceInteractionService.this));
+ }
+
+ @Override
+ public void shutdown() {
+ Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+ VoiceInteractionService::onShutdownInternal, VoiceInteractionService.this));
+ }
+
+ @Override
+ public void soundModelsChanged() {
+ Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+ VoiceInteractionService::onSoundModelsChangedInternal,
+ VoiceInteractionService.this));
+ }
+
+ @Override
+ public void launchVoiceAssistFromKeyguard() {
+ Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+ VoiceInteractionService::onLaunchVoiceAssistFromKeyguard,
+ VoiceInteractionService.this));
+ }
+
+ @Override
+ public void getActiveServiceSupportedActions(List<String> voiceActions,
+ IVoiceActionCheckCallback callback) {
+ Handler.getMain().executeOrSendMessage(
+ PooledLambda.obtainMessage(VoiceInteractionService::onHandleVoiceActionCheck,
+ VoiceInteractionService.this,
+ voiceActions,
+ callback));
}
};
- MyHandler mHandler;
-
IVoiceInteractionManagerService mSystemService;
private final Object mLock = new Object();
@@ -96,33 +123,6 @@
private AlwaysOnHotwordDetector mHotwordDetector;
- static final int MSG_READY = 1;
- static final int MSG_SHUTDOWN = 2;
- static final int MSG_SOUND_MODELS_CHANGED = 3;
- static final int MSG_LAUNCH_VOICE_ASSIST_FROM_KEYGUARD = 4;
-
- class MyHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_READY:
- onReady();
- break;
- case MSG_SHUTDOWN:
- onShutdownInternal();
- break;
- case MSG_SOUND_MODELS_CHANGED:
- onSoundModelsChangedInternal();
- break;
- case MSG_LAUNCH_VOICE_ASSIST_FROM_KEYGUARD:
- onLaunchVoiceAssistFromKeyguard();
- break;
- default:
- super.handleMessage(msg);
- }
- }
- }
-
/**
* Called when a user has activated an affordance to launch voice assist from the Keyguard.
*
@@ -186,7 +186,7 @@
* be any combination of
* {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and
* {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT
- * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}
+ * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}
* to request that the system generate and deliver assist data on the current foreground
* app as part of showing the session UI.
*/
@@ -200,10 +200,22 @@
}
}
- @Override
- public void onCreate() {
- super.onCreate();
- mHandler = new MyHandler();
+ /**
+ * Request to query for what extended voice actions this service supports. This method will
+ * be called when the system checks the supported actions of this
+ * {@link VoiceInteractionService}. Supported actions may be delivered to
+ * {@link VoiceInteractionSession} later to request a session to perform an action.
+ *
+ * <p>Voice actions are defined in support libraries and could vary based on platform context.
+ * For example, car related voice actions will be defined in car support libraries.
+ *
+ * @param voiceActions A set of checked voice actions.
+ * @return Returns a subset of checked voice actions. Additional voice actions in the
+ * returned set will be ignored. Returns null or empty set if no actions are supported.
+ */
+ @Nullable
+ public Set<String> onGetSupportedVoiceActions(@NonNull Set<String> voiceActions) {
+ return null;
}
@Override
@@ -254,6 +266,18 @@
}
}
+ private void onHandleVoiceActionCheck(List<String> voiceActions,
+ IVoiceActionCheckCallback callback) {
+ if (callback != null) {
+ try {
+ Set<String> voiceActionsSet = new ArraySet<>(voiceActions);
+ Set<String> resultSet = onGetSupportedVoiceActions(voiceActionsSet);
+ callback.onComplete(resultSet == null ? null : new ArrayList<>(resultSet));
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
/**
* Creates an {@link AlwaysOnHotwordDetector} for the given keyphrase and locale.
* This instance must be retained and used by the client.
@@ -289,12 +313,12 @@
}
/**
- * Checks if a given keyphrase and locale are supported to create an
- * {@link AlwaysOnHotwordDetector}.
- *
- * @return true if the keyphrase and locale combination is supported, false otherwise.
- * @hide
- */
+ * Checks if a given keyphrase and locale are supported to create an
+ * {@link AlwaysOnHotwordDetector}.
+ *
+ * @return true if the keyphrase and locale combination is supported, false otherwise.
+ * @hide
+ */
@UnsupportedAppUsage
public final boolean isKeyphraseAndLocaleSupportedForHotword(String keyphrase, Locale locale) {
if (mKeyphraseEnrollmentInfo == null) {
diff --git a/core/java/android/text/GraphicsOperations.java b/core/java/android/text/GraphicsOperations.java
index 6edf845..6c15446 100644
--- a/core/java/android/text/GraphicsOperations.java
+++ b/core/java/android/text/GraphicsOperations.java
@@ -58,6 +58,6 @@
/**
* Just like {@link Paint#getTextRunCursor}.
*/
- int getTextRunCursor(int contextStart, int contextEnd, int dir, int offset,
+ int getTextRunCursor(int contextStart, int contextEnd, boolean isRtl, int offset,
int cursorOpt, Paint p);
}
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 33c977b..c89617f 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -619,7 +619,7 @@
previousLineBottom = lbottom;
int lbaseline = lbottom - getLineDescent(i);
- if (start >= spanEnd) {
+ if (end >= spanEnd) {
// These should be infrequent, so we'll use this so that
// we don't have to check as often.
spanEnd = mLineBackgroundSpans.getNextTransition(start, textLength);
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index 9d841e8..c5fabaf 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -1551,7 +1551,7 @@
*
* @param contextStart the start index of the context
* @param contextEnd the (non-inclusive) end index of the context
- * @param dir either DIRECTION_RTL or DIRECTION_LTR
+ * @param dir 1 if the run is RTL, otherwise 0
* @param offset the cursor position to move from
* @param cursorOpt how to move the cursor, one of CURSOR_AFTER,
* CURSOR_AT_OR_AFTER, CURSOR_BEFORE,
@@ -1563,21 +1563,28 @@
@Deprecated
public int getTextRunCursor(int contextStart, int contextEnd, int dir, int offset,
int cursorOpt, Paint p) {
+ return getTextRunCursor(contextStart, contextEnd, dir == 1, offset, cursorOpt, p);
+ }
+
+ /** @hide */
+ @Override
+ public int getTextRunCursor(int contextStart, int contextEnd, boolean isRtl, int offset,
+ int cursorOpt, Paint p) {
int ret;
int contextLen = contextEnd - contextStart;
if (contextEnd <= mGapStart) {
ret = p.getTextRunCursor(mText, contextStart, contextLen,
- dir, offset, cursorOpt);
+ isRtl, offset, cursorOpt);
} else if (contextStart >= mGapStart) {
ret = p.getTextRunCursor(mText, contextStart + mGapLength, contextLen,
- dir, offset + mGapLength, cursorOpt) - mGapLength;
+ isRtl, offset + mGapLength, cursorOpt) - mGapLength;
} else {
char[] buf = TextUtils.obtain(contextLen);
getChars(contextStart, contextEnd, buf, 0);
ret = p.getTextRunCursor(buf, 0, contextLen,
- dir, offset - contextStart, cursorOpt) + contextStart;
+ isRtl, offset - contextStart, cursorOpt) + contextStart;
TextUtils.recycle(buf);
}
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index ad7a851..b49a949 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -802,14 +802,13 @@
}
}
- int dir = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
int cursorOpt = after ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE;
if (mCharsValid) {
return wp.getTextRunCursor(mChars, spanStart, spanLimit - spanStart,
- dir, offset, cursorOpt);
+ runIsRtl, offset, cursorOpt);
} else {
return wp.getTextRunCursor(mText, mStart + spanStart,
- mStart + spanLimit, dir, mStart + offset, cursorOpt) - mStart;
+ mStart + spanLimit, runIsRtl, mStart + offset, cursorOpt) - mStart;
}
}
diff --git a/core/java/android/text/TextWatcher.java b/core/java/android/text/TextWatcher.java
index bad09f2..a0aef69 100644
--- a/core/java/android/text/TextWatcher.java
+++ b/core/java/android/text/TextWatcher.java
@@ -17,7 +17,7 @@
package android.text;
/**
- * When an object of a type is attached to an Editable, its methods will
+ * When an object of this type is attached to an Editable, its methods will
* be called when the text is changed.
*/
public interface TextWatcher extends NoCopySpan {
diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java
index 5f0a46d..d4bcd12 100644
--- a/core/java/android/text/method/BaseKeyListener.java
+++ b/core/java/android/text/method/BaseKeyListener.java
@@ -310,7 +310,7 @@
return len;
}
- offset = paint.getTextRunCursor(text, offset, len, Paint.DIRECTION_LTR /* not used */,
+ offset = paint.getTextRunCursor(text, offset, len, false /* LTR, not used */,
offset, Paint.CURSOR_AFTER);
return adjustReplacementSpan(text, offset, false /* move to the end */);
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index 5210447..be47320 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -420,7 +420,7 @@
intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_HASHCODE, hashCode());
context.sendBroadcast(intent);
} else {
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = context.getSystemService(InputMethodManager.class);
if (imm != null) {
imm.notifySuggestionPicked(this, original, index);
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index ff6e86e..183e833 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -35,6 +35,7 @@
public static final String FFLAG_OVERRIDE_PREFIX = FFLAG_PREFIX + "override.";
public static final String PERSIST_PREFIX = "persist." + FFLAG_OVERRIDE_PREFIX;
public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
+ public static final String EMERGENCY_DIAL_SHORTCUTS = "settings_emergency_dial_shortcuts";
private static final Map<String, String> DEFAULT_FLAGS;
static {
@@ -47,6 +48,7 @@
DEFAULT_FLAGS.put("settings_data_usage_v2", "false");
DEFAULT_FLAGS.put("settings_seamless_transfer", "false");
DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "true");
+ DEFAULT_FLAGS.put(EMERGENCY_DIAL_SHORTCUTS, "false");
}
/**
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 533d725..1203541 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -410,11 +410,11 @@
NoSuchAlgorithmException {
try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
SignatureInfo signatureInfo = findSignature(apk);
- return ApkSigningBlockUtils.generateApkVerity(apkPath, bufferFactory, signatureInfo);
+ return ApkVerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
}
}
- static byte[] generateFsverityRootHash(String apkPath)
+ static byte[] generateApkVerityRootHash(String apkPath)
throws IOException, SignatureNotFoundException, DigestException,
NoSuchAlgorithmException {
try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
@@ -423,7 +423,7 @@
if (vSigner.verityRootHash == null) {
return null;
}
- return ApkVerityBuilder.generateFsverityRootHash(
+ return ApkVerityBuilder.generateApkVerityRootHash(
apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo);
}
}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index 758cd2b..939522d 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -534,11 +534,11 @@
NoSuchAlgorithmException {
try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
SignatureInfo signatureInfo = findSignature(apk);
- return ApkSigningBlockUtils.generateApkVerity(apkPath, bufferFactory, signatureInfo);
+ return ApkVerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
}
}
- static byte[] generateFsverityRootHash(String apkPath)
+ static byte[] generateApkVerityRootHash(String apkPath)
throws NoSuchAlgorithmException, DigestException, IOException,
SignatureNotFoundException {
try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
@@ -547,7 +547,7 @@
if (vSigner.verityRootHash == null) {
return null;
}
- return ApkVerityBuilder.generateFsverityRootHash(
+ return ApkVerityBuilder.generateApkVerityRootHash(
apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo);
}
}
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index de9f55b..a299b11 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -432,16 +432,16 @@
*
* @return FSverity root hash
*/
- public static byte[] generateFsverityRootHash(String apkPath)
+ public static byte[] generateApkVerityRootHash(String apkPath)
throws NoSuchAlgorithmException, DigestException, IOException {
// first try v3
try {
- return ApkSignatureSchemeV3Verifier.generateFsverityRootHash(apkPath);
+ return ApkSignatureSchemeV3Verifier.generateApkVerityRootHash(apkPath);
} catch (SignatureNotFoundException e) {
// try older version
}
try {
- return ApkSignatureSchemeV2Verifier.generateFsverityRootHash(apkPath);
+ return ApkSignatureSchemeV2Verifier.generateApkVerityRootHash(apkPath);
} catch (SignatureNotFoundException e) {
return null;
}
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index e247c87..081033a 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -332,7 +332,7 @@
try {
byte[] expectedRootHash = parseVerityDigestAndVerifySourceLength(expectedDigest,
apk.length(), signatureInfo);
- ApkVerityBuilder.ApkVerityResult verity = ApkVerityBuilder.generateApkVerity(apk,
+ ApkVerityBuilder.ApkVerityResult verity = ApkVerityBuilder.generateApkVerityTree(apk,
signatureInfo, new ByteBufferFactory() {
@Override
public ByteBuffer create(int capacity) {
@@ -348,26 +348,6 @@
}
/**
- * Generates the fsverity header and hash tree to be used by kernel for the given apk. This
- * method does not check whether the root hash exists in the Signing Block or not.
- *
- * <p>The output is stored in the {@link ByteBuffer} created by the given {@link
- * ByteBufferFactory}.
- *
- * @return the root hash of the generated hash tree.
- */
- public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory,
- SignatureInfo signatureInfo)
- throws IOException, SignatureNotFoundException, SecurityException, DigestException,
- NoSuchAlgorithmException {
- try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
- ApkVerityBuilder.ApkVerityResult result = ApkVerityBuilder.generateApkVerity(apk,
- signatureInfo, bufferFactory);
- return result.rootHash;
- }
- }
-
- /**
* 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.
diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java
index 2dd0117..553511d 100644
--- a/core/java/android/util/apk/ApkVerityBuilder.java
+++ b/core/java/android/util/apk/ApkVerityBuilder.java
@@ -16,6 +16,9 @@
package android.util.apk;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
@@ -26,8 +29,10 @@
import java.util.ArrayList;
/**
- * ApkVerityBuilder builds the APK verity tree and the verity header, which will be used by the
- * kernel to verity the APK content on access.
+ * ApkVerityBuilder builds the APK verity tree and the verity header. The generated tree format can
+ * be stored on disk for apk-verity setup and used by kernel. Note that since the current
+ * implementation is different from the upstream, we call this implementation apk-verity instead of
+ * fs-verity.
*
* <p>Unlike a regular Merkle tree, APK verity tree does not cover the content fully. Due to
* the existing APK format, it has to skip APK Signing Block and also has some special treatment for
@@ -47,26 +52,28 @@
private static final byte[] DEFAULT_SALT = new byte[8];
static class ApkVerityResult {
- public final ByteBuffer fsverityData;
+ public final ByteBuffer verityData;
+ public final int merkleTreeSize;
public final byte[] rootHash;
- ApkVerityResult(ByteBuffer fsverityData, byte[] rootHash) {
- this.fsverityData = fsverityData;
+ ApkVerityResult(ByteBuffer verityData, int merkleTreeSize, byte[] rootHash) {
+ this.verityData = verityData;
+ this.merkleTreeSize = merkleTreeSize;
this.rootHash = rootHash;
}
}
/**
- * Generates fsverity metadata and the Merkle tree into the {@link ByteBuffer} created by the
- * {@link ByteBufferFactory}. The bytes layout in the buffer will be used by the kernel and is
- * ready to be appended to the target file to set up fsverity. For fsverity to work, this data
- * must be placed at the next page boundary, and the caller must add additional padding in that
- * case.
+ * Generates the 4k, SHA-256 based Merkle tree for the given APK and stores in the {@link
+ * ByteBuffer} created by the {@link ByteBufferFactory}. The Merkle tree is suitable to be used
+ * as the on-disk format for apk-verity.
*
- * @return ApkVerityResult containing the fsverity data and the root hash of the Merkle tree.
+ * @return ApkVerityResult containing a buffer with the generated Merkle tree stored at the
+ * front, the tree size, and the calculated root hash.
*/
- static ApkVerityResult generateApkVerity(RandomAccessFile apk,
- SignatureInfo signatureInfo, ByteBufferFactory bufferFactory)
+ @NonNull
+ static ApkVerityResult generateApkVerityTree(@NonNull RandomAccessFile apk,
+ @Nullable SignatureInfo signatureInfo, @NonNull ByteBufferFactory bufferFactory)
throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
long signingBlockSize =
signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
@@ -76,86 +83,69 @@
ByteBuffer output = bufferFactory.create(
merkleTreeSize
- + CHUNK_SIZE_BYTES); // maximum size of fsverity metadata
+ + CHUNK_SIZE_BYTES); // maximum size of apk-verity metadata
output.order(ByteOrder.LITTLE_ENDIAN);
ByteBuffer tree = slice(output, 0, merkleTreeSize);
- ByteBuffer header = slice(output, merkleTreeSize,
- merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES);
- ByteBuffer extensions = slice(output, merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES,
- merkleTreeSize + CHUNK_SIZE_BYTES);
- byte[] apkDigestBytes = new byte[DIGEST_SIZE_BYTES];
- ByteBuffer apkDigest = ByteBuffer.wrap(apkDigestBytes);
- apkDigest.order(ByteOrder.LITTLE_ENDIAN);
+ byte[] apkRootHash = generateApkVerityTreeInternal(apk, signatureInfo, DEFAULT_SALT,
+ levelOffset, tree);
+ return new ApkVerityResult(output, merkleTreeSize, apkRootHash);
+ }
- // NB: Buffer limit is set inside once finished.
- calculateFsveritySignatureInternal(apk, signatureInfo, tree, apkDigest, header, extensions);
-
- // Put the reverse offset to fs-verity header at the end.
- output.position(merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES + extensions.limit());
- output.putInt(FSVERITY_HEADER_SIZE_BYTES + extensions.limit()
- + 4); // size of this integer right before EOF
- output.flip();
-
- return new ApkVerityResult(output, apkDigestBytes);
+ static void generateApkVerityFooter(@NonNull RandomAccessFile apk,
+ @NonNull SignatureInfo signatureInfo, @NonNull ByteBuffer footerOutput)
+ throws IOException {
+ footerOutput.order(ByteOrder.LITTLE_ENDIAN);
+ generateApkVerityHeader(footerOutput, apk.length(), DEFAULT_SALT);
+ long signingBlockSize =
+ signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
+ generateApkVerityExtensions(footerOutput, signatureInfo.apkSigningBlockOffset,
+ signingBlockSize, signatureInfo.eocdOffset);
}
/**
- * Calculates the fsverity root hash for integrity measurement. This needs to be consistent to
- * what kernel returns.
+ * Calculates the apk-verity root hash for integrity measurement. This needs to be consistent
+ * to what kernel returns.
*/
- static byte[] generateFsverityRootHash(RandomAccessFile apk, ByteBuffer apkDigest,
- SignatureInfo signatureInfo)
+ @NonNull
+ static byte[] generateApkVerityRootHash(@NonNull RandomAccessFile apk,
+ @NonNull ByteBuffer apkDigest, @NonNull SignatureInfo signatureInfo)
throws NoSuchAlgorithmException, DigestException, IOException {
- ByteBuffer verityBlock = ByteBuffer.allocate(CHUNK_SIZE_BYTES)
- .order(ByteOrder.LITTLE_ENDIAN);
- ByteBuffer header = slice(verityBlock, 0, FSVERITY_HEADER_SIZE_BYTES);
- ByteBuffer extensions = slice(verityBlock, FSVERITY_HEADER_SIZE_BYTES,
- CHUNK_SIZE_BYTES - FSVERITY_HEADER_SIZE_BYTES);
+ assertSigningBlockAlignedAndHasFullPages(signatureInfo);
- calculateFsveritySignatureInternal(apk, signatureInfo, null, null, header, extensions);
+ ByteBuffer footer = ByteBuffer.allocate(CHUNK_SIZE_BYTES).order(ByteOrder.LITTLE_ENDIAN);
+ generateApkVerityFooter(apk, signatureInfo, footer);
+ footer.flip();
MessageDigest md = MessageDigest.getInstance(JCA_DIGEST_ALGORITHM);
- md.update(header);
- md.update(extensions);
+ md.update(footer);
md.update(apkDigest);
return md.digest();
}
/**
- * Internal method to generate various parts of FSVerity constructs, including the header,
- * extensions, Merkle tree, and the tree's root hash. The output buffer is flipped to the
- * generated data size and is readey for consuming.
+ * Generates the apk-verity header and hash tree to be used by kernel for the given apk. This
+ * method does not check whether the root hash exists in the Signing Block or not.
+ *
+ * <p>The output is stored in the {@link ByteBuffer} created by the given {@link
+ * ByteBufferFactory}.
+ *
+ * @return the root hash of the generated hash tree.
*/
- private static void calculateFsveritySignatureInternal(
- RandomAccessFile apk, SignatureInfo signatureInfo, ByteBuffer treeOutput,
- ByteBuffer rootHashOutput, ByteBuffer headerOutput, ByteBuffer extensionsOutput)
- throws IOException, NoSuchAlgorithmException, DigestException {
- assertSigningBlockAlignedAndHasFullPages(signatureInfo);
- long signingBlockSize =
- signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
- long dataSize = apk.length() - signingBlockSize;
- int[] levelOffset = calculateVerityLevelOffset(dataSize);
-
- if (treeOutput != null) {
- byte[] apkRootHash = generateApkVerityTree(apk, signatureInfo, DEFAULT_SALT,
- levelOffset, treeOutput);
- if (rootHashOutput != null) {
- rootHashOutput.put(apkRootHash);
- rootHashOutput.flip();
- }
- }
-
- if (headerOutput != null) {
- headerOutput.order(ByteOrder.LITTLE_ENDIAN);
- generateFsverityHeader(headerOutput, apk.length(), levelOffset.length - 1,
- DEFAULT_SALT);
- }
-
- if (extensionsOutput != null) {
- extensionsOutput.order(ByteOrder.LITTLE_ENDIAN);
- generateFsverityExtensions(extensionsOutput, signatureInfo.apkSigningBlockOffset,
- signingBlockSize, signatureInfo.eocdOffset);
+ @NonNull
+ static byte[] generateApkVerity(@NonNull String apkPath,
+ @NonNull ByteBufferFactory bufferFactory, @NonNull SignatureInfo signatureInfo)
+ throws IOException, SignatureNotFoundException, SecurityException, DigestException,
+ NoSuchAlgorithmException {
+ try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+ ApkVerityResult result = generateApkVerityTree(apk, signatureInfo, bufferFactory);
+ ByteBuffer footer = slice(result.verityData, result.merkleTreeSize,
+ result.verityData.limit());
+ generateApkVerityFooter(apk, signatureInfo, footer);
+ // Put the reverse offset to apk-verity header at the end.
+ footer.putInt(footer.position() + 4);
+ result.verityData.limit(result.merkleTreeSize + footer.position());
+ return result.rootHash;
}
}
@@ -297,9 +287,13 @@
digester.fillUpLastOutputChunk();
}
- private static byte[] generateApkVerityTree(RandomAccessFile apk, SignatureInfo signatureInfo,
- byte[] salt, int[] levelOffset, ByteBuffer output)
+ @NonNull
+ private static byte[] generateApkVerityTreeInternal(@NonNull RandomAccessFile apk,
+ @Nullable SignatureInfo signatureInfo, @NonNull byte[] salt,
+ @NonNull int[] levelOffset, @NonNull ByteBuffer output)
throws IOException, NoSuchAlgorithmException, DigestException {
+ assertSigningBlockAlignedAndHasFullPages(signatureInfo);
+
// 1. Digest the apk to generate the leaf level hashes.
generateApkVerityDigestAtLeafLevel(apk, signatureInfo, salt, slice(output,
levelOffset[levelOffset.length - 2], levelOffset[levelOffset.length - 1]));
@@ -324,7 +318,7 @@
return rootHash;
}
- private static ByteBuffer generateFsverityHeader(ByteBuffer buffer, long fileSize, int depth,
+ private static ByteBuffer generateApkVerityHeader(ByteBuffer buffer, long fileSize,
byte[] salt) {
if (salt.length != 8) {
throw new IllegalArgumentException("salt is not 8 bytes long");
@@ -351,13 +345,12 @@
buffer.put(salt); // salt (8 bytes)
skip(buffer, 22); // reserved
- buffer.flip();
return buffer;
}
- private static ByteBuffer generateFsverityExtensions(ByteBuffer buffer, long signingBlockOffset,
- long signingBlockSize, long eocdOffset) {
- // Snapshot of the FSVerity structs (subject to change once upstreamed).
+ private static ByteBuffer generateApkVerityExtensions(ByteBuffer buffer,
+ long signingBlockOffset, long signingBlockSize, long eocdOffset) {
+ // Snapshot of the experimental fs-verity structs (different from upstream).
//
// struct fsverity_extension_elide {
// __le64 offset;
@@ -409,7 +402,6 @@
skip(buffer, kPadding); // padding
}
- buffer.flip();
return buffer;
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index bfe1e95..3bab87a 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -17,8 +17,6 @@
package android.view;
import com.android.internal.os.IResultReceiver;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodClient;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
@@ -71,8 +69,7 @@
boolean stopViewServer(); // Transaction #2
boolean isViewServerRunning(); // Transaction #3
- IWindowSession openSession(in IWindowSessionCallback callback, in IInputMethodClient client,
- in IInputContext inputContext);
+ IWindowSession openSession(in IWindowSessionCallback callback);
void getInitialDisplaySize(int displayId, out Point size);
void getBaseDisplaySize(int displayId, out Point size);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c29fbbb..19e3f7f62 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7315,7 +7315,7 @@
// Here we check whether we still need the default focus highlight, and switch it on/off.
switchDefaultFocusHighlight();
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
if (!gainFocus) {
if (isPressed()) {
setPressed(false);
@@ -8523,6 +8523,11 @@
}
if (importance == IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS
|| importance == IMPORTANT_FOR_AUTOFILL_NO) {
+ if (Log.isLoggable(AUTOFILL_LOG_TAG, Log.VERBOSE)) {
+ Log.v(AUTOFILL_LOG_TAG, "View (autofillId=" + getAutofillViewId() + ", "
+ + getClass() + ") is not important for autofill because its "
+ + "importance is " + importance);
+ }
return false;
}
@@ -12476,7 +12481,7 @@
mPrivateFlags3 &= ~PFLAG3_TEMPORARY_DETACH;
onFinishTemporaryDetach();
if (hasWindowFocus() && hasFocus()) {
- InputMethodManager.getInstance().focusIn(this);
+ getContext().getSystemService(InputMethodManager.class).focusIn(this);
}
notifyEnterOrExitForAutoFillIfNeeded(true);
}
@@ -12867,7 +12872,7 @@
* focus, false otherwise.
*/
public void onWindowFocusChanged(boolean hasWindowFocus) {
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
if (!hasWindowFocus) {
if (isPressed()) {
setPressed(false);
@@ -17932,7 +17937,7 @@
rebuildOutline();
if (isFocused()) {
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
if (imm != null) {
imm.focusIn(this);
}
@@ -18515,7 +18520,7 @@
onDetachedFromWindow();
onDetachedFromWindowInternal();
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
if (imm != null) {
imm.onViewDetachedFromWindow(this);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 16d202b..2ee7ab9 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2579,7 +2579,7 @@
.mayUseInputMethod(mWindowAttributes.flags);
if (imTarget != mLastWasImTarget) {
mLastWasImTarget = imTarget;
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
if (imm != null && imTarget) {
imm.onPreWindowFocus(mView, hasWindowFocus);
imm.onPostWindowFocus(mView, mView.findFocus(),
@@ -2695,7 +2695,7 @@
mLastWasImTarget = WindowManager.LayoutParams
.mayUseInputMethod(mWindowAttributes.flags);
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
imm.onPreWindowFocus(mView, hasWindowFocus);
}
@@ -4329,7 +4329,7 @@
enqueueInputEvent(event, null, 0, true);
} break;
case MSG_CHECK_FOCUS: {
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
if (imm != null) {
imm.checkFocus();
}
@@ -4871,7 +4871,7 @@
@Override
protected int onProcess(QueuedInputEvent q) {
if (mLastWasImTarget && !isInLocalFocusMode()) {
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
if (imm != null) {
final InputEvent event = q.mEvent;
if (DEBUG_IMF) Log.v(mTag, "Sending input event to IME: " + event);
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index a7ec6df..982737a 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -25,6 +25,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StyleRes;
+import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.app.WindowConfiguration;
import android.content.Context;
@@ -1079,8 +1080,18 @@
setFlags(flags, flags);
}
- /** @hide */
- @UnsupportedAppUsage
+ /**
+ * Add private flag bits.
+ *
+ * <p>Refer to the individual flags for the permissions needed.
+ *
+ * <p>Note: Only for updateable system components (aka. mainline modules)
+ *
+ * @param flags The flag bits to add.
+ *
+ * @hide
+ */
+ @SystemApi
public void addPrivateFlags(int flags) {
setPrivateFlags(flags, flags);
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 0404df0..742df5e8 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1668,6 +1668,7 @@
* this window is visible.
* @hide
*/
+ @SystemApi
@RequiresPermission(permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS)
public static final int PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 0x00080000;
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 4ca9a14..92d145c3 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -196,7 +196,11 @@
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
- InputMethodManager imm = InputMethodManager.getInstance();
+ if (InputMethodManager.ENABLE_LEGACY_EAGER_INITIALIZATION) {
+ // Emulate the legacy behavior. The global instance of InputMethodManager
+ // was instantiated here.
+ InputMethodManager.getInstance();
+ }
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@@ -204,8 +208,7 @@
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
- },
- imm.getClient(), imm.getInputContext());
+ });
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -468,7 +471,7 @@
View view = root.getView();
if (view != null) {
- InputMethodManager imm = InputMethodManager.getInstance();
+ InputMethodManager imm = view.getContext().getSystemService(InputMethodManager.class);
if (imm != null) {
imm.windowDismissed(mViews.get(index).getWindowToken());
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index a3fa2ce..4d3f0fc 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -632,6 +632,8 @@
private static final int BOOLEAN_PROPERTY_IS_HEADING = 0x0200000;
+ private static final int BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY = 0x0400000;
+
/**
* Bits that provide the id of a virtual descendant of a view.
*/
@@ -2461,6 +2463,30 @@
}
/**
+ * Returns whether node represents a text entry key that is part of a keyboard or keypad.
+ *
+ * @return {@code true} if the node is a text entry key., {@code false} otherwise.
+ */
+ public boolean isTextEntryKey() {
+ return getBooleanProperty(BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY);
+ }
+
+ /**
+ * Sets whether the node represents a text entry key that is part of a keyboard or keypad.
+ *
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param isTextEntryKey {@code true} if the node is a text entry key, {@code false} otherwise.
+ */
+ public void setTextEntryKey(boolean isTextEntryKey) {
+ setBooleanProperty(BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY, isTextEntryKey);
+ }
+
+ /**
* Gets the package this node comes from.
*
* @return The package name.
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 9419e93..e87048e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -45,6 +45,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Slog;
import android.util.SparseArray;
import android.view.Choreographer;
import android.view.KeyEvent;
@@ -559,6 +560,9 @@
// different bridge based on which activity is currently focused
// in the current process. Since compat would be rarely used, just
// create and register a new instance every time.
+ if (sDebug) {
+ Slog.d(TAG, "creating CompatibilityBridge for " + mContext);
+ }
mCompatibilityBridge = new CompatibilityBridge();
}
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 508509a..9ede57f 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -57,7 +57,6 @@
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.autofill.AutofillManager;
-import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import com.android.internal.os.SomeArgs;
import com.android.internal.view.IInputConnectionWrapper;
@@ -226,6 +225,44 @@
static final String PENDING_EVENT_COUNTER = "aq:imm";
+ /**
+ * {@code true} if we want to instantiate {@link InputMethodManager} eagerly in
+ * {@link android.view.WindowManagerGlobal#getWindowSession()}, which is often called in an
+ * early stage of process startup, which is how Android has worked.
+ *
+ * <p>We still have this settings because we know there are possible compatibility concerns if
+ * we stop doing so. Here are scenarios we know and there could be more scenarios we are not
+ * aware of right know.</p>
+ *
+ * <ul>
+ * <li>Apps that directly access {@link #sInstance} via reflection, which is currently
+ * allowed because of {@link UnsupportedAppUsage} annotation. Currently
+ * {@link android.view.WindowManagerGlobal#getWindowSession()} is likely to guarantee that
+ * {@link #sInstance} is not {@code null} when such an app is accessing it, but removing
+ * that code from {@link android.view.WindowManagerGlobal#getWindowSession()} can reveal
+ * untested code paths in their apps, which probably happen in an early startup time of that
+ * app.</li>
+ * <li>Apps that directly access {@link #peekInstance()} via reflection, which is currently
+ * allowed because of {@link UnsupportedAppUsage} annotation. Currently
+ * {@link android.view.WindowManagerGlobal#getWindowSession()} is likely to guarantee that
+ * {@link #peekInstance()} returns non-{@code null} object when such an app is calling
+ * {@link #peekInstance()}, but removing that code from
+ * {@link android.view.WindowManagerGlobal#getWindowSession()} can reveal untested code
+ * paths in their apps, which probably happen in an early startup time of that app. The good
+ * news is that unlike {@link #sInstance}'s case we can at least work around this scenario
+ * by changing the semantics of {@link #peekInstance()}, which is currently defined as
+ * "retrieve the global {@link InputMethodManager} instance, if it exists" to something that
+ * always returns non-{@code null} {@link InputMethodManager}. However, introducing such an
+ * workaround can also trigger different compatibility issues if {@link #peekInstance()} was
+ * called before {@link android.view.WindowManagerGlobal#getWindowSession()} and it expected
+ * {@link #peekInstance()} to return {@code null} as written in the JavaDoc.</li>
+ * </ul>
+ *
+ * <p>TODO(Bug 116157766): Check if we can set {@code false} here then remove this settings.</p>
+ * @hide
+ */
+ public static final boolean ENABLE_LEGACY_EAGER_INITIALIZATION = true;
+
@UnsupportedAppUsage
static InputMethodManager sInstance;
@@ -387,9 +424,6 @@
final Pool<PendingEvent> mPendingEventPool = new SimplePool<>(20);
final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20);
- private final InputMethodPrivilegedOperationsRegistry mPrivOpsRegistry =
- new InputMethodPrivilegedOperationsRegistry();
-
// -----------------------------------------------------------
static final int MSG_DUMP = 1;
@@ -655,8 +689,10 @@
synchronized (InputMethodManager.class) {
if (sInstance == null) {
try {
- sInstance = new InputMethodManager(Looper.getMainLooper());
- } catch (ServiceNotFoundException e) {
+ final InputMethodManager imm = new InputMethodManager(Looper.getMainLooper());
+ imm.mService.addClient(imm.mClient, imm.mIInputContext);
+ sInstance = imm;
+ } catch (ServiceNotFoundException | RemoteException e) {
throw new IllegalStateException(e);
}
}
@@ -665,10 +701,12 @@
}
/**
- * Private optimization: retrieve the global InputMethodManager instance,
- * if it exists.
+ * Private optimization: retrieve the global InputMethodManager instance, if it exists.
* @hide
+ * @deprecated Use {@link Context#getSystemService(Class)} instead. This method cannot fully
+ * support multi-display scenario.
*/
+ @Deprecated
@UnsupportedAppUsage
public static InputMethodManager peekInstance() {
return sInstance;
@@ -739,7 +777,7 @@
*/
@Deprecated
public void showStatusIcon(IBinder imeToken, String packageName, @DrawableRes int iconId) {
- mPrivOpsRegistry.get(imeToken).updateStatusIcon(packageName, iconId);
+ InputMethodPrivilegedOperationsRegistry.get(imeToken).updateStatusIcon(packageName, iconId);
}
/**
@@ -749,7 +787,7 @@
*/
@Deprecated
public void hideStatusIcon(IBinder imeToken) {
- mPrivOpsRegistry.get(imeToken).updateStatusIcon(null, 0);
+ InputMethodPrivilegedOperationsRegistry.get(imeToken).updateStatusIcon(null, 0);
}
/** @hide */
@@ -1350,6 +1388,10 @@
mServedView.getWindowToken() == appWindowToken) {
finishInputLocked();
}
+ if (mCurRootView != null &&
+ mCurRootView.getWindowToken() == appWindowToken) {
+ mCurRootView = null;
+ }
}
}
@@ -1790,7 +1832,7 @@
public void setInputMethod(IBinder token, String id) {
if (token == null) {
// Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
- // Thus we cannot always rely on mPrivOpsRegistry unfortunately.
+ // Thus we cannot always rely on InputMethodPrivilegedOperationsRegistry unfortunately.
// TODO(Bug 114488811): Consider deprecating null token rule.
try {
mService.setInputMethod(token, id);
@@ -1799,7 +1841,7 @@
}
return;
}
- mPrivOpsRegistry.get(token).setInputMethod(id);
+ InputMethodPrivilegedOperationsRegistry.get(token).setInputMethod(id);
}
/**
@@ -1819,7 +1861,7 @@
public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
if (token == null) {
// Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
- // Thus we cannot always rely on mPrivOpsRegistry unfortunately.
+ // Thus we cannot always rely on InputMethodPrivilegedOperationsRegistry unfortunately.
// TODO(Bug 114488811): Consider deprecating null token rule.
try {
mService.setInputMethodAndSubtype(token, id, subtype);
@@ -1828,7 +1870,7 @@
}
return;
}
- mPrivOpsRegistry.get(token).setInputMethodAndSubtype(id, subtype);
+ InputMethodPrivilegedOperationsRegistry.get(token).setInputMethodAndSubtype(id, subtype);
}
/**
@@ -1848,7 +1890,7 @@
*/
@Deprecated
public void hideSoftInputFromInputMethod(IBinder token, int flags) {
- mPrivOpsRegistry.get(token).hideMySoftInput(flags);
+ InputMethodPrivilegedOperationsRegistry.get(token).hideMySoftInput(flags);
}
/**
@@ -1869,7 +1911,7 @@
*/
@Deprecated
public void showSoftInputFromInputMethod(IBinder token, int flags) {
- mPrivOpsRegistry.get(token).showMySoftInput(flags);
+ InputMethodPrivilegedOperationsRegistry.get(token).showMySoftInput(flags);
}
/**
@@ -2229,7 +2271,7 @@
public boolean switchToLastInputMethod(IBinder imeToken) {
if (imeToken == null) {
// Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
- // Thus we cannot always rely on mPrivOpsRegistry unfortunately.
+ // Thus we cannot always rely on InputMethodPrivilegedOperationsRegistry unfortunately.
// TODO(Bug 114488811): Consider deprecating null token rule.
try {
return mService.switchToPreviousInputMethod(imeToken);
@@ -2237,7 +2279,7 @@
throw e.rethrowFromSystemServer();
}
}
- return mPrivOpsRegistry.get(imeToken).switchToPreviousInputMethod();
+ return InputMethodPrivilegedOperationsRegistry.get(imeToken).switchToPreviousInputMethod();
}
/**
@@ -2257,7 +2299,7 @@
public boolean switchToNextInputMethod(IBinder imeToken, boolean onlyCurrentIme) {
if (imeToken == null) {
// Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
- // Thus we cannot always rely on mPrivOpsRegistry unfortunately.
+ // Thus we cannot always rely on InputMethodPrivilegedOperationsRegistry unfortunately.
// TODO(Bug 114488811): Consider deprecating null token rule.
try {
return mService.switchToNextInputMethod(imeToken, onlyCurrentIme);
@@ -2265,7 +2307,8 @@
throw e.rethrowFromSystemServer();
}
}
- return mPrivOpsRegistry.get(imeToken).switchToNextInputMethod(onlyCurrentIme);
+ return InputMethodPrivilegedOperationsRegistry.get(imeToken)
+ .switchToNextInputMethod(onlyCurrentIme);
}
/**
@@ -2284,7 +2327,8 @@
*/
@Deprecated
public boolean shouldOfferSwitchingToNextInputMethod(IBinder imeToken) {
- return mPrivOpsRegistry.get(imeToken).shouldOfferSwitchingToNextInputMethod();
+ return InputMethodPrivilegedOperationsRegistry.get(imeToken)
+ .shouldOfferSwitchingToNextInputMethod();
}
/**
@@ -2420,34 +2464,4 @@
sb.append(",temporaryDetach=" + view.isTemporarilyDetached());
return sb.toString();
}
-
- /**
- * Called by {@link InputMethodService} so that API calls to deprecated ones defined in this
- * class can be forwarded to {@link InputMethodPrivilegedOperations}.
- *
- * <p>Note: this method does not hold strong references to {@code token} and {@code ops}. The
- * registry entry will be automatically cleared after {@code token} is garbage collected.</p>
- *
- * @param token IME token that is associated with {@code ops}
- * @param ops {@link InputMethodPrivilegedOperations} that is associated with {@code token}
- * @hide
- */
- public void registerInputMethodPrivOps(IBinder token, InputMethodPrivilegedOperations ops) {
- mPrivOpsRegistry.put(token, ops);
- }
-
- /**
- * Called from {@link InputMethodService#onDestroy()} to make sure that deprecated IME APIs
- * defined in this class can no longer access to {@link InputMethodPrivilegedOperations}.
- *
- * <p>Note: Calling this method is optional, but at least gives more explict error message in
- * logcat when IME developers are doing something unsupported (e.g. trying to call IME APIs
- * after {@link InputMethodService#onDestroy()}).</p>
- *
- * @param token IME token to be removed.
- * @hide
- */
- public void unregisterInputMethodPrivOps(IBinder token) {
- mPrivOpsRegistry.remove(token);
- }
}
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index cbd624e..7d6564f 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -1158,7 +1158,7 @@
* <p>Closes the drop down if present on screen.</p>
*/
public void dismissDropDown() {
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
if (imm != null) {
imm.displayCompletions(this, null);
}
@@ -1247,7 +1247,7 @@
private void buildImeCompletions() {
final ListAdapter adapter = mAdapter;
if (adapter != null) {
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
if (imm != null) {
final int count = Math.min(adapter.getCount(), 20);
CompletionInfo[] completions = new CompletionInfo[count];
diff --git a/core/java/android/widget/CursorAdapter.java b/core/java/android/widget/CursorAdapter.java
index cc8b550..e250f63 100644
--- a/core/java/android/widget/CursorAdapter.java
+++ b/core/java/android/widget/CursorAdapter.java
@@ -365,7 +365,7 @@
* closed.
*
* @param newCursor The new cursor to be used.
- * @return Returns the previously set Cursor, or null if there wasa not one.
+ * @return Returns the previously set Cursor, or null if there was not one.
* If the given new Cursor is the same instance is the previously set
* Cursor, null is also returned.
*/
diff --git a/core/java/android/widget/DatePickerSpinnerDelegate.java b/core/java/android/widget/DatePickerSpinnerDelegate.java
index f88a4e2..5f15110 100644
--- a/core/java/android/widget/DatePickerSpinnerDelegate.java
+++ b/core/java/android/widget/DatePickerSpinnerDelegate.java
@@ -639,7 +639,7 @@
// changed the value via the IME and there is a next input the IME will
// be shown, otherwise the user chose another means of changing the
// value and having the IME up makes no sense.
- InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
+ InputMethodManager inputMethodManager = mContext.getSystemService(InputMethodManager.class);
if (inputMethodManager != null) {
if (inputMethodManager.isActive(mYearSpinnerInput)) {
mYearSpinnerInput.clearFocus();
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 4428598..8dd30f6 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1539,6 +1539,10 @@
}
}
+ private InputMethodManager getInputMethodManager() {
+ return mTextView.getContext().getSystemService(InputMethodManager.class);
+ }
+
public void beginBatchEdit() {
mInBatchEditControllers = true;
final InputMethodState ims = mInputMethodState;
@@ -1707,7 +1711,7 @@
if (req == null) {
return false;
}
- final InputMethodManager imm = InputMethodManager.peekInstance();
+ final InputMethodManager imm = getInputMethodManager();
if (imm == null) {
return false;
}
@@ -1742,7 +1746,7 @@
private void sendUpdateSelection() {
if (null != mInputMethodState && mInputMethodState.mBatchEditNesting <= 0) {
- final InputMethodManager imm = InputMethodManager.peekInstance();
+ final InputMethodManager imm = getInputMethodManager();
if (null != imm) {
final int selectionStart = mTextView.getSelectionStart();
final int selectionEnd = mTextView.getSelectionEnd();
@@ -1768,7 +1772,7 @@
final InputMethodState ims = mInputMethodState;
if (ims != null && ims.mBatchEditNesting == 0) {
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getInputMethodManager();
if (imm != null) {
if (imm.isActive(mTextView)) {
if (ims.mContentChanged || ims.mSelectionModeChanged) {
@@ -2245,7 +2249,7 @@
&& mTextView.isTextEditable() && !mTextView.isTextSelectable()
&& mShowSoftInputOnFocus) {
// Show the IME to be able to replace text, except when selecting non editable text.
- final InputMethodManager imm = InputMethodManager.peekInstance();
+ final InputMethodManager imm = getInputMethodManager();
if (imm != null) {
imm.showSoftInput(mTextView, 0, null);
}
@@ -2255,7 +2259,7 @@
private boolean extractedTextModeWillBeStarted() {
if (!(mTextView.isInExtractedMode())) {
- final InputMethodManager imm = InputMethodManager.peekInstance();
+ final InputMethodManager imm = getInputMethodManager();
return imm != null && imm.isFullscreenMode();
}
return false;
@@ -4272,7 +4276,7 @@
if (ims == null || ims.mBatchEditNesting > 0) {
return;
}
- final InputMethodManager imm = InputMethodManager.peekInstance();
+ final InputMethodManager imm = getInputMethodManager();
if (null == imm) {
return;
}
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index b6ed22c..a28cc40 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -1293,7 +1293,8 @@
* Shows the soft input for its input text.
*/
private void showSoftInput() {
- InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
+ InputMethodManager inputMethodManager =
+ getContext().getSystemService(InputMethodManager.class);
if (inputMethodManager != null) {
if (mHasSelectorWheel) {
mInputText.setVisibility(View.VISIBLE);
@@ -1307,7 +1308,8 @@
* Hides the soft input if it is active for the input text.
*/
private void hideSoftInput() {
- InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
+ InputMethodManager inputMethodManager =
+ getContext().getSystemService(InputMethodManager.class);
if (inputMethodManager != null && inputMethodManager.isActive(mInputText)) {
inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
}
diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java
index 9a24061..c78f4ac 100644
--- a/core/java/android/widget/ShareActionProvider.java
+++ b/core/java/android/widget/ShareActionProvider.java
@@ -268,7 +268,7 @@
* Intent shareIntent = new Intent(Intent.ACTION_SEND);
* shareIntent.setType("image/*");
* Uri uri = Uri.fromFile(new File(getFilesDir(), "foo.jpg"));
- * shareIntent.putExtra(Intent.EXTRA_STREAM, uri));</pre>
+ * shareIntent.putExtra(Intent.EXTRA_STREAM, uri);</pre>
*
* @param shareIntent The share intent.
*
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index a86e6f8..bbfac44 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -2156,7 +2156,7 @@
if (!enabled) {
// Hide the soft input if the currently active TextView is disabled
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getInputMethodManager();
if (imm != null && imm.isActive(this)) {
imm.hideSoftInputFromWindow(getWindowToken(), 0);
}
@@ -2166,7 +2166,7 @@
if (enabled) {
// Make sure IME is updated with current editor info.
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getInputMethodManager();
if (imm != null) imm.restartInput(this);
}
@@ -2392,7 +2392,7 @@
if (mEditor != null) mEditor.mInputType = EditorInfo.TYPE_NULL;
}
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getInputMethodManager();
if (imm != null) imm.restartInput(this);
}
@@ -5769,7 +5769,7 @@
Editable t = mEditableFactory.newEditable(text);
text = t;
setFilters(t, mFilters);
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getInputMethodManager();
if (imm != null) imm.restartInput(this);
} else if (precomputed != null) {
if (mTextDir == null) {
@@ -6148,7 +6148,7 @@
setTextInternal(removeSuggestionSpans(mText));
}
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getInputMethodManager();
if (imm != null) imm.restartInput(this);
}
@@ -6436,7 +6436,7 @@
return;
} else if (actionCode == EditorInfo.IME_ACTION_DONE) {
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getInputMethodManager();
if (imm != null && imm.isActive(this)) {
imm.hideSoftInputFromWindow(getWindowToken(), 0);
}
@@ -7902,7 +7902,7 @@
if (!hasOnClickListeners()) {
if (mMovement != null && mText instanceof Editable
&& mLayout != null && onCheckIsTextEditor()) {
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getInputMethodManager();
viewClicked(imm);
if (imm != null && getShowSoftInputOnFocus()) {
imm.showSoftInput(this, 0);
@@ -7956,7 +7956,7 @@
& KeyEvent.FLAG_EDITOR_ACTION) != 0) {
// No target for next focus, but make sure the IME
// if this came from it.
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getInputMethodManager();
if (imm != null && imm.isActive(this)) {
imm.hideSoftInputFromWindow(getWindowToken(), 0);
}
@@ -10260,7 +10260,7 @@
if (touchIsFinished && (isTextEditable() || textIsSelectable)) {
// Show the IME, except when selecting in read-only text.
- final InputMethodManager imm = InputMethodManager.peekInstance();
+ final InputMethodManager imm = getInputMethodManager();
viewClicked(imm);
if (isTextEditable() && mEditor.mShowSoftInputOnFocus && imm != null) {
imm.showSoftInput(this, 0);
@@ -11299,7 +11299,7 @@
// Show the IME, except when selecting in read-only text.
if ((mMovement != null || onCheckIsTextEditor()) && hasSpannableText() && mLayout != null
&& (isTextEditable() || isTextSelectable()) && isFocused()) {
- final InputMethodManager imm = InputMethodManager.peekInstance();
+ final InputMethodManager imm = getInputMethodManager();
viewClicked(imm);
if (!isTextSelectable() && mEditor.mShowSoftInputOnFocus && imm != null) {
handled |= imm.showSoftInput(this, 0);
@@ -11367,13 +11367,17 @@
sendAccessibilityEventUnchecked(event);
}
+ private InputMethodManager getInputMethodManager() {
+ return getContext().getSystemService(InputMethodManager.class);
+ }
+
/**
* Returns whether this text view is a current input method target. The
* default implementation just checks with {@link InputMethodManager}.
* @return True if the TextView is a current input method target; false otherwise.
*/
public boolean isInputMethodTarget() {
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getInputMethodManager();
return imm != null && imm.isActive(this);
}
@@ -12491,11 +12495,11 @@
advancesIndex);
}
- public int getTextRunCursor(int contextStart, int contextEnd, int dir,
+ public int getTextRunCursor(int contextStart, int contextEnd, boolean isRtl,
int offset, int cursorOpt, Paint p) {
int contextCount = contextEnd - contextStart;
return p.getTextRunCursor(mChars, contextStart + mStart,
- contextCount, dir, offset + mStart, cursorOpt);
+ contextCount, isRtl, offset + mStart, cursorOpt);
}
}
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 77670b3..6a3a8f0 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -269,7 +269,7 @@
mRadialTimePickerModeButton.setContentDescription(
mTextInputPickerModeEnabledDescription);
updateTextInputPicker();
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
if (imm != null) {
imm.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0);
}
diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java
index cc79b9c..83c86d5 100644
--- a/core/java/android/widget/TimePickerSpinnerDelegate.java
+++ b/core/java/android/widget/TimePickerSpinnerDelegate.java
@@ -471,7 +471,7 @@
// changed the value via the IME and there is a next input the IME will
// be shown, otherwise the user chose another means of changing the
// value and having the IME up makes no sense.
- InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
+ InputMethodManager inputMethodManager = mContext.getSystemService(InputMethodManager.class);
if (inputMethodManager != null) {
if (inputMethodManager.isActive(mHourSpinnerInput)) {
mHourSpinnerInput.clearFocus();
diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java
index 9171959..0f8295a 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -16,8 +16,7 @@
package com.android.internal.app;
-import com.android.internal.R;
-
+import android.annotation.NonNull;
import android.app.SearchManager;
import android.content.ComponentName;
import android.content.Context;
@@ -32,6 +31,9 @@
import android.provider.Settings;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.Set;
+
/**
* Utility method for dealing with the assistant aspects of
* {@link com.android.internal.app.IVoiceInteractionManagerService IVoiceInteractionManagerService}.
@@ -62,6 +64,30 @@
return false;
}
+ /**
+ * Checks the availability of a set of voice actions for the current active voice service.
+ *
+ * @param voiceActions A set of supported voice actions to be checked.
+ * @param callback The callback which will deliver a set of supported voice actions. If
+ * no voice actions are supported for the given voice action set, then null
+ * or empty set is provided.
+ */
+ public void getActiveServiceSupportedActions(@NonNull Set<String> voiceActions,
+ @NonNull IVoiceActionCheckCallback callback) {
+ try {
+ if (mVoiceInteractionManagerService != null) {
+ mVoiceInteractionManagerService
+ .getActiveServiceSupportedActions(new ArrayList<>(voiceActions), callback);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to call activeServiceSupportedActions", e);
+ try {
+ callback.onComplete(null);
+ } catch (RemoteException re) {
+ }
+ }
+ }
+
public void launchVoiceAssistFromKeyguard() {
try {
if (mVoiceInteractionManagerService != null) {
@@ -157,7 +183,7 @@
return getActiveServiceComponentName();
}
final SearchManager searchManager =
- (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
+ (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
if (searchManager == null) {
return null;
}
diff --git a/packages/SystemUI/res/values-h600dp/config.xml b/core/java/com/android/internal/app/IVoiceActionCheckCallback.aidl
similarity index 63%
rename from packages/SystemUI/res/values-h600dp/config.xml
rename to core/java/com/android/internal/app/IVoiceActionCheckCallback.aidl
index 8616e3e..66ba93d 100644
--- a/packages/SystemUI/res/values-h600dp/config.xml
+++ b/core/java/com/android/internal/app/IVoiceActionCheckCallback.aidl
@@ -1,22 +1,21 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (c) 2017, The Android Open Source Project
+/*
+ * 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
+ * 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.
-*/
--->
+ */
-<resources>
- <!-- The number of rows in the QuickSettings -->
- <integer name="quick_settings_num_rows">3</integer>
-</resources>
+package com.android.internal.app;
+
+oneway interface IVoiceActionCheckCallback {
+ void onComplete(in List<String> voiceActions);
+}
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index ff75a8b..5088cca 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -20,6 +20,7 @@
import android.content.Intent;
import android.os.Bundle;
+import com.android.internal.app.IVoiceActionCheckCallback;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.IVoiceInteractionSessionListener;
@@ -143,4 +144,11 @@
* Register a voice interaction listener.
*/
void registerVoiceInteractionSessionListener(IVoiceInteractionSessionListener listener);
+
+ /**
+ * Checks the availability of a set of voice actions for the current active voice service.
+ * Returns all supported voice actions.
+ */
+ void getActiveServiceSupportedActions(in List<String> voiceActions,
+ in IVoiceActionCheckCallback callback);
}
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index b33a5c4..81dab2f 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -232,6 +232,7 @@
displayName = FileUtils.buildValidFatFilename(displayName);
final File before = getFileForDocId(docId);
+ final File beforeVisibleFile = getFileForDocId(docId, true);
final File after = FileUtils.buildUniqueFile(before.getParentFile(), displayName);
if (!before.renameTo(after)) {
throw new IllegalStateException("Failed to rename to " + after);
@@ -241,7 +242,6 @@
onDocIdChanged(docId);
onDocIdChanged(afterDocId);
- final File beforeVisibleFile = getFileForDocId(docId, true);
final File afterVisibleFile = getFileForDocId(afterDocId, true);
moveInMediaStore(beforeVisibleFile, afterVisibleFile);
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry.java
index 3255153..1436aed 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry.java
@@ -29,12 +29,19 @@
/**
* A weak-reference-based mapper from IME token to {@link InputMethodPrivilegedOperations} that is
* used only to support deprecated IME APIs in {@link android.view.inputmethod.InputMethodManager}.
+ *
+ * <p>This class is designed to be used as a per-process global registry.</p>
*/
public final class InputMethodPrivilegedOperationsRegistry {
- private final Object mLock = new Object();
- @GuardedBy("mLock")
- private final WeakHashMap<IBinder, WeakReference<InputMethodPrivilegedOperations>>
- mRegistry = new WeakHashMap<>();
+ private InputMethodPrivilegedOperationsRegistry() {
+ // Not intended to be instantiated.
+ }
+
+ private static final Object sLock = new Object();
+
+ @Nullable
+ @GuardedBy("sLock")
+ private static WeakHashMap<IBinder, WeakReference<InputMethodPrivilegedOperations>> sRegistry;
@Nullable
private static InputMethodPrivilegedOperations sNop;
@@ -62,10 +69,13 @@
* @param ops {@link InputMethodPrivilegedOperations} to be associated with the given IME token
*/
@AnyThread
- public void put(IBinder token, InputMethodPrivilegedOperations ops) {
- synchronized (mLock) {
+ public static void put(IBinder token, InputMethodPrivilegedOperations ops) {
+ synchronized (sLock) {
+ if (sRegistry == null) {
+ sRegistry = new WeakHashMap<>();
+ }
final WeakReference<InputMethodPrivilegedOperations> previousOps =
- mRegistry.put(token, new WeakReference<>(ops));
+ sRegistry.put(token, new WeakReference<>(ops));
if (previousOps != null) {
throw new IllegalStateException(previousOps.get() + " is already registered for "
+ " this token=" + token + " newOps=" + ops);
@@ -84,9 +94,12 @@
*/
@NonNull
@AnyThread
- public InputMethodPrivilegedOperations get(IBinder token) {
- synchronized (mLock) {
- final WeakReference<InputMethodPrivilegedOperations> wrapperRef = mRegistry.get(token);
+ public static InputMethodPrivilegedOperations get(IBinder token) {
+ synchronized (sLock) {
+ if (sRegistry == null) {
+ return getNopOps();
+ }
+ final WeakReference<InputMethodPrivilegedOperations> wrapperRef = sRegistry.get(token);
if (wrapperRef == null) {
return getNopOps();
}
@@ -108,9 +121,15 @@
* @param token IME token to be removed.
*/
@AnyThread
- public void remove(IBinder token) {
- synchronized (mLock) {
- mRegistry.remove(token);
+ public static void remove(IBinder token) {
+ synchronized (sLock) {
+ if (sRegistry == null) {
+ return;
+ }
+ sRegistry.remove(token);
+ if (sRegistry.isEmpty()) {
+ sRegistry = null;
+ }
}
}
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index d0795c9..31f13c3 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5758,8 +5758,6 @@
for (int i = 0; i < N; i++) {
int uid = mapUid(ws.get(i));
getUidStatsLocked(uid).noteBluetoothScanResultsLocked(numNewResults);
- StatsLog.write_non_chained(StatsLog.BLE_SCAN_RESULT_RECEIVED, ws.get(i), ws.getName(i),
- numNewResults);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -5768,8 +5766,6 @@
final WorkChain wc = workChains.get(i);
int uid = mapUid(wc.getAttributionUid());
getUidStatsLocked(uid).noteBluetoothScanResultsLocked(numNewResults);
- StatsLog.write(StatsLog.BLE_SCAN_RESULT_RECEIVED,
- wc.getUids(), wc.getTags(), numNewResults);
}
}
}
diff --git a/core/java/com/android/internal/os/KernelCpuProcReader.java b/core/java/com/android/internal/os/KernelCpuProcReader.java
index 396deb4..c233ea8 100644
--- a/core/java/com/android/internal/os/KernelCpuProcReader.java
+++ b/core/java/com/android/internal/os/KernelCpuProcReader.java
@@ -24,13 +24,14 @@
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
-import java.nio.channels.FileChannel;
+import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.nio.file.StandardOpenOption;
+import java.util.Arrays;
/**
* Reads cpu time proc files with throttling (adjustable interval).
@@ -55,7 +56,6 @@
private static final int ERROR_THRESHOLD = 5;
// Throttle interval in milliseconds
private static final long DEFAULT_THROTTLE_INTERVAL = 3000L;
- private static final int INITIAL_BUFFER_SIZE = 8 * 1024;
private static final int MAX_BUFFER_SIZE = 1024 * 1024;
private static final String PROC_UID_FREQ_TIME = "/proc/uid_cpupower/time_in_state";
private static final String PROC_UID_ACTIVE_TIME = "/proc/uid_cpupower/concurrent_active_time";
@@ -84,13 +84,12 @@
private long mThrottleInterval = DEFAULT_THROTTLE_INTERVAL;
private long mLastReadTime = Long.MIN_VALUE;
private final Path mProc;
- private ByteBuffer mBuffer;
+ private byte[] mBuffer = new byte[8 * 1024];
+ private int mContentSize;
@VisibleForTesting
public KernelCpuProcReader(String procFile) {
mProc = Paths.get(procFile);
- mBuffer = ByteBuffer.allocateDirect(INITIAL_BUFFER_SIZE);
- mBuffer.clear();
}
/**
@@ -108,38 +107,45 @@
return null;
}
if (SystemClock.elapsedRealtime() < mLastReadTime + mThrottleInterval) {
- if (mBuffer.limit() > 0 && mBuffer.limit() < mBuffer.capacity()) {
- // mBuffer has data.
- return mBuffer.asReadOnlyBuffer().order(ByteOrder.nativeOrder());
+ if (mContentSize > 0) {
+ return ByteBuffer.wrap(mBuffer, 0, mContentSize).asReadOnlyBuffer()
+ .order(ByteOrder.nativeOrder());
}
return null;
}
mLastReadTime = SystemClock.elapsedRealtime();
- mBuffer.clear();
+ mContentSize = 0;
final int oldMask = StrictMode.allowThreadDiskReadsMask();
- try (FileChannel fc = FileChannel.open(mProc, StandardOpenOption.READ)) {
- while (fc.read(mBuffer) == mBuffer.capacity()) {
- if (!resize()) {
- mErrors++;
- Slog.e(TAG, "Proc file is too large: " + mProc);
- return null;
+ try (InputStream in = Files.newInputStream(mProc)) {
+ int numBytes = 0;
+ int curr;
+ while ((curr = in.read(mBuffer, numBytes, mBuffer.length - numBytes)) >= 0) {
+ numBytes += curr;
+ if (numBytes == mBuffer.length) {
+ // Hit the limit. Resize mBuffer.
+ if (mBuffer.length == MAX_BUFFER_SIZE) {
+ mErrors++;
+ Slog.e(TAG, "Proc file is too large: " + mProc);
+ return null;
+ }
+ mBuffer = Arrays.copyOf(mBuffer,
+ Math.min(mBuffer.length << 1, MAX_BUFFER_SIZE));
}
- fc.position(0);
}
+ mContentSize = numBytes;
+ return ByteBuffer.wrap(mBuffer, 0, mContentSize).asReadOnlyBuffer()
+ .order(ByteOrder.nativeOrder());
} catch (NoSuchFileException | FileNotFoundException e) {
// Happens when the kernel does not provide this file. Not a big issue. Just log it.
mErrors++;
Slog.w(TAG, "File not exist: " + mProc);
- return null;
} catch (IOException e) {
mErrors++;
Slog.e(TAG, "Error reading: " + mProc, e);
- return null;
} finally {
StrictMode.setThreadPolicyMask(oldMask);
}
- mBuffer.flip();
- return mBuffer.asReadOnlyBuffer().order(ByteOrder.nativeOrder());
+ return null;
}
/**
@@ -153,14 +159,4 @@
mThrottleInterval = throttleInterval;
}
}
-
- private boolean resize() {
- if (mBuffer.capacity() >= MAX_BUFFER_SIZE) {
- return false;
- }
- int newSize = Math.min(mBuffer.capacity() << 1, MAX_BUFFER_SIZE);
- // Slog.i(TAG, "Resize buffer " + mBuffer.capacity() + " => " + newSize);
- mBuffer = ByteBuffer.allocateDirect(newSize);
- return true;
- }
}
diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java
index 02a8b22..0650d0af 100644
--- a/core/java/com/android/internal/os/LooperStats.java
+++ b/core/java/com/android/internal/os/LooperStats.java
@@ -106,6 +106,7 @@
synchronized (entry) {
entry.exceptionCount++;
}
+
recycleSession(session);
}
@@ -116,29 +117,29 @@
/** Returns an array of {@link ExportedEntry entries} with the aggregated statistics. */
public List<ExportedEntry> getEntries() {
- final ArrayList<ExportedEntry> entries;
+ final ArrayList<ExportedEntry> exportedEntries;
synchronized (mLock) {
final int size = mEntries.size();
- entries = new ArrayList<>(size);
+ exportedEntries = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
Entry entry = mEntries.valueAt(i);
synchronized (entry) {
- entries.add(new ExportedEntry(entry));
+ exportedEntries.add(new ExportedEntry(entry));
}
}
}
// Add the overflow and collision entries only if they have any data.
- if (mOverflowEntry.messageCount > 0 || mOverflowEntry.exceptionCount > 0) {
- synchronized (mOverflowEntry) {
- entries.add(new ExportedEntry(mOverflowEntry));
+ maybeAddSpecialEntry(exportedEntries, mOverflowEntry);
+ maybeAddSpecialEntry(exportedEntries, mHashCollisionEntry);
+ return exportedEntries;
+ }
+
+ private void maybeAddSpecialEntry(List<ExportedEntry> exportedEntries, Entry specialEntry) {
+ synchronized (specialEntry) {
+ if (specialEntry.messageCount > 0 || specialEntry.exceptionCount > 0) {
+ exportedEntries.add(new ExportedEntry(specialEntry));
}
}
- if (mHashCollisionEntry.messageCount > 0 || mHashCollisionEntry.exceptionCount > 0) {
- synchronized (mHashCollisionEntry) {
- entries.add(new ExportedEntry(mHashCollisionEntry));
- }
- }
- return entries;
}
/** Removes all collected data. */
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 543f4a5..5f1243f 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -31,6 +31,8 @@
* applications.
*/
interface IInputMethodManager {
+ void addClient(in IInputMethodClient client, in IInputContext inputContext);
+
// TODO: Use ParceledListSlice instead
List<InputMethodInfo> getInputMethodList();
List<InputMethodInfo> getVrInputMethodList();
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index 7548c22..101fd41 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -139,8 +139,7 @@
* The client should try to restart input when its {@link android.view.Window} is focused
* again.</p>
*
- * @see com.android.server.wm.WindowManagerInternal#inputMethodClientHasFocus(
- * IInputMethodClient)
+ * @see com.android.server.wm.WindowManagerInternal#isInputMethodClientFocus(int, int)
*/
int ERROR_NOT_IME_TARGET_WINDOW = 11;
/**
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 814973d..1df3f7f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3054,6 +3054,18 @@
<permission android:name="android.permission.MANAGE_DEVICE_ADMINS"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Allows an app to reset the device password.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.RESET_PASSWORD"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an app to lock the device.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.LOCK_DEVICE"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi Allows low-level access to setting the orientation (actually
rotation) of the screen.
<p>Not for use by third-party applications.
diff --git a/packages/SettingsLib/res/drawable/ic_info.xml b/core/res/res/drawable/ic_info.xml
similarity index 94%
rename from packages/SettingsLib/res/drawable/ic_info.xml
rename to core/res/res/drawable/ic_info.xml
index afe7e6b..f14c4b4 100644
--- a/packages/SettingsLib/res/drawable/ic_info.xml
+++ b/core/res/res/drawable/ic_info.xml
@@ -18,7 +18,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?android:attr/colorAccent">
+ android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/>
diff --git a/core/res/res/values-mcc262-mnc02/strings.xml b/core/res/res/values-mcc262-mnc02/strings.xml
deleted file mode 100644
index 2b89401..0000000
--- a/core/res/res/values-mcc262-mnc02/strings.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/**
- * Copyright (c) 2017, Google Inc.
- *
- * 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.
- */
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Do not translate. Template for showing mobile network operator name while WFC is active -->
- <string-array name="wfcSpnFormats">
- <item>%s</item>
- <item>%s Wi-Fi Calling</item>
- </string-array>
-</resources>
diff --git a/core/res/res/values-mcc302-mnc370/strings.xml b/core/res/res/values-mcc302-mnc370/strings.xml
deleted file mode 100644
index f5b8496..0000000
--- a/core/res/res/values-mcc302-mnc370/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Template for showing mobile network operator name while WFC is active -->
- <string-array name="wfcSpnFormats">
- <item>%s</item>
- <item>%s Wi-Fi</item>
- </string-array>
-</resources>
diff --git a/core/res/res/values-mcc302-mnc720/strings.xml b/core/res/res/values-mcc302-mnc720/strings.xml
deleted file mode 100644
index f5b8496..0000000
--- a/core/res/res/values-mcc302-mnc720/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Template for showing mobile network operator name while WFC is active -->
- <string-array name="wfcSpnFormats">
- <item>%s</item>
- <item>%s Wi-Fi</item>
- </string-array>
-</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 293d90e..1404383 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -913,7 +913,7 @@
<!-- Control whether to lock day/night mode change from normal application. When it is
true, day / night mode change is only allowed to apps with MODIFY_DAY_NIGHT_MODE
permission. -->
- <bool name="config_lockDayNightMode">true</bool>
+ <bool name="config_lockDayNightMode">false</bool>
<!-- Control the default night mode to use when there is no other mode override set.
One of the following values (see UiModeManager.java):
@@ -3486,6 +3486,9 @@
<!-- Name of the font family used for system surfaces where the font should use medium weight -->
<string name="config_headlineFontFamilyMedium">@string/font_family_button_material</string>
+ <!-- Size of icon shown beside a preference locked by admin -->
+ <dimen name="config_restricted_icon_size">@dimen/restricted_icon_size_material</dimen>
+
<string translatable="false" name="config_batterySaverDeviceSpecificConfig"></string>
<!-- Package name that should be granted Notification Assistant access -->
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 210f30e..a0b40ed 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -117,6 +117,9 @@
<!-- Default rounded corner for controls -->
<dimen name="control_corner_material">2dp</dimen>
+ <!-- Size of icon shown beside a preference locked by admin -->
+ <dimen name="restricted_icon_size_material">16dp</dimen>
+
<dimen name="edit_text_inset_horizontal_material">4dp</dimen>
<dimen name="edit_text_inset_top_material">10dp</dimen>
<dimen name="edit_text_inset_bottom_material">7dp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index c751af3..cc99a4e 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2909,14 +2909,16 @@
<public name="opticalInsetBottom" />
<public name="allowForceDark" />
<public name="supportsAmbientMode" />
- </public-group>
-
- <public-group type="attr" first-id="0x0101058d">
<!-- @hide For use by platform and tools only. Developers should not specify this value. -->
<public name="usesNonSdkApi" />
<public name="minimumUiTimeout" />
</public-group>
+ <public-group type="drawable" first-id="0x010800b4">
+ <!-- @hide @SystemApi -->
+ <public name="ic_info" />
+ </public-group>
+
<public-group type="style" first-id="0x010302e2">
</public-group>
@@ -2943,6 +2945,11 @@
<public name="config_sendPackageName" />
</public-group>
+ <public-group type="dimen" first-id="0x01050007">
+ <!-- @hide @SystemApi -->
+ <public name="config_restricted_icon_size" />
+ </public-group>
+
<!-- ===============================================================
DO NOT ADD UN-GROUPED ITEMS HERE
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 1a5b7b6..64620f3 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -240,10 +240,31 @@
<item>Issue registering Wi\u2011Fi calling with your carrier: <xliff:g id="code" example="REG09 - No 911 Address">%1$s</xliff:g></item>
</string-array>
<!-- Template for showing mobile network operator name while WFC is active -->
- <string-array name="wfcSpnFormats">
- <item>%s</item>
- <item>%s Wi-Fi Calling</item>
+ <string-array name="wfcSpnFormats" translatable="false">
+ <item>@string/wfcSpnFormat_spn</item>
+ <item>@string/wfcSpnFormat_spn_wifi_calling</item>
+ <item>@string/wfcSpnFormat_wlan_call</item>
+ <item>@string/wfcSpnFormat_spn_wlan_call</item>
+ <item>@string/wfcSpnFormat_spn_wifi</item>
+ <item>@string/wfcSpnFormat_wifi_calling_bar_spn</item>
+ <item>@string/wfcSpnFormat_spn_vowifi</item>
</string-array>
+
+ <!-- Spn during Wi-Fi Calling: "<operator>" -->
+ <string name="wfcSpnFormat_spn"><xliff:g id="spn" example="Operator">%s</xliff:g></string>
+ <!-- Spn during Wi-Fi Calling: "<operator> Wi-Fi Calling" -->
+ <string name="wfcSpnFormat_spn_wifi_calling"><xliff:g id="spn" example="Operator">%s</xliff:g> Wi-Fi Calling</string>
+ <!-- Spn during Wi-Fi Calling: "WLAN Call" -->
+ <string name="wfcSpnFormat_wlan_call">WLAN Call</string>
+ <!-- Spn during Wi-Fi Calling: "<operator> WLAN Call" -->
+ <string name="wfcSpnFormat_spn_wlan_call"><xliff:g id="spn" example="Operator">%s</xliff:g> WLAN Call</string>
+ <!-- Spn during Wi-Fi Calling: "<operator> Wi-Fi" -->
+ <string name="wfcSpnFormat_spn_wifi"><xliff:g id="spn" example="Operator">%s</xliff:g> Wi-Fi</string>
+ <!-- Spn during Wi-Fi Calling: "WiFi Calling | <operator>" -->
+ <string name="wfcSpnFormat_wifi_calling_bar_spn">WiFi Calling | <xliff:g id="spn" example="Operator">%s</xliff:g></string>
+ <!-- Spn during Wi-Fi Calling: "<operator> VoWifi" -->
+ <string name="wfcSpnFormat_spn_vowifi"><xliff:g id="spn" example="Operator">%s</xliff:g> VoWifi</string>
+
<!-- WFC, summary for Disabled -->
<string name="wifi_calling_off_summary">Off</string>
<!-- WFC, summary for Wi-Fi Preferred -->
diff --git a/core/tests/coretests/src/android/graphics/PaintTest.java b/core/tests/coretests/src/android/graphics/PaintTest.java
index 2f28606..b5ed01f 100644
--- a/core/tests/coretests/src/android/graphics/PaintTest.java
+++ b/core/tests/coretests/src/android/graphics/PaintTest.java
@@ -186,44 +186,28 @@
Paint p = new Paint();
final int count = end - start;
- final float[][] advanceArrays = new float[4][count];
-
- final float advance = p.getTextRunAdvances(str, start, end, contextStart, contextEnd,
- isRtl, advanceArrays[0], 0);
-
+ final int contextCount = contextEnd - contextStart;
+ final float[][] advanceArrays = new float[2][count];
char chars[] = str.toCharArray();
- final float advance_c = p.getTextRunAdvances(chars, start, count, contextStart,
- contextEnd - contextStart, isRtl, advanceArrays[1], 0);
- assertEquals(advance, advance_c, 1.0f);
-
+ final float advance = p.getTextRunAdvances(chars, start, count,
+ contextStart, contextCount, isRtl, advanceArrays[0], 0);
for (int c = 1; c < count; ++c) {
- final float firstPartAdvance = p.getTextRunAdvances(str, start, start + c,
- contextStart, contextEnd, isRtl, advanceArrays[2], 0);
- final float secondPartAdvance = p.getTextRunAdvances(str, start + c, end,
- contextStart, contextEnd, isRtl, advanceArrays[2], c);
+ final float firstPartAdvance = p.getTextRunAdvances(chars, start, c,
+ contextStart, contextCount, isRtl, advanceArrays[1], 0);
+ final float secondPartAdvance = p.getTextRunAdvances(chars, start + c, count - c,
+ contextStart, contextCount, isRtl, advanceArrays[1], c);
assertEquals(advance, firstPartAdvance + secondPartAdvance, 1.0f);
-
- final float firstPartAdvance_c = p.getTextRunAdvances(chars, start, c,
- contextStart, contextEnd - contextStart, isRtl, advanceArrays[3], 0);
- final float secondPartAdvance_c = p.getTextRunAdvances(chars, start + c,
- count - c, contextStart, contextEnd - contextStart, isRtl,
- advanceArrays[3], c);
- assertEquals(advance, firstPartAdvance_c + secondPartAdvance_c, 1.0f);
- assertEquals(firstPartAdvance, firstPartAdvance_c, 1.0f);
- assertEquals(secondPartAdvance, secondPartAdvance_c, 1.0f);
-
- for (int i = 1; i < advanceArrays.length; i++) {
- for (int j = 0; j < count; j++) {
- assertEquals(advanceArrays[0][j], advanceArrays[i][j], 1.0f);
- }
+ for (int j = 0; j < count; j++) {
+ assertEquals(advanceArrays[0][j], advanceArrays[1][j], 1.0f);
}
+
// Compare results with measureText, getRunAdvance, and getTextWidths.
if (compareWithOtherMethods && start == contextStart && end == contextEnd) {
assertEquals(advance, p.measureText(str, start, end), 1.0f);
assertEquals(advance, p.getRunAdvance(
- str, start, end, contextStart, contextEnd, isRtl, end), 1.0f);
+ chars, start, count, contextStart, contextCount, isRtl, end), 1.0f);
final float[] widths = new float[count];
p.getTextWidths(str, start, end, widths);
@@ -236,19 +220,7 @@
public void testGetTextRunAdvances_invalid() {
Paint p = new Paint();
- String text = "test";
-
- try {
- p.getTextRunAdvances((String)null, 0, 0, 0, 0, false, null, 0);
- fail("Should throw an IllegalArgumentException.");
- } catch (IllegalArgumentException e) {
- }
-
- try {
- p.getTextRunAdvances((CharSequence)null, 0, 0, 0, 0, false, null, 0);
- fail("Should throw an IllegalArgumentException.");
- } catch (IllegalArgumentException e) {
- }
+ char[] text = "test".toCharArray();
try {
p.getTextRunAdvances((char[])null, 0, 0, 0, 0, false, null, 0);
@@ -257,50 +229,43 @@
}
try {
- p.getTextRunAdvances(text, 0, text.length(), 0, text.length(), false,
- new float[text.length() - 1], 0);
+ p.getTextRunAdvances(text, 0, text.length, 0, text.length, false,
+ new float[text.length - 1], 0);
fail("Should throw an IndexOutOfBoundsException.");
} catch (IndexOutOfBoundsException e) {
}
try {
- p.getTextRunAdvances(text, 0, text.length(), 0, text.length(), false,
- new float[text.length()], 1);
+ p.getTextRunAdvances(text, 0, text.length, 0, text.length, false,
+ new float[text.length], 1);
fail("Should throw an IndexOutOfBoundsException.");
} catch (IndexOutOfBoundsException e) {
}
// 0 > contextStart
try {
- p.getTextRunAdvances(text, 0, text.length(), -1, text.length(), false, null, 0);
+ p.getTextRunAdvances(text, 0, text.length, -1, text.length, false, null, 0);
fail("Should throw an IndexOutOfBoundsException.");
} catch (IndexOutOfBoundsException e) {
}
// contextStart > start
try {
- p.getTextRunAdvances(text, 0, text.length(), 1, text.length(), false, null, 0);
- fail("Should throw an IndexOutOfBoundsException.");
- } catch (IndexOutOfBoundsException e) {
- }
-
- // start > end
- try {
- p.getTextRunAdvances(text, 1, 0, 0, text.length(), false, null, 0);
+ p.getTextRunAdvances(text, 0, text.length, 1, text.length, false, null, 0);
fail("Should throw an IndexOutOfBoundsException.");
} catch (IndexOutOfBoundsException e) {
}
// end > contextEnd
try {
- p.getTextRunAdvances(text, 0, text.length(), 0, text.length() - 1, false, null, 0);
+ p.getTextRunAdvances(text, 0, text.length, 0, text.length - 1, false, null, 0);
fail("Should throw an IndexOutOfBoundsException.");
} catch (IndexOutOfBoundsException e) {
}
// contextEnd > text.length
try {
- p.getTextRunAdvances(text, 0, text.length(), 0, text.length() + 1, false, null, 0);
+ p.getTextRunAdvances(text, 0, text.length, 0, text.length + 1, false, null, 0);
fail("Should throw an IndexOutOfBoundsException.");
} catch (IndexOutOfBoundsException e) {
}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index e84aed1..fee470d 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -241,7 +241,6 @@
Settings.Global.EUICC_SUPPORTED_COUNTRIES,
Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS,
Settings.Global.FANCY_IME_ANIMATIONS,
- Settings.Global.FASTER_EMERGENCY_PHONE_CALL_ENABLED,
Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
Settings.Global.FORCED_APP_STANDBY_ENABLED,
Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 922b79a..69d2828 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -57,7 +57,7 @@
// The number of flags held in boolean properties. Their values should also be double-checked
// in the methods above.
- private static final int NUM_BOOLEAN_PROPERTIES = 17;
+ private static final int NUM_BOOLEAN_PROPERTIES = 18;
@Test
public void testStandardActions_serializationFlagIsValid() {
diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
index 0eb3d06..565a3ec 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -342,6 +342,20 @@
assertThat(looperStats.getEntries().get(0).messageCount).isEqualTo(2);
}
+ @Test
+ public void testReset() {
+ TestableLooperStats looperStats = new TestableLooperStats(1, 1);
+
+ Object token1 = looperStats.messageDispatchStarting();
+ looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
+ Object token2 = looperStats.messageDispatchStarting();
+ looperStats.messageDispatched(token2, mHandlerFirst.obtainMessage(2000));
+ looperStats.reset();
+
+ List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+ assertThat(entries).hasSize(0);
+ }
+
private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
try {
r.run();
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index c4017d1..73c10d2 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -210,6 +210,11 @@
<allow-in-power-save-except-idle package="com.android.providers.calendar" />
<allow-in-power-save-except-idle package="com.android.providers.contacts" />
+ <!-- The PAC proxy process must have network access, otherwise no app will
+ be able to connect to the internet when such a proxy is in use, since
+ all outgoing connections originate from this app. -->
+ <allow-in-power-save-except-idle package="com.android.proxyhandler" />
+
<!-- These are the packages that are white-listed to be able to run as system user -->
<system-user-whitelisted-app package="com.android.settings" />
diff --git a/data/keyboards/Vendor_054c_Product_0268.kl b/data/keyboards/Vendor_054c_Product_0268.kl
index 522db3c..b463dd8 100644
--- a/data/keyboards/Vendor_054c_Product_0268.kl
+++ b/data/keyboards/Vendor_054c_Product_0268.kl
@@ -21,8 +21,6 @@
key 0x126 DPAD_DOWN
key 0x127 DPAD_LEFT
-key 0x120 BUTTON_SELECT
-key 0x123 BUTTON_START
key 0x12e BUTTON_A
key 0x12d BUTTON_B
key 0x12f BUTTON_X
@@ -34,9 +32,6 @@
key 0x121 BUTTON_THUMBL
key 0x122 BUTTON_THUMBR
-# PS key
-key 0x2d0 BUTTON_MODE
-
# Left Analog Stick
axis 0x00 X
axis 0x01 Y
@@ -74,3 +69,11 @@
# Square
# axis 0x37
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x120 BUTTON_SELECT
+# Start
+key 0x123 BUTTON_START
+# PS key
+key 0x2d0 BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_0268_Version_8000.kl b/data/keyboards/Vendor_054c_Product_0268_Version_8000.kl
new file mode 100644
index 0000000..3d93f0f
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0268_Version_8000.kl
@@ -0,0 +1,57 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R)3 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.12
+# and when connected over Bluetooth
+#
+
+key 0x220 DPAD_UP
+key 0x223 DPAD_RIGHT
+key 0x221 DPAD_DOWN
+key 0x222 DPAD_LEFT
+
+key 0x130 BUTTON_A
+key 0x131 BUTTON_B
+key 0x134 BUTTON_X
+key 0x133 BUTTON_Y
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+key 0x13d BUTTON_THUMBL
+key 0x13e BUTTON_THUMBR
+
+# left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# L2 trigger
+axis 0x02 LTRIGGER
+
+# R2 trigger
+axis 0x05 RTRIGGER
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x13a BUTTON_SELECT
+# Start
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_0268_Version_8100.kl b/data/keyboards/Vendor_054c_Product_0268_Version_8100.kl
new file mode 100644
index 0000000..3d93f0f
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0268_Version_8100.kl
@@ -0,0 +1,57 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R)3 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.12
+# and when connected over Bluetooth
+#
+
+key 0x220 DPAD_UP
+key 0x223 DPAD_RIGHT
+key 0x221 DPAD_DOWN
+key 0x222 DPAD_LEFT
+
+key 0x130 BUTTON_A
+key 0x131 BUTTON_B
+key 0x134 BUTTON_X
+key 0x133 BUTTON_Y
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+key 0x13d BUTTON_THUMBL
+key 0x13e BUTTON_THUMBR
+
+# left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# L2 trigger
+axis 0x02 LTRIGGER
+
+# R2 trigger
+axis 0x05 RTRIGGER
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x13a BUTTON_SELECT
+# Start
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_0268_Version_8111.kl b/data/keyboards/Vendor_054c_Product_0268_Version_8111.kl
new file mode 100644
index 0000000..5fe35f7
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0268_Version_8111.kl
@@ -0,0 +1,57 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R)3 Controller
+# - Version 0x8111 is for Linux hid-sony driver >=4.12 and when
+# connected over USB
+#
+
+key 0x220 DPAD_UP
+key 0x223 DPAD_RIGHT
+key 0x221 DPAD_DOWN
+key 0x222 DPAD_LEFT
+
+key 0x130 BUTTON_A
+key 0x131 BUTTON_B
+key 0x134 BUTTON_X
+key 0x133 BUTTON_Y
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+key 0x13d BUTTON_THUMBL
+key 0x13e BUTTON_THUMBR
+
+# left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# L2 trigger
+axis 0x02 LTRIGGER
+
+# R2 trigger
+axis 0x05 RTRIGGER
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x13a BUTTON_SELECT
+# Start
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_05c4.kl b/data/keyboards/Vendor_054c_Product_05c4.kl
index a1284a4..cd7ab1f 100644
--- a/data/keyboards/Vendor_054c_Product_05c4.kl
+++ b/data/keyboards/Vendor_054c_Product_05c4.kl
@@ -60,7 +60,6 @@
key 0x138 BUTTON_SELECT
# Options
key 0x139 BUTTON_START
-
# PS key
key 0x13c BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_05c4_Version_8000.kl b/data/keyboards/Vendor_054c_Product_05c4_Version_8000.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_05c4_Version_8000.kl
@@ -0,0 +1,68 @@
+# 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.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+# and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# L2 axis
+axis 0x02 LTRIGGER
+# R2 axis
+axis 0x05 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a BUTTON_SELECT
+# Options
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_05c4_Version_8100.kl b/data/keyboards/Vendor_054c_Product_05c4_Version_8100.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_05c4_Version_8100.kl
@@ -0,0 +1,68 @@
+# 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.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+# and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# L2 axis
+axis 0x02 LTRIGGER
+# R2 axis
+axis 0x05 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a BUTTON_SELECT
+# Options
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_05c4_Version_8111.kl b/data/keyboards/Vendor_054c_Product_05c4_Version_8111.kl
new file mode 100644
index 0000000..d38bdec
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_05c4_Version_8111.kl
@@ -0,0 +1,68 @@
+# 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.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8111 is for Linux hid-sony driver >=4.10 and when
+# connected over USB
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# L2 axis
+axis 0x02 LTRIGGER
+# R2 axis
+axis 0x05 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a BUTTON_SELECT
+# Options
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_09cc.kl b/data/keyboards/Vendor_054c_Product_09cc.kl
index a1284a4..cd7ab1f 100644
--- a/data/keyboards/Vendor_054c_Product_09cc.kl
+++ b/data/keyboards/Vendor_054c_Product_09cc.kl
@@ -60,7 +60,6 @@
key 0x138 BUTTON_SELECT
# Options
key 0x139 BUTTON_START
-
# PS key
key 0x13c BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_09cc_Version_8000.kl b/data/keyboards/Vendor_054c_Product_09cc_Version_8000.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_09cc_Version_8000.kl
@@ -0,0 +1,68 @@
+# 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.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+# and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# L2 axis
+axis 0x02 LTRIGGER
+# R2 axis
+axis 0x05 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a BUTTON_SELECT
+# Options
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_09cc_Version_8100.kl b/data/keyboards/Vendor_054c_Product_09cc_Version_8100.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_09cc_Version_8100.kl
@@ -0,0 +1,68 @@
+# 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.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+# and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# L2 axis
+axis 0x02 LTRIGGER
+# R2 axis
+axis 0x05 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a BUTTON_SELECT
+# Options
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_09cc_Version_8111.kl b/data/keyboards/Vendor_054c_Product_09cc_Version_8111.kl
new file mode 100644
index 0000000..d38bdec
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_09cc_Version_8111.kl
@@ -0,0 +1,68 @@
+# 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.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8111 is for Linux hid-sony driver >=4.10 and when
+# connected over USB
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# L2 axis
+axis 0x02 LTRIGGER
+# R2 axis
+axis 0x05 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a BUTTON_SELECT
+# Options
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_0ba0.kl b/data/keyboards/Vendor_054c_Product_0ba0.kl
new file mode 100644
index 0000000..bc6fc3b
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0ba0.kl
@@ -0,0 +1,70 @@
+# 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.
+
+#
+# Sony Playstation(R) DualShock 4 USB Dongle
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x130 BUTTON_X
+# Cross
+key 0x131 BUTTON_A
+# Circle
+key 0x132 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x134 BUTTON_L1
+key 0x135 BUTTON_R1
+key 0x136 BUTTON_L2
+key 0x137 BUTTON_R2
+
+# L2 axis
+axis 0x03 LTRIGGER
+# R2 axis
+axis 0x04 RTRIGGER
+
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# Left stick click
+key 0x13a BUTTON_THUMBL
+# Right stick click
+key 0x13b BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x138 BUTTON_SELECT
+# Options
+key 0x139 BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# Touchpad press
+# The touchpad for this joystick will become a separate input device in future releases
+# and this button will be equivalent to left mouse button
+# Therefore, map it to KEYCODE_BUTTON_1 here to allow apps to still handle this on earlier versions
+key 0x13d BUTTON_1
diff --git a/data/keyboards/Vendor_054c_Product_0ba0_Version_8111.kl b/data/keyboards/Vendor_054c_Product_0ba0_Version_8111.kl
new file mode 100644
index 0000000..8b85a38
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0ba0_Version_8111.kl
@@ -0,0 +1,67 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 USB Dongle
+# - Version 0x8111 is for Linux hid-sony driver >=4.10
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# L2 axis
+axis 0x02 LTRIGGER
+# R2 axis
+axis 0x05 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a BUTTON_SELECT
+# Options
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 6f30653..33caa00 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -17,7 +17,10 @@
package android.graphics;
import android.annotation.ColorInt;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.Px;
import android.annotation.Size;
import android.annotation.UnsupportedAppUsage;
@@ -35,6 +38,8 @@
import libcore.util.NativeAllocationRegistry;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -307,38 +312,47 @@
*/
public static final int DIRECTION_RTL = 1;
+ /** @hide */
+ @IntDef(prefix = { "CURSOR_" }, value = {
+ CURSOR_AFTER, CURSOR_AT_OR_AFTER, CURSOR_BEFORE, CURSOR_AT_OR_BEFORE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CursorOption {}
+
/**
- * Option for getTextRunCursor to compute the valid cursor after
- * offset or the limit of the context, whichever is less.
- * @hide
+ * Option for getTextRunCursor.
+ *
+ * Compute the valid cursor after offset or the limit of the context, whichever is less.
*/
public static final int CURSOR_AFTER = 0;
/**
- * Option for getTextRunCursor to compute the valid cursor at or after
- * the offset or the limit of the context, whichever is less.
- * @hide
+ * Option for getTextRunCursor.
+ *
+ * Compute the valid cursor at or after the offset or the limit of the context, whichever is
+ * less.
*/
public static final int CURSOR_AT_OR_AFTER = 1;
/**
- * Option for getTextRunCursor to compute the valid cursor before
- * offset or the start of the context, whichever is greater.
- * @hide
+ * Option for getTextRunCursor.
+ *
+ * Compute the valid cursor before offset or the start of the context, whichever is greater.
*/
public static final int CURSOR_BEFORE = 2;
/**
- * Option for getTextRunCursor to compute the valid cursor at or before
- * offset or the start of the context, whichever is greater.
- * @hide
+ * Option for getTextRunCursor.
+ *
+ * Compute the valid cursor at or before offset or the start of the context, whichever is
+ * greater.
*/
public static final int CURSOR_AT_OR_BEFORE = 3;
/**
- * Option for getTextRunCursor to return offset if the cursor at offset
- * is valid, or -1 if it isn't.
- * @hide
+ * Option for getTextRunCursor.
+ *
+ * Return offset if the cursor at offset is valid, or -1 if it isn't.
*/
public static final int CURSOR_AT = 4;
@@ -2325,17 +2339,53 @@
}
/**
- * Convenience overload that takes a char array instead of a
- * String.
+ * Retrieve the character advances of the text.
*
- * @see #getTextRunAdvances(String, int, int, int, int, boolean, float[], int)
- * @hide
+ * Returns the total advance width for the characters in the run from {@code index} for
+ * {@code count} of chars, and if {@code advances} is not null, the advance assigned to each of
+ * these characters (java chars).
+ *
+ * <p>
+ * The trailing surrogate in a valid surrogate pair is assigned an advance of 0. Thus the
+ * number of returned advances is always equal to count, not to the number of unicode codepoints
+ * represented by the run.
+ * </p>
+ *
+ * <p>
+ * In the case of conjuncts or combining marks, the total advance is assigned to the first
+ * logical character, and the following characters are assigned an advance of 0.
+ * </p>
+ *
+ * <p>
+ * This generates the sum of the advances of glyphs for characters in a reordered cluster as the
+ * width of the first logical character in the cluster, and 0 for the widths of all other
+ * characters in the cluster. In effect, such clusters are treated like conjuncts.
+ * </p>
+ *
+ * <p>
+ * The shaping bounds limit the amount of context available outside start and end that can be
+ * used for shaping analysis. These bounds typically reflect changes in bidi level or font
+ * metrics across which shaping does not occur.
+ * </p>
+ *
+ * @param chars the text to measure.
+ * @param index the index of the first character to measure
+ * @param count the number of characters to measure
+ * @param contextIndex the index of the first character to use for shaping context.
+ * Context must cover the measureing target.
+ * @param contextCount the number of character to use for shaping context.
+ * Context must cover the measureing target.
+ * @param isRtl whether the run is in RTL direction
+ * @param advances array to receive the advances, must have room for all advances.
+ * This can be null if only total advance is needed
+ * @param advancesIndex the position in advances at which to put the advance corresponding to
+ * the character at start
+ * @return the total advance in pixels
*/
- @UnsupportedAppUsage
- public float getTextRunAdvances(char[] chars, int index, int count,
- int contextIndex, int contextCount, boolean isRtl, float[] advances,
- int advancesIndex) {
-
+ public float getTextRunAdvances(@NonNull char[] chars, @IntRange(from = 0) int index,
+ @IntRange(from = 0) int count, @IntRange(from = 0) int contextIndex,
+ @IntRange(from = 0) int contextCount, boolean isRtl, @Nullable float[] advances,
+ @IntRange(from = 0) int advancesIndex) {
if (chars == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -2372,159 +2422,32 @@
}
/**
- * Convenience overload that takes a CharSequence instead of a
- * String.
+ * Returns the next cursor position in the run.
*
- * @see #getTextRunAdvances(String, int, int, int, int, boolean, float[], int)
- * @hide
- */
- public float getTextRunAdvances(CharSequence text, int start, int end,
- int contextStart, int contextEnd, boolean isRtl, float[] advances,
- int advancesIndex) {
- if (text == null) {
- throw new IllegalArgumentException("text cannot be null");
- }
- if ((start | end | contextStart | contextEnd | advancesIndex | (end - start)
- | (start - contextStart) | (contextEnd - end)
- | (text.length() - contextEnd)
- | (advances == null ? 0 :
- (advances.length - advancesIndex - (end - start)))) < 0) {
- throw new IndexOutOfBoundsException();
- }
-
- if (text instanceof String) {
- return getTextRunAdvances((String) text, start, end,
- contextStart, contextEnd, isRtl, advances, advancesIndex);
- }
- if (text instanceof SpannedString ||
- text instanceof SpannableString) {
- return getTextRunAdvances(text.toString(), start, end,
- contextStart, contextEnd, isRtl, advances, advancesIndex);
- }
- if (text instanceof GraphicsOperations) {
- return ((GraphicsOperations) text).getTextRunAdvances(start, end,
- contextStart, contextEnd, isRtl, advances, advancesIndex, this);
- }
- if (text.length() == 0 || end == start) {
- return 0f;
- }
-
- int contextLen = contextEnd - contextStart;
- int len = end - start;
- char[] buf = TemporaryBuffer.obtain(contextLen);
- TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
- float result = getTextRunAdvances(buf, start - contextStart, len,
- 0, contextLen, isRtl, advances, advancesIndex);
- TemporaryBuffer.recycle(buf);
- return result;
- }
-
- /**
- * Returns the total advance width for the characters in the run
- * between start and end, and if advances is not null, the advance
- * assigned to each of these characters (java chars).
+ * This avoids placing the cursor between surrogates, between characters that form conjuncts,
+ * between base characters and combining marks, or within a reordering cluster.
*
- * <p>The trailing surrogate in a valid surrogate pair is assigned
- * an advance of 0. Thus the number of returned advances is
- * always equal to count, not to the number of unicode codepoints
- * represented by the run.
+ * <p>
+ * ContextStart and offset are relative to the start of text.
+ * The context is the shaping context for cursor movement, generally the bounds of the metric
+ * span enclosing the cursor in the direction of movement.
*
- * <p>In the case of conjuncts or combining marks, the total
- * advance is assigned to the first logical character, and the
- * following characters are assigned an advance of 0.
- *
- * <p>This generates the sum of the advances of glyphs for
- * characters in a reordered cluster as the width of the first
- * logical character in the cluster, and 0 for the widths of all
- * other characters in the cluster. In effect, such clusters are
- * treated like conjuncts.
- *
- * <p>The shaping bounds limit the amount of context available
- * outside start and end that can be used for shaping analysis.
- * These bounds typically reflect changes in bidi level or font
- * metrics across which shaping does not occur.
- *
- * @param text the text to measure. Cannot be null.
- * @param start the index of the first character to measure
- * @param end the index past the last character to measure
- * @param contextStart the index of the first character to use for shaping context,
- * must be <= start
- * @param contextEnd the index past the last character to use for shaping context,
- * must be >= end
- * @param isRtl whether the run is in RTL direction
- * @param advances array to receive the advances, must have room for all advances,
- * can be null if only total advance is needed
- * @param advancesIndex the position in advances at which to put the
- * advance corresponding to the character at start
- * @return the total advance
- *
- * @hide
- */
- public float getTextRunAdvances(String text, int start, int end, int contextStart,
- int contextEnd, boolean isRtl, float[] advances, int advancesIndex) {
- if (text == null) {
- throw new IllegalArgumentException("text cannot be null");
- }
- if ((start | end | contextStart | contextEnd | advancesIndex | (end - start)
- | (start - contextStart) | (contextEnd - end)
- | (text.length() - contextEnd)
- | (advances == null ? 0 :
- (advances.length - advancesIndex - (end - start)))) < 0) {
- throw new IndexOutOfBoundsException();
- }
-
- if (text.length() == 0 || start == end) {
- return 0f;
- }
-
- if (!mHasCompatScaling) {
- return nGetTextAdvances(mNativePaint, text, start, end, contextStart, contextEnd,
- isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances, advancesIndex);
- }
-
- final float oldSize = getTextSize();
- setTextSize(oldSize * mCompatScaling);
- final float totalAdvance = nGetTextAdvances(mNativePaint, text, start, end, contextStart,
- contextEnd, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances, advancesIndex);
- setTextSize(oldSize);
-
- if (advances != null) {
- for (int i = advancesIndex, e = i + (end - start); i < e; i++) {
- advances[i] *= mInvCompatScaling;
- }
- }
- return totalAdvance * mInvCompatScaling; // assume errors are insignificant
- }
-
- /**
- * Returns the next cursor position in the run. This avoids placing the
- * cursor between surrogates, between characters that form conjuncts,
- * between base characters and combining marks, or within a reordering
- * cluster.
- *
- * <p>ContextStart and offset are relative to the start of text.
- * The context is the shaping context for cursor movement, generally
- * the bounds of the metric span enclosing the cursor in the direction of
- * movement.
- *
- * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid
- * cursor position, this returns -1. Otherwise this will never return a
- * value before contextStart or after contextStart + contextLength.
+ * <p>
+ * If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid cursor position, this
+ * returns -1. Otherwise this will never return a value before contextStart or after
+ * contextStart + contextLength.
*
* @param text the text
* @param contextStart the start of the context
* @param contextLength the length of the context
- * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
+ * @param isRtl true if the paragraph context is RTL, otherwise false
* @param offset the cursor position to move from
- * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
- * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
- * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT}
+ * @param cursorOpt how to move the cursor
* @return the offset of the next position, or -1
- * @hide
*/
- @UnsupportedAppUsage
- public int getTextRunCursor(char[] text, int contextStart, int contextLength,
- int dir, int offset, int cursorOpt) {
+ public int getTextRunCursor(@NonNull char[] text, @IntRange(from = 0) int contextStart,
+ @IntRange(from = 0) int contextLength, boolean isRtl, @IntRange(from = 0) int offset,
+ @CursorOption int cursorOpt) {
int contextEnd = contextStart + contextLength;
if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
| (offset - contextStart) | (contextEnd - offset)
@@ -2533,85 +2456,87 @@
throw new IndexOutOfBoundsException();
}
- return nGetTextRunCursor(mNativePaint, text, contextStart, contextLength, dir, offset,
- cursorOpt);
+ return nGetTextRunCursor(mNativePaint, text, contextStart, contextLength,
+ isRtl ? DIRECTION_RTL : DIRECTION_LTR, offset, cursorOpt);
}
/**
- * Returns the next cursor position in the run. This avoids placing the
- * cursor between surrogates, between characters that form conjuncts,
- * between base characters and combining marks, or within a reordering
- * cluster.
+ * Returns the next cursor position in the run.
*
- * <p>ContextStart, contextEnd, and offset are relative to the start of
+ * This avoids placing the cursor between surrogates, between characters that form conjuncts,
+ * between base characters and combining marks, or within a reordering cluster.
+ *
+ * <p>
+ * ContextStart, contextEnd, and offset are relative to the start of
* text. The context is the shaping context for cursor movement, generally
* the bounds of the metric span enclosing the cursor in the direction of
* movement.
*
- * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid
- * cursor position, this returns -1. Otherwise this will never return a
- * value before contextStart or after contextEnd.
+ * <p>
+ * If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid cursor position, this
+ * returns -1. Otherwise this will never return a value before contextStart or after
+ * contextEnd.
*
* @param text the text
* @param contextStart the start of the context
* @param contextEnd the end of the context
- * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
+ * @param isRtl true if the paragraph context is RTL, otherwise false
* @param offset the cursor position to move from
- * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
- * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
- * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT}
+ * @param cursorOpt how to move the cursor
* @return the offset of the next position, or -1
- * @hide
*/
- public int getTextRunCursor(CharSequence text, int contextStart,
- int contextEnd, int dir, int offset, int cursorOpt) {
+ public int getTextRunCursor(@NonNull CharSequence text, @IntRange(from = 0) int contextStart,
+ @IntRange(from = 0) int contextEnd, boolean isRtl, @IntRange(from = 0) int offset,
+ @CursorOption int cursorOpt) {
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
return getTextRunCursor(text.toString(), contextStart, contextEnd,
- dir, offset, cursorOpt);
+ isRtl, offset, cursorOpt);
}
if (text instanceof GraphicsOperations) {
return ((GraphicsOperations) text).getTextRunCursor(
- contextStart, contextEnd, dir, offset, cursorOpt, this);
+ contextStart, contextEnd, isRtl, offset, cursorOpt, this);
}
int contextLen = contextEnd - contextStart;
char[] buf = TemporaryBuffer.obtain(contextLen);
TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
- int relPos = getTextRunCursor(buf, 0, contextLen, dir, offset - contextStart, cursorOpt);
+ int relPos = getTextRunCursor(buf, 0, contextLen, isRtl, offset - contextStart, cursorOpt);
TemporaryBuffer.recycle(buf);
return (relPos == -1) ? -1 : relPos + contextStart;
}
/**
- * Returns the next cursor position in the run. This avoids placing the
- * cursor between surrogates, between characters that form conjuncts,
- * between base characters and combining marks, or within a reordering
- * cluster.
+ * Returns the next cursor position in the run.
*
- * <p>ContextStart, contextEnd, and offset are relative to the start of
- * text. The context is the shaping context for cursor movement, generally
- * the bounds of the metric span enclosing the cursor in the direction of
- * movement.
+ * This avoids placing the cursor between surrogates, between characters that form conjuncts,
+ * between base characters and combining marks, or within a reordering cluster.
*
- * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid
- * cursor position, this returns -1. Otherwise this will never return a
- * value before contextStart or after contextEnd.
+ * <p>
+ * ContextStart, contextEnd, and offset are relative to the start of text. The context is the
+ * shaping context for cursor movement, generally the bounds of the metric span enclosing the
+ * cursor in the direction of movement.
+ * </p>
+ *
+ * <p>
+ * If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid cursor position, this
+ * returns -1. Otherwise this will never return a value before contextStart or after
+ * contextEnd.
+ * </p>
*
* @param text the text
* @param contextStart the start of the context
* @param contextEnd the end of the context
- * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
+ * @param isRtl true if the paragraph context is RTL, otherwise false.
* @param offset the cursor position to move from
- * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
- * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
- * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT}
+ * @param cursorOpt how to move the cursor
* @return the offset of the next position, or -1
* @hide
*/
- public int getTextRunCursor(String text, int contextStart, int contextEnd,
- int dir, int offset, int cursorOpt) {
+ public int getTextRunCursor(@NonNull String text, @IntRange(from = 0) int contextStart,
+ @IntRange(from = 0) int contextEnd, boolean isRtl, @IntRange(from = 0) int offset,
+ @CursorOption int cursorOpt) {
if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
| (offset - contextStart) | (contextEnd - offset)
| (text.length() - contextEnd) | cursorOpt) < 0)
@@ -2619,8 +2544,8 @@
throw new IndexOutOfBoundsException();
}
- return nGetTextRunCursor(mNativePaint, text, contextStart, contextEnd, dir, offset,
- cursorOpt);
+ return nGetTextRunCursor(mNativePaint, text, contextStart, contextEnd,
+ isRtl ? DIRECTION_RTL : DIRECTION_LTR, offset, cursorOpt);
}
/**
@@ -2686,11 +2611,13 @@
* Return in bounds (allocated by the caller) the smallest rectangle that
* encloses all of the characters, with an implied origin at (0,0).
*
+ * Note that styles are ignored even if you pass {@link android.text.Spanned} instance.
+ * Use {@link android.text.StaticLayout} for measuring bounds of {@link android.text.Spanned}.
+ *
* @param text text to measure and return its bounds
* @param start index of the first char in the text to measure
* @param end 1 past the last char in the text to measure
* @param bounds returns the unioned bounds of all the text. Must be allocated by the caller
- * @hide
*/
public void getTextBounds(CharSequence text, int start, int end, Rect bounds) {
if ((start | end | (end - start) | (text.length() - end)) < 0) {
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index b011363..11dad2e 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -175,6 +175,7 @@
"pipeline/skia/SkiaRecordingCanvas.cpp",
"pipeline/skia/SkiaVulkanPipeline.cpp",
"pipeline/skia/VectorDrawableAtlas.cpp",
+ "pipeline/skia/VkFunctorDrawable.cpp",
"renderstate/RenderState.cpp",
"renderthread/CacheManager.cpp",
"renderthread/CanvasContext.cpp",
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index b5b87d5..21fbbdc 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -26,8 +26,6 @@
#include <log/log.h>
-#include <GLES2/gl2.h>
-
namespace android {
namespace uirenderer {
@@ -46,39 +44,12 @@
1920, // viewportH
};
-static DeviceInfo* sDeviceInfo = nullptr;
-static std::once_flag sInitializedFlag;
-
const DeviceInfo* DeviceInfo::get() {
- LOG_ALWAYS_FATAL_IF(!sDeviceInfo, "DeviceInfo not yet initialized.");
- return sDeviceInfo;
+ static DeviceInfo sDeviceInfo;
+ return &sDeviceInfo;
}
-void DeviceInfo::initialize() {
- std::call_once(sInitializedFlag, []() {
- sDeviceInfo = new DeviceInfo();
- sDeviceInfo->load();
- });
-}
-
-void DeviceInfo::initialize(int maxTextureSize) {
- std::call_once(sInitializedFlag, [maxTextureSize]() {
- sDeviceInfo = new DeviceInfo();
- sDeviceInfo->mDisplayInfo = DeviceInfo::queryDisplayInfo();
- sDeviceInfo->mMaxTextureSize = maxTextureSize;
- sDeviceInfo->queryCompositionPreference(&sDeviceInfo->mTargetDataSpace,
- &sDeviceInfo->mTargetPixelFormat);
- });
-}
-
-void DeviceInfo::load() {
- mDisplayInfo = queryDisplayInfo();
- glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
- sDeviceInfo->queryCompositionPreference(&sDeviceInfo->mTargetDataSpace,
- &sDeviceInfo->mTargetPixelFormat);
-}
-
-DisplayInfo DeviceInfo::queryDisplayInfo() {
+DisplayInfo QueryDisplayInfo() {
if (Properties::isolatedProcess) {
return sDummyDisplay;
}
@@ -90,17 +61,36 @@
return displayInfo;
}
-void DeviceInfo::queryCompositionPreference(ui::Dataspace* dataSpace,
- ui::PixelFormat* pixelFormat) {
+void QueryCompositionPreference(ui::Dataspace* dataSpace,
+ ui::PixelFormat* pixelFormat) {
if (Properties::isolatedProcess) {
*dataSpace = ui::Dataspace::V0_SRGB;
*pixelFormat = ui::PixelFormat::RGBA_8888;
}
status_t status =
- SurfaceComposerClient::getCompositionPreference(dataSpace, pixelFormat);
+ SurfaceComposerClient::getCompositionPreference(dataSpace, pixelFormat);
LOG_ALWAYS_FATAL_IF(status, "Failed to get composition preference, error %d", status);
}
+DeviceInfo::DeviceInfo() {
+#if HWUI_NULL_GPU
+ mMaxTextureSize = NULL_GPU_MAX_TEXTURE_SIZE;
+#else
+ mMaxTextureSize = -1;
+#endif
+ mDisplayInfo = QueryDisplayInfo();
+ QueryCompositionPreference(&mTargetDataSpace, &mTargetPixelFormat);
+}
+
+int DeviceInfo::maxTextureSize() const {
+ LOG_ALWAYS_FATAL_IF(mMaxTextureSize < 0, "MaxTextureSize has not been initialized yet.");
+ return mMaxTextureSize;
+}
+
+void DeviceInfo::setMaxTextureSize(int maxTextureSize) {
+ const_cast<DeviceInfo*>(DeviceInfo::get())->mMaxTextureSize = maxTextureSize;
+}
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
index 416af17..1d747741 100644
--- a/libs/hwui/DeviceInfo.h
+++ b/libs/hwui/DeviceInfo.h
@@ -19,51 +19,38 @@
#include <ui/DisplayInfo.h>
#include <ui/GraphicTypes.h>
-#include "Extensions.h"
#include "utils/Macros.h"
namespace android {
namespace uirenderer {
+namespace renderthread {
+ class RenderThread;
+}
+
class DeviceInfo {
PREVENT_COPY_AND_ASSIGN(DeviceInfo);
public:
- // returns nullptr if DeviceInfo is not initialized yet
- // Note this does not have a memory fence so it's up to the caller
- // to use one if required. Normally this should not be necessary
static const DeviceInfo* get();
- // only call this after GL has been initialized, or at any point if compiled
- // with HWUI_NULL_GPU
- static void initialize();
- static void initialize(int maxTextureSize);
+ // this value is only valid after the GPU has been initialized and there is a valid graphics
+ // context or if you are using the HWUI_NULL_GPU
+ int maxTextureSize() const;
- int maxTextureSize() const { return mMaxTextureSize; }
ui::Dataspace getTargetDataSpace() const { return mTargetDataSpace; }
ui::PixelFormat getTargetPixelFormat() const { return mTargetPixelFormat; }
const DisplayInfo& displayInfo() const { return mDisplayInfo; }
- const Extensions& extensions() const { return mExtensions; }
-
- static uint32_t multiplyByResolution(uint32_t in) {
- auto di = DeviceInfo::get()->displayInfo();
- return di.w * di.h * in;
- }
-
- static DisplayInfo queryDisplayInfo();
private:
- static void queryCompositionPreference(ui::Dataspace* dataSpace,
- ui::PixelFormat* pixelFormat);
+ friend class renderthread::RenderThread;
+ static void setMaxTextureSize(int maxTextureSize);
- DeviceInfo() {}
- ~DeviceInfo() {}
-
- void load();
+ DeviceInfo();
int mMaxTextureSize;
DisplayInfo mDisplayInfo;
- Extensions mExtensions;
+
// TODO(lpy) Replace below with android_ prefix types.
ui::Dataspace mTargetDataSpace;
ui::PixelFormat mTargetPixelFormat;
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
deleted file mode 100644
index e90f40c..0000000
--- a/libs/hwui/Extensions.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2010 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 ANDROID_HWUI_EXTENSIONS_H
-#define ANDROID_HWUI_EXTENSIONS_H
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Classes
-///////////////////////////////////////////////////////////////////////////////
-
-class Extensions {
-public:
- Extensions() {}
-
- inline bool hasNPot() const { return false; }
- inline bool hasFramebufferFetch() const { return false; }
- inline bool hasDiscardFramebuffer() const { return false; }
- inline bool hasDebugMarker() const { return false; }
- inline bool has1BitStencil() const { return false; }
- inline bool has4BitStencil() const { return false; }
- inline bool hasUnpackRowLength() const { return mVersionMajor >= 3; }
- inline bool hasPixelBufferObjects() const { return mVersionMajor >= 3; }
- inline bool hasOcclusionQueries() const { return mVersionMajor >= 3; }
- inline bool hasFloatTextures() const { return mVersionMajor >= 3; }
- inline bool hasRenderableFloatTextures() const {
- return (mVersionMajor >= 3 && mVersionMinor >= 2);
- }
- inline bool hasSRGB() const { return false; }
- inline bool hasSRGBWriteControl() const { return hasSRGB() && false; }
- inline bool hasLinearBlending() const { return hasSRGB() && false; }
-
- inline int getMajorGlVersion() const { return mVersionMajor; }
- inline int getMinorGlVersion() const { return mVersionMinor; }
-
-private:
- int mVersionMajor = 2;
- int mVersionMinor = 0;
-}; // class Extensions
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_EXTENSIONS_H
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index ab80d3d..165fc48 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -45,7 +45,7 @@
}
static void checkIdleTimeout() {
- std::lock_guard{sLock};
+ std::lock_guard _lock{sLock};
if (sPendingUploads == 0 && shouldTimeOutLocked()) {
sEglManager.destroy();
} else {
@@ -54,7 +54,7 @@
}
static void beginUpload() {
- std::lock_guard{sLock};
+ std::lock_guard _lock{sLock};
sPendingUploads++;
if (!sUploadThread) {
@@ -75,13 +75,13 @@
}
static void endUpload() {
- std::lock_guard{sLock};
+ std::lock_guard _lock{sLock};
sPendingUploads--;
sLastUpload = systemTime();
}
static EGLDisplay getUploadEglDisplay() {
- std::lock_guard{sLock};
+ std::lock_guard _lock{sLock};
LOG_ALWAYS_FATAL_IF(!sEglManager.hasEglContext(), "Forgot to begin an upload?");
return sEglManager.eglDisplay();
}
@@ -107,39 +107,6 @@
#define FENCE_TIMEOUT 2000000000
-class AutoEglImage {
-public:
- AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer) : mDisplay(display) {
- EGLint imageAttrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
- image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer,
- imageAttrs);
- }
-
- ~AutoEglImage() {
- if (image != EGL_NO_IMAGE_KHR) {
- eglDestroyImageKHR(mDisplay, image);
- }
- }
-
- EGLImageKHR image = EGL_NO_IMAGE_KHR;
-
-private:
- EGLDisplay mDisplay = EGL_NO_DISPLAY;
-};
-
-class AutoSkiaGlTexture {
-public:
- AutoSkiaGlTexture() {
- glGenTextures(1, &mTexture);
- glBindTexture(GL_TEXTURE_2D, mTexture);
- }
-
- ~AutoSkiaGlTexture() { glDeleteTextures(1, &mTexture); }
-
-private:
- GLuint mTexture = 0;
-};
-
struct FormatInfo {
PixelFormat pixelFormat;
GLint format, type;
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index e6d2a6f..f2d50cd 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -146,7 +146,7 @@
frame[FrameInfoIndex::IntendedVsync] + mFrameInterval);
// If we hit the deadline, cool!
- if (frame[FrameInfoIndex::FrameCompleted] < mSwapDeadline) {
+ if (frame[FrameInfoIndex::FrameCompleted] < mSwapDeadline || totalDuration < mFrameInterval) {
if (isTripleBuffered) {
mData->reportJankType(JankType::kHighInputLatency);
(*mGlobalData)->reportJankType(JankType::kHighInputLatency);
diff --git a/libs/hwui/debug/wrap_gles.h b/libs/hwui/debug/wrap_gles.h
index 27a29aa..3da6e80 100644
--- a/libs/hwui/debug/wrap_gles.h
+++ b/libs/hwui/debug/wrap_gles.h
@@ -28,6 +28,9 @@
#include <GLES3/gl31.h>
#include <GLES3/gl32.h>
+// constant used by the NULL GPU implementation as well as HWUI's unit tests
+constexpr int NULL_GPU_MAX_TEXTURE_SIZE = 2048;
+
// Generate stubs that route all the calls to our function table
#include "gles_redefine.h"
diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index dcfe6b3..e4ba13d 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -138,7 +138,7 @@
renderNodeDrawable->getRenderNode()->output(mOutput, mLevel + 1);
return;
}
- auto glFunctorDrawable = getGLFunctorDrawable(drawable);
+ auto glFunctorDrawable = getFunctorDrawable(drawable);
if (nullptr != glFunctorDrawable) {
mOutput << std::string(mLevel * 2, ' ') << "drawGLFunctorDrawable" << std::endl;
return;
@@ -157,10 +157,10 @@
return nullptr;
}
- GLFunctorDrawable* getGLFunctorDrawable(SkDrawable* drawable) {
+ FunctorDrawable* getFunctorDrawable(SkDrawable* drawable) {
for (auto& child : mDisplayList.mChildFunctors) {
- if (drawable == &child) {
- return &child;
+ if (drawable == child) {
+ return child;
}
}
return nullptr;
diff --git a/libs/hwui/pipeline/skia/FunctorDrawable.h b/libs/hwui/pipeline/skia/FunctorDrawable.h
new file mode 100644
index 0000000..162d137
--- /dev/null
+++ b/libs/hwui/pipeline/skia/FunctorDrawable.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "GlFunctorLifecycleListener.h"
+
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+
+#include <utils/Functor.h>
+
+namespace android {
+namespace uirenderer {
+
+namespace skiapipeline {
+
+/**
+ * This drawable wraps a functor enabling it to be recorded into a list
+ * of Skia drawing commands.
+ */
+class FunctorDrawable : public SkDrawable {
+public:
+ FunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
+ : mFunctor(functor), mListener(listener), mBounds(canvas->getLocalClipBounds()) {}
+ virtual ~FunctorDrawable() {}
+
+ virtual void syncFunctor() const = 0;
+
+protected:
+ virtual SkRect onGetBounds() override { return mBounds; }
+
+ Functor* mFunctor;
+ sp<GlFunctorLifecycleListener> mListener;
+ const SkRect mBounds;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index b0fec7a..90d5e71 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -18,7 +18,6 @@
#include <GrContext.h>
#include <private/hwui/DrawGlInfo.h>
#include "GlFunctorLifecycleListener.h"
-#include "Properties.h"
#include "RenderNode.h"
#include "SkAndroidFrameworkUtils.h"
#include "SkClipStack.h"
@@ -80,11 +79,6 @@
return;
}
- if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
- canvas->clear(SK_ColorRED);
- return;
- }
-
GLuint fboID = 0;
SkISize fboSize;
if (!GetFboDetails(canvas, &fboID, &fboSize)) {
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
index d9e65c9..dd6ef25 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
@@ -16,12 +16,8 @@
#pragma once
-#include "GlFunctorLifecycleListener.h"
+#include "FunctorDrawable.h"
-#include <SkCanvas.h>
-#include <SkDrawable.h>
-
-#include <utils/Functor.h>
#include <utils/RefBase.h>
namespace android {
@@ -33,22 +29,16 @@
* This drawable wraps a OpenGL functor enabling it to be recorded into a list
* of Skia drawing commands.
*/
-class GLFunctorDrawable : public SkDrawable {
+class GLFunctorDrawable : public FunctorDrawable {
public:
GLFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
- : mFunctor(functor), mListener(listener), mBounds(canvas->getLocalClipBounds()) {}
+ : FunctorDrawable(functor, listener, canvas) {}
virtual ~GLFunctorDrawable();
- void syncFunctor() const;
+ void syncFunctor() const override;
protected:
- virtual SkRect onGetBounds() override { return mBounds; }
virtual void onDraw(SkCanvas* canvas) override;
-
-private:
- Functor* mFunctor;
- sp<GlFunctorLifecycleListener> mListener;
- const SkRect mBounds;
};
}; // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index 82179a3..78b64b2 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -29,7 +29,7 @@
void SkiaDisplayList::syncContents() {
for (auto& functor : mChildFunctors) {
- functor.syncFunctor();
+ functor->syncFunctor();
}
for (auto& animatedImage : mAnimatedImages) {
animatedImage->syncProperties();
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 4f30f98..4c78539 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -17,7 +17,7 @@
#pragma once
#include "hwui/AnimatedImageDrawable.h"
-#include "GLFunctorDrawable.h"
+#include "FunctorDrawable.h"
#include "RecordingCanvas.h"
#include "RenderNodeDrawable.h"
#include "TreeInfo.h"
@@ -77,7 +77,7 @@
* that creates them. Allocator dtor invokes all SkDrawable dtors.
*/
template <class T, typename... Params>
- SkDrawable* allocateDrawable(Params&&... params) {
+ T* allocateDrawable(Params&&... params) {
return allocator.create<T>(std::forward<Params>(params)...);
}
@@ -155,7 +155,7 @@
* cannot relocate.
*/
std::deque<RenderNodeDrawable> mChildNodes;
- std::deque<GLFunctorDrawable> mChildFunctors;
+ std::deque<FunctorDrawable*> mChildFunctors;
std::vector<SkImage*> mMutableImages;
std::vector<VectorDrawableRoot*> mVectorDrawables;
std::vector<AnimatedImageDrawable*> mAnimatedImages;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 83d7e6a..3c281e7 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -23,6 +23,8 @@
#include "NinePatchUtils.h"
#include "RenderNode.h"
#include "pipeline/skia/AnimatedDrawables.h"
+#include "pipeline/skia/GLFunctorDrawable.h"
+#include "pipeline/skia/VkFunctorDrawable.h"
namespace android {
namespace uirenderer {
@@ -118,9 +120,16 @@
void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor,
uirenderer::GlFunctorLifecycleListener* listener) {
- // Drawable dtor will be invoked when mChildFunctors deque is cleared.
- mDisplayList->mChildFunctors.emplace_back(functor, listener, asSkCanvas());
- drawDrawable(&mDisplayList->mChildFunctors.back());
+ FunctorDrawable* functorDrawable;
+ if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+ functorDrawable = mDisplayList->allocateDrawable<VkFunctorDrawable>(functor, listener,
+ asSkCanvas());
+ } else {
+ functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, listener,
+ asSkCanvas());
+ }
+ mDisplayList->mChildFunctors.push_back(functorDrawable);
+ drawDrawable(functorDrawable);
}
class VectorDrawable : public SkDrawable {
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 5cbe33d..e34f160 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -22,6 +22,7 @@
#include "SkiaProfileRenderer.h"
#include "renderstate/RenderState.h"
#include "renderthread/Frame.h"
+#include "VkFunctorDrawable.h"
#include <SkSurface.h>
#include <SkTypes.h>
@@ -146,9 +147,7 @@
}
void SkiaVulkanPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) {
- // TODO: we currently don't support OpenGL WebView's
- DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
- (*functor)(mode, nullptr);
+ VkFunctorDrawable::vkInvokeFunctor(functor);
}
sk_sp<Bitmap> SkiaVulkanPipeline::allocateHardwareBitmap(renderthread::RenderThread& renderThread,
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
new file mode 100644
index 0000000..6486ddb
--- /dev/null
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "VkFunctorDrawable.h"
+#include <private/hwui/DrawGlInfo.h>
+
+#include "renderthread/EglManager.h"
+#include "thread/ThreadBase.h"
+#include "utils/TimeUtils.h"
+#include <thread>
+#include <utils/Color.h>
+#include <utils/Trace.h>
+#include <utils/TraceUtils.h>
+
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+
+#include <utils/GLUtils.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+static std::mutex sLock{};
+static ThreadBase* sGLDrawThread = nullptr;
+static renderthread::EglManager sEglManager;
+
+// ScopedDrawRequest makes sure a GL thread is started and EGL context is initialized on it.
+class ScopedDrawRequest {
+public:
+ ScopedDrawRequest() { beginDraw(); }
+private:
+ void beginDraw() {
+ std::lock_guard{sLock};
+
+ if (!sGLDrawThread) {
+ sGLDrawThread = new ThreadBase{};
+ }
+
+ if (!sGLDrawThread->isRunning()) {
+ sGLDrawThread->start("GLFunctorThread");
+ }
+
+ if (!sEglManager.hasEglContext()) {
+ sGLDrawThread->queue().runSync([]() {
+ sEglManager.initialize();
+ });
+ }
+ }
+};
+
+void VkFunctorDrawable::vkInvokeFunctor(Functor* functor) {
+ ScopedDrawRequest _drawRequest{};
+ sGLDrawThread->queue().runSync([&]() {
+ EGLDisplay display = sEglManager.eglDisplay();
+ DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
+ if (display != EGL_NO_DISPLAY) {
+ mode = DrawGlInfo::kModeProcess;
+ }
+ (*functor)(mode, nullptr);
+ });
+}
+
+#define FENCE_TIMEOUT 2000000000
+
+void VkFunctorDrawable::onDraw(SkCanvas* canvas) {
+ ATRACE_CALL();
+
+ if (canvas->getGrContext() == nullptr) {
+ SkDEBUGF(("Attempting to draw VkFunctor into an unsupported surface"));
+ return;
+ }
+
+ ScopedDrawRequest _drawRequest{};
+
+ SkImageInfo surfaceInfo = canvas->imageInfo();
+
+ if (!mFrameBuffer.get() || mFBInfo != surfaceInfo) {
+ // Buffer will be used as an OpenGL ES render target.
+ mFrameBuffer = new GraphicBuffer(
+ //TODO: try to reduce the size of the buffer: possibly by using clip bounds.
+ static_cast<uint32_t>(surfaceInfo.width()),
+ static_cast<uint32_t>(surfaceInfo.height()),
+ ColorTypeToPixelFormat(surfaceInfo.colorType()),
+ GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
+ GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_HW_RENDER,
+ std::string("VkFunctorDrawable::onDraw pid [") + std::to_string(getpid()) + "]");
+ status_t error = mFrameBuffer->initCheck();
+ if (error < 0) {
+ ALOGW("VkFunctorDrawable::onDraw() failed in GraphicBuffer.create()");
+ return;
+ }
+
+ mFBInfo = surfaceInfo;
+ }
+
+ //TODO: Synchronization is needed on mFrameBuffer to guarantee that the previous Vulkan
+ //TODO: draw command has completed.
+ //TODO: A simple but inefficient way is to flush and issue a QueueWaitIdle call. See
+ //TODO: GrVkGpu::destroyResources() for example.
+ bool success = sGLDrawThread->queue().runSync([&]() -> bool {
+ ATRACE_FORMAT("WebViewDraw_%dx%d", mFBInfo.width(), mFBInfo.height());
+ EGLDisplay display = sEglManager.eglDisplay();
+ LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY,
+ "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
+ uirenderer::renderthread::EglManager::eglErrorString());
+ // We use an EGLImage to access the content of the GraphicBuffer
+ // The EGL image is later bound to a 2D texture
+ EGLClientBuffer clientBuffer = (EGLClientBuffer)mFrameBuffer->getNativeBuffer();
+ AutoEglImage autoImage(display, clientBuffer);
+ if (autoImage.image == EGL_NO_IMAGE_KHR) {
+ ALOGW("Could not create EGL image, err =%s",
+ uirenderer::renderthread::EglManager::eglErrorString());
+ return false;
+ }
+
+ AutoSkiaGlTexture glTexture;
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
+ GL_CHECKPOINT(MODERATE);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ DrawGlInfo info;
+ SkMatrix44 mat4(canvas->getTotalMatrix());
+ SkIRect clipBounds = canvas->getDeviceClipBounds();
+
+ info.clipLeft = clipBounds.fLeft;
+ info.clipTop = clipBounds.fTop;
+ info.clipRight = clipBounds.fRight;
+ info.clipBottom = clipBounds.fBottom;
+ info.isLayer = true;
+ info.width = mFBInfo.width();
+ info.height = mFBInfo.height();
+ mat4.asColMajorf(&info.transform[0]);
+
+ glViewport(0, 0, info.width, info.height);
+
+ AutoGLFramebuffer glFb;
+ // Bind texture to the frame buffer.
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ glTexture.mTexture, 0);
+ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Failed framebuffer check for created target buffer: %s",
+ GLUtils::getGLFramebufferError());
+ return false;
+ }
+
+ glDisable(GL_STENCIL_TEST);
+ glDisable(GL_SCISSOR_TEST);
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ (*mFunctor)(DrawGlInfo::kModeDraw, &info);
+
+ EGLSyncKHR glDrawFinishedFence =
+ eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
+ LOG_ALWAYS_FATAL_IF(glDrawFinishedFence == EGL_NO_SYNC_KHR,
+ "Could not create sync fence %#x", eglGetError());
+ glFlush();
+ // TODO: export EGLSyncKHR in file descr
+ // TODO: import file desc in Vulkan Semaphore
+ // TODO: instead block the GPU: probably by using external Vulkan semaphore.
+ // Block the CPU until the glFlush finish.
+ EGLint waitStatus = eglClientWaitSyncKHR(display, glDrawFinishedFence, 0,
+ FENCE_TIMEOUT);
+ LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
+ "Failed to wait for the fence %#x", eglGetError());
+ eglDestroySyncKHR(display, glDrawFinishedFence);
+ return true;
+ });
+
+ if (!success) {
+ return;
+ }
+
+ SkPaint paint;
+ paint.setBlendMode(SkBlendMode::kSrcOver);
+ canvas->save();
+ // The size of the image matches the size of the canvas. We've used the matrix already, while
+ // drawing into the offscreen surface, so we need to reset it here.
+ canvas->resetMatrix();
+
+ auto functorImage = SkImage::MakeFromAHardwareBuffer(
+ reinterpret_cast<AHardwareBuffer*>(mFrameBuffer.get()), kPremul_SkAlphaType,
+ nullptr, kBottomLeft_GrSurfaceOrigin);
+ canvas->drawImage(functorImage, 0, 0, &paint);
+ canvas->restore();
+}
+
+VkFunctorDrawable::~VkFunctorDrawable() {
+ if (mListener.get() != nullptr) {
+ ScopedDrawRequest _drawRequest{};
+ sGLDrawThread->queue().runSync([&]() {
+ mListener->onGlFunctorReleased(mFunctor);
+ });
+ }
+}
+
+void VkFunctorDrawable::syncFunctor() const {
+ ScopedDrawRequest _drawRequest{};
+ sGLDrawThread->queue().runSync([&]() {
+ (*mFunctor)(DrawGlInfo::kModeSync, nullptr);
+ });
+}
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.h b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
new file mode 100644
index 0000000..e37f6fb
--- /dev/null
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "FunctorDrawable.h"
+
+#include <utils/RefBase.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+namespace uirenderer {
+
+namespace skiapipeline {
+
+/**
+ * This drawable wraps a Vulkan functor enabling it to be recorded into a list
+ * of Skia drawing commands.
+ */
+class VkFunctorDrawable : public FunctorDrawable {
+public:
+ VkFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
+ : FunctorDrawable(functor, listener, canvas) {}
+ virtual ~VkFunctorDrawable();
+
+ void syncFunctor() const override;
+
+ static void vkInvokeFunctor(Functor* functor);
+
+protected:
+ virtual void onDraw(SkCanvas* canvas) override;
+
+private:
+
+ // Variables below describe/store temporary offscreen buffer used for Vulkan pipeline.
+ sp<GraphicBuffer> mFrameBuffer;
+ SkImageInfo mFBInfo;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 675df41..0cb23e5 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -23,7 +23,6 @@
#include "utils/Color.h"
#include "utils/StringUtils.h"
-#include "DeviceInfo.h"
#include "Frame.h"
#include "Properties.h"
@@ -127,7 +126,6 @@
createContext();
createPBufferSurface();
makeCurrent(mPBufferSurface, nullptr, /* force */ true);
- DeviceInfo::initialize();
mSurfaceColorGamut = DataSpaceToColorGamut(
static_cast<android_dataspace>(DeviceInfo::get()->getTargetDataSpace()));
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 7258a0a..a5dcc72 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -162,7 +162,7 @@
}
void RenderThread::initThreadLocals() {
- mDisplayInfo = DeviceInfo::queryDisplayInfo();
+ mDisplayInfo = DeviceInfo::get()->displayInfo();
nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1000000000 / mDisplayInfo.fps);
mTimeLord.setFrameInterval(frameIntervalNanos);
initializeDisplayEventReceiver();
@@ -246,6 +246,9 @@
mGrContext->releaseResourcesAndAbandonContext();
}
mGrContext = std::move(context);
+ if (mGrContext) {
+ DeviceInfo::setMaxTextureSize(mGrContext->maxRenderTargetSize());
+ }
}
int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 038e13c..4881172 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -16,7 +16,6 @@
#include "VulkanManager.h"
-#include "DeviceInfo.h"
#include "Properties.h"
#include "RenderThread.h"
#include "renderstate/RenderState.h"
@@ -399,8 +398,6 @@
free_features_extensions_structs(features);
- DeviceInfo::initialize(mRenderThread.getGrContext()->maxRenderTargetSize());
-
if (Properties::enablePartialUpdates && Properties::useBufferAge) {
mSwapBehavior = SwapBehavior::BufferAge;
}
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 0e6582c..c35f512 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -16,7 +16,6 @@
#pragma once
-#include <DeviceInfo.h>
#include <DisplayList.h>
#include <Matrix.h>
#include <Properties.h>
@@ -179,12 +178,6 @@
static sp<RenderNode> createNode(
int left, int top, int right, int bottom,
std::function<void(RenderProperties& props, Canvas& canvas)> setup) {
-#if HWUI_NULL_GPU
- // if RenderNodes are being sync'd/used, device info will be needed, since
- // DeviceInfo::maxTextureSize() affects layer property
- DeviceInfo::initialize();
-#endif
-
sp<RenderNode> node = new RenderNode();
RenderProperties& props = node->mutateStagingProperties();
props.setLeftTopRightBottom(left, top, right, bottom);
@@ -202,12 +195,6 @@
static sp<RenderNode> createNode(
int left, int top, int right, int bottom,
std::function<void(RenderProperties& props, RecordingCanvasType& canvas)> setup) {
-#if HWUI_NULL_GPU
- // if RenderNodes are being sync'd/used, device info will be needed, since
- // DeviceInfo::maxTextureSize() affects layer property
- DeviceInfo::initialize();
-#endif
-
sp<RenderNode> node = new RenderNode();
RenderProperties& props = node->mutateStagingProperties();
props.setLeftTopRightBottom(left, top, right, bottom);
@@ -233,11 +220,6 @@
std::function<void(RenderProperties& props, skiapipeline::SkiaRecordingCanvas& canvas)>
setup,
const char* name = nullptr, skiapipeline::SkiaDisplayList* displayList = nullptr) {
-#if HWUI_NULL_GPU
- // if RenderNodes are being sync'd/used, device info will be needed, since
- // DeviceInfo::maxTextureSize() affects layer property
- DeviceInfo::initialize();
-#endif
sp<RenderNode> node = new RenderNode();
if (name) {
node->setName(name);
diff --git a/libs/hwui/tests/unit/RenderPropertiesTests.cpp b/libs/hwui/tests/unit/RenderPropertiesTests.cpp
index 85655fc..3e8e057 100644
--- a/libs/hwui/tests/unit/RenderPropertiesTests.cpp
+++ b/libs/hwui/tests/unit/RenderPropertiesTests.cpp
@@ -22,8 +22,6 @@
using namespace android::uirenderer;
TEST(RenderProperties, layerValidity) {
- DeviceInfo::initialize();
-
const int maxTextureSize = DeviceInfo::get()->maxTextureSize();
ASSERT_LE(2048, maxTextureSize);
ASSERT_GT(100000, maxTextureSize);
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 6c398ee..415f9e8 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -20,6 +20,7 @@
#include "AnimationContext.h"
#include "DamageAccumulator.h"
#include "IContextFactory.h"
+#include "pipeline/skia/GLFunctorDrawable.h"
#include "pipeline/skia/SkiaDisplayList.h"
#include "renderthread/CanvasContext.h"
#include "tests/common/TestUtils.h"
@@ -46,7 +47,8 @@
SkCanvas dummyCanvas;
RenderNodeDrawable drawable(nullptr, &dummyCanvas);
skiaDL->mChildNodes.emplace_back(nullptr, &dummyCanvas);
- skiaDL->mChildFunctors.emplace_back(nullptr, nullptr, &dummyCanvas);
+ GLFunctorDrawable functorDrawable(nullptr, nullptr, &dummyCanvas);
+ skiaDL->mChildFunctors.push_back(&functorDrawable);
skiaDL->mMutableImages.push_back(nullptr);
skiaDL->mVectorDrawables.push_back(nullptr);
skiaDL->mProjectionReceiver = &drawable;
@@ -95,7 +97,8 @@
SkCanvas dummyCanvas;
TestUtils::MockFunctor functor;
- skiaDL.mChildFunctors.emplace_back(&functor, nullptr, &dummyCanvas);
+ GLFunctorDrawable functorDrawable(&functor, nullptr, &dummyCanvas);
+ skiaDL.mChildFunctors.push_back(&functorDrawable);
SkRect bounds = SkRect::MakeWH(200, 200);
VectorDrawableRoot vectorDrawable(new VectorDrawable::Group());
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index d35fe4f..9f71e91 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -72,6 +72,26 @@
}
}
+android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType) {
+ switch (colorType) {
+ case kRGBA_8888_SkColorType:
+ return PIXEL_FORMAT_RGBA_8888;
+ case kRGBA_F16_SkColorType:
+ return PIXEL_FORMAT_RGBA_FP16;
+ case kRGB_565_SkColorType:
+ return PIXEL_FORMAT_RGB_565;
+ case kRGB_888x_SkColorType:
+ return PIXEL_FORMAT_RGBX_8888;
+ case kRGBA_1010102_SkColorType:
+ return PIXEL_FORMAT_RGBA_1010102;
+ case kARGB_4444_SkColorType:
+ return PIXEL_FORMAT_RGBA_4444;
+ default:
+ ALOGW("Unsupported colorType: %d, return RGBA_8888 by default", (int)colorType);
+ return PIXEL_FORMAT_RGBA_8888;
+ }
+}
+
SkColorSpace::Gamut DataSpaceToColorGamut(android_dataspace dataSpace) {
switch (dataSpace & HAL_DATASPACE_STANDARD_MASK) {
case HAL_DATASPACE_STANDARD_BT709:
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index ff0e755..e935a0d 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -18,6 +18,7 @@
#include <math.h>
#include <system/graphics.h>
+#include <ui/PixelFormat.h>
#include <SkColor.h>
#include <SkColorSpace.h>
@@ -116,6 +117,8 @@
SkColorType PixelFormatToColorType(android_pixel_format pixelFormat);
+android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType);
+
SkColorSpace::Gamut DataSpaceToColorGamut(android_dataspace dataSpace);
sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
diff --git a/libs/hwui/utils/GLUtils.cpp b/libs/hwui/utils/GLUtils.cpp
index bf27300..fcd036c 100644
--- a/libs/hwui/utils/GLUtils.cpp
+++ b/libs/hwui/utils/GLUtils.cpp
@@ -59,5 +59,22 @@
#endif
}
+const char* GLUtils::getGLFramebufferError() {
+ switch (glCheckFramebufferStatus(GL_FRAMEBUFFER)) {
+ case GL_FRAMEBUFFER_COMPLETE:
+ return "GL_FRAMEBUFFER_COMPLETE";
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
+ return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
+ return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
+ case GL_FRAMEBUFFER_UNSUPPORTED:
+ return "GL_FRAMEBUFFER_UNSUPPORTED";
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
+ return "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
+ default:
+ return "Unknown error";
+ }
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/utils/GLUtils.h b/libs/hwui/utils/GLUtils.h
index debfb5d..ca8810b 100644
--- a/libs/hwui/utils/GLUtils.h
+++ b/libs/hwui/utils/GLUtils.h
@@ -20,6 +20,12 @@
#include <log/log.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+
namespace android {
namespace uirenderer {
@@ -43,8 +49,53 @@
*/
static bool dumpGLErrors();
+ static const char* getGLFramebufferError();
}; // class GLUtils
+class AutoEglImage {
+public:
+ AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer) : mDisplay(display) {
+ EGLint imageAttrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
+ image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer,
+ imageAttrs);
+ }
+
+ ~AutoEglImage() {
+ if (image != EGL_NO_IMAGE_KHR) {
+ eglDestroyImageKHR(mDisplay, image);
+ }
+ }
+
+ EGLImageKHR image = EGL_NO_IMAGE_KHR;
+
+private:
+ EGLDisplay mDisplay = EGL_NO_DISPLAY;
+};
+
+class AutoSkiaGlTexture {
+public:
+ AutoSkiaGlTexture() {
+ glGenTextures(1, &mTexture);
+ glBindTexture(GL_TEXTURE_2D, mTexture);
+ }
+
+ ~AutoSkiaGlTexture() { glDeleteTextures(1, &mTexture); }
+
+ GLuint mTexture = 0;
+};
+
+class AutoGLFramebuffer {
+public:
+ AutoGLFramebuffer() {
+ glGenFramebuffers(1, &mFb);
+ glBindFramebuffer(GL_FRAMEBUFFER, mFb);
+ }
+
+ ~AutoGLFramebuffer() { glDeleteFramebuffers(1, &mFb); }
+
+ GLuint mFb;
+};
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/services/include/android/os/StatsLogEventWrapper.h b/libs/services/include/android/os/StatsLogEventWrapper.h
index 255619c..52cb75e 100644
--- a/libs/services/include/android/os/StatsLogEventWrapper.h
+++ b/libs/services/include/android/os/StatsLogEventWrapper.h
@@ -25,6 +25,58 @@
namespace android {
namespace os {
+/**
+ * A wrapper for a union type to contain multiple types of values.
+ *
+ */
+struct StatsLogValue {
+ // Keep in sync with FieldValue.h
+ enum STATS_LOG_VALUE_TYPE {
+ UNKNOWN = 0,
+ INT = 1,
+ LONG = 2,
+ FLOAT = 3,
+ DOUBLE = 4,
+ STRING = 5,
+ STORAGE = 6
+ };
+
+ StatsLogValue() : type(UNKNOWN) {}
+
+ StatsLogValue(int32_t v) {
+ int_value = v;
+ type = INT;
+ }
+
+ StatsLogValue(int64_t v) {
+ long_value = v;
+ type = LONG;
+ }
+
+ StatsLogValue(float v) {
+ float_value = v;
+ type = FLOAT;
+ }
+
+ StatsLogValue(const std::string& v) {
+ str_value = v;
+ type = STRING;
+ }
+
+ void setType(STATS_LOG_VALUE_TYPE t) { type = t; }
+
+ union {
+ int32_t int_value;
+ int64_t long_value;
+ float float_value;
+ double double_value;
+ };
+ std::string str_value;
+ std::vector<uint8_t> storage_value;
+
+ STATS_LOG_VALUE_TYPE type;
+};
+
// Represents a parcelable object. Only used to send data from Android OS to statsd.
class StatsLogEventWrapper : public android::Parcelable {
public:
@@ -36,8 +88,22 @@
android::status_t readFromParcel(const android::Parcel* in);
- // These are public for ease of conversion.
- std::vector<uint8_t> bytes;
+ int getTagId() const { return mTagId; }
+
+ int64_t getElapsedRealTimeNs() const { return mElapsedRealTimeNs; }
+
+ int64_t getWallClockTimeNs() const { return mWallClockTimeNs; }
+
+ std::vector<StatsLogValue> getElements() const { return mElements; }
+
+ private:
+ int mTagId;
+
+ int64_t mElapsedRealTimeNs;
+
+ int64_t mWallClockTimeNs;
+
+ std::vector<StatsLogValue> mElements;
};
} // Namespace os
} // Namespace android
diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp
index 8b3aa9a..04c4629 100644
--- a/libs/services/src/os/StatsLogEventWrapper.cpp
+++ b/libs/services/src/os/StatsLogEventWrapper.cpp
@@ -32,13 +32,70 @@
StatsLogEventWrapper::StatsLogEventWrapper(){};
status_t StatsLogEventWrapper::writeToParcel(Parcel* out) const {
- out->writeByteVector(bytes);
- return ::android::NO_ERROR;
+ // Implement me if desired. We don't currently use this.
+ ALOGE(
+ "Cannot do c++ StatsLogEventWrapper.writeToParcel(); it is not "
+ "implemented.");
+ (void)out; // To prevent compile error of unused parameter 'in'
+ return UNKNOWN_ERROR;
};
status_t StatsLogEventWrapper::readFromParcel(const Parcel* in) {
- in->readByteVector(&bytes);
- return ::android::NO_ERROR;
+ status_t res = OK;
+ if (in == NULL) {
+ ALOGE("statsd received parcel argument was NULL.");
+ return BAD_VALUE;
+ }
+ if ((res = in->readInt32(&mTagId)) != OK) {
+ ALOGE("statsd could not read tagId from parcel");
+ return res;
+ }
+ if ((res = in->readInt64(&mElapsedRealTimeNs)) != OK) {
+ ALOGE("statsd could not read elapsed real time from parcel");
+ return res;
+ }
+ if ((res = in->readInt64(&mWallClockTimeNs)) != OK) {
+ ALOGE("statsd could not read wall clock time from parcel");
+ return res;
+ }
+ int dataSize = 0;
+ if ((res = in->readInt32(&dataSize)) != OK) {
+ ALOGE("statsd could not read data size from parcel");
+ return res;
+ }
+ if (mTagId <= 0 || mElapsedRealTimeNs <= 0 || mWallClockTimeNs <= 0 ||
+ dataSize <= 0) {
+ ALOGE("statsd received invalid parcel");
+ return BAD_VALUE;
+ }
+
+ for (int i = 0; i < dataSize; i++) {
+ int type = in->readInt32();
+ switch (type) {
+ case StatsLogValue::INT:
+ mElements.push_back(StatsLogValue(in->readInt32()));
+ break;
+ case StatsLogValue::LONG:
+ mElements.push_back(StatsLogValue(in->readInt64()));
+ break;
+ case StatsLogValue::STRING:
+ mElements.push_back(
+ StatsLogValue(std::string(String8(in->readString16()).string())));
+ break;
+ case StatsLogValue::FLOAT:
+ mElements.push_back(StatsLogValue(in->readFloat()));
+ break;
+ case StatsLogValue::STORAGE:
+ mElements.push_back(StatsLogValue());
+ mElements.back().setType(StatsLogValue::STORAGE);
+ in->readByteVector(&(mElements.back().storage_value));
+ break;
+ default:
+ ALOGE("unrecognized data type: %d", type);
+ return BAD_TYPE;
+ }
+ }
+ return NO_ERROR;
};
} // Namespace os
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index d1d605f..1d27c03 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -43,6 +43,8 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
/**
MediaCodec class can be used to access low-level media codecs, i.e. encoder/decoder components.
@@ -3582,7 +3584,18 @@
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- private long mNativeContext;
+ private long mNativeContext = 0;
+ private final Lock mNativeContextLock = new ReentrantLock();
+
+ private final long lockAndGetContext() {
+ mNativeContextLock.lock();
+ return mNativeContext;
+ }
+
+ private final void setAndUnlockContext(long context) {
+ mNativeContext = context;
+ mNativeContextLock.unlock();
+ }
/** @hide */
public static class MediaImage extends Image {
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 89827bc..7492aa6 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -1108,12 +1108,9 @@
/**
* Sets playback rate using {@link PlaybackParams}. The object sets its internal
- * PlaybackParams to the input, except that the object remembers previous speed
- * when input speed is zero. This allows the object to resume at previous speed
- * when play() is called. Calling it before the object is prepared does not change
- * the object state. After the object is prepared, calling it with zero speed is
- * equivalent to calling pause(). After the object is prepared, calling it with
- * non-zero speed is equivalent to calling play().
+ * PlaybackParams to the input. This allows the object to resume at previous speed
+ * when play() is called. Speed of zero is not allowed. Calling it does not change
+ * the object state.
*
* @param params the playback params.
*/
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 6ae4d40..6187900 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -544,29 +544,11 @@
private static final int INVOKE_ID_GET_SELECTED_TRACK = 7;
/**
- * Create a request parcel which can be routed to the native media
- * player using {@link #invoke(Parcel, Parcel)}. The Parcel
- * returned has the proper InterfaceToken set. The caller should
- * not overwrite that token, i.e it can only append data to the
- * Parcel.
- *
- * @return A parcel suitable to hold a request for the native
- * player.
- * {@hide}
- */
- @Override
- public Parcel newRequest() {
- Parcel parcel = Parcel.obtain();
- return parcel;
- }
-
- /**
* Invoke a generic method on the native player using opaque protocol
* buffer message for the request and reply. Both payloads' format is a
* convention between the java caller and the native player.
*
- * @param request PlayerMessage for the extension. The
- * caller must use {@link #newRequest()} to get one.
+ * @param msg PlayerMessage for the extension.
*
* @return PlayerMessage with the data returned by the
* native player.
@@ -1536,91 +1518,6 @@
}
/**
- * Gets the media metadata.
- *
- * @param update_only controls whether the full set of available
- * metadata is returned or just the set that changed since the
- * last call. See {@see #METADATA_UPDATE_ONLY} and {@see
- * #METADATA_ALL}.
- *
- * @param apply_filter if true only metadata that matches the
- * filter is returned. See {@see #APPLY_METADATA_FILTER} and {@see
- * #BYPASS_METADATA_FILTER}.
- *
- * @return The metadata, possibly empty. null if an error occured.
- // FIXME: unhide.
- * {@hide}
- */
- @Override
- public Metadata getMetadata(final boolean update_only,
- final boolean apply_filter) {
- Parcel reply = Parcel.obtain();
- Metadata data = new Metadata();
-
- if (!native_getMetadata(update_only, apply_filter, reply)) {
- reply.recycle();
- return null;
- }
-
- // Metadata takes over the parcel, don't recycle it unless
- // there is an error.
- if (!data.parse(reply)) {
- reply.recycle();
- return null;
- }
- return data;
- }
-
- /**
- * Set a filter for the metadata update notification and update
- * retrieval. The caller provides 2 set of metadata keys, allowed
- * and blocked. The blocked set always takes precedence over the
- * allowed one.
- * Metadata.MATCH_ALL and Metadata.MATCH_NONE are 2 sets available as
- * shorthands to allow/block all or no metadata.
- *
- * By default, there is no filter set.
- *
- * @param allow Is the set of metadata the client is interested
- * in receiving new notifications for.
- * @param block Is the set of metadata the client is not interested
- * in receiving new notifications for.
- * @return The call status code.
- *
- // FIXME: unhide.
- * {@hide}
- */
- @Override
- public int setMetadataFilter(Set<Integer> allow, Set<Integer> block) {
- // Do our serialization manually instead of calling
- // Parcel.writeArray since the sets are made of the same type
- // we avoid paying the price of calling writeValue (used by
- // writeArray) which burns an extra int per element to encode
- // the type.
- Parcel request = newRequest();
-
- // The parcel starts already with an interface token. There
- // are 2 filters. Each one starts with a 4bytes number to
- // store the len followed by a number of int (4 bytes as well)
- // representing the metadata type.
- int capacity = request.dataSize() + 4 * (1 + allow.size() + 1 + block.size());
-
- if (request.dataCapacity() < capacity) {
- request.setDataCapacity(capacity);
- }
-
- request.writeInt(allow.size());
- for(Integer t: allow) {
- request.writeInt(t);
- }
- request.writeInt(block.size());
- for(Integer t: block) {
- request.writeInt(t);
- }
- return native_setMetadataFilter(request);
- }
-
- /**
* Resets the MediaPlayer2 to its uninitialized state. After calling
* this method, you will have to initialize it again by setting the
* data source and calling prepare().
@@ -1802,32 +1699,6 @@
private native void _setAuxEffectSendLevel(float level);
- /*
- * @param update_only If true fetch only the set of metadata that have
- * changed since the last invocation of getMetadata.
- * The set is built using the unfiltered
- * notifications the native player sent to the
- * MediaPlayer2Manager during that period of
- * time. If false, all the metadatas are considered.
- * @param apply_filter If true, once the metadata set has been built based on
- * the value update_only, the current filter is applied.
- * @param reply[out] On return contains the serialized
- * metadata. Valid only if the call was successful.
- * @return The status code.
- */
- private native final boolean native_getMetadata(boolean update_only,
- boolean apply_filter,
- Parcel reply);
-
- /*
- * @param request Parcel with the 2 serialized lists of allowed
- * metadata types followed by the one to be
- * dropped. Each list starts with an integer
- * indicating the number of metadata type elements.
- * @return The status code.
- */
- private native final int native_setMetadataFilter(Parcel request);
-
private static native final void native_init();
private native final void native_setup(Object mediaplayer2_this);
private native final void native_finalize();
@@ -1903,25 +1774,6 @@
mFormat = format;
}
- /**
- * Flatten this object in to a Parcel.
- *
- * @param dest The Parcel in which the object should be written.
- * @param flags Additional flags about how the object should be written.
- * May be 0 or {@link android.os.Parcelable#PARCELABLE_WRITE_RETURN_VALUE}.
- */
- /* package private */ void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mTrackType);
- dest.writeString(getLanguage());
-
- if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
- dest.writeString(mFormat.getString(MediaFormat.KEY_MIME));
- dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_AUTOSELECT));
- dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_DEFAULT));
- dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE));
- }
- }
-
@Override
public String toString() {
StringBuilder out = new StringBuilder(128);
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 7681cc3..3870124 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -88,8 +88,6 @@
name: "libmedia2_jni",
srcs: [
- "android_media_Media2HTTPConnection.cpp",
- "android_media_Media2HTTPService.cpp",
"android_media_Media2DataSource.cpp",
"android_media_MediaMetricsJNI.cpp",
"android_media_MediaPlayer2.cpp",
@@ -142,6 +140,7 @@
"libstagefright_player2",
"libstagefright_rtsp",
"libstagefright_timedtext2",
+ "libmedia2_jni_core",
],
group_static_libs: true,
diff --git a/media/jni/android_media_Media2HTTPConnection.cpp b/media/jni/android_media_Media2HTTPConnection.cpp
deleted file mode 100644
index d02ee06..0000000
--- a/media/jni/android_media_Media2HTTPConnection.cpp
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * 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.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "Media2HTTPConnection-JNI"
-#include <utils/Log.h>
-
-#include <mediaplayer2/JavaVMHelper.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <nativehelper/ScopedLocalRef.h>
-
-#include "android_media_Media2HTTPConnection.h"
-#include "android_util_Binder.h"
-
-#include "log/log.h"
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-
-namespace android {
-
-static const size_t kBufferSize = 32768;
-
-JMedia2HTTPConnection::JMedia2HTTPConnection(JNIEnv *env, jobject thiz) {
- mMedia2HTTPConnectionObj = env->NewGlobalRef(thiz);
- CHECK(mMedia2HTTPConnectionObj != NULL);
-
- ScopedLocalRef<jclass> media2HTTPConnectionClass(
- env, env->GetObjectClass(mMedia2HTTPConnectionObj));
- CHECK(media2HTTPConnectionClass.get() != NULL);
-
- mConnectMethod = env->GetMethodID(
- media2HTTPConnectionClass.get(),
- "connect",
- "(Ljava/lang/String;Ljava/lang/String;)Z");
- CHECK(mConnectMethod != NULL);
-
- mDisconnectMethod = env->GetMethodID(
- media2HTTPConnectionClass.get(),
- "disconnect",
- "()V");
- CHECK(mDisconnectMethod != NULL);
-
- mReadAtMethod = env->GetMethodID(
- media2HTTPConnectionClass.get(),
- "readAt",
- "(J[BI)I");
- CHECK(mReadAtMethod != NULL);
-
- mGetSizeMethod = env->GetMethodID(
- media2HTTPConnectionClass.get(),
- "getSize",
- "()J");
- CHECK(mGetSizeMethod != NULL);
-
- mGetMIMETypeMethod = env->GetMethodID(
- media2HTTPConnectionClass.get(),
- "getMIMEType",
- "()Ljava/lang/String;");
- CHECK(mGetMIMETypeMethod != NULL);
-
- mGetUriMethod = env->GetMethodID(
- media2HTTPConnectionClass.get(),
- "getUri",
- "()Ljava/lang/String;");
- CHECK(mGetUriMethod != NULL);
-
- ScopedLocalRef<jbyteArray> tmp(
- env, env->NewByteArray(kBufferSize));
- mByteArrayObj = (jbyteArray)env->NewGlobalRef(tmp.get());
- CHECK(mByteArrayObj != NULL);
-}
-
-JMedia2HTTPConnection::~JMedia2HTTPConnection() {
- JNIEnv *env = JavaVMHelper::getJNIEnv();
- env->DeleteGlobalRef(mMedia2HTTPConnectionObj);
- env->DeleteGlobalRef(mByteArrayObj);
-}
-
-bool JMedia2HTTPConnection::connect(
- const char *uri, const KeyedVector<String8, String8> *headers) {
- String8 tmp("");
- if (headers != NULL) {
- for (size_t i = 0; i < headers->size(); ++i) {
- tmp.append(headers->keyAt(i));
- tmp.append(String8(": "));
- tmp.append(headers->valueAt(i));
- tmp.append(String8("\r\n"));
- }
- }
-
- JNIEnv* env = JavaVMHelper::getJNIEnv();
- jstring juri = env->NewStringUTF(uri);
- jstring jheaders = env->NewStringUTF(tmp.string());
-
- jboolean ret =
- env->CallBooleanMethod(mMedia2HTTPConnectionObj, mConnectMethod, juri, jheaders);
-
- env->DeleteLocalRef(juri);
- env->DeleteLocalRef(jheaders);
-
- return (bool)ret;
-}
-
-void JMedia2HTTPConnection::disconnect() {
- JNIEnv* env = JavaVMHelper::getJNIEnv();
- env->CallVoidMethod(mMedia2HTTPConnectionObj, mDisconnectMethod);
-}
-
-ssize_t JMedia2HTTPConnection::readAt(off64_t offset, void *data, size_t size) {
- JNIEnv* env = JavaVMHelper::getJNIEnv();
-
- if (size > kBufferSize) {
- size = kBufferSize;
- }
-
- jint n = env->CallIntMethod(
- mMedia2HTTPConnectionObj, mReadAtMethod, (jlong)offset, mByteArrayObj, (jint)size);
-
- if (n > 0) {
- env->GetByteArrayRegion(
- mByteArrayObj,
- 0,
- n,
- (jbyte *)data);
- }
-
- return n;
-}
-
-off64_t JMedia2HTTPConnection::getSize() {
- JNIEnv* env = JavaVMHelper::getJNIEnv();
- return (off64_t)(env->CallLongMethod(mMedia2HTTPConnectionObj, mGetSizeMethod));
-}
-
-status_t JMedia2HTTPConnection::getMIMEType(String8 *mimeType) {
- JNIEnv* env = JavaVMHelper::getJNIEnv();
- jstring jmime = (jstring)env->CallObjectMethod(mMedia2HTTPConnectionObj, mGetMIMETypeMethod);
- jboolean flag = env->ExceptionCheck();
- if (flag) {
- env->ExceptionClear();
- return UNKNOWN_ERROR;
- }
-
- const char *str = env->GetStringUTFChars(jmime, 0);
- if (str != NULL) {
- *mimeType = String8(str);
- } else {
- *mimeType = "application/octet-stream";
- }
- env->ReleaseStringUTFChars(jmime, str);
- return OK;
-}
-
-status_t JMedia2HTTPConnection::getUri(String8 *uri) {
- JNIEnv* env = JavaVMHelper::getJNIEnv();
- jstring juri = (jstring)env->CallObjectMethod(mMedia2HTTPConnectionObj, mGetUriMethod);
- jboolean flag = env->ExceptionCheck();
- if (flag) {
- env->ExceptionClear();
- return UNKNOWN_ERROR;
- }
-
- const char *str = env->GetStringUTFChars(juri, 0);
- *uri = String8(str);
- env->ReleaseStringUTFChars(juri, str);
- return OK;
-}
-
-} // namespace android
diff --git a/media/jni/android_media_Media2HTTPConnection.h b/media/jni/android_media_Media2HTTPConnection.h
deleted file mode 100644
index 14bc677..0000000
--- a/media/jni/android_media_Media2HTTPConnection.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _ANDROID_MEDIA_MEDIA2HTTPCONNECTION_H_
-#define _ANDROID_MEDIA_MEDIA2HTTPCONNECTION_H_
-
-#include "jni.h"
-
-#include <media/MediaHTTPConnection.h>
-#include <media/stagefright/foundation/ABase.h>
-
-namespace android {
-
-struct JMedia2HTTPConnection : public MediaHTTPConnection {
- JMedia2HTTPConnection(JNIEnv *env, jobject thiz);
-
- virtual bool connect(
- const char *uri, const KeyedVector<String8, String8> *headers) override;
-
- virtual void disconnect() override;
- virtual ssize_t readAt(off64_t offset, void *data, size_t size) override;
- virtual off64_t getSize() override;
- virtual status_t getMIMEType(String8 *mimeType) override;
- virtual status_t getUri(String8 *uri) override;
-
-protected:
- virtual ~JMedia2HTTPConnection();
-
-private:
- jobject mMedia2HTTPConnectionObj;
- jmethodID mConnectMethod;
- jmethodID mDisconnectMethod;
- jmethodID mReadAtMethod;
- jmethodID mGetSizeMethod;
- jmethodID mGetMIMETypeMethod;
- jmethodID mGetUriMethod;
-
- jbyteArray mByteArrayObj;
-
- DISALLOW_EVIL_CONSTRUCTORS(JMedia2HTTPConnection);
-};
-
-} // namespace android
-
-#endif // _ANDROID_MEDIA_MEDIA2HTTPCONNECTION_H_
diff --git a/media/jni/android_media_Media2HTTPService.cpp b/media/jni/android_media_Media2HTTPService.cpp
deleted file mode 100644
index 1c63889..0000000
--- a/media/jni/android_media_Media2HTTPService.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "Media2HTTPService-JNI"
-#include <utils/Log.h>
-
-#include "android_media_Media2HTTPConnection.h"
-#include "android_media_Media2HTTPService.h"
-
-#include "log/log.h"
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-
-#include <mediaplayer2/JavaVMHelper.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <nativehelper/ScopedLocalRef.h>
-
-namespace android {
-
-JMedia2HTTPService::JMedia2HTTPService(JNIEnv *env, jobject thiz) {
- mMedia2HTTPServiceObj = env->NewGlobalRef(thiz);
- CHECK(mMedia2HTTPServiceObj != NULL);
-
- ScopedLocalRef<jclass> media2HTTPServiceClass(env, env->GetObjectClass(mMedia2HTTPServiceObj));
- CHECK(media2HTTPServiceClass.get() != NULL);
-
- mMakeHTTPConnectionMethod = env->GetMethodID(
- media2HTTPServiceClass.get(),
- "makeHTTPConnection",
- "()Landroid/media/Media2HTTPConnection;");
- CHECK(mMakeHTTPConnectionMethod != NULL);
-}
-
-JMedia2HTTPService::~JMedia2HTTPService() {
- JNIEnv *env = JavaVMHelper::getJNIEnv();
- env->DeleteGlobalRef(mMedia2HTTPServiceObj);
-}
-
-sp<MediaHTTPConnection> JMedia2HTTPService::makeHTTPConnection() {
- JNIEnv* env = JavaVMHelper::getJNIEnv();
- jobject media2HTTPConnectionObj =
- env->CallObjectMethod(mMedia2HTTPServiceObj, mMakeHTTPConnectionMethod);
-
- return new JMedia2HTTPConnection(env, media2HTTPConnectionObj);
-}
-
-} // namespace android
diff --git a/media/jni/android_media_Media2HTTPService.h b/media/jni/android_media_Media2HTTPService.h
deleted file mode 100644
index 30d03f5..0000000
--- a/media/jni/android_media_Media2HTTPService.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _ANDROID_MEDIA_MEDIA2HTTPSERVICE_H_
-#define _ANDROID_MEDIA_MEDIA2HTTPSERVICE_H_
-
-#include "jni.h"
-
-#include <media/MediaHTTPService.h>
-#include <media/stagefright/foundation/ABase.h>
-
-namespace android {
-
-struct JMedia2HTTPService : public MediaHTTPService {
- JMedia2HTTPService(JNIEnv *env, jobject thiz);
-
- virtual sp<MediaHTTPConnection> makeHTTPConnection() override;
-
-protected:
- virtual ~JMedia2HTTPService();
-
-private:
- jobject mMedia2HTTPServiceObj;
-
- jmethodID mMakeHTTPConnectionMethod;
-
- DISALLOW_EVIL_CONSTRUCTORS(JMedia2HTTPService);
-};
-
-} // namespace android
-
-#endif // _ANDROID_MEDIA_MEDIA2HTTPSERVICE_H_
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 3490ff8..5037209 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -108,8 +108,9 @@
} gCodecInfo;
struct fields_t {
- jfieldID context;
jmethodID postEventFromNativeID;
+ jmethodID lockAndGetContextID;
+ jmethodID setAndUnlockContextID;
jfieldID cryptoInfoNumSubSamplesID;
jfieldID cryptoInfoNumBytesOfClearDataID;
jfieldID cryptoInfoNumBytesOfEncryptedDataID;
@@ -931,7 +932,7 @@
static sp<JMediaCodec> setMediaCodec(
JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) {
- sp<JMediaCodec> old = (JMediaCodec *)env->GetLongField(thiz, gFields.context);
+ sp<JMediaCodec> old = (JMediaCodec *)env->CallLongMethod(thiz, gFields.lockAndGetContextID);
if (codec != NULL) {
codec->incStrong(thiz);
}
@@ -944,13 +945,15 @@
old->release();
old->decStrong(thiz);
}
- env->SetLongField(thiz, gFields.context, (jlong)codec.get());
+ env->CallVoidMethod(thiz, gFields.setAndUnlockContextID, (jlong)codec.get());
return old;
}
static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) {
- return (JMediaCodec *)env->GetLongField(thiz, gFields.context);
+ sp<JMediaCodec> codec = (JMediaCodec *)env->CallLongMethod(thiz, gFields.lockAndGetContextID);
+ env->CallVoidMethod(thiz, gFields.setAndUnlockContextID, (jlong)codec.get());
+ return codec;
}
static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) {
@@ -1876,15 +1879,21 @@
env, env->FindClass("android/media/MediaCodec"));
CHECK(clazz.get() != NULL);
- gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
- CHECK(gFields.context != NULL);
-
gFields.postEventFromNativeID =
env->GetMethodID(
clazz.get(), "postEventFromNative", "(IIILjava/lang/Object;)V");
-
CHECK(gFields.postEventFromNativeID != NULL);
+ gFields.lockAndGetContextID =
+ env->GetMethodID(
+ clazz.get(), "lockAndGetContext", "()J");
+ CHECK(gFields.lockAndGetContextID != NULL);
+
+ gFields.setAndUnlockContextID =
+ env->GetMethodID(
+ clazz.get(), "setAndUnlockContext", "(J)V");
+ CHECK(gFields.setAndUnlockContextID != NULL);
+
jfieldID field;
field = env->GetStaticFieldID(clazz.get(), "CRYPTO_MODE_UNENCRYPTED", "I");
CHECK(field != NULL);
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index d4c84b5..1a844cc 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -30,6 +30,7 @@
#include <media/stagefright/foundation/ByteUtils.h> // for FOURCC definition
#include <mediaplayer2/JAudioTrack.h>
#include <mediaplayer2/JavaVMHelper.h>
+#include <mediaplayer2/JMedia2HTTPService.h>
#include <mediaplayer2/mediaplayer2.h>
#include <stdio.h>
#include <assert.h>
@@ -45,7 +46,6 @@
#include "utils/KeyedVector.h"
#include "utils/String8.h"
#include "android_media_BufferingParams.h"
-#include "android_media_Media2HTTPService.h"
#include "android_media_Media2DataSource.h"
#include "android_media_MediaMetricsJNI.h"
#include "android_media_PlaybackParams.h"
diff --git a/packages/PrintSpooler/res/layout/print_activity.xml b/packages/PrintSpooler/res/layout/print_activity.xml
index 212f398..0ccf13e 100644
--- a/packages/PrintSpooler/res/layout/print_activity.xml
+++ b/packages/PrintSpooler/res/layout/print_activity.xml
@@ -107,6 +107,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="16dip"
android:elevation="@dimen/preview_controls_elevation"
+ android:tint="?android:attr/textColorPrimaryInverse"
android:background="@drawable/print_button">
</ImageButton>
diff --git a/packages/SettingsLib/HelpUtils/res/values/strings.xml b/packages/SettingsLib/HelpUtils/res/values/strings.xml
index ae07f5d..3e882bc 100644
--- a/packages/SettingsLib/HelpUtils/res/values/strings.xml
+++ b/packages/SettingsLib/HelpUtils/res/values/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Label for Help and feedback menu item -->
+ <!-- Label for Help and feedback menu item [CHAR LIMIT=45]-->
<string name="help_feedback_label">Help & feedback</string>
</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/RestrictedLockUtils/Android.bp b/packages/SettingsLib/RestrictedLockUtils/Android.bp
index d4c9794..b2f0882 100644
--- a/packages/SettingsLib/RestrictedLockUtils/Android.bp
+++ b/packages/SettingsLib/RestrictedLockUtils/Android.bp
@@ -2,6 +2,7 @@
name: "SettingsLibRestrictedLockUtils",
srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
libs: [
"androidx.annotation_annotation",
diff --git a/packages/SettingsLib/res/layout/restricted_icon.xml b/packages/SettingsLib/RestrictedLockUtils/res/layout/restricted_icon.xml
similarity index 76%
rename from packages/SettingsLib/res/layout/restricted_icon.xml
rename to packages/SettingsLib/RestrictedLockUtils/res/layout/restricted_icon.xml
index 724a524..0f02abd 100644
--- a/packages/SettingsLib/res/layout/restricted_icon.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/layout/restricted_icon.xml
@@ -15,6 +15,7 @@
-->
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/restricted_icon"
- android:layout_width="@dimen/restricted_icon_size"
- android:layout_height="@dimen/restricted_icon_size"
- android:src="@drawable/ic_info" />
\ No newline at end of file
+ android:layout_width="@*android:dimen/config_restricted_icon_size"
+ android:layout_height="@*android:dimen/config_restricted_icon_size"
+ android:tint="?android:attr/colorAccent"
+ android:src="@*android:drawable/ic_info" />
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values/strings.xml
new file mode 100644
index 0000000..7e4460b
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Summary for switch preference to denote it is switched on [CHAR LIMIT=50] -->
+ <string name="enabled_by_admin">Enabled by admin</string>
+ <!-- Summary for switch preference to denote it is switched off [CHAR LIMIT=50] -->
+ <string name="disabled_by_admin">Disabled by admin</string>
+
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/layout/restricted_switch_widget.xml b/packages/SettingsLib/res/layout/restricted_switch_widget.xml
index b286df0..e1f6cdf 100644
--- a/packages/SettingsLib/res/layout/restricted_switch_widget.xml
+++ b/packages/SettingsLib/res/layout/restricted_switch_widget.xml
@@ -16,9 +16,10 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/restricted_icon"
- android:layout_width="@dimen/restricted_icon_size"
- android:layout_height="@dimen/restricted_icon_size"
- android:src="@drawable/ic_info"
+ android:layout_width="@*android:dimen/config_restricted_icon_size"
+ android:layout_height="@*android:dimen/config_restricted_icon_size"
+ android:tint="?android:attr/colorAccent"
+ android:src="@*android:drawable/ic_info"
android:gravity="end|center_vertical" />
<!-- Based off frameworks/base/core/res/res/layout/preference_widget_switch.xml -->
<Switch xmlns:android="http://schemas.android.com/apk/res/android"
@@ -28,4 +29,4 @@
android:focusable="false"
android:clickable="false"
android:background="@null" />
-</merge>
\ No newline at end of file
+</merge>
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index 8094b02..a9c5061 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -36,7 +36,6 @@
<dimen name="two_target_pref_medium_icon_size">32dp</dimen>
<!-- Lock icon for preferences locked by admin -->
- <dimen name="restricted_icon_size">16dp</dimen>
<dimen name="restricted_icon_padding">4dp</dimen>
<dimen name="wifi_preference_badge_padding">8dip</dimen>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index ea6844e..332ced6 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -965,11 +965,6 @@
<!-- Summary for settings preference disabled by administrator [CHAR LIMIT=50] -->
<string name="disabled_by_admin_summary_text">Controlled by admin</string>
- <!-- Summary for switch preference to denote it is switched on [CHAR LIMIT=50] -->
- <string name="enabled_by_admin">Enabled by admin</string>
- <!-- Summary for switch preference to denote it is switched off [CHAR LIMIT=50] -->
- <string name="disabled_by_admin">Disabled by admin</string>
-
<!-- [CHAR LIMIT=25] Manage applications, text telling using an application is disabled. -->
<string name="disabled">Disabled</string>
<!-- Summary of app trusted to install apps [CHAR LIMIT=45] -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index 0094c2c..c03ba9a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -29,6 +29,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -55,9 +56,15 @@
* @return drawables for displaying with settings that are locked by a device admin.
*/
public static Drawable getRestrictedPadlock(Context context) {
- Drawable restrictedPadlock = context.getDrawable(R.drawable.ic_info);
+ Drawable restrictedPadlock = context.getDrawable(android.R.drawable.ic_info);
final int iconSize = context.getResources().getDimensionPixelSize(
- R.dimen.restricted_icon_size);
+ android.R.dimen.config_restricted_icon_size);
+
+ TypedArray ta = context.obtainStyledAttributes(new int[]{android.R.attr.colorAccent});
+ int colorAccent = ta.getColor(0, 0);
+ ta.recycle();
+ restrictedPadlock.setTint(colorAccent);
+
restrictedPadlock.setBounds(0, 0, iconSize, iconSize);
return restrictedPadlock;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index e8f47e1..5a64e02 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -211,6 +211,26 @@
}
}
+ /**
+ * Attempts to get the name of a remote device, otherwise returns the address.
+ *
+ * @param device The remote device.
+ * @return The name, or if unavailable, the address.
+ */
+ public String getName(BluetoothDevice device) {
+ CachedBluetoothDevice cachedDevice = findDevice(device);
+ if (cachedDevice != null && cachedDevice.getName() != null) {
+ return cachedDevice.getName();
+ }
+
+ String name = device.getAliasName();
+ if (name != null) {
+ return name;
+ }
+
+ return device.getAddress();
+ }
+
public synchronized void clearNonBondedDevices() {
mCachedDevicesMapForHearingAids.entrySet().removeIf(entries
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
index 8c4bff5..c8d4fc8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
@@ -29,7 +29,7 @@
import java.util.List;
/**
- * HidProfile handles Bluetooth HID profile.
+ * HidDeviceProfile handles Bluetooth HID Device role
*/
public class HidDeviceProfile implements LocalBluetoothProfile {
private static final String TAG = "HidDeviceProfile";
@@ -37,7 +37,6 @@
private static final int ORDINAL = 18;
// HID Device Profile is always preferred.
private static final int PREFERRED_VALUE = -1;
- private static final boolean DEBUG = true;
private final CachedBluetoothDeviceManager mDeviceManager;
private final LocalBluetoothProfileManager mProfileManager;
@@ -59,9 +58,7 @@
implements BluetoothProfile.ServiceListener {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
- if (DEBUG) {
- Log.d(TAG,"Bluetooth service connected :-)");
- }
+ Log.d(TAG, "Bluetooth service connected :-), profile:" + profile);
mService = (BluetoothHidDevice) proxy;
// We just bound to the service, so refresh the UI for any connected HID devices.
List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -81,9 +78,7 @@
}
public void onServiceDisconnected(int profile) {
- if (DEBUG) {
- Log.d(TAG, "Bluetooth service disconnected");
- }
+ Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
mIsProfileReady = false;
}
}
@@ -110,6 +105,7 @@
@Override
public boolean connect(BluetoothDevice device) {
+ // Don't invoke method in service because settings is not allowed to connect this profile.
return false;
}
@@ -126,11 +122,7 @@
if (mService == null) {
return BluetoothProfile.STATE_DISCONNECTED;
}
- List<BluetoothDevice> deviceList = mService.getConnectedDevices();
-
- return !deviceList.isEmpty() && deviceList.contains(device)
- ? mService.getConnectionState(device)
- : BluetoothProfile.STATE_DISCONNECTED;
+ return mService.getConnectionState(device);
}
@Override
@@ -185,9 +177,7 @@
}
protected void finalize() {
- if (DEBUG) {
- Log.d(TAG, "finalize()");
- }
+ Log.d(TAG, "finalize()");
if (mService != null) {
try {
BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.HID_DEVICE,
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
index 7d334eb..7ad2e28c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -32,11 +32,10 @@
import java.util.List;
/**
- * MapClientProfile handles Bluetooth MAP profile.
+ * MapClientProfile handles the Bluetooth MAP MCE role.
*/
public final class MapClientProfile implements LocalBluetoothProfile {
private static final String TAG = "MapClientProfile";
- private static boolean V = false;
private BluetoothMapClient mService;
private boolean mIsProfileReady;
@@ -60,7 +59,7 @@
implements BluetoothProfile.ServiceListener {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
- if (V) Log.d(TAG,"Bluetooth service connected");
+ Log.d(TAG, "Bluetooth service connected, profile:" + profile);
mService = (BluetoothMapClient) proxy;
// We just bound to the service, so refresh the UI for any connected MAP devices.
List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -82,14 +81,14 @@
}
public void onServiceDisconnected(int profile) {
- if (V) Log.d(TAG,"Bluetooth service disconnected");
+ Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
mProfileManager.callServiceDisconnectedListeners();
mIsProfileReady=false;
}
}
public boolean isProfileReady() {
- if(V) Log.d(TAG,"isProfileReady(): "+ mIsProfileReady);
+ Log.d(TAG, "isProfileReady(): "+ mIsProfileReady);
return mIsProfileReady;
}
@@ -115,18 +114,16 @@
}
public boolean connect(BluetoothDevice device) {
- if (mService == null) return false;
- List<BluetoothDevice> connectedDevices = getConnectedDevices();
- if (connectedDevices != null && connectedDevices.contains(device)) {
- // Connect to same device, Ignore it
- Log.d(TAG,"Ignoring Connect");
- return true;
+ if (mService == null) {
+ return false;
}
return mService.connect(device);
}
public boolean disconnect(BluetoothDevice device) {
- if (mService == null) return false;
+ if (mService == null) {
+ return false;
+ }
// Downgrade priority as user is disconnecting.
if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
@@ -135,23 +132,30 @@
}
public int getConnectionStatus(BluetoothDevice device) {
- if (mService == null) return BluetoothProfile.STATE_DISCONNECTED;
-
+ if (mService == null) {
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
return mService.getConnectionState(device);
}
public boolean isPreferred(BluetoothDevice device) {
- if (mService == null) return false;
+ if (mService == null) {
+ return false;
+ }
return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
}
public int getPreferred(BluetoothDevice device) {
- if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+ if (mService == null) {
+ return BluetoothProfile.PRIORITY_OFF;
+ }
return mService.getPriority(device);
}
public void setPreferred(BluetoothDevice device, boolean preferred) {
- if (mService == null) return;
+ if (mService == null) {
+ return;
+ }
if (preferred) {
if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
@@ -162,7 +166,9 @@
}
public List<BluetoothDevice> getConnectedDevices() {
- if (mService == null) return new ArrayList<BluetoothDevice>(0);
+ if (mService == null) {
+ return new ArrayList<BluetoothDevice>(0);
+ }
return mService.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_CONNECTING,
@@ -200,11 +206,11 @@
}
protected void finalize() {
- if (V) Log.d(TAG, "finalize()");
+ Log.d(TAG, "finalize()");
if (mService != null) {
try {
BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.MAP_CLIENT,
- mService);
+ mService);
mService = null;
}catch (Throwable t) {
Log.w(TAG, "Error cleaning up MAP Client proxy", t);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index b295f24..8fefb2f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -34,7 +34,6 @@
public final class PbapClientProfile implements LocalBluetoothProfile {
private static final String TAG = "PbapClientProfile";
- private static boolean V = false;
private BluetoothPbapClient mService;
private boolean mIsProfileReady;
@@ -56,9 +55,7 @@
implements BluetoothProfile.ServiceListener {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
- if (V) {
- Log.d(TAG,"Bluetooth service connected");
- }
+ Log.d(TAG, "Bluetooth service connected, profile:" + profile);
mService = (BluetoothPbapClient) proxy;
// We just bound to the service, so refresh the UI for any connected PBAP devices.
List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -77,9 +74,7 @@
}
public void onServiceDisconnected(int profile) {
- if (V) {
- Log.d(TAG,"Bluetooth service disconnected");
- }
+ Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
mIsProfileReady = false;
}
}
@@ -131,31 +126,16 @@
}
public boolean connect(BluetoothDevice device) {
- if (V) {
- Log.d(TAG,"PBAPClientProfile got connect request");
- }
+ Log.d(TAG,"PBAPClientProfile got connect request");
if (mService == null) {
return false;
}
- List<BluetoothDevice> srcs = getConnectedDevices();
- if (srcs != null) {
- for (BluetoothDevice src : srcs) {
- if (src.equals(device)) {
- // Connect to same device, Ignore it
- Log.d(TAG,"Ignoring Connect");
- return true;
- }
- }
- }
Log.d(TAG,"PBAPClientProfile attempting to connect to " + device.getAddress());
-
return mService.connect(device);
}
public boolean disconnect(BluetoothDevice device) {
- if (V) {
- Log.d(TAG,"PBAPClientProfile got disconnect request");
- }
+ Log.d(TAG,"PBAPClientProfile got disconnect request");
if (mService == null) {
return false;
}
@@ -218,9 +198,7 @@
}
protected void finalize() {
- if (V) {
- Log.d(TAG, "finalize()");
- }
+ Log.d(TAG, "finalize()");
if (mService != null) {
try {
BluetoothAdapter.getDefaultAdapter().closeProfileProxy(
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsDetailLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsDetailLoader.java
new file mode 100644
index 0000000..a070b2a
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsDetailLoader.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.net;
+
+import android.app.usage.NetworkStatsManager;
+import android.app.usage.NetworkStats;
+import android.content.Context;
+import android.os.RemoteException;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import androidx.loader.content.AsyncTaskLoader;
+
+/**
+ * Loader for retrieving the network stats details for all UIDs.
+ */
+public class NetworkStatsDetailLoader extends AsyncTaskLoader<NetworkStats> {
+
+ private static final String TAG = "NetworkDetailLoader";
+ private final NetworkStatsManager mNetworkStatsManager;
+ private final TelephonyManager mTelephonyManager;
+ private final long mStart;
+ private final long mEnd;
+ private final int mSubId;
+ private final int mNetworkType;
+
+ private NetworkStatsDetailLoader(Builder builder) {
+ super(builder.mContext);
+ mStart = builder.mStart;
+ mEnd = builder.mEnd;
+ mSubId = builder.mSubId;
+ mNetworkType = builder.mNetworkType;
+ mNetworkStatsManager = (NetworkStatsManager)
+ builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE);
+ mTelephonyManager =
+ (TelephonyManager) builder.mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ }
+
+ @Override
+ protected void onStartLoading() {
+ super.onStartLoading();
+ forceLoad();
+ }
+
+ @Override
+ public NetworkStats loadInBackground() {
+ try {
+ return mNetworkStatsManager.queryDetails(
+ mNetworkType, mTelephonyManager.getSubscriberId(mSubId), mStart, mEnd);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception querying network detail.", e);
+ return null;
+ }
+ }
+
+ @Override
+ protected void onStopLoading() {
+ super.onStopLoading();
+ cancelLoad();
+ }
+
+ @Override
+ protected void onReset() {
+ super.onReset();
+ cancelLoad();
+ }
+
+ public static class Builder {
+ private final Context mContext;
+ private long mStart;
+ private long mEnd;
+ private int mSubId;
+ private int mNetworkType;
+
+ public Builder(Context context) {
+ mContext = context;
+ }
+
+ public Builder setStartTime(long start) {
+ mStart = start;
+ return this;
+ }
+
+ public Builder setEndTime(long end) {
+ mEnd = end;
+ return this;
+ }
+
+ public Builder setSubscriptionId(int subId) {
+ mSubId = subId;
+ return this;
+ }
+
+ public Builder setNetworkType(int networkType) {
+ mNetworkType = networkType;
+ return this;
+ }
+
+ public NetworkStatsDetailLoader build() {
+ return new NetworkStatsDetailLoader(this);
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java
index c311337..82bb011 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java
@@ -25,6 +25,12 @@
import androidx.loader.content.AsyncTaskLoader;
+/**
+ * Deprecated in favor of {@link NetworkStatsDetailLoader}
+ *
+ * @deprecated
+ */
+@Deprecated
public class SummaryForAllUidLoaderCompat extends AsyncTaskLoader<NetworkStats> {
private static final String KEY_TEMPLATE = "template";
private static final String KEY_START = "start";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
index dde1746..0ca2e87 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
@@ -52,6 +52,8 @@
@Override
public List<ResourcePath> getIncludedResourcePaths() {
final List<ResourcePath> paths = super.getIncludedResourcePaths();
+ paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/HelpUtils/res"));
+ paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/RestrictedLockUtils/res"));
paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/res"));
paths.add(resourcePath("file:frameworks/base/core/res/res"));
paths.add(resourcePath("file:out/soong/.intermediates/prebuilts/sdk/current/androidx/androidx.appcompat_appcompat-nodeps/android_common/aar/res/"));
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 8ac611f..7baded8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -139,7 +139,7 @@
public void testGetName_validCachedDevice_nameFound() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
- assertThat(mCachedDeviceManager.findDevice(mDevice1).getName()).isEqualTo(DEVICE_ALIAS_1);
+ assertThat(mCachedDeviceManager.getName(mDevice1)).isEqualTo(DEVICE_ALIAS_1);
}
/**
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java
new file mode 100644
index 0000000..c91ee22
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHidDevice;
+import android.bluetooth.BluetoothProfile;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadow.api.Shadow;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
+public class HidDeviceProfileTest {
+
+ @Mock
+ private CachedBluetoothDeviceManager mDeviceManager;
+ @Mock
+ private LocalBluetoothProfileManager mProfileManager;
+ @Mock
+ private BluetoothHidDevice mService;
+ @Mock
+ private CachedBluetoothDevice mCachedBluetoothDevice;
+ @Mock
+ private BluetoothDevice mBluetoothDevice;
+ private BluetoothProfile.ServiceListener mServiceListener;
+ private HidDeviceProfile mProfile;
+ private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ mProfile = new HidDeviceProfile(RuntimeEnvironment.application,
+ mDeviceManager, mProfileManager);
+ mServiceListener = mShadowBluetoothAdapter.getServiceListener();
+ mServiceListener.onServiceConnected(BluetoothProfile.HID_DEVICE, mService);
+ }
+
+ @Test
+ public void connect_shouldReturnFalse() {
+ assertThat(mProfile.connect(mBluetoothDevice)).isFalse();
+ }
+
+ @Test
+ public void disconnect_shouldDisconnectBluetoothHidDevice() {
+ mProfile.disconnect(mBluetoothDevice);
+ verify(mService).disconnect(mBluetoothDevice);
+ }
+
+ @Test
+ public void getConnectionStatus_shouldReturnConnectionState() {
+ when(mService.getConnectionState(mBluetoothDevice)).
+ thenReturn(BluetoothProfile.STATE_CONNECTED);
+ assertThat(mProfile.getConnectionStatus(mBluetoothDevice)).
+ isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
new file mode 100644
index 0000000..c4c48a8
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothMapClient;
+import android.bluetooth.BluetoothProfile;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadow.api.Shadow;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
+public class MapClientProfileTest {
+
+ @Mock
+ private CachedBluetoothDeviceManager mDeviceManager;
+ @Mock
+ private LocalBluetoothProfileManager mProfileManager;
+ @Mock
+ private BluetoothMapClient mService;
+ @Mock
+ private CachedBluetoothDevice mCachedBluetoothDevice;
+ @Mock
+ private BluetoothDevice mBluetoothDevice;
+ private BluetoothProfile.ServiceListener mServiceListener;
+ private MapClientProfile mProfile;
+ private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ mProfile = new MapClientProfile(RuntimeEnvironment.application,
+ mDeviceManager, mProfileManager);
+ mServiceListener = mShadowBluetoothAdapter.getServiceListener();
+ mServiceListener.onServiceConnected(BluetoothProfile.MAP_CLIENT, mService);
+ }
+
+ @Test
+ public void connect_shouldConnectBluetoothMapClient() {
+ mProfile.connect(mBluetoothDevice);
+ verify(mService).connect(mBluetoothDevice);
+ }
+
+ @Test
+ public void disconnect_shouldDisconnectBluetoothMapClient() {
+ mProfile.disconnect(mBluetoothDevice);
+ verify(mService).disconnect(mBluetoothDevice);
+ }
+
+ @Test
+ public void getConnectionStatus_shouldReturnConnectionState() {
+ when(mService.getConnectionState(mBluetoothDevice)).
+ thenReturn(BluetoothProfile.STATE_CONNECTED);
+ assertThat(mProfile.getConnectionStatus(mBluetoothDevice)).
+ isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
new file mode 100644
index 0000000..e4a444c
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothPbapClient;
+import android.bluetooth.BluetoothProfile;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadow.api.Shadow;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
+public class PbapClientProfileTest {
+
+ @Mock
+ private CachedBluetoothDeviceManager mDeviceManager;
+ @Mock
+ private LocalBluetoothProfileManager mProfileManager;
+ @Mock
+ private BluetoothPbapClient mService;
+ @Mock
+ private CachedBluetoothDevice mCachedBluetoothDevice;
+ @Mock
+ private BluetoothDevice mBluetoothDevice;
+ private BluetoothProfile.ServiceListener mServiceListener;
+ private PbapClientProfile mProfile;
+ private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ mProfile = new PbapClientProfile(RuntimeEnvironment.application,
+ mDeviceManager, mProfileManager);
+ mServiceListener = mShadowBluetoothAdapter.getServiceListener();
+ mServiceListener.onServiceConnected(BluetoothProfile.PBAP_CLIENT, mService);
+ }
+
+ @Test
+ public void connect_shouldConnectBluetoothPbapClient() {
+ mProfile.connect(mBluetoothDevice);
+ verify(mService).connect(mBluetoothDevice);
+ }
+
+ @Test
+ public void disconnect_shouldDisconnectBluetoothPbapClient() {
+ mProfile.disconnect(mBluetoothDevice);
+ verify(mService).disconnect(mBluetoothDevice);
+ }
+
+ @Test
+ public void getConnectionStatus_shouldReturnConnectionState() {
+ when(mService.getConnectionState(mBluetoothDevice)).
+ thenReturn(BluetoothProfile.STATE_CONNECTED);
+ assertThat(mProfile.getConnectionStatus(mBluetoothDevice)).
+ isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
index a501ffa..40e7386 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
@@ -77,10 +77,10 @@
@Test
public void getIcon_hasIconMetadata_returnIcon() {
- mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON, R.drawable.ic_info);
+ mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON, android.R.drawable.ic_info);
assertThat(mTile.getIcon(RuntimeEnvironment.application).getResId())
- .isEqualTo(R.drawable.ic_info);
+ .isEqualTo(android.R.drawable.ic_info);
}
@Test
diff --git a/packages/SystemUI/res/layout/menu_ime.xml b/packages/SystemUI/res/layout/menu_ime.xml
index b047efb..8a3a0b1 100644
--- a/packages/SystemUI/res/layout/menu_ime.xml
+++ b/packages/SystemUI/res/layout/menu_ime.xml
@@ -19,7 +19,6 @@
android:id="@+id/menu_container"
android:layout_width="@dimen/navigation_key_width"
android:layout_height="match_parent"
- android:focusable="false"
android:importantForAccessibility="no"
>
<!-- Use nav button width & height=match_parent for parent FrameLayout and buttons because they
diff --git a/packages/SystemUI/res/layout/qs_paged_page.xml b/packages/SystemUI/res/layout/qs_paged_page.xml
index 07f0c83..a8960d9 100644
--- a/packages/SystemUI/res/layout/qs_paged_page.xml
+++ b/packages/SystemUI/res/layout/qs_paged_page.xml
@@ -19,7 +19,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tile_page"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:paddingStart="@dimen/notification_side_paddings"
android:paddingEnd="@dimen/notification_side_paddings"
android:clipChildren="false"
diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
index e96a09b..11a0187 100644
--- a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
+++ b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
@@ -19,6 +19,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_weight="1"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingBottom="@dimen/qs_paged_tile_layout_padding_bottom">
diff --git a/packages/SystemUI/res/layout/recents_onboarding.xml b/packages/SystemUI/res/layout/recents_onboarding.xml
index adf1e74..2538612 100644
--- a/packages/SystemUI/res/layout/recents_onboarding.xml
+++ b/packages/SystemUI/res/layout/recents_onboarding.xml
@@ -37,7 +37,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
- android:textColor="@android:color/white"
+ android:textColor="?attr/wallpaperTextColor"
android:textSize="16sp"/>
<ImageView
android:id="@+id/dismiss"
@@ -49,6 +49,7 @@
android:layout_marginEnd="2dp"
android:alpha="0.7"
android:src="@drawable/ic_close_white"
+ android:tint="?attr/wallpaperTextColor"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/accessibility_desc_close"/>
</LinearLayout>
diff --git a/packages/SystemUI/res/values-h320dp/config.xml b/packages/SystemUI/res/values-h320dp/config.xml
deleted file mode 100644
index a9c19db..0000000
--- a/packages/SystemUI/res/values-h320dp/config.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * 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.
-*/
--->
-
-<resources>
- <!-- The number of rows in the QuickSettings -->
- <integer name="quick_settings_num_rows">2</integer>
-</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 11bd392..24dcd3e 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -107,7 +107,7 @@
<integer name="quick_settings_num_columns">3</integer>
<!-- The number of rows in the QuickSettings -->
- <integer name="quick_settings_num_rows">1</integer>
+ <integer name="quick_settings_max_rows">3</integer>
<!-- The number of columns that the top level tiles span in the QuickSettings -->
<integer name="quick_settings_user_time_settings_tile_span">1</integer>
@@ -146,19 +146,29 @@
<!-- Should "LTE"/"4G" be shown instead of "LTE+"/"4G+" when on NETWORK_TYPE_LTE_CA? -->
<bool name="config_hideLtePlus">false</bool>
- <!-- milliseconds before the heads up notification auto-dismisses. -->
+ <!-- The number of milliseconds before the heads up notification auto-dismisses. -->
<integer name="heads_up_notification_decay">5000</integer>
- <!-- milliseconds after a heads up notification is pushed back
+ <!-- The number of milliseconds after a heads up notification is pushed back
before the app can interrupt again. -->
<integer name="heads_up_default_snooze_length_ms">60000</integer>
<!-- Minimum display time for a heads up notification, in milliseconds. -->
<integer name="heads_up_notification_minimum_time">2000</integer>
- <!-- milliseconds before the heads up notification accepts touches. -->
+ <!-- The number of milliseconds before the heads up notification accepts touches. -->
<integer name="touch_acceptance_delay">700</integer>
+ <!-- The number of milliseconds before the ambient notification auto-dismisses. This will
+ override the default pulse length. -->
+ <integer name="ambient_notification_decay">6000</integer>
+
+ <!-- Minimum display time for a heads up notification, in milliseconds. -->
+ <integer name="ambient_notification_minimum_time">2000</integer>
+
+ <!-- The number of milliseconds to extend ambient pulse by when prompted (e.g. on touch) -->
+ <integer name="ambient_notification_extension_time">6000</integer>
+
<!-- The duration in seconds to wait before the dismiss buttons are shown. -->
<integer name="recents_task_bar_dismiss_delay_seconds">1000</integer>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 26eadb5..c168d4e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -955,7 +955,7 @@
<dimen name="nav_content_padding">0dp</dimen>
<dimen name="nav_quick_scrub_track_edge_padding">24dp</dimen>
<dimen name="nav_quick_scrub_track_thickness">10dp</dimen>
- <dimen name="nav_home_back_gesture_drag_limit">60dp</dimen>
+ <dimen name="nav_home_back_gesture_drag_limit">40dp</dimen>
<!-- Navigation bar shadow params. -->
<dimen name="nav_key_button_shadow_offset_x">0dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 04d72ce..258b6f6 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -30,6 +30,7 @@
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
@@ -137,6 +138,7 @@
providers.put(NotificationGroupManager.class, NotificationGroupManager::new);
providers.put(NotificationMediaManager.class, () -> new NotificationMediaManager(context));
providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager(context));
+ providers.put(AmbientPulseManager.class, () -> new AmbientPulseManager(context));
providers.put(NotificationBlockingHelperManager.class,
() -> new NotificationBlockingHelperManager(context));
providers.put(NotificationRemoteInputManager.class,
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index d6a1cf0..5739c99 100644
--- a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -16,7 +16,6 @@
package com.android.systemui.car;
import android.content.Context;
-import android.service.notification.StatusBarNotification;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.NotificationData;
@@ -41,7 +40,7 @@
}
@Override
- public boolean shouldPeek(NotificationData.Entry entry, StatusBarNotification sbn) {
+ public boolean shouldHeadsUp(NotificationData.Entry entry) {
// Because space is usually constrained in the auto use-case, there should not be a
// pinned notification when the shade has been expanded. Ensure this by not pinning any
// notification if the shade is already opened.
@@ -49,6 +48,6 @@
return false;
}
- return super.shouldPeek(entry, sbn);
+ return super.shouldHeadsUp(entry);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 6a29299..bb05980 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -48,7 +48,15 @@
void onIgnoreTouchWhilePulsing(boolean ignore);
interface Callback {
- default void onNotificationHeadsUp() {}
+ /**
+ * Called when a high priority notification is added.
+ */
+ default void onNotificationAlerted() {}
+
+ /**
+ * Called when battery state or power save mode changes.
+ * @param active whether power save is active or not
+ */
default void onPowerSaveChanged(boolean active) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 1589969..31548b9 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -409,7 +409,7 @@
private DozeHost.Callback mHostCallback = new DozeHost.Callback() {
@Override
- public void onNotificationHeadsUp() {
+ public void onNotificationAlerted() {
onNotification();
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index cc1b9e8..bde7f1b 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -55,6 +55,7 @@
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArraySet;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
@@ -318,8 +319,8 @@
ArraySet<String> addedKeys = new ArraySet<String>();
mHasLogoutButton = false;
mHasLockdownButton = false;
- mSeparatedEmergencyButtonEnabled = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.FASTER_EMERGENCY_PHONE_CALL_ENABLED, 0) != 0;
+ mSeparatedEmergencyButtonEnabled = FeatureFlagUtils
+ .isEnabled(mContext, FeatureFlagUtils.EMERGENCY_DIAL_SHORTCUTS);
for (int i = 0; i < defaultActions.length; i++) {
String actionKey = defaultActions[i];
if (addedKeys.contains(actionKey)) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 1655c01..757c821 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -20,6 +20,9 @@
import static android.view.Display.INVALID_DISPLAY;
import static com.android.internal.telephony.IccCardConstants.State.ABSENT;
+import static com.android.internal.telephony.IccCardConstants.State.PIN_REQUIRED;
+import static com.android.internal.telephony.IccCardConstants.State.PUK_REQUIRED;
+import static com.android.internal.telephony.IccCardConstants.State.READY;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -30,7 +33,6 @@
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.StatusBarManager;
-import android.hardware.biometrics.BiometricSourceType;
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -38,6 +40,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricSourceType;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Bundle;
@@ -58,6 +61,7 @@
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseArray;
import android.view.ViewGroup;
import android.view.WindowManagerPolicyConstants;
import android.view.animation.Animation;
@@ -70,21 +74,21 @@
import com.android.internal.policy.IKeyguardExitCallback;
import com.android.internal.policy.IKeyguardStateCallback;
import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardConstants;
import com.android.keyguard.KeyguardDisplayManager;
import com.android.keyguard.KeyguardSecurityView;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.internal.util.LatencyTracker;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.Dependency;
import com.android.systemui.SystemUI;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -274,6 +278,12 @@
private KeyguardUpdateMonitor mUpdateMonitor;
+ /**
+ * Last SIM state reported by the telephony system.
+ * Index is the slotId - in case of multiple SIM cards.
+ */
+ private final SparseArray<IccCardConstants.State> mLastSimStates = new SparseArray<>();
+
private boolean mDeviceInteractive;
private boolean mGoingToSleep;
@@ -450,6 +460,14 @@
}
}
+ boolean simWasLocked;
+ synchronized (KeyguardViewMediator.this) {
+ IccCardConstants.State lastState = mLastSimStates.get(slotId);
+ simWasLocked = (lastState == PIN_REQUIRED || lastState == PUK_REQUIRED)
+ && simState == READY;
+ mLastSimStates.append(slotId, simState);
+ }
+
switch (simState) {
case NOT_READY:
case ABSENT:
@@ -503,6 +521,9 @@
case READY:
synchronized (KeyguardViewMediator.this) {
if (DEBUG_SIM_STATES) Log.d(TAG, "READY, reset state? " + mShowing);
+ if (mShowing && simWasLocked) {
+ resetStateLocked();
+ }
mLockWhenSimRemoved = true;
}
break;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index ca1b489..fcd479c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -6,14 +6,12 @@
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
+
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
@@ -41,14 +39,12 @@
return t * t * t + 1.0f;
};
-
private final ArrayList<TileRecord> mTiles = new ArrayList<>();
private final ArrayList<TilePage> mPages = new ArrayList<>();
private PageIndicator mPageIndicator;
private float mPageIndicatorPosition;
- private int mNumPages;
private PageListener mPageListener;
private boolean mListening;
@@ -56,6 +52,7 @@
private AnimatorSet mBounceAnimatorSet;
private float mLastExpansion;
+ private boolean mDistributeTiles = false;
public PagedTileLayout(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -122,7 +119,7 @@
public void setPageIndicator(PageIndicator indicator) {
mPageIndicator = indicator;
- mPageIndicator.setNumPages(mNumPages);
+ mPageIndicator.setNumPages(mPages.size());
mPageIndicator.setLocation(mPageIndicatorPosition);
}
@@ -136,13 +133,15 @@
@Override
public void addTile(TileRecord tile) {
mTiles.add(tile);
- postDistributeTiles();
+ mDistributeTiles = true;
+ requestLayout();
}
@Override
public void removeTile(TileRecord tile) {
if (mTiles.remove(tile)) {
- postDistributeTiles();
+ mDistributeTiles = true;
+ requestLayout();
}
}
@@ -175,44 +174,50 @@
mPageListener = listener;
}
- private void postDistributeTiles() {
- removeCallbacks(mDistribute);
- post(mDistribute);
- }
-
private void distributeTiles() {
+ emptyAndInflateOrRemovePages();
+
+ final int tileCount = mPages.get(0).maxTiles();
if (DEBUG) Log.d(TAG, "Distributing tiles");
- final int NP = mPages.size();
- for (int i = 0; i < NP; i++) {
- mPages.get(i).removeAllViews();
- }
int index = 0;
final int NT = mTiles.size();
for (int i = 0; i < NT; i++) {
TileRecord tile = mTiles.get(i);
- if (mPages.get(index).isFull()) {
- if (++index == mPages.size()) {
- if (DEBUG) Log.d(TAG, "Adding page for "
- + tile.tile.getClass().getSimpleName());
- mPages.add((TilePage) LayoutInflater.from(getContext())
- .inflate(R.layout.qs_paged_page, this, false));
- }
+ if (mPages.get(index).mRecords.size() == tileCount) index++;
+ if (DEBUG) {
+ Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to "
+ + index);
}
- if (DEBUG) Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to "
- + index);
mPages.get(index).addTile(tile);
}
- if (mNumPages != index + 1) {
- mNumPages = index + 1;
- while (mPages.size() > mNumPages) {
- mPages.remove(mPages.size() - 1);
- }
- if (DEBUG) Log.d(TAG, "Size: " + mNumPages);
- mPageIndicator.setNumPages(mNumPages);
- setAdapter(mAdapter);
- mAdapter.notifyDataSetChanged();
- setCurrentItem(0, false);
+ }
+
+ private void emptyAndInflateOrRemovePages() {
+ final int nTiles = mTiles.size();
+ int numPages = nTiles / mPages.get(0).maxTiles();
+ // Add one more not full page if needed
+ numPages += (nTiles % mPages.get(0).maxTiles() == 0 ? 0 : 1);
+
+ final int NP = mPages.size();
+ for (int i = 0; i < NP; i++) {
+ mPages.get(i).removeAllViews();
}
+ if (NP == numPages) {
+ return;
+ }
+ while (mPages.size() < numPages) {
+ if (DEBUG) Log.d(TAG, "Adding page");
+ mPages.add((TilePage) LayoutInflater.from(getContext())
+ .inflate(R.layout.qs_paged_page, this, false));
+ }
+ while (mPages.size() > numPages) {
+ if (DEBUG) Log.d(TAG, "Removing page");
+ mPages.remove(mPages.size() - 1);
+ }
+ mPageIndicator.setNumPages(mPages.size());
+ setAdapter(mAdapter);
+ mAdapter.notifyDataSetChanged();
+ setCurrentItem(0, false);
}
@Override
@@ -222,20 +227,39 @@
setPadding(0, 0, 0,
getContext().getResources().getDimensionPixelSize(
R.dimen.qs_paged_tile_layout_padding_bottom));
-
boolean changed = false;
for (int i = 0; i < mPages.size(); i++) {
changed |= mPages.get(i).updateResources();
}
if (changed) {
- distributeTiles();
+ mDistributeTiles = true;
+ requestLayout();
}
return changed;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+ final int nTiles = mTiles.size();
+ if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
+
+ // Only change the pages if the number of rows or columns (from updateResources) has
+ // changed or the tiles have changed
+ if (mPages.get(0).updateMaxRows(heightMeasureSpec, nTiles) || mDistributeTiles) {
+ mDistributeTiles = false;
+ distributeTiles();
+ }
+
+ final int nRows = mPages.get(0).mRows;
+ for (int i = 0; i < mPages.size(); i++) {
+ TilePage t = mPages.get(i);
+ t.mRows = nRows;
+ }
+ }
+
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
// The ViewPager likes to eat all of the space, instead force it to wrap to the max height
// of the pages.
int maxHeight = 0;
@@ -249,13 +273,6 @@
setMeasuredDimension(getMeasuredWidth(), maxHeight + getPaddingBottom());
}
- private final Runnable mDistribute = new Runnable() {
- @Override
- public void run() {
- distributeTiles();
- }
- };
-
public int getColumnCount() {
if (mPages.size() == 0) return 0;
return mPages.get(0).mColumns;
@@ -346,33 +363,17 @@
};
public static class TilePage extends TileLayout {
- private int mMaxRows = 3;
+
public TilePage(Context context, AttributeSet attrs) {
super(context, attrs);
- updateResources();
- }
-
- @Override
- public boolean updateResources() {
- final int rows = getRows();
- boolean changed = rows != mMaxRows;
- if (changed) {
- mMaxRows = rows;
- requestLayout();
- }
- return super.updateResources() || changed;
- }
-
- private int getRows() {
- return Math.max(1, getResources().getInteger(R.integer.quick_settings_num_rows));
- }
-
- public void setMaxRows(int maxRows) {
- mMaxRows = maxRows;
}
public boolean isFull() {
- return mRecords.size() >= mColumns * mMaxRows;
+ return mRecords.size() >= mColumns * mRows;
+ }
+
+ public int maxTiles() {
+ return mColumns * mRows;
}
}
@@ -391,6 +392,9 @@
position = mPages.size() - 1 - position;
}
ViewGroup view = mPages.get(position);
+ if (view.getParent() != null) {
+ container.removeView(view);
+ }
container.addView(view);
updateListening();
return view;
@@ -398,7 +402,7 @@
@Override
public int getCount() {
- return mNumPages;
+ return mPages.size();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index feff5d4..1451e71 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -23,6 +23,7 @@
import android.content.res.Configuration;
import android.graphics.Point;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
@@ -91,15 +92,24 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // Since we control our own bottom, be whatever size we want.
- // Otherwise the QSPanel ends up with 0 height when the window is only the
- // size of the status bar.
- mQSPanel.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(
- MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED));
+ // QSPanel will show as many rows as it can (up to TileLayout.MAX_ROWS) such that the
+ // bottom and footer are inside the screen.
+ Configuration config = getResources().getConfiguration();
+ boolean navBelow = config.smallestScreenWidthDp >= 600
+ || config.orientation != Configuration.ORIENTATION_LANDSCAPE;
+ MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanel.getLayoutParams();
+
+ // The footer is pinned to the bottom of QSPanel (same bottoms), therefore we don't need to
+ // subtract its height. We do not care if the collapsed notifications fit in the screen.
+ int maxQs = getDisplayHeight() - layoutParams.topMargin - layoutParams.bottomMargin
+ - getPaddingBottom();
+ if (navBelow) {
+ maxQs -= getResources().getDimensionPixelSize(R.dimen.navigation_bar_height);
+ }
+ mQSPanel.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(maxQs, MeasureSpec.AT_MOST));
int width = mQSPanel.getMeasuredWidth();
- LayoutParams layoutParams = (LayoutParams) mQSPanel.getLayoutParams();
int height = layoutParams.topMargin + layoutParams.bottomMargin
- + mQSPanel.getMeasuredHeight();
+ + mQSPanel.getMeasuredHeight() + getPaddingBottom();
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index c67165e..01ff72e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -24,10 +24,12 @@
protected int mCellMarginHorizontal;
protected int mCellMarginVertical;
protected int mSidePadding;
+ protected int mRows = 1;
protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
private int mCellMarginTop;
private boolean mListening;
+ protected int mMaxAllowedRows = 3;
public TileLayout(Context context) {
this(context, null);
@@ -86,6 +88,7 @@
mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);
mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
mSidePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_layout_margin_side);
+ mMaxAllowedRows = Math.max(1, getResources().getInteger(R.integer.quick_settings_max_rows));
if (mColumns != columns) {
mColumns = columns;
requestLayout();
@@ -96,10 +99,16 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // If called with AT_MOST, it will limit the number of rows. If called with UNSPECIFIED
+ // it will show all its tiles. In this case, the tiles have to be entered before the
+ // container is measured. Any change in the tiles, should trigger a remeasure.
final int numTiles = mRecords.size();
final int width = MeasureSpec.getSize(widthMeasureSpec)
- getPaddingStart() - getPaddingEnd();
- final int numRows = (numTiles + mColumns - 1) / mColumns;
+ final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ if (heightMode == MeasureSpec.UNSPECIFIED) {
+ mRows = (numTiles + mColumns - 1) / mColumns;
+ }
mCellWidth = (width - mSidePadding * 2 - (mCellMarginHorizontal * mColumns)) / mColumns;
// Measure each QS tile.
@@ -112,13 +121,35 @@
// Only include the top margin in our measurement if we have more than 1 row to show.
// Otherwise, don't add the extra margin buffer at top.
- int height = (mCellHeight + mCellMarginVertical) * numRows +
- (numRows != 0 ? (mCellMarginTop - mCellMarginVertical) : 0);
+ int height = (mCellHeight + mCellMarginVertical) * mRows +
+ (mRows != 0 ? (mCellMarginTop - mCellMarginVertical) : 0);
if (height < 0) height = 0;
setMeasuredDimension(width, height);
}
+ /**
+ * Determines the maximum number of rows that can be shown based on height. Clips at a minimum
+ * of 1 and a maximum of mMaxAllowedRows.
+ *
+ * @param heightMeasureSpec Available height.
+ * @param tilesCount Upper limit on the number of tiles to show. to prevent empty rows.
+ */
+ public boolean updateMaxRows(int heightMeasureSpec, int tilesCount) {
+ final int availableHeight = MeasureSpec.getSize(heightMeasureSpec) - mCellMarginTop;
+ final int previousRows = mRows;
+ mRows = availableHeight / (mCellHeight + mCellMarginVertical);
+ if (mRows >= mMaxAllowedRows) {
+ mRows = mMaxAllowedRows;
+ } else if (mRows <= 1) {
+ mRows = 1;
+ }
+ if (mRows > (tilesCount + mColumns - 1) / mColumns) {
+ mRows = (tilesCount + mColumns - 1) / mColumns;
+ }
+ return previousRows != mRows;
+ }
+
@Override
public boolean hasOverlappingRendering() {
return false;
@@ -135,7 +166,8 @@
int column = 0;
// Layout each QS tile.
- for (int i = 0; i < numRecords; i++, column++) {
+ final int tilesToLayout = Math.min(numRecords, mRows * mColumns);
+ for (int i = 0; i < tilesToLayout; i++, column++) {
// If we reached the last column available to layout a tile, wrap back to the next row.
if (column == mColumns) {
column = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
index d7abb38..177362c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
@@ -16,23 +16,16 @@
package com.android.systemui.recents.events;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Log;
-import android.util.MutableBoolean;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
-import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -117,11 +110,7 @@
* <p>
* Currently, there is a single EventBus that handles {@link EventBus.Event}s for each subscriber
* on the main application thread. Publishers can send() events to synchronously call subscribers
- * of that event, or post() events to be processed in the next run of the {@link Looper}. In
- * addition, the EventBus supports sending and handling {@link EventBus.InterprocessEvent}s
- * (within the same package) implemented using standard {@link BroadcastReceiver} mechanism.
- * Interprocess events must be posted using postInterprocess() to ensure that it is dispatched
- * correctly across processes.
+ * of that event, or post() events to be processed in the next run of the {@link Looper}.
*
* <p>
* Subscribers must be registered with a particular EventBus before they will receive events, and
@@ -135,26 +124,12 @@
* <li>Methods must take one parameter, of class type deriving from {@link EventBus.Event}
* </ul>
*
- * <p>
- * Interprocess-Event method signature:<ul>
- * <li>Methods must be public final
- * <li>Methods must return void
- * <li>Methods must be called "onInterprocessBusEvent"
- * <li>Methods must take one parameter, of class type deriving from {@link EventBus.InterprocessEvent}
- * </ul>
- * </p>
- *
* </p>
* Each subscriber can be registered with a given priority (default 1), and events will be dispatch
* in decreasing order of priority. For subscribers with the same priority, events will be
* dispatched by latest registration time to earliest.
*
* <p>
- * Interprocess events must extend {@link EventBus.InterprocessEvent}, have a constructor which
- * takes a {@link Bundle} and implement toBundle(). This allows us to serialize events to be sent
- * across processes.
- *
- * <p>
* Caveats:<ul>
* <li>The EventBus keeps a {@link WeakReference} to the publisher to prevent memory leaks, so
* there must be another strong reference to the publisher for it to not get garbage-collected and
@@ -201,7 +176,7 @@
* result?
* </p>
*/
-public class EventBus extends BroadcastReceiver {
+public class EventBus {
private static final String TAG = "EventBus";
private static final boolean DEBUG_TRACE_ALL = false;
@@ -314,35 +289,6 @@
}
/**
- * An inter-process event super class that allows us to track user state across subscriber
- * invocations.
- */
- public static class InterprocessEvent extends Event {
- private static final String EXTRA_USER = "_user";
-
- // The user which this event originated from
- public final int user;
-
- // Only accessible from derived events
- protected InterprocessEvent(int user) {
- this.user = user;
- }
-
- /**
- * Called from the event bus
- */
- protected InterprocessEvent(Bundle b) {
- user = b.getInt(EXTRA_USER);
- }
-
- protected Bundle toBundle() {
- Bundle b = new Bundle();
- b.putInt(EXTRA_USER, user);
- return b;
- }
- }
-
- /**
* Proguard must also know, and keep, all methods matching this signature.
*
* -keepclassmembers class ** {
@@ -351,13 +297,6 @@
* }
*/
private static final String METHOD_PREFIX = "onBusEvent";
- private static final String INTERPROCESS_METHOD_PREFIX = "onInterprocessBusEvent";
-
- // Ensures that interprocess events can only be sent from a process holding this permission. */
- private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
-
- // Used for passing event data across process boundaries
- private static final String EXTRA_INTERPROCESS_EVENT_BUNDLE = "interprocess_event_bundle";
// The default priority of all subscribers
private static final int DEFAULT_SUBSCRIBER_PRIORITY = 1;
@@ -383,10 +322,6 @@
// The handler to post all events
private Handler mHandler;
- // Keep track of whether we have registered a broadcast receiver already, so that we can
- // unregister ourselves before re-registering again with a new IntentFilter.
- private boolean mHasRegisteredReceiver;
-
/**
* Map from event class -> event handler list. Keeps track of the actual mapping from event
* to subscriber method.
@@ -401,13 +336,6 @@
private HashMap<Class<? extends Object>, ArrayList<EventHandlerMethod>> mSubscriberTypeMap = new HashMap<>();
/**
- * Map from interprocess event name -> interprocess event class. Used for mapping the event
- * name after receiving the broadcast, to the event type. After which a new instance is created
- * and posted in the local process.
- */
- private HashMap<String, Class<? extends InterprocessEvent>> mInterprocessEventNameMap = new HashMap<>();
-
- /**
* Set of all currently registered subscribers
*/
private ArrayList<Subscriber> mSubscribers = new ArrayList<>();
@@ -447,7 +375,7 @@
* be scanned for appropriate event handler methods.
*/
public void register(Object subscriber) {
- registerSubscriber(subscriber, DEFAULT_SUBSCRIBER_PRIORITY, null);
+ registerSubscriber(subscriber, DEFAULT_SUBSCRIBER_PRIORITY);
}
/**
@@ -460,44 +388,7 @@
* subscribers
*/
public void register(Object subscriber, int priority) {
- registerSubscriber(subscriber, priority, null);
- }
-
- /**
- * Explicitly registers a subscriber to receive interprocess events with the default priority.
- *
- * @param subscriber the subscriber to handle events. If this is the first instance of the
- * subscriber's class type that has been registered, the class's methods will
- * be scanned for appropriate event handler methods.
- */
- public void registerInterprocessAsCurrentUser(Context context, Object subscriber) {
- registerInterprocessAsCurrentUser(context, subscriber, DEFAULT_SUBSCRIBER_PRIORITY);
- }
-
- /**
- * Registers a subscriber to receive interprocess events with the given priority.
- *
- * @param subscriber the subscriber to handle events. If this is the first instance of the
- * subscriber's class type that has been registered, the class's methods will
- * be scanned for appropriate event handler methods.
- * @param priority the priority that this subscriber will receive events relative to other
- * subscribers
- */
- public void registerInterprocessAsCurrentUser(Context context, Object subscriber, int priority) {
- if (DEBUG_TRACE_ALL) {
- logWithPid("registerInterprocessAsCurrentUser(" + subscriber.getClass().getSimpleName() + ")");
- }
-
- // Register the subscriber normally, and update the broadcast receiver filter if this is
- // a new subscriber type with interprocess events
- MutableBoolean hasInterprocessEventsChanged = new MutableBoolean(false);
- registerSubscriber(subscriber, priority, hasInterprocessEventsChanged);
- if (DEBUG_TRACE_ALL) {
- logWithPid("hasInterprocessEventsChanged: " + hasInterprocessEventsChanged.value);
- }
- if (hasInterprocessEventsChanged.value) {
- registerReceiverForInterprocessEvents(context);
- }
+ registerSubscriber(subscriber, priority);
}
/**
@@ -538,18 +429,6 @@
}
/**
- * Explicit unregistration for interprocess event subscribers. This actually behaves exactly
- * the same as unregister() since we also do not want to stop listening for specific
- * inter-process messages in case new instances of that subscriber is registered.
- */
- public void unregisterInterprocess(Context context, Object subscriber) {
- if (DEBUG_TRACE_ALL) {
- logWithPid("unregisterInterprocess()");
- }
- unregister(subscriber);
- }
-
- /**
* Sends an event to the subscribers of the given event type immediately. This can only be
* called from the same thread as the EventBus's looper thread (for the default EventBus, this
* is the main application thread).
@@ -599,57 +478,6 @@
}
}
- /** Prevent post()ing an InterprocessEvent */
- @Deprecated
- public void post(InterprocessEvent event) {
- throw new RuntimeException("Not supported, use postInterprocess");
- }
-
- /** Prevent send()ing an InterprocessEvent */
- @Deprecated
- public void send(InterprocessEvent event) {
- throw new RuntimeException("Not supported, use postInterprocess");
- }
-
- /**
- * Posts an interprocess event.
- */
- public void postInterprocess(Context context, final InterprocessEvent event) {
- if (DEBUG_TRACE_ALL) {
- logWithPid("postInterprocess(" + event.getClass().getSimpleName() + ")");
- }
- String eventType = event.getClass().getName();
- Bundle eventBundle = event.toBundle();
- Intent intent = new Intent(eventType);
- intent.setPackage(context.getPackageName());
- intent.putExtra(EXTRA_INTERPROCESS_EVENT_BUNDLE, eventBundle);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
- Intent.FLAG_RECEIVER_FOREGROUND);
- context.sendBroadcastAsUser(intent, UserHandle.ALL);
- }
-
- /**
- * Receiver for interprocess events.
- */
- @Override
- public void onReceive(Context context, Intent intent) {
- if (DEBUG_TRACE_ALL) {
- logWithPid("onReceive(" + intent.getAction() + ", user " + UserHandle.myUserId() + ")");
- }
-
- Bundle eventBundle = intent.getBundleExtra(EXTRA_INTERPROCESS_EVENT_BUNDLE);
- Class<? extends InterprocessEvent> eventType = mInterprocessEventNameMap.get(intent.getAction());
- try {
- Constructor<? extends InterprocessEvent> ctor = eventType.getConstructor(Bundle.class);
- send((Event) ctor.newInstance(eventBundle));
- } catch (NoSuchMethodException|
- InvocationTargetException|
- InstantiationException|
- IllegalAccessException e) {
- Log.e(TAG, "Failed to create InterprocessEvent", e.getCause());
- }
- }
-
/**
* @return a dump of the current state of the EventBus
*/
@@ -711,8 +539,7 @@
/**
* Registers a new subscriber.
*/
- private void registerSubscriber(Object subscriber, int priority,
- MutableBoolean hasInterprocessEventsChangedOut) {
+ private void registerSubscriber(Object subscriber, int priority) {
// Fail immediately if we are being called from the non-main thread
long callingThreadId = Thread.currentThread().getId();
if (callingThreadId != mHandler.getLooper().getThread().getId()) {
@@ -759,32 +586,16 @@
}
// Find all the valid event bus handler methods of the subscriber
- MutableBoolean isInterprocessEvent = new MutableBoolean(false);
Method[] methods = subscriberType.getDeclaredMethods();
for (Method m : methods) {
Class<?>[] parameterTypes = m.getParameterTypes();
- isInterprocessEvent.value = false;
- if (isValidEventBusHandlerMethod(m, parameterTypes, isInterprocessEvent)) {
+ if (isValidEventBusHandlerMethod(m, parameterTypes)) {
Class<? extends Event> eventType = (Class<? extends Event>) parameterTypes[0];
ArrayList<EventHandler> eventTypeHandlers = mEventTypeMap.get(eventType);
if (eventTypeHandlers == null) {
eventTypeHandlers = new ArrayList<>();
mEventTypeMap.put(eventType, eventTypeHandlers);
}
- if (isInterprocessEvent.value) {
- try {
- // Enforce that the event must have a Bundle constructor
- eventType.getConstructor(Bundle.class);
-
- mInterprocessEventNameMap.put(eventType.getName(),
- (Class<? extends InterprocessEvent>) eventType);
- if (hasInterprocessEventsChangedOut != null) {
- hasInterprocessEventsChangedOut.value = true;
- }
- } catch (NoSuchMethodException e) {
- throw new RuntimeException("Expected InterprocessEvent to have a Bundle constructor");
- }
- }
EventHandlerMethod method = new EventHandlerMethod(m, eventType);
EventHandler handler = new EventHandler(sub, method, priority);
eventTypeHandlers.add(handler);
@@ -793,8 +604,7 @@
if (DEBUG_TRACE_ALL) {
logWithPid(" * Method: " + m.getName() +
- " event: " + parameterTypes[0].getSimpleName() +
- " interprocess? " + isInterprocessEvent.value);
+ " event: " + parameterTypes[0].getSimpleName());
}
}
}
@@ -830,12 +640,7 @@
final EventHandler eventHandler = eventHandlers.get(i);
if (eventHandler.subscriber.getReference() != null) {
if (event.requiresPost) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- processEvent(eventHandler, event);
- }
- });
+ mHandler.post(() -> processEvent(eventHandler, event));
hasPostedEvent = true;
} else {
processEvent(eventHandler, event);
@@ -845,12 +650,7 @@
// Clean up after this event, deferring until all subscribers have been called
if (hasPostedEvent) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- event.onPostDispatch();
- }
- });
+ mHandler.post(event::onPostDispatch);
} else {
event.onPostDispatch();
}
@@ -898,29 +698,6 @@
}
/**
- * Re-registers the broadcast receiver for any new messages that we want to listen for.
- */
- private void registerReceiverForInterprocessEvents(Context context) {
- if (DEBUG_TRACE_ALL) {
- logWithPid("registerReceiverForInterprocessEvents()");
- }
- // Rebuild the receiver filter with the new interprocess events
- IntentFilter filter = new IntentFilter();
- for (String eventName : mInterprocessEventNameMap.keySet()) {
- filter.addAction(eventName);
- if (DEBUG_TRACE_ALL) {
- logWithPid(" filter: " + eventName);
- }
- }
- // Re-register the receiver with the new filter
- if (mHasRegisteredReceiver) {
- context.unregisterReceiver(this);
- }
- context.registerReceiverAsUser(this, UserHandle.ALL, filter, PERMISSION_SELF, mHandler);
- mHasRegisteredReceiver = true;
- }
-
- /**
* Returns whether this subscriber is currently registered. If {@param removeFoundSubscriber}
* is true, then remove the subscriber before returning.
*/
@@ -940,28 +717,19 @@
/**
* @return whether {@param method} is a valid (normal or interprocess) event bus handler method
*/
- private boolean isValidEventBusHandlerMethod(Method method, Class<?>[] parameterTypes,
- MutableBoolean isInterprocessEventOut) {
+ private boolean isValidEventBusHandlerMethod(Method method, Class<?>[] parameterTypes) {
int modifiers = method.getModifiers();
if (Modifier.isPublic(modifiers) &&
Modifier.isFinal(modifiers) &&
method.getReturnType().equals(Void.TYPE) &&
parameterTypes.length == 1) {
- if (EventBus.InterprocessEvent.class.isAssignableFrom(parameterTypes[0]) &&
- method.getName().startsWith(INTERPROCESS_METHOD_PREFIX)) {
- isInterprocessEventOut.value = true;
- return true;
- } else if (EventBus.Event.class.isAssignableFrom(parameterTypes[0]) &&
+ if (EventBus.Event.class.isAssignableFrom(parameterTypes[0]) &&
method.getName().startsWith(METHOD_PREFIX)) {
- isInterprocessEventOut.value = false;
return true;
} else {
if (DEBUG_TRACE_ALL) {
if (!EventBus.Event.class.isAssignableFrom(parameterTypes[0])) {
logWithPid(" Expected method take an Event-based parameter: " + method.getName());
- } else if (!method.getName().startsWith(INTERPROCESS_METHOD_PREFIX) &&
- !method.getName().startsWith(METHOD_PREFIX)) {
- logWithPid(" Expected method start with method prefix: " + method.getName());
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index 4516518..b6e88d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -165,7 +165,7 @@
* Whether or not the given notification is alerting and managed by this manager.
* @return true if the notification is alerting
*/
- public boolean contains(@NonNull String key) {
+ public boolean isAlerting(@NonNull String key) {
return mAlertEntries.containsKey(key);
}
@@ -294,7 +294,7 @@
removeAutoRemovalCallbacks();
if (!isSticky()) {
- long finishTime = mPostTime + mAutoDismissNotificationDecay;
+ long finishTime = calculateFinishTime();
long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
mHandler.postDelayed(mRemoveAlertRunnable, removeDelay);
}
@@ -357,6 +357,14 @@
protected long calculatePostTime() {
return mClock.currentTimeMillis();
}
+
+ /**
+ * Calculate when the notification should auto-dismiss itself.
+ * @return the finish time
+ */
+ protected long calculateFinishTime() {
+ return mPostTime + mAutoDismissNotificationDecay;
+ }
}
protected final static class Clock {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
new file mode 100644
index 0000000..2c384d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationData;
+
+/**
+ * Manager which handles high priority notifications that should "pulse" in when the device is
+ * dozing and/or in AOD. The pulse uses the notification's ambient view and pops in briefly
+ * before automatically dismissing the alert.
+ */
+public final class AmbientPulseManager extends AlertingNotificationManager {
+
+ protected final ArraySet<OnAmbientChangedListener> mListeners = new ArraySet<>();
+ @VisibleForTesting
+ protected long mExtensionTime;
+
+ public AmbientPulseManager(@NonNull final Context context) {
+ Resources resources = context.getResources();
+ mAutoDismissNotificationDecay = resources.getInteger(R.integer.ambient_notification_decay);
+ mMinimumDisplayTime = resources.getInteger(R.integer.ambient_notification_minimum_time);
+ mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
+ }
+
+ /**
+ * Adds an OnAmbientChangedListener to observe events.
+ */
+ public void addListener(@NonNull OnAmbientChangedListener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes the OnAmbientChangedListener from the observer list.
+ */
+ public void removeListener(@NonNull OnAmbientChangedListener listener) {
+ mListeners.remove(listener);
+ }
+
+ /**
+ * Extends the lifetime of the currently showing pulsing notification so that the pulse lasts
+ * longer.
+ */
+ public void extendPulse() {
+ AmbientEntry topEntry = getTopEntry();
+ if (topEntry == null) {
+ return;
+ }
+ topEntry.extendPulse();
+ }
+
+ @Override
+ protected void onAlertEntryAdded(AlertEntry alertEntry) {
+ NotificationData.Entry entry = alertEntry.mEntry;
+ entry.row.setAmbientPulsing(true);
+ for (OnAmbientChangedListener listener : mListeners) {
+ listener.onAmbientStateChanged(entry, true);
+ }
+ }
+
+ @Override
+ protected void onAlertEntryRemoved(AlertEntry alertEntry) {
+ NotificationData.Entry entry = alertEntry.mEntry;
+ entry.row.setAmbientPulsing(false);
+ for (OnAmbientChangedListener listener : mListeners) {
+ listener.onAmbientStateChanged(entry, false);
+ }
+ }
+
+ @Override
+ protected AlertEntry createAlertEntry() {
+ return new AmbientEntry();
+ }
+
+ /**
+ * Get the top pulsing entry. This should be the currently showing one if there are multiple.
+ * @return the currently showing entry
+ */
+ private AmbientEntry getTopEntry() {
+ if (mAlertEntries.isEmpty()) {
+ return null;
+ }
+ AlertEntry topEntry = null;
+ for (AlertEntry entry : mAlertEntries.values()) {
+ if (topEntry == null || entry.compareTo(topEntry) < 0) {
+ topEntry = entry;
+ }
+ }
+ return (AmbientEntry) topEntry;
+ }
+
+ /**
+ * Observer interface for any changes in the ambient entries.
+ */
+ public interface OnAmbientChangedListener {
+ /**
+ * Called when an entry starts or stops pulsing.
+ * @param entry the entry that changed
+ * @param isPulsing true if the entry is now pulsing, false otherwise
+ */
+ void onAmbientStateChanged(NotificationData.Entry entry, boolean isPulsing);
+ }
+
+ private final class AmbientEntry extends AlertEntry {
+ private boolean extended;
+
+ /**
+ * Extend the lifetime of the alertEntry so that it auto-removes later. Can only be
+ * extended once.
+ */
+ private void extendPulse() {
+ if (!extended) {
+ extended = true;
+ updateEntry(false);
+ }
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ extended = false;
+ }
+
+ @Override
+ protected long calculateFinishTime() {
+ return super.calculateFinishTime() + (extended ? mExtensionTime : 0);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index f7615e6..d8f7b61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -121,7 +121,8 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- Dependency.get(StatusBarStateController.class).addListener(mStateListener);
+ Dependency.get(StatusBarStateController.class)
+ .addListener(mStateListener, StatusBarStateController.RANK_SHELF);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index d479838..f69ad43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -321,7 +321,7 @@
&& !row.isLowPriority()));
}
- entry.row.setShowAmbient(mPresenter.isDozing());
+ entry.row.setOnAmbient(mPresenter.isDozing());
int userId = entry.notification.getUserId();
boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
entry.notification) && !entry.row.isRemoved();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
index 7a69f2e..d6719f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
@@ -16,42 +16,70 @@
package com.android.systemui.statusbar;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
import android.util.ArraySet;
+import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+import java.lang.annotation.Retention;
+import java.util.ArrayList;
+import java.util.Comparator;
/**
* Tracks and reports on {@link StatusBarState}.
*/
public class StatusBarStateController {
+ private static final String TAG = "SbStateController";
private static final int MAX_STATE = StatusBarState.FULLSCREEN_USER_SWITCHER;
private static final int MIN_STATE = StatusBarState.SHADE;
- private final ArraySet<StateListener> mListeners = new ArraySet<>();
+ private static final Comparator <RankedListener> mComparator
+ = (o1, o2) -> Integer.compare(o1.rank, o2.rank);
+
+ private final ArrayList<RankedListener> mListeners = new ArrayList<>();
private int mState;
private int mLastState;
private boolean mLeaveOpenOnKeyguardHide;
+ // TODO: b/115739177 (remove this explicit ordering if we can)
+ @Retention(SOURCE)
+ @IntDef({RANK_STATUS_BAR, RANK_STATUS_BAR_WINDOW_CONTROLLER, RANK_STACK_SCROLLER, RANK_SHELF})
+ public @interface SbStateListenerRank {}
+ // This is the set of known dependencies when updating StatusBarState
+ public static final int RANK_STATUS_BAR = 0;
+ public static final int RANK_STATUS_BAR_WINDOW_CONTROLLER = 1;
+ public static final int RANK_STACK_SCROLLER = 2;
+ public static final int RANK_SHELF = 3;
+
public int getState() {
return mState;
}
- public void setState(int state) {
+ public boolean setState(int state) {
if (state > MAX_STATE || state < MIN_STATE) {
throw new IllegalArgumentException("Invalid state " + state);
}
if (state == mState) {
- return;
+ return false;
}
synchronized (mListeners) {
- for (StateListener listener : new ArraySet<>(mListeners)) {
- listener.onStatePreChange(mState, state);
+ for (RankedListener rl : new ArrayList<>(mListeners)) {
+ rl.listener.onStatePreChange(mState, state);
}
mLastState = mState;
mState = state;
- for (StateListener listener : new ArraySet<>(mListeners)) {
- listener.onStateChanged(mState);
+ for (RankedListener rl : new ArrayList<>(mListeners)) {
+ rl.listener.onStateChanged(mState);
+ }
+
+ for (RankedListener rl : new ArrayList<>(mListeners)) {
+ rl.listener.onStatePostChange();
}
}
+
+ return true;
}
public boolean goingToFullShade() {
@@ -72,20 +100,67 @@
public void addListener(StateListener listener) {
synchronized (mListeners) {
- mListeners.add(listener);
+ addListenerInternalLocked(listener, Integer.MAX_VALUE);
}
}
+ /**
+ * Add a listener and a rank based on the priority of this message
+ * @param listener the listener
+ * @param rank the order in which you'd like to be called. Ranked listeners will be
+ * notified before unranked, and we will sort ranked listeners from low to high
+ *
+ * @deprecated This method exists only to solve latent inter-dependencies from refactoring
+ * StatusBarState out of StatusBar.java. Any new listeners should be built not to need ranking
+ * (i.e., they are non-dependent on the order of operations of StatusBarState listeners).
+ */
+ public void addListener(StateListener listener, @SbStateListenerRank int rank) {
+ synchronized (mListeners) {
+ addListenerInternalLocked(listener, rank);
+ }
+ }
+
+ @GuardedBy("mListeners")
+ private void addListenerInternalLocked(StateListener listener, int rank) {
+ // Protect against double-subscribe
+ for (RankedListener rl : mListeners) {
+ if (rl.listener.equals(listener)) {
+ return;
+ }
+ }
+
+ RankedListener rl = new RankedListener(listener, rank);
+ mListeners.add(rl);
+ mListeners.sort(mComparator);
+ }
+
public void removeListener(StateListener listener) {
synchronized (mListeners) {
- mListeners.remove(listener);
+ mListeners.removeIf((it) -> it.listener.equals(listener));
}
}
+ public static String describe(int state) {
+ return StatusBarState.toShortString(state);
+ }
+
public interface StateListener {
public default void onStatePreChange(int oldState, int newState) {
}
+ public default void onStatePostChange() {
+ }
+
public void onStateChanged(int newState);
}
+
+ private class RankedListener {
+ private final StateListener listener;
+ private final int rank;
+
+ private RankedListener(StateListener l, int r) {
+ listener = l;
+ rank = r;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index 804e842..d097c8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -440,6 +440,8 @@
} else if (isHeadsUp) {
// Provide consistent ranking with headsUpManager
return mHeadsUpManager.compare(a, b);
+ } else if (a.row.isAmbientPulsing() != b.row.isAmbientPulsing()) {
+ return a.row.isAmbientPulsing() ? -1 : 1;
} else if (aMedia != bMedia) {
// Upsort current media notification.
return aMedia ? -1 : 1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index ac01fa3..935eaac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -58,6 +58,8 @@
import com.android.systemui.UiOffloadThread;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
+import com.android.systemui.statusbar.AlertingNotificationManager;
+import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -91,7 +93,7 @@
ExpandableNotificationRow.ExpansionLogger, NotificationUpdateHandler,
VisualStabilityManager.Callback {
private static final String TAG = "NotificationEntryMgr";
- protected static final boolean DEBUG = false;
+ protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
protected static final boolean ENABLE_HEADS_UP = true;
protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
@@ -121,6 +123,7 @@
Dependency.get(ForegroundServiceController.class);
protected final NotificationListener mNotificationListener =
Dependency.get(NotificationListener.class);
+ protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
protected IStatusBarService mBarService;
protected NotificationPresenter mPresenter;
@@ -264,6 +267,7 @@
}
mNotificationLifetimeExtenders.add(mHeadsUpManager);
+ mNotificationLifetimeExtenders.add(mAmbientPulseManager);
mNotificationLifetimeExtenders.add(mGutsManager);
mNotificationLifetimeExtenders.addAll(mRemoteInputManager.getLifetimeExtenders());
@@ -381,7 +385,7 @@
final int userId = n.getUserId();
try {
int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
- if (isHeadsUp(n.getKey())) {
+ if (mHeadsUpManager.isAlerting(n.getKey())) {
dismissalSurface = NotificationStats.DISMISSAL_PEEK;
} else if (mListContainer.hasPulsingNotifications()) {
dismissalSurface = NotificationStats.DISMISSAL_AOD;
@@ -432,12 +436,14 @@
}
private void addEntry(NotificationData.Entry shadeEntry) {
- boolean isHeadsUped = shouldPeek(shadeEntry);
- if (isHeadsUped) {
+ if (shouldHeadsUp(shadeEntry)) {
mHeadsUpManager.showNotification(shadeEntry);
// Mark as seen immediately
setNotificationShown(shadeEntry.notification);
}
+ if (shouldPulse(shadeEntry)) {
+ mAmbientPulseManager.showNotification(shadeEntry);
+ }
addNotificationViews(shadeEntry);
mCallback.onNotificationAdded(shadeEntry);
}
@@ -465,7 +471,11 @@
private void removeNotificationInternal(String key,
@Nullable NotificationListenerService.RankingMap ranking, boolean forceRemove) {
abortExistingInflation(key);
- if (mHeadsUpManager.contains(key)) {
+
+ // Attempt to remove notifications from their alert managers (heads up, ambient pulse).
+ // Though the remove itself may fail, it lets the manager know to remove as soon as
+ // possible.
+ if (mHeadsUpManager.isAlerting(key)) {
// A cancel() in response to a remote input shouldn't be delayed, as it makes the
// sending look longer than it takes.
// Also we should not defer the removal if reordering isn't allowed since otherwise
@@ -473,10 +483,11 @@
boolean ignoreEarliestRemovalTime = mRemoteInputManager.getController().isSpinning(key)
&& !FORCE_REMOTE_INPUT_HISTORY
|| !mVisualStabilityManager.isReorderingAllowed();
-
- // Attempt to remove notification.
mHeadsUpManager.removeNotification(key, ignoreEarliestRemovalTime);
}
+ if (mAmbientPulseManager.isAlerting(key)) {
+ mAmbientPulseManager.removeNotification(key, false /* ignoreEarliestRemovalTime */);
+ }
NotificationData.Entry entry = mNotificationData.get(key);
@@ -651,13 +662,15 @@
private void addNotificationInternal(StatusBarNotification notification,
NotificationListenerService.RankingMap rankingMap) throws InflationException {
String key = notification.getKey();
- if (DEBUG) Log.d(TAG, "addNotification key=" + key);
+ if (DEBUG) {
+ Log.d(TAG, "addNotification key=" + key);
+ }
mNotificationData.updateRanking(rankingMap);
NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
rankingMap.getRanking(key, ranking);
NotificationData.Entry shadeEntry = createNotificationViews(notification, ranking);
- boolean isHeadsUped = shouldPeek(shadeEntry);
+ boolean isHeadsUped = shouldHeadsUp(shadeEntry);
if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
if (shouldSuppressFullScreenIntent(shadeEntry)) {
if (DEBUG) {
@@ -750,7 +763,6 @@
extender.setShouldManageLifetime(entry, false /* shouldManage */);
}
- Notification n = notification.getNotification();
mNotificationData.updateRanking(ranking);
final StatusBarNotification oldNotification = entry.notification;
@@ -763,10 +775,12 @@
mForegroundServiceController.updateNotification(notification,
mNotificationData.getImportance(key));
- boolean shouldPeek = shouldPeek(entry, notification);
- boolean alertAgain = alertAgain(entry, n);
-
- updateHeadsUp(key, entry, shouldPeek, alertAgain);
+ boolean alertAgain = alertAgain(entry, entry.notification.getNotification());
+ if (mPresenter.isDozing()) {
+ updateAlertState(entry, shouldPulse(entry), alertAgain, mAmbientPulseManager);
+ } else {
+ updateAlertState(entry, shouldHeadsUp(entry), alertAgain, mHeadsUpManager);
+ }
updateNotifications();
if (!notification.isClearable()) {
@@ -851,66 +865,147 @@
}
}
- protected boolean shouldPeek(NotificationData.Entry entry) {
- return shouldPeek(entry, entry.notification);
- }
+ /**
+ * Whether the notification should peek in from the top and alert the user.
+ *
+ * @param entry the entry to check
+ * @return true if the entry should heads up, false otherwise
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
+ public boolean shouldHeadsUp(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
- public boolean shouldPeek(NotificationData.Entry entry, StatusBarNotification sbn) {
- if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
- if (DEBUG) Log.d(TAG, "No peeking: no huns or vr mode");
+ if (mPresenter.isDozing()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: device is dozing: " + sbn.getKey());
+ }
return false;
}
- if (mNotificationData.shouldFilterOut(entry)) {
- if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey());
+ if (!canAlertCommon(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: notification shouldn't alert: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: no huns or vr mode");
+ }
return false;
}
boolean inUse = mPowerManager.isScreenOn() && !mSystemServicesProxy.isDreaming();
- if (!inUse && !mPresenter.isDozing()) {
+ if (!inUse) {
if (DEBUG) {
- Log.d(TAG, "No peeking: not in use: " + sbn.getKey());
+ Log.d(TAG, "No heads up: not in use: " + sbn.getKey());
}
return false;
}
- if (!mPresenter.isDozing() && mNotificationData.shouldSuppressPeek(entry)) {
- if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
- return false;
- }
-
- // Peeking triggers an ambient display pulse, so disable peek is ambient is active
- if (mPresenter.isDozing() && mNotificationData.shouldSuppressAmbient(entry)) {
- if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
- return false;
- }
-
- if (entry.hasJustLaunchedFullScreenIntent()) {
- if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey());
+ if (mNotificationData.shouldSuppressPeek(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: suppressed by DND: " + sbn.getKey());
+ }
return false;
}
if (isSnoozedPackage(sbn)) {
- if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: snoozed package: " + sbn.getKey());
+ }
return false;
}
- // Allow peeking for DEFAULT notifications only if we're on Ambient Display.
- int importanceLevel = mPresenter.isDozing() ? NotificationManager.IMPORTANCE_DEFAULT
- : NotificationManager.IMPORTANCE_HIGH;
- if (mNotificationData.getImportance(sbn.getKey()) < importanceLevel) {
- if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());
+ if (entry.hasJustLaunchedFullScreenIntent()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: recent fullscreen: " + sbn.getKey());
+ }
return false;
}
- // Don't peek notifications that are suppressed due to group alert behavior
+ if (mNotificationData.getImportance(sbn.getKey()) < NotificationManager.IMPORTANCE_HIGH) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: unimportant notification: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (!mCallback.canHeadsUp(entry, sbn)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Whether or not the notification should "pulse" on the user's display when the phone is
+ * dozing. This displays the ambient view of the notification.
+ *
+ * @param entry the entry to check
+ * @return true if the entry should ambient pulse, false otherwise
+ */
+ protected boolean shouldPulse(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+
+ if (!mPresenter.isDozing()) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: not dozing: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (!canAlertCommon(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: notification shouldn't alert: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (mNotificationData.shouldSuppressAmbient(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: ambient effect suppressed: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (mNotificationData.getImportance(sbn.getKey())
+ < NotificationManager.IMPORTANCE_DEFAULT) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: not important enough: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Common checks between heads up alerting and ambient pulse alerting. See
+ * {@link NotificationEntryManager#shouldHeadsUp(NotificationData.Entry)} and
+ * {@link NotificationEntryManager#shouldPulse(NotificationData.Entry)}. Notifications that
+ * fail any of these checks should not alert at all.
+ *
+ * @param entry the entry to check
+ * @return true if these checks pass, false if the notification should not alert
+ */
+ protected boolean canAlertCommon(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+
+ if (mNotificationData.shouldFilterOut(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No alerting: filtered notification: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ // Don't alert notifications that are suppressed due to group alert behavior
if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
- if (DEBUG) Log.d(TAG, "No peeking: suppressed due to group alert behavior");
- return false;
- }
-
- if (!mCallback.shouldPeek(entry, sbn)) {
+ if (DEBUG) {
+ Log.d(TAG, "No alerting: suppressed due to group alert behavior");
+ }
return false;
}
@@ -933,26 +1028,31 @@
return mHeadsUpManager.isSnoozed(sbn.getPackageName());
}
- protected void updateHeadsUp(String key, NotificationData.Entry entry, boolean shouldPeek,
- boolean alertAgain) {
- final boolean wasHeadsUp = isHeadsUp(key);
- if (wasHeadsUp) {
- if (!shouldPeek) {
+ /**
+ * Update the entry's alert state and call the appropriate {@link AlertingNotificationManager}
+ * method.
+ * @param entry entry to update
+ * @param shouldAlert whether or not it should be alerting
+ * @param alertAgain whether or not an alert should actually come in as if it were new
+ * @param alertManager the alerting notification manager that manages the alert state
+ */
+ private void updateAlertState(NotificationData.Entry entry, boolean shouldAlert,
+ boolean alertAgain, AlertingNotificationManager alertManager) {
+ final boolean wasAlerting = alertManager.isAlerting(entry.key);
+ if (wasAlerting) {
+ if (!shouldAlert) {
// We don't want this to be interrupting anymore, lets remove it
- mHeadsUpManager.removeNotification(key, false /* ignoreEarliestRemovalTime */);
+ alertManager.removeNotification(entry.key,
+ false /* ignoreEarliestRemovalTime */);
} else {
- mHeadsUpManager.updateNotification(entry.key, alertAgain);
+ alertManager.updateNotification(entry.key, alertAgain);
}
- } else if (shouldPeek && alertAgain) {
- // This notification was updated to be a heads-up, show it!
- mHeadsUpManager.showNotification(entry);
+ } else if (shouldAlert && alertAgain) {
+ // This notification was updated to be alerting, show it!
+ alertManager.showNotification(entry);
}
}
- protected boolean isHeadsUp(String key) {
- return mHeadsUpManager.contains(key);
- }
-
/**
* Callback for NotificationEntryManager.
*/
@@ -1008,12 +1108,12 @@
void onPerformRemoveNotification(StatusBarNotification statusBarNotification);
/**
- * Returns true if NotificationEntryManager should peek this notification.
+ * Returns true if NotificationEntryManager can heads up this notification.
*
- * @param entry entry of the notification that might be peeked
- * @param sbn notification that might be peeked
- * @return true if the notification should be peeked
+ * @param entry entry of the notification that might be heads upped
+ * @param sbn notification that might be heads upped
+ * @return true if the notification can be heads upped
*/
- boolean shouldPeek(NotificationData.Entry entry, StatusBarNotification sbn);
+ boolean canHeadsUp(NotificationData.Entry entry, StatusBarNotification sbn);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ShadeViewRefactor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ShadeViewRefactor.java
index f204c42..5ad2ba9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ShadeViewRefactor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ShadeViewRefactor.java
@@ -14,7 +14,10 @@
* limitations under the License
*/
-package java.lang.annotation;
+package com.android.systemui.statusbar.notification;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.SOURCE)
public @interface ShadeViewRefactor {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 216ed68..019e88b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -174,6 +174,11 @@
*/
private boolean mOnKeyguard;
+ /**
+ * Whether or not the row is currently on the doze screen.
+ */
+ private boolean mOnAmbient;
+
private Animator mTranslateAnim;
private ArrayList<View> mTranslateableViews;
private NotificationContentView mPublicLayout;
@@ -186,7 +191,18 @@
private NotificationData.Entry mEntry;
private StatusBarNotification mStatusBarNotification;
private String mAppName;
+
+ /**
+ * Whether or not the notification is using the heads up view and should peek from the top.
+ */
private boolean mIsHeadsUp;
+
+ /**
+ * Whether or not the notification is using the ambient display view and is pulsing. This
+ * occurs when a high priority notification alerts while the phone is dozing or is on AOD.
+ */
+ private boolean mIsAmbientPulsing;
+
private boolean mLastChronometerRunning = true;
private ViewStub mChildrenContainerStub;
private NotificationGroupManager mGroupManager;
@@ -289,7 +305,6 @@
private float mContentTransformationAmount;
private boolean mIconsVisible = true;
private boolean mAboveShelf;
- private boolean mShowAmbient;
private boolean mIsLastChild;
private Runnable mOnDismissRunnable;
private boolean mIsLowPriority;
@@ -606,6 +621,14 @@
}
}
+ public boolean isAmbientPulsing() {
+ return mIsAmbientPulsing;
+ }
+
+ public void setAmbientPulsing(boolean isAmbientPulsing) {
+ mIsAmbientPulsing = isAmbientPulsing;
+ }
+
public void setGroupManager(NotificationGroupManager groupManager) {
mGroupManager = groupManager;
mPrivateLayout.setGroupManager(groupManager);
@@ -1854,7 +1877,7 @@
public void setDark(boolean dark, boolean fade, long delay) {
super.setDark(dark, fade, delay);
mDark = dark;
- if (!mIsHeadsUp) {
+ if (!mIsAmbientPulsing) {
// Only fade the showing view of the pulsing notification.
fade = false;
}
@@ -2155,7 +2178,7 @@
return mPrivateLayout.getMinHeight();
} else if (mSensitive && mHideSensitiveForIntrinsicHeight) {
return getMinHeight();
- } else if (mIsSummaryWithChildren && (!mOnKeyguard || mShowAmbient)) {
+ } else if (mIsSummaryWithChildren && (!mOnKeyguard || mOnAmbient)) {
return mChildrenContainer.getIntrinsicHeight();
} else if (isHeadsUpAllowed() && (mIsHeadsUp || mHeadsupDisappearRunning)) {
if (isPinned() || mHeadsupDisappearRunning) {
@@ -2173,7 +2196,7 @@
}
private boolean isHeadsUpAllowed() {
- return !mOnKeyguard && !mShowAmbient;
+ return !mOnKeyguard && !mOnAmbient;
}
@Override
@@ -2818,11 +2841,11 @@
|| mExpandAnimationRunning || mChildIsExpanding);
}
- public void setShowAmbient(boolean showAmbient) {
- if (showAmbient != mShowAmbient) {
- mShowAmbient = showAmbient;
+ public void setOnAmbient(boolean onAmbient) {
+ if (onAmbient != mOnAmbient) {
+ mOnAmbient = onAmbient;
if (mChildrenContainer != null) {
- mChildrenContainer.notifyShowAmbientChanged();
+ mChildrenContainer.notifyDozingStateChanged();
}
notifyHeightChanged(false /* needsAnimation */);
}
@@ -2891,8 +2914,8 @@
return getCurrentBottomRoundness() == 0.0f && getCurrentTopRoundness() == 0.0f;
}
- public boolean isShowingAmbient() {
- return mShowAmbient;
+ public boolean isOnAmbient() {
+ return mOnAmbient;
}
public void setAboveShelf(boolean aboveShelf) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 0110610..4963a0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -725,7 +725,7 @@
}
public int getMaxHeight() {
- if (mContainingNotification.isShowingAmbient()) {
+ if (mContainingNotification.isOnAmbient()) {
return getShowingAmbientView().getHeight();
} else if (mExpandedChild != null) {
return getViewHeight(VISIBLE_TYPE_EXPANDED)
@@ -752,7 +752,7 @@
}
public int getMinHeight(boolean likeGroupExpanded) {
- if (mContainingNotification.isShowingAmbient()) {
+ if (mContainingNotification.isOnAmbient()) {
return getShowingAmbientView().getHeight();
} else if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
return getViewHeight(VISIBLE_TYPE_CONTRACTED);
@@ -1039,7 +1039,7 @@
* @return one of the static enum types in this view, calculated form the current state
*/
public int calculateVisibleType() {
- if (mContainingNotification.isShowingAmbient()) {
+ if (mContainingNotification.isOnAmbient()) {
if (mIsChildInGroup && mAmbientSingleLineChild != null) {
return VISIBLE_TYPE_AMBIENT_SINGLELINE;
} else if (mAmbientChild != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 15eaaac..8969aca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -20,14 +20,15 @@
import android.content.Context;
import android.view.View;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.util.ArrayList;
@@ -44,7 +45,7 @@
private int mSpeedBumpIndex = -1;
private boolean mDark;
private boolean mHideSensitive;
- private HeadsUpManager mHeadsUpManager;
+ private AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
private float mStackTranslation;
private int mLayoutHeight;
private int mTopPadding;
@@ -207,10 +208,6 @@
mSpeedBumpIndex = shelfIndex;
}
- public void setHeadsUpManager(HeadsUpManager headsUpManager) {
- mHeadsUpManager = headsUpManager;
- }
-
public float getStackTranslation() {
return mStackTranslation;
}
@@ -334,10 +331,10 @@
}
public boolean isPulsing(NotificationData.Entry entry) {
- if (!mPulsing || mHeadsUpManager == null) {
+ if (!mPulsing || mAmbientPulseManager == null) {
return false;
}
- return mHeadsUpManager.getAllEntries().anyMatch(e -> (e == entry));
+ return mAmbientPulseManager.isAlerting(entry.key);
}
public boolean isPanelTracking() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 3d44e3c..da089b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -213,7 +213,7 @@
// calculated correctly as they are used to calculate how many we can fit on the screen.
boolean isOverflow = i == overflowIndex;
child.setSingleLineWidthIndention(isOverflow && mOverflowNumber != null &&
- !mContainingNotification.isShowingAmbient()
+ !mContainingNotification.isOnAmbient()
? mOverflowNumber.getMeasuredWidth() : 0);
child.measure(widthMeasureSpec, newHeightSpec);
// layout the divider
@@ -406,7 +406,7 @@
if (childCount > maxAllowedVisibleChildren) {
int number = childCount - maxAllowedVisibleChildren;
mOverflowNumber = mHybridGroupManager.bindOverflowNumber(mOverflowNumber, number);
- if (mContainingNotification.isShowingAmbient()) {
+ if (mContainingNotification.isOnAmbient()) {
ExpandableNotificationRow overflowView = mChildren.get(0);
HybridNotificationView ambientSingleLineView = overflowView == null ? null
: overflowView.getAmbientSingleLineView();
@@ -522,7 +522,7 @@
if (mUserLocked) {
expandFactor = getGroupExpandFraction();
}
- boolean childrenExpanded = mChildrenExpanded || mContainingNotification.isShowingAmbient();
+ boolean childrenExpanded = mChildrenExpanded || mContainingNotification.isOnAmbient();
for (int i = 0; i < childCount; i++) {
if (visibleChildren >= maxAllowedVisibleChildren) {
break;
@@ -641,7 +641,7 @@
getMaxAllowedVisibleChildren(true /* likeCollapsed */), childCount) - 1);
mGroupOverFlowState.copyFrom(resultState.getViewStateForView(overflowView));
- if (mContainingNotification.isShowingAmbient()) {
+ if (mContainingNotification.isOnAmbient()) {
mGroupOverFlowState.alpha = 0.0f;
} else if (!mChildrenExpanded) {
HybridNotificationView alignView = overflowView.getSingleLineView();
@@ -710,7 +710,7 @@
@VisibleForTesting
int getMaxAllowedVisibleChildren(boolean likeCollapsed) {
- if (mContainingNotification.isShowingAmbient()) {
+ if (mContainingNotification.isOnAmbient()) {
return NUMBER_OF_CHILDREN_WHEN_AMBIENT;
}
if (!likeCollapsed && (mChildrenExpanded || mContainingNotification.isUserLocked())
@@ -900,7 +900,7 @@
return mCurrentHeader;
}
- public void notifyShowAmbientChanged() {
+ public void notifyDozingStateChanged() {
updateHeaderVisibility(false);
updateGroupOverflow();
}
@@ -970,7 +970,7 @@
private ViewGroup calculateDesiredHeader() {
ViewGroup desiredHeader;
- if (mContainingNotification.isShowingAmbient()) {
+ if (mContainingNotification.isOnAmbient()) {
desiredHeader = mNotificationHeaderAmbient;
} else if (showingAsLowPriority()) {
desiredHeader = mNotificationHeaderLowPriority;
@@ -1126,7 +1126,7 @@
}
public int getMinHeight() {
- return getMinHeight(mContainingNotification.isShowingAmbient()
+ return getMinHeight(mContainingNotification.isOnAmbient()
? NUMBER_OF_CHILDREN_WHEN_AMBIENT
: NUMBER_OF_CHILDREN_WHEN_COLLAPSED, false /* likeHighPriority */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 52844fe..958a162 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -115,6 +115,8 @@
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.ShadeViewRefactor;
+import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
@@ -134,8 +136,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.lang.annotation.ShadeViewRefactor;
-import java.lang.annotation.ShadeViewRefactor.RefactorComponent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -620,7 +620,8 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- Dependency.get(StatusBarStateController.class).addListener(mStateListener);
+ Dependency.get(StatusBarStateController.class)
+ .addListener(mStateListener, StatusBarStateController.RANK_STACK_SCROLLER);
Dependency.get(ConfigurationController.class).addCallback(this);
}
@@ -4248,8 +4249,9 @@
mDimAnimator.addUpdateListener(mDimUpdateListener);
mDimAnimator.start();
}
+
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setHideSensitive(boolean hideSensitive, boolean animate) {
+ private void setHideSensitive(boolean hideSensitive, boolean animate) {
if (hideSensitive != mAmbientState.isHideSensitive()) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -4826,7 +4828,6 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
mHeadsUpManager = headsUpManager;
- mAmbientState.setHeadsUpManager(headsUpManager);
mHeadsUpManager.addListener(mRoundnessManager);
mHeadsUpManager.setAnimationStateHandler(this);
}
@@ -5008,8 +5009,12 @@
private void setStatusBarState(int statusBarState) {
mStatusBarState = statusBarState;
mAmbientState.setStatusBarState(statusBarState);
+ }
+
+ private void onStatePostChange() {
boolean onKeyguard = onKeyguard();
boolean publicMode = mLockscreenUserManager.isAnyProfilePublicMode();
+
if (mHeadsUpAppearanceController != null) {
mHeadsUpAppearanceController.setPublicMode(publicMode);
}
@@ -5026,6 +5031,8 @@
updateFooter();
updateChildren();
onUpdateRowStates();
+
+ mEntryManager.updateNotifications();
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -5949,5 +5956,10 @@
public void onStateChanged(int newState) {
setStatusBarState(newState);
}
- };
+
+ @Override
+ public void onStatePostChange() {
+ NotificationStackScrollLayout.this.onStatePostChange();
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index 235ff2b..c32dcea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -67,6 +67,10 @@
mView.setBackground(mBarBackground);
}
+ public void destroy() {
+ // To be overridden
+ }
+
public int getMode() {
return mMode;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
index 81b596c..424acfd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
@@ -34,9 +34,11 @@
private val inCarMode: Boolean
private var uiMode: Int = 0
private var localeList: LocaleList? = null
+ private val context: Context
init {
val currentConfig = context.resources.configuration
+ this.context = context
fontScale = currentConfig.fontScale
density = currentConfig.densityDpi
inCarMode = currentConfig.uiMode and Configuration.UI_MODE_TYPE_MASK ==
@@ -82,6 +84,10 @@
}
if (uiModeChanged) {
+ // We need to force the style re-evaluation to make sure that it's up to date
+ // and attrs were reloaded.
+ context.theme.applyStyle(context.themeResId, true)
+
this.uiMode = uiMode
listeners.filterForEach({ this.listeners.contains(it) }) {
it.onUiModeChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 0d3ba77..25db4f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -63,9 +63,14 @@
if (!mDozing) {
return;
}
- mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration());
- mHandler.postDelayed(mPulseOutExtended,
- mDozeParameters.getPulseVisibleDurationExtended());
+ // All pulses except notifications should time out on their own. Pulses due to
+ // notifications should instead be managed externally based off the notification's
+ // lifetime.
+ if (mPulseReason != DozeLog.PULSE_REASON_NOTIFICATION) {
+ mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration());
+ mHandler.postDelayed(mPulseOutExtended,
+ mDozeParameters.getPulseVisibleDurationExtended());
+ }
mFullyPulsing = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 4a05989..cfc3271 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -177,7 +177,7 @@
mReleaseOnExpandFinish = false;
} else {
for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
- if (contains(entry.key)) {
+ if (isAlerting(entry.key)) {
// Maybe the heads-up was removed already
removeAlertEntry(entry.key);
}
@@ -345,7 +345,7 @@
public void onReorderingAllowed() {
mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false);
for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) {
- if (contains(entry.key)) {
+ if (isAlerting(entry.key)) {
// Maybe the heads-up was removed already
removeAlertEntry(entry.key);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 10b019d..66486ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -307,7 +307,10 @@
@Override
public void onDestroyView() {
super.onDestroyView();
- mNavigationBarView.getLightTransitionsController().destroy(getContext());
+ if (mNavigationBarView != null) {
+ mNavigationBarView.getBarTransitions().destroy();
+ mNavigationBarView.getLightTransitionsController().destroy(getContext());
+ }
mOverviewProxyService.removeCallback(mOverviewProxyListener);
getContext().unregisterReceiver(mBroadcastReceiver);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
index e09d31c..d58b554 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -44,6 +44,17 @@
private boolean mAutoDim;
private View mNavButtons;
+ private final Handler mHandler = Handler.getMain();
+ private final IWallpaperVisibilityListener mWallpaperVisibilityListener =
+ new IWallpaperVisibilityListener.Stub() {
+ @Override
+ public void onWallpaperVisibilityChanged(boolean newVisibility,
+ int displayId) throws RemoteException {
+ mWallpaperVisible = newVisibility;
+ mHandler.post(() -> applyLightsOut(true, false));
+ }
+ };
+
public NavigationBarTransitions(NavigationBarView view) {
super(view, R.drawable.nav_background);
mView = view;
@@ -55,17 +66,9 @@
.getBoolean(R.bool.config_navigation_bar_enable_auto_dim_no_visible_wallpaper);
IWindowManager windowManagerService = Dependency.get(IWindowManager.class);
- Handler handler = Handler.getMain();
try {
mWallpaperVisible = windowManagerService.registerWallpaperVisibilityListener(
- new IWallpaperVisibilityListener.Stub() {
- @Override
- public void onWallpaperVisibilityChanged(boolean newVisibility,
- int displayId) throws RemoteException {
- mWallpaperVisible = newVisibility;
- handler.post(() -> applyLightsOut(true, false));
- }
- }, Display.DEFAULT_DISPLAY);
+ mWallpaperVisibilityListener, Display.DEFAULT_DISPLAY);
} catch (RemoteException e) {
}
mView.addOnLayoutChangeListener(
@@ -88,6 +91,16 @@
}
@Override
+ public void destroy() {
+ IWindowManager windowManagerService = Dependency.get(IWindowManager.class);
+ try {
+ windowManagerService.unregisterWallpaperVisibilityListener(mWallpaperVisibilityListener,
+ Display.DEFAULT_DISPLAY);
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
public void setAutoDim(boolean autoDim) {
if (mAutoDim == autoDim) return;
mAutoDim = autoDim;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 2141016..9d13ea2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -615,6 +615,7 @@
((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0);
getImeSwitchButton().setVisibility(showImeButton ? View.VISIBLE : View.INVISIBLE);
getImeSwitchButton().setImageDrawable(mImeIcon);
+ updateContextualContainerVisibility();
// Update menu button, visibility logic in method
setMenuVisibility(mShowMenu, true);
@@ -796,6 +797,7 @@
((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) == 0);
getMenuButton().setVisibility(shouldShow ? View.VISIBLE : View.INVISIBLE);
+ updateContextualContainerVisibility();
}
public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) {
@@ -810,6 +812,7 @@
getAccessibilityButton().setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
getAccessibilityButton().setLongClickable(longClickable);
+ updateContextualContainerVisibility();
}
public void updateRotateSuggestionButtonStyle(@StyleRes int style, boolean setIcon) {
@@ -839,6 +842,7 @@
getRotateSuggestionButton().setVisibility(vis);
mShowRotateButton = visible;
+ updateContextualContainerVisibility();
// Stop any active animations if hidden
if (!visible && mRotateSuggestionIcon.canAnimate()) {
@@ -855,6 +859,13 @@
public boolean isRotateButtonVisible() { return mShowRotateButton; }
+ private void updateContextualContainerVisibility() {
+ // Only show the menu container when one of its buttons are visible
+ getMenuContainer().setVisibility((mShowAccessibilityButton || mShowRotateButton || mShowMenu
+ || (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0)
+ ? VISIBLE : INVISIBLE);
+ }
+
void hideRecentsOnboarding() {
mRecentsOnboarding.hide(true);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java
index 7b9ed88..09833d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java
@@ -98,7 +98,6 @@
return mClickableChildren
.stream()
.filter(v -> v.isAttachedToWindow())
- .filter(v -> v.isFocusable())
.map(v -> new Pair<>(distance(v, event), v))
.min(Comparator.comparingInt(f -> f.first))
.get().second;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 6b6566c..c08366a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import android.annotation.NonNull;
import android.app.Notification;
import android.os.SystemClock;
import android.service.notification.StatusBarNotification;
@@ -23,6 +24,9 @@
import android.util.Log;
import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.AlertingNotificationManager;
+import com.android.systemui.statusbar.AmbientPulseManager;
+import com.android.systemui.statusbar.AmbientPulseManager.OnAmbientChangedListener;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -43,15 +47,18 @@
/**
* A class to handle notifications and their corresponding groups.
*/
-public class NotificationGroupManager implements OnHeadsUpChangedListener {
+public class NotificationGroupManager implements OnHeadsUpChangedListener,
+ OnAmbientChangedListener {
private static final String TAG = "NotificationGroupManager";
- private static final long HEADS_UP_TRANSFER_TIMEOUT = 300;
+ private static final long ALERT_TRANSFER_TIMEOUT = 300;
private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>();
private OnGroupChangeListener mListener;
private int mBarState = -1;
private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
private HeadsUpManager mHeadsUpManager;
+ private AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
+ private boolean mIsDozing;
private boolean mIsUpdatingUnchangedGroup;
private HashMap<String, NotificationData.Entry> mPendingNotifications;
@@ -162,40 +169,58 @@
mListener.onGroupCreatedFromChildren(group);
}
}
- cleanUpHeadsUpStatesOnAdd(group, false /* addIsPending */);
+ cleanUpAlertStatesOnAdd(group, false /* addIsPending */);
}
public void onPendingEntryAdded(NotificationData.Entry shadeEntry) {
String groupKey = getGroupKey(shadeEntry.notification);
NotificationGroup group = mGroupMap.get(groupKey);
if (group != null) {
- cleanUpHeadsUpStatesOnAdd(group, true /* addIsPending */);
+ cleanUpAlertStatesOnAdd(group, true /* addIsPending */);
}
}
/**
- * Clean up the heads up states when a new child was added.
+ * Set whether or not the device is dozing. This allows the group manager to reset some
+ * specific alert state logic based off when the state changes.
+ * @param isDozing if the device is dozing.
+ */
+ public void setDozing(boolean isDozing) {
+ if (mIsDozing != isDozing) {
+ for (NotificationGroup group : mGroupMap.values()) {
+ group.lastAlertTransfer = 0;
+ group.alertSummaryOnNextAddition = false;
+ }
+ }
+ mIsDozing = isDozing;
+ }
+
+ /**
+ * Clean up the alert states when a new child was added.
* @param group The group where a view was added or will be added.
* @param addIsPending True if is the addition still pending or false has it already been added.
*/
- private void cleanUpHeadsUpStatesOnAdd(NotificationGroup group, boolean addIsPending) {
- if (!addIsPending && group.hunSummaryOnNextAddition) {
- if (!mHeadsUpManager.contains(group.summary.key)) {
- mHeadsUpManager.showNotification(group.summary);
+ private void cleanUpAlertStatesOnAdd(NotificationGroup group, boolean addIsPending) {
+
+ AlertingNotificationManager alertManager =
+ mIsDozing ? mAmbientPulseManager : mHeadsUpManager;
+ if (!addIsPending && group.alertSummaryOnNextAddition) {
+ if (!alertManager.isAlerting(group.summary.key)) {
+ alertManager.showNotification(group.summary);
}
- group.hunSummaryOnNextAddition = false;
+ group.alertSummaryOnNextAddition = false;
}
// Because notification groups are not delivered as a whole unit, it may happen that a
// group child gets added quite a bit after the summary got posted. Our guidance is, that
// apps should always post the group summary as well and we'll hide it for them if the child
- // is the only child in a group. Because of this, we also have to transfer heads up to the
- // child, otherwise the invisible summary would be heads-upped.
+ // is the only child in a group. Because of this, we also have to transfer alert to the
+ // child, otherwise the invisible summary would be alerted.
// This transfer to the child is not always correct in case the app has just posted another
// child in addition to the existing one, but it hasn't arrived in systemUI yet. In such
- // a scenario we would transfer the heads up to the old child and the wrong notification
- // would be heads-upped. In oder to avoid this, we'll recover from this issue and hun the
+ // a scenario we would transfer the alert to the old child and the wrong notification
+ // would be alerted. In order to avoid this, we'll recover from this issue and alert the
// summary again instead of the old child if it's within a certain timeout.
- if (SystemClock.elapsedRealtime() - group.lastHeadsUpTransfer < HEADS_UP_TRANSFER_TIMEOUT) {
+ if (SystemClock.elapsedRealtime() - group.lastAlertTransfer < ALERT_TRANSFER_TIMEOUT) {
if (!onlySummaryAlerts(group.summary)) {
return;
}
@@ -215,26 +240,24 @@
int size = children.size();
for (int i = 0; i < size; i++) {
NotificationData.Entry entry = children.get(i);
- if (onlySummaryAlerts(entry) && entry.row.isHeadsUp()) {
+ if (onlySummaryAlerts(entry) && alertManager.isAlerting(entry.key)) {
releasedChild = true;
- mHeadsUpManager.removeNotification(
- entry.key, true /* releaseImmediately */);
+ alertManager.removeNotification(entry.key, true /* releaseImmediately */);
}
}
if (isolatedChild != null && onlySummaryAlerts(isolatedChild)
- && isolatedChild.row.isHeadsUp()) {
+ && alertManager.isAlerting(isolatedChild.key)) {
releasedChild = true;
- mHeadsUpManager.removeNotification(
- isolatedChild.key, true /* releaseImmediately */);
+ alertManager.removeNotification(isolatedChild.key, true /* releaseImmediately */);
}
- if (releasedChild && !mHeadsUpManager.contains(group.summary.key)) {
+ if (releasedChild && !alertManager.isAlerting(group.summary.key)) {
boolean notifyImmediately = (numChildren - numPendingChildren) > 1;
if (notifyImmediately) {
- mHeadsUpManager.showNotification(group.summary);
+ alertManager.showNotification(group.summary);
} else {
- group.hunSummaryOnNextAddition = true;
+ group.alertSummaryOnNextAddition = true;
}
- group.lastHeadsUpTransfer = 0;
+ group.lastAlertTransfer = 0;
}
}
}
@@ -264,8 +287,8 @@
}
private void onEntryBecomingChild(NotificationData.Entry entry) {
- if (entry.row.isHeadsUp()) {
- onHeadsUpStateChanged(entry, true);
+ if (shouldIsolate(entry)) {
+ isolateNotification(entry);
}
}
@@ -281,7 +304,11 @@
&& hasIsolatedChildren(group)));
if (prevSuppressed != group.suppressed) {
if (group.suppressed) {
- handleSuppressedSummaryHeadsUpped(group.summary);
+ if (mHeadsUpManager.isAlerting(group.summary.key)) {
+ handleSuppressedSummaryAlerted(group.summary, mHeadsUpManager);
+ } else if (mAmbientPulseManager.isAlerting(group.summary.key)) {
+ handleSuppressedSummaryAlerted(group.summary, mAmbientPulseManager);
+ }
}
if (!mIsUpdatingUnchangedGroup && mListener != null) {
mListener.onGroupsChanged();
@@ -495,54 +522,56 @@
}
@Override
+ public void onAmbientStateChanged(NotificationData.Entry entry, boolean isAmbient) {
+ onAlertStateChanged(entry, isAmbient, mAmbientPulseManager);
+ }
+
+ @Override
public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+ onAlertStateChanged(entry, isHeadsUp, mHeadsUpManager);
+ }
+
+ private void onAlertStateChanged(NotificationData.Entry entry, boolean isAlerting,
+ AlertingNotificationManager alertManager) {
final StatusBarNotification sbn = entry.notification;
- if (entry.row.isHeadsUp()) {
- if (shouldIsolate(sbn)) {
- // We will be isolated now, so lets update the groups
- onEntryRemovedInternal(entry, entry.notification);
-
- mIsolatedEntries.put(sbn.getKey(), sbn);
-
- onEntryAdded(entry);
- // We also need to update the suppression of the old group, because this call comes
- // even before the groupManager knows about the notification at all.
- // When the notification gets added afterwards it is already isolated and therefore
- // it doesn't lead to an update.
- updateSuppression(mGroupMap.get(entry.notification.getGroupKey()));
- mListener.onGroupsChanged();
- } else {
- handleSuppressedSummaryHeadsUpped(entry);
+ if (isAlerting) {
+ if (shouldIsolate(entry)) {
+ isolateNotification(entry);
+ } else if (sbn.getNotification().isGroupSummary()
+ && isGroupSuppressed(sbn.getGroupKey())){
+ handleSuppressedSummaryAlerted(entry, alertManager);
}
} else {
- if (mIsolatedEntries.containsKey(sbn.getKey())) {
- // not isolated anymore, we need to update the groups
- onEntryRemovedInternal(entry, entry.notification);
- mIsolatedEntries.remove(sbn.getKey());
- onEntryAdded(entry);
- mListener.onGroupsChanged();
- }
+ stopIsolatingNotification(entry);
}
}
- private void handleSuppressedSummaryHeadsUpped(NotificationData.Entry entry) {
- StatusBarNotification sbn = entry.notification;
+ /**
+ * Handles the scenario where a summary that has been suppressed is alerted. A suppressed
+ * summary should for all intents and purposes be invisible to the user and as a result should
+ * not alert. When this is the case, it is our responsibility to pass the alert to the
+ * appropriate child which will be the representative notification alerting for the group.
+ * @param summary the summary that is suppressed and alerting
+ * @param alertManager the alert manager that manages the alerting summary
+ */
+ private void handleSuppressedSummaryAlerted(@NonNull NotificationData.Entry summary,
+ @NonNull AlertingNotificationManager alertManager) {
+ StatusBarNotification sbn = summary.notification;
if (!isGroupSuppressed(sbn.getGroupKey())
|| !sbn.getNotification().isGroupSummary()
- || !entry.row.isHeadsUp()) {
+ || !alertManager.isAlerting(sbn.getKey())) {
return;
}
- // The parent of a suppressed group got huned, lets hun the child!
+ // The parent of a suppressed group got alerted, lets alert the child!
NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
- if (pendingInflationsWillAddChildren(notificationGroup)) {
- // New children will actually be added to this group, let's not transfer the heads
- // up
- return;
- }
-
if (notificationGroup != null) {
+ if (pendingInflationsWillAddChildren(notificationGroup)) {
+ // New children will actually be added to this group, let's not transfer the alert.
+ return;
+ }
+
Iterator<NotificationData.Entry> iterator
= notificationGroup.children.values().iterator();
NotificationData.Entry child = iterator.hasNext() ? iterator.next() : null;
@@ -551,20 +580,35 @@
}
if (child != null) {
if (child.row.keepInParent() || child.row.isRemoved() || child.row.isDismissed()) {
- // the notification is actually already removed, no need to do heads-up on it.
+ // the notification is actually already removed, no need to do alert on it.
return;
}
- if (mHeadsUpManager.contains(child.key)) {
- mHeadsUpManager.updateNotification(child.key, true /* alert */);
- } else {
- if (onlySummaryAlerts(entry)) {
- notificationGroup.lastHeadsUpTransfer = SystemClock.elapsedRealtime();
- }
- mHeadsUpManager.showNotification(child);
- }
+ transferAlertStateToChild(summary, child, alertManager);
}
}
- mHeadsUpManager.removeNotification(entry.key, true /* releaseImmediately */);
+ }
+
+ /**
+ * Transfers the alert state from a given summary notification to the specified child. The
+ * result is the child will now alert while the summary does not.
+ *
+ * @param summary the currently alerting summary notification
+ * @param child the child that should receive the alert
+ * @param alertManager the manager for the alert
+ */
+ private void transferAlertStateToChild(@NonNull NotificationData.Entry summary,
+ @NonNull NotificationData.Entry child,
+ @NonNull AlertingNotificationManager alertManager) {
+ NotificationGroup notificationGroup = mGroupMap.get(summary.notification.getGroupKey());
+ if (alertManager.isAlerting(child.key)) {
+ alertManager.updateNotification(child.key, true /* alert */);
+ } else {
+ if (onlySummaryAlerts(summary)) {
+ notificationGroup.lastAlertTransfer = SystemClock.elapsedRealtime();
+ }
+ alertManager.showNotification(child);
+ }
+ alertManager.removeNotification(summary.key, true /* releaseImmediately */);
}
private boolean onlySummaryAlerts(NotificationData.Entry entry) {
@@ -596,13 +640,69 @@
return false;
}
- private boolean shouldIsolate(StatusBarNotification sbn) {
+ /**
+ * Whether a notification that is normally part of a group should be temporarily isolated from
+ * the group and put in their own group visually. This generally happens when the notification
+ * is alerting.
+ *
+ * @param entry the notification to check
+ * @return true if the entry should be isolated
+ */
+
+ private boolean shouldIsolate(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
- return (sbn.isGroup() && !sbn.getNotification().isGroupSummary())
- && (sbn.getNotification().fullScreenIntent != null
- || notificationGroup == null
- || !notificationGroup.expanded
- || isGroupNotFullyVisible(notificationGroup));
+ if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) {
+ return false;
+ }
+ if (!mIsDozing && !mHeadsUpManager.isAlerting(entry.key)) {
+ return false;
+ }
+ if (mIsDozing && !mAmbientPulseManager.isAlerting(entry.key)) {
+ return false;
+ }
+ return (sbn.getNotification().fullScreenIntent != null
+ || notificationGroup == null
+ || !notificationGroup.expanded
+ || isGroupNotFullyVisible(notificationGroup));
+ }
+
+ /**
+ * Isolate a notification from its group so that it visually shows as its own group.
+ *
+ * @param entry the notification to isolate
+ */
+ private void isolateNotification(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+
+ // We will be isolated now, so lets update the groups
+ onEntryRemovedInternal(entry, entry.notification);
+
+ mIsolatedEntries.put(sbn.getKey(), sbn);
+
+ onEntryAdded(entry);
+ // We also need to update the suppression of the old group, because this call comes
+ // even before the groupManager knows about the notification at all.
+ // When the notification gets added afterwards it is already isolated and therefore
+ // it doesn't lead to an update.
+ updateSuppression(mGroupMap.get(entry.notification.getGroupKey()));
+ mListener.onGroupsChanged();
+ }
+
+ /**
+ * Stop isolating a notification and re-group it with its original logical group.
+ *
+ * @param entry the notification to un-isolate
+ */
+ private void stopIsolatingNotification(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+ if (mIsolatedEntries.containsKey(sbn.getKey())) {
+ // not isolated anymore, we need to update the groups
+ onEntryRemovedInternal(entry, entry.notification);
+ mIsolatedEntries.remove(sbn.getKey());
+ onEntryAdded(entry);
+ mListener.onGroupsChanged();
+ }
}
private boolean isGroupNotFullyVisible(NotificationGroup notificationGroup) {
@@ -641,11 +741,11 @@
*/
public boolean suppressed;
/**
- * The time when the last heads transfer from group to child happened, while the summary
- * has the flags to heads up on its own.
+ * The time when the last alert transfer from group to child happened, while the summary
+ * has the flags to alert up on its own.
*/
- public long lastHeadsUpTransfer;
- public boolean hunSummaryOnNextAddition;
+ public long lastAlertTransfer;
+ public boolean alertSummaryOnNextAddition;
@Override
public String toString() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index bd4d790..30e6afa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -77,6 +77,8 @@
/** Experiment to swipe home button left to execute a back key press */
private static final String PULL_HOME_GO_BACK_PROP = "persist.quickstepcontroller.homegoesback";
private static final String HIDE_BACK_BUTTON_PROP = "persist.quickstepcontroller.hideback";
+ private static final String BACK_AFTER_END_PROP
+ = "persist.quickstepcontroller.homegoesbackwhenend";
private static final long BACK_BUTTON_FADE_OUT_ALPHA = 60;
private static final long BACK_BUTTON_FADE_IN_ALPHA = 150;
private static final long BACK_GESTURE_POLL_TIMEOUT = 1000;
@@ -84,9 +86,6 @@
/** When the home-swipe-back gesture is disallowed, make it harder to pull */
private static final float DISALLOW_GESTURE_DAMPING_FACTOR = 0.16f;
- /** When dragging the home button too far during back gesture, make it harder to pull */
- private static final float EXCEED_DRAG_HOME_DAMPING_FACTOR = 0.33f;
-
private NavigationBarView mNavigationBarView;
private boolean mQuickScrubActive;
@@ -361,7 +360,7 @@
// Once the user drags the home button past a certain limit, the distance
// will lessen as the home button dampens showing that it was pulled too far
float distanceAfterDragLimit = (Math.abs(diff) - mHomeBackGestureDragLimit)
- * EXCEED_DRAG_HOME_DAMPING_FACTOR;
+ * DISALLOW_GESTURE_DAMPING_FACTOR;
diff = (int)(distanceAfterDragLimit + mHomeBackGestureDragLimit);
if (mDragPositive) {
diff *= -1;
@@ -580,15 +579,21 @@
if (!mBackGestureActive) {
mBackGestureActive = true;
mNavigationBarView.getHomeButton().abortCurrentGesture();
+ final boolean runBackMidGesture
+ = !SystemProperties.getBoolean(BACK_AFTER_END_PROP, false);
if (mCanPerformBack) {
if (!shouldhideBackButton()) {
mNavigationBarView.getBackButton().setAlpha(0 /* alpha */, true /* animate */,
BACK_BUTTON_FADE_OUT_ALPHA);
}
- performBack();
+ if (runBackMidGesture) {
+ performBack();
+ }
}
mHandler.removeCallbacks(mExecuteBackRunnable);
- mHandler.postDelayed(mExecuteBackRunnable, BACK_GESTURE_POLL_TIMEOUT);
+ if (runBackMidGesture) {
+ mHandler.postDelayed(mExecuteBackRunnable, BACK_GESTURE_POLL_TIMEOUT);
+ }
}
}
@@ -609,6 +614,9 @@
mNavigationBarView.getBackButton().setAlpha(
mOverviewEventSender.getBackButtonAlpha(), true /* animate */);
}
+ if (SystemProperties.getBoolean(BACK_AFTER_END_PROP, false)) {
+ performBack();
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 7316c02dd..e1d8638 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -187,6 +187,7 @@
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.stackdivider.WindowManagerProxy;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.AppOpsListener;
import com.android.systemui.statusbar.BackDropView;
@@ -254,7 +255,7 @@
ActivityStarter, OnUnlockMethodChangedListener,
OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter,
- StatusBarStateController.StateListener {
+ StatusBarStateController.StateListener, AmbientPulseManager.OnAmbientChangedListener {
public static final boolean MULTIUSER_DEBUG = false;
public static final boolean ENABLE_CHILD_NOTIFICATIONS
@@ -632,7 +633,7 @@
mColorExtractor = Dependency.get(SysuiColorExtractor.class);
mColorExtractor.addOnColorsChangedListener(this);
- mStatusBarStateController.addListener(this);
+ mStatusBarStateController.addListener(this, StatusBarStateController.RANK_STATUS_BAR);
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
@@ -865,6 +866,8 @@
mHeadsUpManager.addListener(mNotificationPanel);
mHeadsUpManager.addListener(mGroupManager);
mHeadsUpManager.addListener(mVisualStabilityManager);
+ mAmbientPulseManager.addListener(this);
+ mAmbientPulseManager.addListener(mGroupManager);
mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
mGroupManager.setHeadsUpManager(mHeadsUpManager);
putComponent(HeadsUpManager.class, mHeadsUpManager);
@@ -1144,11 +1147,6 @@
@Override
public void onUiModeChanged() {
- // UiMode will change the style was already evaluated.
- // We need to force the re-evaluation to make sure that all parents
- // are up to date and new attrs will be rettrieved.
- mContext.getTheme().applyStyle(mContext.getThemeResId(), true);
-
if (mBrightnessMirrorController != null) {
mBrightnessMirrorController.onUiModeChanged();
}
@@ -1238,7 +1236,7 @@
@Override
public void onPerformRemoveNotification(StatusBarNotification n) {
if (mNotificationPanel.hasPulsingNotifications() &&
- !mHeadsUpManager.hasNotifications()) {
+ !mAmbientPulseManager.hasNotifications()) {
// We were showing a pulse for a notification, but no notifications are pulsing anymore.
// Finish the pulse.
mDozeScrimController.pulseOutNow();
@@ -1702,8 +1700,12 @@
}
@Override
- public boolean shouldPeek(Entry entry, StatusBarNotification sbn) {
- if (mIsOccluded && !isDozing()) {
+ public boolean canHeadsUp(Entry entry, StatusBarNotification sbn) {
+ if (isDozing()) {
+ return false;
+ }
+
+ if (mIsOccluded) {
boolean devicePublic = mLockscreenUserManager.
isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
boolean userPublic = devicePublic
@@ -1716,17 +1718,14 @@
if (!panelsEnabled()) {
if (DEBUG) {
- Log.d(TAG, "No peeking: disabled panel : " + sbn.getKey());
+ Log.d(TAG, "No heads up: disabled panel : " + sbn.getKey());
}
return false;
}
if (sbn.getNotification().fullScreenIntent != null) {
if (mAccessibilityManager.isTouchExplorationEnabled()) {
- if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
- return false;
- } else if (isDozing()) {
- // We never want heads up when we are dozing.
+ if (DEBUG) Log.d(TAG, "No heads up: accessible fullscreen: " + sbn.getKey());
return false;
} else {
// we only allow head-up on the lockscreen if it doesn't have a fullscreen intent
@@ -1802,9 +1801,16 @@
@Override
public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) {
mEntryManager.updateNotificationRanking(null /* rankingMap */);
+ }
- if (isHeadsUp) {
- mDozeServiceHost.fireNotificationHeadsUp();
+ @Override
+ public void onAmbientStateChanged(Entry entry, boolean isAmbient) {
+ mEntryManager.updateNotificationRanking(null);
+ if (isAmbient) {
+ mDozeServiceHost.fireNotificationPulse();
+ } else if (!mAmbientPulseManager.hasNotifications()) {
+ // There are no longer any notifications to show. We should end the pulse now.
+ mDozeScrimController.pulseOutNow();
}
}
@@ -3458,7 +3464,14 @@
mIsKeyguard = false;
Trace.beginSection("StatusBar#hideKeyguard");
boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide();
- mStatusBarStateController.setState(StatusBarState.SHADE);
+ if (!(mStatusBarStateController.setState(StatusBarState.SHADE))) {
+ //TODO: StatusBarStateController should probably know about hiding the keyguard and
+ // notify listeners.
+
+ // If the state didn't change, we may still need to update public mode
+ updatePublicMode();
+ mEntryManager.updateNotifications();
+ }
View viewToClick = null;
if (mStatusBarStateController.leaveOpenOnKeyguardHide()) {
if (!mKeyguardRequested) {
@@ -3601,6 +3614,7 @@
mKeyguardIndicationController.setDozing(mDozing);
mNotificationPanel.setDozing(mDozing, animate, mWakeUpTouchLocation);
mNotificationLogger.setDozing(mDozing);
+ mGroupManager.setDozing(mDozing);
updateQsExpansionEnabled();
Trace.endSection();
}
@@ -4147,6 +4161,7 @@
@Override
public void onStartedWakingUp() {
mDeviceInteractive = true;
+ mAmbientPulseManager.releaseAllImmediately();
mVisualStabilityManager.setScreenOn(true);
mNotificationPanel.setTouchAndAnimationDisabled(false);
mDozeServiceHost.stopDozing();
@@ -4179,11 +4194,7 @@
public void onScreenTurnedOff() {
mFalsingManager.onScreenOff();
mScrimController.onScreenTurnedOff();
- // If we pulse in from AOD, we turn the screen off first. However, updatingIsKeyguard
- // in that case destroys the HeadsUpManager state, so don't do it in that case.
- if (!isPulsing()) {
- updateIsKeyguard();
- }
+ updateIsKeyguard();
}
};
@@ -4422,9 +4433,9 @@
}
}
- public void fireNotificationHeadsUp() {
+ public void fireNotificationPulse() {
for (Callback callback : mCallbacks) {
- callback.onNotificationHeadsUp();
+ callback.onNotificationAlerted();
}
}
@@ -4460,7 +4471,7 @@
@Override
public void onPulseStarted() {
callback.onPulseStarted();
- if (mHeadsUpManager.hasNotifications()) {
+ if (mAmbientPulseManager.hasNotifications()) {
// Only pulse the stack scroller if there's actually something to show.
// Otherwise just show the always-on screen.
setPulsing(true);
@@ -4541,7 +4552,11 @@
@Override
public void extendPulse() {
- mDozeScrimController.extendPulse();
+ if (mDozeScrimController.isPulsing() && mAmbientPulseManager.hasNotifications()) {
+ mAmbientPulseManager.extendPulse();
+ } else {
+ mDozeScrimController.extendPulse();
+ }
}
@Override
@@ -4627,6 +4642,8 @@
// for heads up notifications
protected HeadsUpManagerPhone mHeadsUpManager;
+ protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
+
private AboveShelfObserver mAboveShelfObserver;
// handling reordering
@@ -4732,7 +4749,7 @@
final boolean wasOccluded = mIsOccluded;
dismissKeyguardThenExecute(() -> {
// TODO: Some of this code may be able to move to NotificationEntryManager.
- if (mHeadsUpManager != null && mHeadsUpManager.contains(notificationKey)) {
+ if (mHeadsUpManager != null && mHeadsUpManager.isAlerting(notificationKey)) {
// Release the HUN notification to the shade.
if (isPresenterFullyCollapsed()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index e8389af..3db1456 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -460,6 +460,11 @@
boolean staying = mStatusBar.hideKeyguard();
if (!staying) {
mStatusBarWindowController.setKeyguardFadingAway(true);
+ // hide() will happen asynchronously and might arrive after the scrims
+ // were already hidden, this means that the transition callback won't
+ // be triggered anymore and StatusBarWindowController will be forever in
+ // the fadingAway state.
+ mStatusBar.updateScrimController();
wakeAndUnlockDejank();
} else {
mStatusBar.finishKeyguardFadingAway();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index 167bba6..57c7e28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -93,7 +93,8 @@
mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation();
mDozeParameters = dozeParameters;
mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
- Dependency.get(StatusBarStateController.class).addListener(mStateListener);
+ Dependency.get(StatusBarStateController.class).addListener(
+ mStateListener, StatusBarStateController.RANK_STATUS_BAR_WINDOW_CONTROLLER);
Dependency.get(ConfigurationController.class).addCallback(this);
}
@@ -135,6 +136,7 @@
mWindowManager.addView(mStatusBarView, mLp);
mLpChanged = new WindowManager.LayoutParams();
mLpChanged.copyFrom(mLp);
+ onThemeChanged();
}
public void setDozeScreenBrightness(int value) {
@@ -483,6 +485,10 @@
@Override
public void onThemeChanged() {
+ if (mStatusBarView == null) {
+ return;
+ }
+
StatusBarStateController state = Dependency.get(StatusBarStateController.class);
int which;
if (state.getState() == StatusBarState.KEYGUARD
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 52b813f..7dd0d0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -586,7 +586,8 @@
final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
if (mShowImeOnInputConnection && inputConnection != null) {
- final InputMethodManager imm = InputMethodManager.getInstance();
+ final InputMethodManager imm =
+ getContext().getSystemService(InputMethodManager.class);
if (imm != null) {
// onCreateInputConnection is called by InputMethodManager in the middle of
// setting up the connection to the IME; wait with requesting the IME until that
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 0adb439..29a6b95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -403,7 +403,7 @@
boolean hasCACerts = !(conn.getService().getUserCaAliases().getList().isEmpty());
return new Pair<Integer, Boolean>(userId[0], hasCACerts);
} catch (RemoteException | InterruptedException | AssertionError e) {
- Log.i(TAG, e.getMessage());
+ Log.i(TAG, "failed to get CA certs", e);
new Handler(Dependency.get(Dependency.BG_LOOPER)).postDelayed(
() -> new CACertLoader().execute(userId[0]),
CA_CERT_LOADING_RETRY_TIME_IN_MS);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index c2da7f5..5c8336c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -88,14 +88,14 @@
mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
- mHost.callback.onNotificationHeadsUp();
+ mHost.callback.onNotificationAlerted();
mSensors.getMockProximitySensor().sendProximityResult(false); /* Near */
verify(mMachine, never()).requestState(any());
verify(mMachine, never()).requestPulse(anyInt());
- mHost.callback.onNotificationHeadsUp();
+ mHost.callback.onNotificationAlerted();
mSensors.getMockProximitySensor().sendProximityResult(true); /* Far */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index f21ce27..8b41516 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -56,19 +56,19 @@
private static final String TEST_PACKAGE_NAME = "test";
private static final int TEST_UID = 0;
- private static final int TEST_MINIMUM_DISPLAY_TIME = 200;
- private static final int TEST_AUTO_DISMISS_TIME = 500;
+ protected static final int TEST_MINIMUM_DISPLAY_TIME = 200;
+ protected static final int TEST_AUTO_DISMISS_TIME = 500;
// Number of notifications to use in tests requiring multiple notifications
private static final int TEST_NUM_NOTIFICATIONS = 4;
- private static final int TEST_TIMEOUT_TIME = 10000;
- private final Runnable TEST_TIMEOUT_RUNNABLE = () -> mTimedOut = true;
+ protected static final int TEST_TIMEOUT_TIME = 10000;
+ protected final Runnable TEST_TIMEOUT_RUNNABLE = () -> mTimedOut = true;
private AlertingNotificationManager mAlertingNotificationManager;
protected NotificationData.Entry mEntry;
protected Handler mTestHandler;
private StatusBarNotification mSbn;
- private boolean mTimedOut = false;
+ protected boolean mTimedOut = false;
@Mock protected ExpandableNotificationRow mRow;
@@ -122,7 +122,7 @@
public void testShowNotification_addsEntry() {
mAlertingNotificationManager.showNotification(mEntry);
- assertTrue(mAlertingNotificationManager.contains(mEntry.key));
+ assertTrue(mAlertingNotificationManager.isAlerting(mEntry.key));
assertTrue(mAlertingNotificationManager.hasNotifications());
assertEquals(mEntry, mAlertingNotificationManager.getEntry(mEntry.key));
}
@@ -136,7 +136,7 @@
TestableLooper.get(this).processMessages(1);
assertFalse("Test timed out", mTimedOut);
- assertFalse(mAlertingNotificationManager.contains(mEntry.key));
+ assertFalse(mAlertingNotificationManager.isAlerting(mEntry.key));
}
@Test
@@ -146,7 +146,7 @@
// Try to remove but defer, since the notification has not been shown long enough.
mAlertingNotificationManager.removeNotification(mEntry.key, false /* releaseImmediately */);
- assertTrue(mAlertingNotificationManager.contains(mEntry.key));
+ assertTrue(mAlertingNotificationManager.isAlerting(mEntry.key));
}
@Test
@@ -156,7 +156,7 @@
// Remove forcibly with releaseImmediately = true.
mAlertingNotificationManager.removeNotification(mEntry.key, true /* releaseImmediately */);
- assertFalse(mAlertingNotificationManager.contains(mEntry.key));
+ assertFalse(mAlertingNotificationManager.isAlerting(mEntry.key));
}
@Test
@@ -174,10 +174,18 @@
}
@Test
- public void testShouldExtendLifetime_notShownLongEnough() {
+ public void testCanRemoveImmediately_notShownLongEnough() {
mAlertingNotificationManager.showNotification(mEntry);
- // The entry has just been added so the lifetime should be extended
+ // The entry has just been added so we should not remove immediately.
+ assertFalse(mAlertingNotificationManager.canRemoveImmediately(mEntry.key));
+ }
+
+ @Test
+ public void testShouldExtendLifetime() {
+ mAlertingNotificationManager.showNotification(mEntry);
+
+ // While the entry is alerting, it should not be removable.
assertTrue(mAlertingNotificationManager.shouldExtendLifetime(mEntry));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AmbientPulseManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AmbientPulseManagerTest.java
new file mode 100644
index 0000000..f0344e6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AmbientPulseManagerTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class AmbientPulseManagerTest extends AlertingNotificationManagerTest {
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ private static final int TEST_EXTENSION_TIME = 500;
+ private AmbientPulseManager mAmbientPulseManager;
+ private boolean mLivesPastNormalTime;
+
+ protected AlertingNotificationManager createAlertingNotificationManager() {
+ return mAmbientPulseManager;
+ }
+
+ @Before
+ public void setUp() {
+ mAmbientPulseManager = new AmbientPulseManager(mContext);
+ mAmbientPulseManager.mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
+ mAmbientPulseManager.mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
+ mAmbientPulseManager.mExtensionTime = TEST_EXTENSION_TIME;
+ super.setUp();
+ mAmbientPulseManager.mHandler = mTestHandler;
+ }
+
+ @Test
+ public void testExtendPulse() {
+ mAmbientPulseManager.showNotification(mEntry);
+ Runnable pastNormalTimeRunnable =
+ () -> mLivesPastNormalTime = mAmbientPulseManager.isAlerting(mEntry.key);
+ mTestHandler.postDelayed(pastNormalTimeRunnable,
+ mAmbientPulseManager.mAutoDismissNotificationDecay +
+ mAmbientPulseManager.mExtensionTime / 2);
+ mTestHandler.postDelayed(TEST_TIMEOUT_RUNNABLE, TEST_TIMEOUT_TIME);
+
+ mAmbientPulseManager.extendPulse();
+
+ // Wait for normal time runnable and extended remove runnable and process them on arrival.
+ TestableLooper.get(this).processMessages(2);
+
+ assertFalse("Test timed out", mTimedOut);
+ assertTrue("Pulse was not extended", mLivesPastNormalTime);
+ assertFalse(mAmbientPulseManager.isAlerting(mEntry.key));
+ }
+}
+
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index b2170fa..edf29ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -31,6 +31,7 @@
import android.view.LayoutInflater;
import android.widget.RemoteViews;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.row.NotificationInflaterTest;
@@ -63,6 +64,7 @@
mContext = context;
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mHeadsUpManager = new HeadsUpManagerPhone(mContext, null, mGroupManager, null, null);
+ mGroupManager.setHeadsUpManager(mHeadsUpManager);
}
public ExpandableNotificationRow createRow() throws Exception {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
index 087aa59..2728453 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.notification.stack;
import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.NotificationHeaderView;
@@ -51,7 +50,7 @@
@Test
public void testGetMaxAllowedVisibleChildren_ambient() {
- mGroup.setShowAmbient(true);
+ mGroup.setOnAmbient(true);
Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_AMBIENT);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index ce0bd58..1e3d42b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -19,6 +19,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
@@ -316,7 +317,7 @@
private void setBarStateForTest(int state) {
ArgumentCaptor<StatusBarStateController.StateListener> captor =
ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
- verify(mBarState).addListener(captor.capture());
+ verify(mBarState, atLeastOnce()).addListener(captor.capture());
captor.getValue().onStateChanged(state);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index a81d17f..1070795 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -82,28 +82,26 @@
// Remove should succeed because the notification is swiped out
mHeadsUpManager.removeNotification(mEntry.key, false /* releaseImmediately */);
- assertFalse(mHeadsUpManager.contains(mEntry.key));
+ assertFalse(mHeadsUpManager.isAlerting(mEntry.key));
}
@Test
- public void testShouldExtendLifetime_swipedOut() {
+ public void testCanRemoveImmediately_swipedOut() {
mHeadsUpManager.showNotification(mEntry);
mHeadsUpManager.addSwipedOutNotification(mEntry.key);
- // Notification is swiped so its lifetime should not be extended even if it hasn't been
- // shown long enough
- assertFalse(mHeadsUpManager.shouldExtendLifetime(mEntry));
+ // Notification is swiped so it can be immediately removed.
+ assertTrue(mHeadsUpManager.canRemoveImmediately(mEntry.key));
}
@Test
- public void testShouldExtendLifetime_notTopEntry() {
+ public void testCanRemoveImmediately_notTopEntry() {
NotificationData.Entry laterEntry = new NotificationData.Entry(createNewNotification(1));
laterEntry.row = mRow;
mHeadsUpManager.showNotification(mEntry);
mHeadsUpManager.showNotification(laterEntry);
- // Notification is "behind" a higher priority notification so we have no reason to keep
- // its lifetime extended
- assertFalse(mHeadsUpManager.shouldExtendLifetime(mEntry));
+ // Notification is "behind" a higher priority notification so we can remove it immediately.
+ assertTrue(mHeadsUpManager.canRemoveImmediately(mEntry.key));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java
index 2423e14..667a508 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java
@@ -171,23 +171,6 @@
ev.recycle();
}
- @Test
- public void testFurtherSelectedWhenCloserNotFocusable() {
- View closer = mockViewAt(0, 0, 10, 10);
- View further = mockViewAt(20, 0, 10, 10);
- closer.setFocusable(false);
-
- mNearestTouchFrame.addView(closer);
- mNearestTouchFrame.addView(further);
- mNearestTouchFrame.onMeasure(0, 0);
-
- MotionEvent ev = MotionEvent.obtain(0, 0, 0,
- 12 /* x */, 5 /* y */, 0);
- mNearestTouchFrame.onTouchEvent(ev);
- verify(further).onTouchEvent(eq(ev));
- ev.recycle();
- }
-
private View mockViewAt(int x, int y, int width, int height) {
View v = spy(new View(mContext));
doAnswer(invocation -> {
@@ -204,7 +187,6 @@
v.setRight(width);
v.setTop(0);
v.setBottom(height);
- v.setFocusable(true);
return v;
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
index 6a3c8a8..464f74b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
@@ -36,6 +36,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
@@ -60,15 +61,23 @@
private static final String TEST_CHANNEL_ID = "test_channel";
private static final String TEST_GROUP_ID = "test_group";
private static final String TEST_PACKAGE_NAME = "test_pkg";
- private NotificationGroupManager mGroupManager = new NotificationGroupManager();
+ private NotificationGroupManager mGroupManager;
private int mId = 0;
@Mock HeadsUpManager mHeadsUpManager;
+ @Mock AmbientPulseManager mAmbientPulseManager;
@Before
public void setup() {
- mGroupManager.setHeadsUpManager(mHeadsUpManager);
- mGroupManager.setOnGroupChangeListener(mock(OnGroupChangeListener.class));
+ mDependency.injectTestDependency(AmbientPulseManager.class, mAmbientPulseManager);
+
+ initializeGroupManager();
+ }
+
+ private void initializeGroupManager() {
+ mGroupManager = new NotificationGroupManager();
+ mGroupManager.setHeadsUpManager(mHeadsUpManager);
+ mGroupManager.setOnGroupChangeListener(mock(OnGroupChangeListener.class));
}
@Test
@@ -141,8 +150,7 @@
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
mGroupManager.onEntryAdded(createChildNotification());
- when(childEntry.row.isHeadsUp()).thenReturn(true);
- when(mHeadsUpManager.contains(childEntry.key)).thenReturn(true);
+ when(mHeadsUpManager.isAlerting(childEntry.key)).thenReturn(true);
mGroupManager.onHeadsUpStateChanged(childEntry, true);
@@ -154,17 +162,35 @@
}
@Test
+ public void testAmbientPulseEntryIsIsolated() {
+ mGroupManager.setDozing(true);
+ NotificationData.Entry childEntry = createChildNotification();
+ NotificationData.Entry summaryEntry = createSummaryNotification();
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+ mGroupManager.onEntryAdded(createChildNotification());
+ when(mAmbientPulseManager.isAlerting(childEntry.key)).thenReturn(true);
+
+ mGroupManager.onAmbientStateChanged(childEntry, true);
+
+ // Child entries that are heads upped should be considered separate groups visually even if
+ // they are the same group logically
+ assertEquals(childEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
+ assertEquals(summaryEntry.row,
+ mGroupManager.getLogicalGroupSummary(childEntry.notification));
+ }
+
+ @Test
public void testSuppressedSummaryHeadsUpTransfersToChild() {
NotificationData.Entry summaryEntry = createSummaryNotification();
- when(summaryEntry.row.isHeadsUp()).thenReturn(true);
- when(mHeadsUpManager.contains(summaryEntry.key)).thenReturn(true);
+ when(mHeadsUpManager.isAlerting(summaryEntry.key)).thenReturn(true);
NotificationData.Entry childEntry = createChildNotification();
- // Summary will be suppressed because there is only one child
+ // Summary will be suppressed because there is only one child.
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
- // A suppressed summary should transfer its heads up state to the child
+ // A suppressed summary should transfer its heads up state to the child.
verify(mHeadsUpManager, never()).showNotification(summaryEntry);
verify(mHeadsUpManager).showNotification(childEntry);
}
@@ -175,24 +201,64 @@
mGroupManager.setHeadsUpManager(mHeadsUpManager);
NotificationData.Entry summaryEntry =
createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
- when(summaryEntry.row.isHeadsUp()).thenReturn(true);
NotificationData.Entry childEntry =
createChildNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationData.Entry childEntry2 =
createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+ mHeadsUpManager.showNotification(summaryEntry);
// Trigger a transfer of heads up state from summary to child.
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
- when(summaryEntry.row.isHeadsUp()).thenReturn(false);
- when(childEntry.row.isHeadsUp()).thenReturn(true);
// Add second child notification so that summary is no longer suppressed.
mGroupManager.onEntryAdded(childEntry2);
// The heads up state should transfer back to the summary as there is now more than one
// child and the summary should no longer be suppressed.
- assertTrue(mHeadsUpManager.contains(summaryEntry.key));
- assertFalse(mHeadsUpManager.contains(childEntry.key));
+ assertTrue(mHeadsUpManager.isAlerting(summaryEntry.key));
+ assertFalse(mHeadsUpManager.isAlerting(childEntry.key));
+ }
+
+ @Test
+ public void testSuppressedSummaryAmbientPulseTransfersToChild() {
+ mGroupManager.setDozing(true);
+ NotificationData.Entry summaryEntry = createSummaryNotification();
+ when(mAmbientPulseManager.isAlerting(summaryEntry.key)).thenReturn(true);
+ NotificationData.Entry childEntry = createChildNotification();
+
+ // Summary will be suppressed because there is only one child.
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ // A suppressed summary should transfer its ambient state to the child.
+ verify(mAmbientPulseManager, never()).showNotification(summaryEntry);
+ verify(mAmbientPulseManager).showNotification(childEntry);
+ }
+
+ @Test
+ public void testSuppressedSummaryAmbientPulseTransfersToChildButBackAgain() {
+ mGroupManager.setDozing(true);
+ mAmbientPulseManager = new AmbientPulseManager(mContext);
+ mDependency.injectTestDependency(AmbientPulseManager.class, mAmbientPulseManager);
+ initializeGroupManager();
+ NotificationData.Entry summaryEntry =
+ createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
+ NotificationData.Entry childEntry =
+ createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+ NotificationData.Entry childEntry2 =
+ createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+ mAmbientPulseManager.showNotification(summaryEntry);
+ // Trigger a transfer of ambient state from summary to child.
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ // Add second child notification so that summary is no longer suppressed.
+ mGroupManager.onEntryAdded(childEntry2);
+
+ // The ambient state should transfer back to the summary as there is now more than one
+ // child and the summary should no longer be suppressed.
+ assertTrue(mAmbientPulseManager.isAlerting(summaryEntry.key));
+ assertFalse(mAmbientPulseManager.isAlerting(childEntry.key));
}
private NotificationData.Entry createSummaryNotification() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index cbba251..5006b0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -358,7 +358,7 @@
}
@Test
- public void testShouldPeek_nonSuppressedGroupSummary() {
+ public void testShouldHeadsUp_nonSuppressedGroupSummary() {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
when(mNotificationData.shouldSuppressStatusBar(any())).thenReturn(false);
@@ -375,11 +375,11 @@
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- assertTrue(mEntryManager.shouldPeek(entry, sbn));
+ assertTrue(mEntryManager.shouldHeadsUp(entry));
}
@Test
- public void testShouldPeek_suppressedGroupSummary() {
+ public void testShouldHeadsUp_suppressedGroupSummary() {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
when(mNotificationData.shouldSuppressStatusBar(any())).thenReturn(false);
@@ -396,11 +396,11 @@
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- assertFalse(mEntryManager.shouldPeek(entry, sbn));
+ assertFalse(mEntryManager.shouldHeadsUp(entry));
}
@Test
- public void testShouldPeek_suppressedPeek() {
+ public void testShouldHeadsUp_suppressedHeadsUp() {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
@@ -414,11 +414,11 @@
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- assertFalse(mEntryManager.shouldPeek(entry, sbn));
+ assertFalse(mEntryManager.shouldHeadsUp(entry));
}
@Test
- public void testShouldPeek_noSuppressedPeek() {
+ public void testShouldHeadsUp_noSuppressedHeadsUp() {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
@@ -432,31 +432,31 @@
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- assertTrue(mEntryManager.shouldPeek(entry, sbn));
+ assertTrue(mEntryManager.shouldHeadsUp(entry));
}
@Test
- public void testPeek_disabledStatusBar() {
+ public void testHeadsUp_disabledStatusBar() {
Notification n = new Notification.Builder(getContext(), "a").build();
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
mStatusBar.disable(StatusBarManager.DISABLE_EXPAND, 0, false /* animate */);
- assertFalse("The panel shouldn't allow peek while disabled",
- mStatusBar.shouldPeek(entry, sbn));
+ assertFalse("The panel shouldn't allow heads up while disabled",
+ mStatusBar.canHeadsUp(entry, sbn));
}
@Test
- public void testPeek_disabledNotificationShade() {
+ public void testHeadsUp_disabledNotificationShade() {
Notification n = new Notification.Builder(getContext(), "a").build();
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
mStatusBar.disable(0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */);
- assertFalse("The panel shouldn't allow peek while notitifcation shade disabled",
- mStatusBar.shouldPeek(entry, sbn));
+ assertFalse("The panel shouldn't allow heads up while notification shade disabled",
+ mStatusBar.canHeadsUp(entry, sbn));
}
@Test
@@ -472,7 +472,7 @@
}
@Test
- public void testPanelOpenForPeek() {
+ public void testPanelOpenForHeadsUp() {
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
when(mNotificationData.getActiveNotifications()).thenReturn(mNotificationList);
when(mNotificationList.size()).thenReturn(5);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java
index f8223f6..f7a7e04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -83,4 +84,16 @@
& WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
assertThat(flag).isEqualTo(0);
}
+
+ @Test
+ public void testOnThemeChanged_doesntCrash() {
+ mStatusBarWindowController = new StatusBarWindowController(mContext, mWindowManager,
+ mActivityManager, mDozeParameters);
+ mStatusBarWindowController.onThemeChanged();
+ }
+
+ @Test
+ public void testAdd_updatesVisibilityFlags() {
+ verify(mStatusBarView).setSystemUiVisibility(anyInt());
+ }
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index b71d7a7..da52d40 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1834,7 +1834,7 @@
if (provider.widgets.isEmpty()) {
// cancel the future updates
- cancelBroadcasts(provider);
+ cancelBroadcastsLocked(provider);
// send the broacast saying that the provider is not in use any more
sendDisabledIntentLocked(provider);
@@ -1843,18 +1843,16 @@
}
}
- private void cancelBroadcasts(Provider provider) {
+ private void cancelBroadcastsLocked(Provider provider) {
if (DEBUG) {
- Slog.i(TAG, "cancelBroadcasts() for " + provider);
+ Slog.i(TAG, "cancelBroadcastsLocked() for " + provider);
}
if (provider.broadcast != null) {
- mAlarmManager.cancel(provider.broadcast);
- long token = Binder.clearCallingIdentity();
- try {
- provider.broadcast.cancel();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ final PendingIntent broadcast = provider.broadcast;
+ mSaveStateHandler.post(() -> {
+ mAlarmManager.cancel(broadcast);
+ broadcast.cancel();
+ });
provider.broadcast = null;
}
}
@@ -2315,7 +2313,7 @@
mProviders.remove(provider);
// no need to send the DISABLE broadcast, since the receiver is gone anyway
- cancelBroadcasts(provider);
+ cancelBroadcastsLocked(provider);
}
private void sendEnableIntentLocked(Provider p) {
@@ -2369,17 +2367,14 @@
Binder.restoreCallingIdentity(token);
}
if (!alreadyRegistered) {
- long period = provider.info.updatePeriodMillis;
- if (period < MIN_UPDATE_PERIOD) {
- period = MIN_UPDATE_PERIOD;
- }
- final long oldId = Binder.clearCallingIdentity();
- try {
+ // Set the alarm outside of our locks; we've latched the first-time
+ // invariant and established the PendingIntent safely.
+ final long period = Math.max(provider.info.updatePeriodMillis, MIN_UPDATE_PERIOD);
+ final PendingIntent broadcast = provider.broadcast;
+ mSaveStateHandler.post(() ->
mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + period, period, provider.broadcast);
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
+ SystemClock.elapsedRealtime() + period, period, broadcast)
+ );
}
}
}
@@ -3382,7 +3377,7 @@
// Reschedule for the new updatePeriodMillis (don't worry about handling
// it specially if updatePeriodMillis didn't change because we just sent
// an update, and the next one will be updatePeriodMillis from now).
- cancelBroadcasts(provider);
+ cancelBroadcastsLocked(provider);
registerForBroadcastsLocked(provider, appWidgetIds);
// If it's currently showing, call back with the new
// AppWidgetProviderInfo.
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index ad80cc26..2671327 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -587,6 +587,7 @@
}
final RemoteFillService remoteService = getService();
if (remoteService != null) {
+ if (sVerbose) Slog.v(LOG_TAG, "calling onFillRequest() for id=" + mRequest.getId());
try {
remoteService.mAutoFillService.onFillRequest(mRequest, mCallback);
} catch (RemoteException e) {
@@ -659,6 +660,7 @@
public void run() {
final RemoteFillService remoteService = getService();
if (remoteService != null) {
+ if (sVerbose) Slog.v(LOG_TAG, "calling onSaveRequest()");
try {
remoteService.mAutoFillService.onSaveRequest(mRequest, mCallback);
} catch (RemoteException e) {
diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
index 9af952d..c933833 100644
--- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
@@ -105,7 +105,7 @@
try {
IBackupTransport transport =
transportClient.connectOrThrow(
- "AppBackupUtils.appIsEligibleForBackupAtRuntime");
+ "AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport");
return transport.isAppEligibleForBackup(
packageInfo, AppBackupUtils.appGetsFullBackup(packageInfo));
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index 51d95a2..872261a 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -202,6 +202,15 @@
reset();
pw.println("binder_calls_stats reset.");
return;
+ } else if ("--enable".equals(arg)) {
+ Binder.setObserver(mBinderCallsStats);
+ return;
+ } else if ("--disable".equals(arg)) {
+ Binder.setObserver(null);
+ return;
+ } else if ("--no-sampling".equals(arg)) {
+ mBinderCallsStats.setSamplingInterval(1);
+ return;
} else if ("--enable-detailed-tracking".equals(arg)) {
SystemProperties.set(PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, "1");
mBinderCallsStats.setDetailedTracking(true);
@@ -215,6 +224,9 @@
} else if ("-h".equals(arg)) {
pw.println("binder_calls_stats commands:");
pw.println(" --reset: Reset stats");
+ pw.println(" --enable: Enable tracking binder calls");
+ pw.println(" --disable: Disables tracking binder calls");
+ pw.println(" --no-sampling: Tracks all calls");
pw.println(" --enable-detailed-tracking: Enables detailed tracking");
pw.println(" --disable-detailed-tracking: Disables detailed tracking");
return;
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 380f6a7..a69d416 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -1490,23 +1490,19 @@
}
}
- private static final String TUNNEL_OP = "STOPSHIP"; // = AppOpsManager.OP_MANAGE_IPSEC_TUNNELS;
+ private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
private void enforceTunnelPermissions(String callingPackage) {
checkNotNull(callingPackage, "Null calling package cannot create IpSec tunnels");
- if (false) { // STOPSHIP if this line is present
- switch (getAppOpsManager().noteOp(
- TUNNEL_OP,
- Binder.getCallingUid(), callingPackage)) {
- case AppOpsManager.MODE_DEFAULT:
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
- break;
- case AppOpsManager.MODE_ALLOWED:
- return;
- default:
- throw new SecurityException("Request to ignore AppOps for non-legacy API");
- }
+ switch (getAppOpsManager().noteOp(TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
+ case AppOpsManager.MODE_DEFAULT:
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
+ break;
+ case AppOpsManager.MODE_ALLOWED:
+ return;
+ default:
+ throw new SecurityException("Request to ignore AppOps for non-legacy API");
}
}
diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
index ab7bf28..ee01d86 100644
--- a/services/core/java/com/android/server/LooperStatsService.java
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -189,6 +189,10 @@
} else if ("reset".equals(cmd)) {
mStats.reset();
return 0;
+ } else if ("sampling_interval".equals(cmd)) {
+ int sampling = Integer.parseUnsignedInt(getNextArgRequired());
+ setSamplingInterval(sampling);
+ return 0;
} else {
return handleDefaultCommands(cmd);
}
@@ -198,9 +202,10 @@
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
pw.println(LOOPER_STATS_SERVICE_NAME + " commands:");
- pw.println(" enable: Enable collecting stats");
- pw.println(" disable: Disable collecting stats");
- pw.println(" reset: Reset stats");
+ pw.println(" enable: Enable collecting stats.");
+ pw.println(" disable: Disable collecting stats.");
+ pw.println(" sampling_interval: Change the sampling interval.");
+ pw.println(" reset: Reset stats.");
}
}
}
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index cb03255..f436286 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -42,31 +42,25 @@
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCommand;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.dreams.Sandman;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
-import android.text.TextUtils;
import android.util.Slog;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-
import com.android.internal.R;
import com.android.internal.app.DisableCarModeActivity;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.DumpUtils;
-import com.android.server.power.ShutdownThread;
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
final class UiModeManagerService extends SystemService {
private static final String TAG = UiModeManager.class.getSimpleName();
private static final boolean LOG = false;
@@ -466,7 +460,7 @@
uiMode |= mNightMode << 4;
}
- if (mPowerSave && !mNightModeLocked) {
+ if (mPowerSave) {
uiMode &= ~Configuration.UI_MODE_NIGHT_NO;
uiMode |= Configuration.UI_MODE_NIGHT_YES;
}
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 1866420..01421c7 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -126,11 +126,6 @@
private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
- @VisibleForTesting
- ActivityDisplay(ActivityStackSupervisor supervisor, int displayId) {
- this(supervisor, supervisor.mDisplayManager.getDisplay(displayId));
- }
-
ActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
mSupervisor = supervisor;
mDisplayId = display.getDisplayId();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7533db1..b898a90 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8406,10 +8406,17 @@
}
}
+ /** @deprecated - Use {@link #removeContentProviderExternalAsUser} which takes a user ID. */
+ @Deprecated
+ @Override
public void removeContentProviderExternal(String name, IBinder token) {
+ removeContentProviderExternalAsUser(name, token, UserHandle.getCallingUserId());
+ }
+
+ @Override
+ public void removeContentProviderExternalAsUser(String name, IBinder token, int userId) {
enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
"Do not have permission in call removeContentProviderExternal()");
- int userId = UserHandle.getCallingUserId();
long ident = Binder.clearCallingIdentity();
try {
removeContentProviderExternalUnchecked(name, token, userId);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 628207c..2199bb7 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -17,18 +17,18 @@
package com.android.server.am;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
import static android.app.ActivityOptions.ANIM_CUSTOM;
+import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION;
import static android.app.ActivityOptions.ANIM_SCALE_UP;
import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
-import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
@@ -81,6 +81,8 @@
import static android.os.Build.VERSION_CODES.O;
import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
+
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SAVED_STATE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
@@ -93,6 +95,13 @@
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
+import static com.android.server.am.ActivityRecordProto.IDENTIFIER;
+import static com.android.server.am.ActivityRecordProto.PROC_ID;
+import static com.android.server.am.ActivityRecordProto.STATE;
+import static com.android.server.am.ActivityRecordProto.TRANSLUCENT;
+import static com.android.server.am.ActivityRecordProto.VISIBLE;
import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING;
import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
@@ -110,14 +119,6 @@
import static com.android.server.am.TaskPersister.DEBUG;
import static com.android.server.am.TaskPersister.IMAGE_EXTENSION;
import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
-import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
-import static com.android.server.am.ActivityRecordProto.IDENTIFIER;
-import static com.android.server.am.ActivityRecordProto.PROC_ID;
-import static com.android.server.am.ActivityRecordProto.STATE;
-import static com.android.server.am.ActivityRecordProto.TRANSLUCENT;
-import static com.android.server.am.ActivityRecordProto.VISIBLE;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -132,6 +133,7 @@
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.app.ResultInfo;
+import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ActivityRelaunchItem;
import android.app.servertransaction.ClientTransaction;
@@ -143,7 +145,6 @@
import android.app.servertransaction.PipModeChangeItem;
import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.WindowVisibilityItem;
-import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -1279,20 +1280,14 @@
/**
* Check whether this activity can be launched on the specified display.
+ *
* @param displayId Target display id.
- * @return {@code true} if either it is the default display or this activity is resizeable and
- * can be put a secondary screen.
+ * @return {@code true} if either it is the default display or this activity can be put on a
+ * secondary screen.
*/
boolean canBeLaunchedOnDisplay(int displayId) {
- final TaskRecord task = getTask();
-
- // The resizeability of an Activity's parent task takes precendence over the ActivityInfo.
- // This allows for a non resizable activity to be launched into a resizeable task.
- final boolean resizeable =
- task != null ? task.isResizeable() : supportsResizeableMultiWindow();
-
- return service.mStackSupervisor.canPlaceEntityOnDisplay(displayId,
- resizeable, launchedFromPid, launchedFromUid, info);
+ return service.mStackSupervisor.canPlaceEntityOnDisplay(displayId, launchedFromPid,
+ launchedFromUid, info);
}
/**
@@ -2495,6 +2490,14 @@
}
}
+ /**
+ * @return {@code true} if this activity was reparented to another display but
+ * {@link #ensureActivityConfiguration} is not called.
+ */
+ boolean shouldUpdateConfigForDisplayChanged() {
+ return mLastReportedDisplayId != getDisplayId();
+ }
+
boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow) {
return ensureActivityConfiguration(globalChanges, preserveWindow,
false /* ignoreStopState */);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index aa4e68d..91e677b 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -5375,12 +5375,14 @@
display.positionChildAtTop(this, false /* includingParents */);
}
+ /** NOTE: Should only be called from {@link TaskRecord#reparent}. */
void moveToFrontAndResumeStateIfNeeded(ActivityRecord r, boolean moveToFront, boolean setResume,
boolean setPause, String reason) {
if (!moveToFront) {
return;
}
+ final ActivityState origState = r.getState();
// If the activity owns the last resumed activity, transfer that together,
// so that we don't resume the same activity again in the new stack.
// Apps may depend on onResume()/onPause() being called in pairs.
@@ -5393,9 +5395,14 @@
mPausingActivity = r;
schedulePauseTimeout(r);
}
- // Move the stack in which we are placing the activity to the front. The call will also
- // make sure the activity focus is set.
+ // Move the stack in which we are placing the activity to the front.
moveToFront(reason);
+ // If the original state is resumed, there is no state change to update focused app.
+ // So here makes sure the activity focus is set if it is the top.
+ if (origState == RESUMED && r == mStackSupervisor.getTopResumedActivity()) {
+ // TODO(b/111361570): Support multiple focused apps in WM
+ mService.setResumedActivityUncheckLocked(r, reason);
+ }
}
public int getStackId() {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 310898e..d096fed 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -488,8 +488,8 @@
}
/** Check if placing task or activity on specified display is allowed. */
- boolean canPlaceEntityOnDisplay(int displayId, boolean resizeable, int callingPid,
- int callingUid, ActivityInfo activityInfo) {
+ boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid,
+ ActivityInfo activityInfo) {
if (displayId == DEFAULT_DISPLAY) {
// No restrictions for the default display.
return true;
@@ -498,10 +498,6 @@
// Can't launch on secondary displays if feature is not supported.
return false;
}
- if (!resizeable && !displayConfigMatchesGlobal(displayId)) {
- // Can't apply wrong configuration to non-resizeable activities.
- return false;
- }
if (!isCallerAllowedToLaunchOnDisplay(callingPid, callingUid, displayId, activityInfo)) {
// Can't place activities to a display that has restricted launch rules.
// In this case the request should be made by explicitly adding target display id and
@@ -690,7 +686,7 @@
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
final Display[] displays = mDisplayManager.getDisplays();
- for (int displayNdx = displays.length - 1; displayNdx >= 0; --displayNdx) {
+ for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
final Display display = displays[displayNdx];
final ActivityDisplay activityDisplay = new ActivityDisplay(this, display);
if (activityDisplay.mDisplayId == DEFAULT_DISPLAY) {
@@ -4530,11 +4526,14 @@
mService.setTaskWindowingMode(task.taskId,
WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, true /* toTop */);
if (preferredDisplayId != actualDisplayId) {
- // Display a warning toast that we tried to put a non-resizeable task on a secondary
- // display with config different from global config.
+ Slog.w(TAG, "Failed to put " + task + " on display " + preferredDisplayId);
+ // Display a warning toast that we failed to put a task on a secondary display.
mService.getTaskChangeNotificationController()
.notifyActivityLaunchOnSecondaryDisplayFailed();
return;
+ } else if (!forceNonResizable && handleForcedResizableTask(task,
+ FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY)) {
+ return;
}
}
@@ -4554,16 +4553,23 @@
return;
}
+ handleForcedResizableTask(task, FORCED_RESIZEABLE_REASON_SPLIT_SCREEN);
+ }
+
+ /**
+ * @return {@code true} if the top activity of the task is forced to be resizable and the user
+ * was notified about activity being forced resized.
+ */
+ private boolean handleForcedResizableTask(TaskRecord task, int reason) {
final ActivityRecord topActivity = task.getTopActivity();
if (topActivity != null && topActivity.isNonResizableOrForcedResizable()
- && !topActivity.noDisplay) {
+ && !topActivity.noDisplay) {
final String packageName = topActivity.appInfo.packageName;
- final int reason = isSecondaryDisplayPreferred
- ? FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY
- : FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
mService.getTaskChangeNotificationController().notifyActivityForcedResizable(
task.taskId, reason, packageName);
+ return true;
}
+ return false;
}
void activityRelaunchedLocked(IBinder token) {
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index eb41fe7..7df255d 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -52,6 +52,7 @@
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
@@ -944,7 +945,8 @@
auxiliaryResponse == null ? null : auxiliaryResponse.filters);
}
- void postStartActivityProcessing(ActivityRecord r, int result, ActivityStack targetStack) {
+ void postStartActivityProcessing(ActivityRecord r, int result,
+ ActivityStack startedActivityStack) {
if (ActivityManager.isStartResultFatalError(result)) {
return;
}
@@ -956,14 +958,6 @@
// about this, so it waits for the new activity to become visible instead.
mSupervisor.reportWaitingActivityLaunchedIfNeeded(r, result);
- ActivityStack startedActivityStack = null;
- final ActivityStack currentStack = r.getStack();
- if (currentStack != null) {
- startedActivityStack = currentStack;
- } else if (mTargetStack != null) {
- startedActivityStack = targetStack;
- }
-
if (startedActivityStack == null) {
return;
}
@@ -1239,23 +1233,41 @@
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
ActivityRecord[] outActivity) {
int result = START_CANCELED;
+ final ActivityStack startedActivityStack;
try {
mService.mWindowManager.deferSurfaceLayout();
result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, doResume, options, inTask, outActivity);
} finally {
- // If we are not able to proceed, disassociate the activity from the task. Leaving an
- // activity in an incomplete state can lead to issues, such as performing operations
- // without a window container.
- final ActivityStack stack = mStartActivity.getStack();
- if (!ActivityManager.isStartResultSuccessful(result) && stack != null) {
- stack.finishActivityLocked(mStartActivity, RESULT_CANCELED,
- null /* intentResultData */, "startActivity", true /* oomAdj */);
+ final ActivityStack currentStack = r.getStack();
+ startedActivityStack = currentStack != null ? currentStack : mTargetStack;
+
+ if (ActivityManager.isStartResultSuccessful(result)) {
+ if (startedActivityStack != null) {
+ // If there is no state change (e.g. a resumed activity is reparented to
+ // top of another display) to trigger a visibility/configuration checking,
+ // we have to update the configuration for changing to different display.
+ final ActivityRecord currentTop =
+ startedActivityStack.topRunningActivityLocked();
+ if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) {
+ mSupervisor.ensureVisibilityAndConfig(currentTop, currentTop.getDisplayId(),
+ true /* markFrozenIfConfigChanged */, false /* deferResume */);
+ }
+ }
+ } else {
+ // If we are not able to proceed, disassociate the activity from the task.
+ // Leaving an activity in an incomplete state can lead to issues, such as
+ // performing operations without a window container.
+ final ActivityStack stack = mStartActivity.getStack();
+ if (stack != null) {
+ stack.finishActivityLocked(mStartActivity, RESULT_CANCELED,
+ null /* intentResultData */, "startActivity", true /* oomAdj */);
+ }
}
mService.mWindowManager.continueSurfaceLayout();
}
- postStartActivityProcessing(r, result, mTargetStack);
+ postStartActivityProcessing(r, result, startedActivityStack);
return result;
}
@@ -1901,14 +1913,20 @@
// except... well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have
// the same behavior as if a new instance was being started, which means not bringing it
// to the front if the caller is not itself in the front.
- final ActivityStack focusStack = mSupervisor.getTopDisplayFocusedStack();
- ActivityRecord curTop = (focusStack == null)
- ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
+ final boolean differentTopTask;
+ if (mPreferredDisplayId == mTargetStack.mDisplayId) {
+ final ActivityStack focusStack = mTargetStack.getDisplay().getFocusedStack();
+ final ActivityRecord curTop = (focusStack == null)
+ ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
+ final TaskRecord topTask = curTop != null ? curTop.getTask() : null;
+ differentTopTask = topTask != null
+ && (topTask != intentActivity.getTask() || topTask != focusStack.topTask());
+ } else {
+ // The existing task should always be different from those in other displays.
+ differentTopTask = true;
+ }
- final TaskRecord topTask = curTop != null ? curTop.getTask() : null;
- if (topTask != null
- && (topTask != intentActivity.getTask() || topTask != focusStack.topTask())
- && !mAvoidMoveToFront) {
+ if (differentTopTask && !mAvoidMoveToFront) {
mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
if (mSourceRecord == null || (mSourceStack.getTopActivity() != null &&
mSourceStack.getTopActivity().getTask() == mSourceRecord.getTask())) {
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index 212844a..6935703d 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -4688,11 +4688,7 @@
return mSleeping;
}
- /**
- * Update AMS states when an activity is resumed. This should only be called by
- * {@link ActivityStack#onActivityStateChanged(
- * ActivityRecord, ActivityStack.ActivityState, String)} when an activity is resumed.
- */
+ /** Update AMS states when an activity is resumed. */
void setResumedActivityUncheckLocked(ActivityRecord r, String reason) {
final TaskRecord task = r.getTask();
if (task.isActivityTypeStandard()) {
diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java
index f9dccea0..aad890b 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.os.FileUtils;
+import android.os.SystemProperties;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -38,6 +39,10 @@
final class MemoryStatUtil {
private static final String TAG = TAG_WITH_CLASS_NAME ? "MemoryStatUtil" : TAG_AM;
+ /** True if device has per-app memcg */
+ private static final Boolean DEVICE_HAS_PER_APP_MEMCG =
+ SystemProperties.getBoolean("ro.config.per_app_memcg", false);
+
/** Path to check if device has memcg */
private static final String MEMCG_TEST_PATH = "/dev/memcg/apps/memory.stat";
/** Path to memory stat file for logging app start memory state */
@@ -55,15 +60,12 @@
private static final int PGMAJFAULT_INDEX = 11;
private static final int RSS_IN_BYTES_INDEX = 23;
- /** True if device has memcg */
- private static volatile Boolean sDeviceHasMemCg;
-
private MemoryStatUtil() {}
/**
* Reads memory stat for a process.
*
- * Reads from memcg if available on device, else fallback to procfs.
+ * Reads from per-app memcg if available on device, else fallback to procfs.
* Returns null if no stats can be read.
*/
@Nullable
@@ -156,15 +158,10 @@
}
/**
- * Checks if memcg is available on device.
- *
- * Touches the filesystem to do the check.
+ * Returns whether per-app memcg is available on device.
*/
static boolean hasMemcg() {
- if (sDeviceHasMemCg == null) {
- sDeviceHasMemCg = (new File(MEMCG_TEST_PATH)).exists();
- }
- return sDeviceHasMemCg;
+ return DEVICE_HAS_PER_APP_MEMCG;
}
static final class MemoryStat {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 7256e23..9b42d65 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1521,14 +1521,14 @@
/**
* Check whether this task can be launched on the specified display.
+ *
* @param displayId Target display id.
- * @return {@code true} if either it is the default display or this activity is resizeable and
- * can be put a secondary screen.
+ * @return {@code true} if either it is the default display or this activity can be put on a
+ * secondary display.
*/
boolean canBeLaunchedOnDisplay(int displayId) {
return mService.mStackSupervisor.canPlaceEntityOnDisplay(displayId,
- isResizeable(false /* checkSupportsPip */), -1 /* don't check PID */,
- -1 /* don't check UID */, null /* activityInfo */);
+ -1 /* don't check PID */, -1 /* don't check UID */, null /* activityInfo */);
}
/**
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index 36e7cba..6b09f1b 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -39,12 +39,17 @@
public abstract int handleFailedAttempt();
public abstract void resetFailedAttempts();
+ public abstract String getErrorString(int error, int vendorCode);
+ public abstract String getAcquiredString(int acquireInfo, int vendorCode);
+ /**
+ * @return one of {@link #TYPE_FINGERPRINT} {@link #TYPE_IRIS} or {@link #TYPE_FACE}
+ */
+ public abstract int getBiometricType();
public static final int LOCKOUT_NONE = 0;
public static final int LOCKOUT_TIMED = 1;
public static final int LOCKOUT_PERMANENT = 2;
- private final BiometricAuthenticator mAuthenticator;
// Callback mechanism received from the client
// (BiometricPrompt -> BiometricPromptService -> <Biometric>Service -> AuthenticationClient)
private IBiometricPromptReceiver mDialogReceiverFromClient;
@@ -88,15 +93,13 @@
BiometricService.DaemonWrapper daemon, long halDeviceId, IBinder token,
BiometricService.ServiceListener listener, int targetUserId, int groupId, long opId,
boolean restricted, String owner, Bundle bundle,
- IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService,
- BiometricAuthenticator authenticator) {
+ IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService) {
super(context, metrics, daemon, halDeviceId, token, listener, targetUserId, groupId,
restricted, owner);
mOpId = opId;
mBundle = bundle;
mDialogReceiverFromClient = dialogReceiver;
mStatusBarService = statusBarService;
- mAuthenticator = authenticator;
mHandler = new Handler(Looper.getMainLooper());
}
@@ -115,8 +118,7 @@
if (mBundle != null) {
try {
if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
- mStatusBarService.onBiometricHelp(
- mAuthenticator.getAcquiredString(acquiredInfo, vendorCode));
+ mStatusBarService.onBiometricHelp(getAcquiredString(acquiredInfo, vendorCode));
}
return false; // acquisition continues
} catch (RemoteException e) {
@@ -144,8 +146,7 @@
}
if (mBundle != null) {
try {
- mStatusBarService.onBiometricError(
- mAuthenticator.getErrorString(error, vendorCode));
+ mStatusBarService.onBiometricError(getErrorString(error, vendorCode));
} catch (RemoteException e) {
Slog.e(getLogTag(), "Remote exception when sending error", e);
}
@@ -220,7 +221,7 @@
// Send the lockout message to the system dialog
if (mBundle != null) {
mStatusBarService.onBiometricError(
- mAuthenticator.getErrorString(errorCode, 0 /* vendorCode */));
+ getErrorString(errorCode, 0 /* vendorCode */));
mHandler.postDelayed(() -> {
try {
listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
@@ -268,7 +269,7 @@
if (mBundle != null) {
try {
mStatusBarService.showBiometricDialog(mBundle, mDialogReceiver,
- mAuthenticator.getType());
+ getBiometricType());
} catch (RemoteException e) {
Slog.e(getLogTag(), "Unable to show biometric dialog", e);
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index a181b61..73c4223 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -108,6 +108,8 @@
private ClientMonitor mPendingClient;
private PerformanceStats mPerformanceStats;
protected int mCurrentUserId = UserHandle.USER_NULL;
+ // Tracks if the current authentication makes use of CryptoObjects.
+ protected boolean mIsCrypto;
// Normal authentications are tracked by mPerformanceMap.
protected HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>();
// Transactions that make use of CryptoObjects are tracked by mCryptoPerformaceMap.
@@ -217,16 +219,16 @@
*/
protected void notifyClientActiveCallbacks(boolean isActive) {}
- protected class AuthenticationClientImpl extends AuthenticationClient {
+ protected abstract class AuthenticationClientImpl extends AuthenticationClient {
public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId,
boolean restricted, String owner, Bundle bundle,
IBiometricPromptReceiver dialogReceiver,
- IStatusBarService statusBarService, BiometricAuthenticator authenticator) {
+ IStatusBarService statusBarService) {
super(context, getMetrics(), daemon, halDeviceId, token, listener,
targetUserId, groupId, opId, restricted, owner, bundle, dialogReceiver,
- statusBarService, authenticator);
+ statusBarService);
}
@Override
@@ -715,6 +717,7 @@
pmap.put(mCurrentUserId, stats);
}
mPerformanceStats = stats;
+ mIsCrypto = (opId != 0);
startAuthentication(client, opPackageName);
});
@@ -847,7 +850,7 @@
return mKeyguardPackage.equals(clientPackage);
}
- private int getLockoutMode() {
+ protected int getLockoutMode() {
final int currentUser = ActivityManager.getCurrentUser();
final int failedAttempts = mFailedAttempts.get(currentUser, 0);
if (failedAttempts >= getFailedAttemptsLockoutPermanent()) {
@@ -1109,4 +1112,4 @@
LockoutResetMonitor monitor) {
mLockoutMonitors.remove(monitor);
}
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index 8afac97..660710e 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -49,6 +49,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.biometrics.BiometricService;
@@ -82,6 +83,33 @@
private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 3;
private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 12;
+ private final class FaceAuthClient extends AuthenticationClientImpl {
+ public FaceAuthClient(Context context,
+ DaemonWrapper daemon, long halDeviceId, IBinder token,
+ ServiceListener listener, int targetUserId, int groupId, long opId,
+ boolean restricted, String owner, Bundle bundle,
+ IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService) {
+ super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
+ restricted,
+ owner, bundle, dialogReceiver, statusBarService);
+ }
+
+ @Override
+ public String getErrorString(int error, int vendorCode) {
+ return FaceManager.getErrorString(getContext(), error, vendorCode);
+ }
+
+ @Override
+ public String getAcquiredString(int acquireInfo, int vendorCode) {
+ return FaceManager.getAcquiredString(getContext(), acquireInfo, vendorCode);
+ }
+
+ @Override
+ public int getBiometricType() {
+ return BiometricAuthenticator.TYPE_FACE;
+ }
+ }
+
/**
* Receives the incoming binder calls from FaceManager.
*/
@@ -128,10 +156,10 @@
final String opPackageName) {
checkPermission(USE_BIOMETRIC_INTERNAL);
final boolean restricted = isRestricted();
- final AuthenticationClientImpl client = new AuthenticationClientImpl(getContext(),
+ final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
- null /* bundle */, null /* dialogReceiver */, mStatusBarService, mFaceManager);
+ null /* bundle */, null /* dialogReceiver */, mStatusBarService);
authenticateInternal(client, opId, opPackageName);
}
@@ -143,11 +171,11 @@
int callingUid, int callingPid, int callingUserId) {
checkPermission(USE_BIOMETRIC_INTERNAL);
final boolean restricted = true; // BiometricPrompt is always restricted
- final AuthenticationClientImpl client = new AuthenticationClientImpl(getContext(),
+ final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token,
new BiometricPromptServiceListenerImpl(receiver),
mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
- bundle, dialogReceiver, mStatusBarService, mFaceManager);
+ bundle, dialogReceiver, mStatusBarService);
authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
callingUserId);
}
@@ -338,8 +366,8 @@
*/
if (mBiometricPromptServiceReceiver != null) {
mBiometricPromptServiceReceiver.onAcquired(deviceId,
- mFaceManager.getMappedAcquiredInfo(acquiredInfo, vendorCode),
- mFaceManager.getAcquiredString(acquiredInfo, vendorCode));
+ FaceManager.getMappedAcquiredInfo(acquiredInfo, vendorCode),
+ FaceManager.getAcquiredString(getContext(), acquiredInfo, vendorCode));
}
}
@@ -362,7 +390,7 @@
public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
if (mBiometricPromptServiceReceiver != null) {
mBiometricPromptServiceReceiver.onError(deviceId, error,
- mFaceManager.getErrorString(error, vendorCode));
+ FaceManager.getErrorString(getContext(), error, vendorCode));
}
}
}
@@ -447,8 +475,6 @@
@GuardedBy("this")
private IBiometricsFace mDaemon;
private long mHalDeviceId;
- // Use FaceManager to get strings, so BiometricPrompt interface is cleaner
- private FaceManager mFaceManager;
/**
* Receives callbacks from the HAL.
@@ -589,7 +615,6 @@
super.onStart();
publishBinderService(Context.FACE_SERVICE, new FaceServiceWrapper());
SystemServerInitThreadPool.get().submit(this::getFaceDaemon, TAG + ".onStart");
- mFaceManager = (FaceManager) getContext().getSystemService(Context.FACE_SERVICE);
}
@Override
@@ -862,4 +887,4 @@
mPerformanceMap.clear();
mCryptoPerformanceMap.clear();
}
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index 95fb9e3..9f4fff8 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -50,12 +50,15 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
+import android.util.StatsLog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemServerInitThreadPool;
+import com.android.server.biometrics.AuthenticationClient;
import com.android.server.biometrics.BiometricService;
import com.android.server.biometrics.BiometricUtils;
import com.android.server.biometrics.ClientMonitor;
@@ -102,6 +105,33 @@
}
}
+ private final class FingerprintAuthClient extends AuthenticationClientImpl {
+ public FingerprintAuthClient(Context context,
+ DaemonWrapper daemon, long halDeviceId, IBinder token,
+ ServiceListener listener, int targetUserId, int groupId, long opId,
+ boolean restricted, String owner, Bundle bundle,
+ IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService) {
+ super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
+ restricted,
+ owner, bundle, dialogReceiver, statusBarService);
+ }
+
+ @Override
+ public String getErrorString(int error, int vendorCode) {
+ return FingerprintManager.getErrorString(getContext(), error, vendorCode);
+ }
+
+ @Override
+ public String getAcquiredString(int acquireInfo, int vendorCode) {
+ return FingerprintManager.getAcquiredString(getContext(), acquireInfo, vendorCode);
+ }
+
+ @Override
+ public int getBiometricType() {
+ return BiometricAuthenticator.TYPE_FINGERPRINT;
+ }
+ }
+
/**
* Receives the incoming binder calls from FingerprintManager.
*/
@@ -149,10 +179,10 @@
final IFingerprintServiceReceiver receiver, final int flags,
final String opPackageName) {
final boolean restricted = isRestricted();
- final AuthenticationClientImpl client = new AuthenticationClientImpl(getContext(),
+ final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
mCurrentUserId, groupId, opId, restricted, opPackageName, null /* bundle */,
- null /* dialogReceiver */, mStatusBarService, mFingerprintManager);
+ null /* dialogReceiver */, mStatusBarService);
authenticateInternal(client, opId, opPackageName);
}
@@ -164,11 +194,11 @@
int callingUid, int callingPid, int callingUserId) {
checkPermission(MANAGE_BIOMETRIC);
final boolean restricted = true; // BiometricPrompt is always restricted
- final AuthenticationClientImpl client = new AuthenticationClientImpl(getContext(),
+ final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token,
new BiometricPromptServiceListenerImpl(receiver),
mCurrentUserId, groupId, opId, restricted, opPackageName, bundle,
- dialogReceiver, mStatusBarService, mFingerprintManager);
+ dialogReceiver, mStatusBarService);
authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
callingUserId);
}
@@ -371,7 +401,8 @@
throws RemoteException {
if (mBiometricPromptServiceReceiver != null) {
mBiometricPromptServiceReceiver.onAcquired(deviceId, acquiredInfo,
- mFingerprintManager.getAcquiredString(acquiredInfo, vendorCode));
+ FingerprintManager.getAcquiredString(
+ getContext(), acquiredInfo, vendorCode));
}
}
@@ -394,7 +425,7 @@
public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
if (mBiometricPromptServiceReceiver != null) {
mBiometricPromptServiceReceiver.onError(deviceId, error,
- mFingerprintManager.getErrorString(error, vendorCode));
+ FingerprintManager.getErrorString(getContext(), error, vendorCode));
}
}
}
@@ -567,8 +598,6 @@
private long mHalDeviceId;
private IBinder mToken = new Binder(); // used for internal FingerprintService enumeration
private ArrayList<UserFingerprint> mUnknownFingerprints = new ArrayList<>(); // hw fingerprints
- // Use FingerprintManager to get strings, so BiometricPrompt interface is cleaner.
- private FingerprintManager mFingerprintManager;
/**
* Receives callbacks from the HAL.
@@ -590,6 +619,11 @@
public void onAcquired(final long deviceId, final int acquiredInfo, final int vendorCode) {
mHandler.post(() -> {
FingerprintService.super.handleAcquired(deviceId, acquiredInfo, vendorCode);
+ if (getLockoutMode() == AuthenticationClient.LOCKOUT_NONE
+ && getCurrentClient() instanceof AuthenticationClient) {
+ // Ignore enrollment acquisitions or acquisitions when we are locked out.
+ StatsLog.write(StatsLog.FINGERPRINT_ACQUIRED, mCurrentUserId, mIsCrypto);
+ }
});
}
@@ -599,6 +633,22 @@
mHandler.post(() -> {
Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
FingerprintService.super.handleAuthenticated(fp, token);
+ // Send authentication to statsd.
+ final boolean authenticated = fingerId != 0;
+ StatsLog.write(StatsLog.FINGERPRINT_AUTHENTICATED, mCurrentUserId, mIsCrypto,
+ authenticated);
+ if (!authenticated) {
+ // If we failed to authenticate because of a lockout, inform statsd.
+ final int lockoutMode = getLockoutMode();
+ if (lockoutMode == AuthenticationClient.LOCKOUT_TIMED) {
+ StatsLog.write(StatsLog.FINGERPRINT_ERROR_OCCURRED, mCurrentUserId,
+ mIsCrypto, StatsLog.FINGERPRINT_ERROR_OCCURRED__ERROR__LOCKOUT);
+ } else if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
+ StatsLog.write(StatsLog.FINGERPRINT_ERROR_OCCURRED, mCurrentUserId,
+ mIsCrypto,
+ StatsLog.FINGERPRINT_ERROR_OCCURRED__ERROR__PERMANENT_LOCKOUT);
+ }
+ }
});
}
@@ -717,8 +767,6 @@
super.onStart();
publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
SystemServerInitThreadPool.get().submit(this::getFingerprintDaemon, TAG + ".onStart");
- mFingerprintManager = (FingerprintManager)
- getContext().getSystemService(Context.FINGERPRINT_SERVICE);
}
@Override
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index c028a43..873a8e3 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -639,12 +639,20 @@
}
}
- // Otherwise only focused applications can access the clipboard.
- boolean uidFocused = mWm.isUidFocused(callingUid);
- if (!uidFocused) {
- Slog.e(TAG, "Denying clipboard access to " + callingPackage
- + ", application is not in focus.");
+ switch (op) {
+ case AppOpsManager.OP_READ_CLIPBOARD:
+ // Clipboard can only be read by applications with focus.
+ boolean uidFocused = mWm.isUidFocused(callingUid);
+ if (!uidFocused) {
+ Slog.e(TAG, "Denying clipboard access to " + callingPackage
+ + ", application is not in focus.");
+ }
+ return uidFocused;
+ case AppOpsManager.OP_WRITE_CLIPBOARD:
+ // Writing is allowed without focus.
+ return true;
+ default:
+ throw new IllegalArgumentException("Unknown clipboard appop " + op);
}
- return uidFocused;
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index b5a9f74..3264790 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -18,45 +18,11 @@
import android.content.ComponentName;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodClient;
-
/**
* Input method manager local system service interface.
*/
public abstract class InputMethodManagerInternal {
/**
- * Called by the window manager service when a client process is being attached to the window
- * manager service.
- *
- * <p>The caller must not have WindowManagerService lock. This method internally acquires
- * InputMethodManagerService lock.</p>
- *
- * @param client {@link android.os.Binder} proxy that is associated with the singleton instance
- * of {@link android.view.inputmethod.InputMethodManager} that runs on the client
- * process
- * @param inputContext communication channel for the dummy
- * {@link android.view.inputmethod.InputConnection}
- * @param uid UID of the client process
- * @param pid PID of the client process
- */
- public abstract void addClient(IInputMethodClient client, IInputContext inputContext, int uid,
- int pid);
-
- /**
- * Called by the window manager service when a client process is being attached to the window
- * manager service.
- *
- * <p>The caller must not have WindowManagerService lock. This method internally acquires
- * InputMethodManagerService lock.</p>
- *
- * @param client {@link android.os.Binder} proxy that is associated with the singleton instance
- * of {@link android.view.inputmethod.InputMethodManager} that runs on the client
- * process
- */
- public abstract void removeClient(IInputMethodClient client);
-
- /**
* Called by the power manager to tell the input method manager whether it
* should start watching for wake events.
*/
@@ -78,15 +44,6 @@
public static final InputMethodManagerInternal NOP =
new InputMethodManagerInternal() {
@Override
- public void addClient(IInputMethodClient client, IInputContext inputContext,
- int uid, int pid) {
- }
-
- @Override
- public void removeClient(IInputMethodClient client) {
- }
-
- @Override
public void setInteractive(boolean interactive) {
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index b2287ac..6f5f90a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -401,12 +401,28 @@
}
}
+ private static final class ClientDeathRecipient implements IBinder.DeathRecipient {
+ private final InputMethodManagerService mImms;
+ private final IInputMethodClient mClient;
+
+ ClientDeathRecipient(InputMethodManagerService imms, IInputMethodClient client) {
+ mImms = imms;
+ mClient = client;
+ }
+
+ @Override
+ public void binderDied() {
+ mImms.removeClient(mClient);
+ }
+ }
+
static final class ClientState {
final IInputMethodClient client;
final IInputContext inputContext;
final int uid;
final int pid;
final InputBinding binding;
+ final ClientDeathRecipient clientDeathRecipient;
boolean sessionRequested;
SessionState curSession;
@@ -419,12 +435,13 @@
}
ClientState(IInputMethodClient _client, IInputContext _inputContext,
- int _uid, int _pid) {
+ int _uid, int _pid, ClientDeathRecipient _clientDeathRecipient) {
client = _client;
inputContext = _inputContext;
uid = _uid;
pid = _pid;
binding = new InputBinding(null, inputContext.asBinder(), uid, pid);
+ clientDeathRecipient = _clientDeathRecipient;
}
}
@@ -1716,9 +1733,39 @@
}
}
- void addClient(ClientState clientState) {
+ /**
+ * Called by each application process as a preparation to start interacting with
+ * {@link InputMethodManagerService}.
+ *
+ * <p>As a general principle, IPCs from the application process that take
+ * {@link InputMethodClient} will be rejected without this step.</p>
+ *
+ * @param client {@link android.os.Binder} proxy that is associated with the singleton instance
+ * of {@link android.view.inputmethod.InputMethodManager} that runs on the client
+ * process
+ * @param inputContext communication channel for the dummy
+ * {@link android.view.inputmethod.InputConnection}
+ */
+ @Override
+ public void addClient(IInputMethodClient client, IInputContext inputContext) {
+ final int callerUid = Binder.getCallingUid();
+ final int callerPid = Binder.getCallingPid();
synchronized (mMethodMap) {
- mClients.put(clientState.client.asBinder(), clientState);
+ // TODO: Optimize this linear search.
+ for (ClientState state : mClients.values()) {
+ if (state.uid == callerUid && state.pid == callerPid) {
+ throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid
+ + " is already registered");
+ }
+ }
+ final ClientDeathRecipient deathRecipient = new ClientDeathRecipient(this, client);
+ try {
+ client.asBinder().linkToDeath(deathRecipient, 0);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
+ }
+ mClients.put(client.asBinder(),
+ new ClientState(client, inputContext, callerUid, callerPid, deathRecipient));
}
}
@@ -1726,6 +1773,7 @@
synchronized (mMethodMap) {
ClientState cs = mClients.remove(client.asBinder());
if (cs != null) {
+ client.asBinder().unlinkToDeath(cs.clientDeathRecipient, 0);
clearClientSessionLocked(cs);
if (mCurClient == cs) {
if (mBoundToMethod) {
@@ -2536,7 +2584,11 @@
// We need to check if this is the current client with
// focus in the window manager, to allow this call to
// be made before input is started in it.
- if (!mWindowManagerInternal.inputMethodClientHasFocus(client)) {
+ final ClientState cs = mClients.get(client.asBinder());
+ if (cs == null) {
+ throw new IllegalArgumentException("unknown client " + client.asBinder());
+ }
+ if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) {
Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client);
return false;
}
@@ -2616,7 +2668,11 @@
// We need to check if this is the current client with
// focus in the window manager, to allow this call to
// be made before input is started in it.
- if (!mWindowManagerInternal.inputMethodClientHasFocus(client)) {
+ final ClientState cs = mClients.get(client.asBinder());
+ if (cs == null) {
+ throw new IllegalArgumentException("unknown client " + client.asBinder());
+ }
+ if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) {
if (DEBUG) {
Slog.w(TAG, "Ignoring hideSoftInput of uid " + uid + ": " + client);
}
@@ -2734,7 +2790,7 @@
+ client.asBinder());
}
- if (!mWindowManagerInternal.inputMethodClientHasFocus(cs.client)) {
+ if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) {
// Check with the window manager to make sure this client actually
// has a window with focus. If not, reject. This is thread safe
// because if the focus changes some time before or after, the
@@ -4399,20 +4455,6 @@
}
@Override
- public void addClient(IInputMethodClient client, IInputContext inputContext, int uid,
- int pid) {
- // Work around Bug 113877122: We need to handle this synchronously. Otherwise, some
- // IMM binder calls from the client process before we register this client.
- mService.addClient(new ClientState(client, inputContext, uid, pid));
- }
-
- @Override
- public void removeClient(IInputMethodClient client) {
- // Handle this synchronously to be consistent with addClient().
- mService.removeClient(client);
- }
-
- @Override
public void setInteractive(boolean interactive) {
// Do everything in handler so as not to block the caller.
mService.mHandler.obtainMessage(MSG_SET_INTERACTIVE, interactive ? 1 : 0, 0)
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 050a075..d326c22 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -20,6 +20,7 @@
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
import static android.content.Context.DEVICE_POLICY_SERVICE;
+import static android.os.UserHandle.USER_ALL;
import android.annotation.NonNull;
import android.app.ActivityManager;
@@ -53,12 +54,15 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
import com.android.server.notification.NotificationManagerService.DumpFilter;
@@ -72,6 +76,7 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
@@ -138,10 +143,6 @@
// not mean that we are currently bound to said package/component.
private ArrayMap<Integer, ArrayMap<Boolean, ArraySet<String>>> mApproved = new ArrayMap<>();
- // Kept to de-dupe user change events (experienced after boot, when we receive a settings and a
- // user change).
- private int[] mLastSeenProfileIds;
-
// True if approved services are stored in xml, not settings.
private boolean mUseXml;
@@ -300,7 +301,7 @@
Settings.Secure.putStringForUser(
mContext.getContentResolver(), element, value, userId);
loadAllowedComponentsFromSettings();
- rebindServices(false);
+ rebindServices(false, userId);
}
}
}
@@ -385,7 +386,7 @@
}
}
}
- rebindServices(false);
+ rebindServices(false, USER_ALL);
}
protected void upgradeXml(final int xmlVersion, final int userId) {}
@@ -460,7 +461,7 @@
}
}
- rebindServices(false);
+ rebindServices(false, userId);
}
private String getApprovedValue(String pkgOrComponent) {
@@ -578,7 +579,7 @@
if (anyServicesInvolved) {
// make sure we're still bound to any of our services who may have just upgraded
- rebindServices(false);
+ rebindServices(false, USER_ALL);
}
}
}
@@ -586,21 +587,17 @@
public void onUserRemoved(int user) {
Slog.i(TAG, "Removing approved services for removed user " + user);
mApproved.remove(user);
- rebindServices(true);
+ rebindServices(true, user);
}
public void onUserSwitched(int user) {
if (DEBUG) Slog.d(TAG, "onUserSwitched u=" + user);
- if (Arrays.equals(mLastSeenProfileIds, mUserProfiles.getCurrentProfileIds())) {
- if (DEBUG) Slog.d(TAG, "Current profile IDs didn't change, skipping rebindServices().");
- return;
- }
- rebindServices(true);
+ rebindServices(true, user);
}
public void onUserUnlocked(int user) {
if (DEBUG) Slog.d(TAG, "onUserUnlocked u=" + user);
- rebindServices(false);
+ rebindServices(false, user);
}
private ManagedServiceInfo getServiceFromTokenLocked(IInterface service) {
@@ -694,9 +691,10 @@
component.flattenToShortString());
synchronized (mMutex) {
- final int[] userIds = mUserProfiles.getCurrentProfileIds();
+ final IntArray userIds = mUserProfiles.getCurrentProfileIds();
- for (int userId : userIds) {
+ for (int i = 0; i < userIds.size(); i++) {
+ final int userId = userIds.get(i);
if (enabled) {
if (isPackageOrComponentAllowed(component.toString(), userId)
|| isPackageOrComponentAllowed(component.getPackageName(), userId)) {
@@ -838,20 +836,14 @@
return false;
}
- /**
- * Called whenever packages change, the user switches, or the secure setting
- * is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
- */
- protected void rebindServices(boolean forceRebind) {
- if (DEBUG) Slog.d(TAG, "rebindServices");
- final int[] userIds = mUserProfiles.getCurrentProfileIds();
- final int nUserIds = userIds.length;
-
+ @VisibleForTesting
+ protected SparseArray<ArraySet<ComponentName>> getAllowedComponents(IntArray userIds) {
+ final int nUserIds = userIds.size();
final SparseArray<ArraySet<ComponentName>> componentsByUser = new SparseArray<>();
for (int i = 0; i < nUserIds; ++i) {
- final int userId = userIds[i];
- final ArrayMap<Boolean, ArraySet<String>> approvedLists = mApproved.get(userIds[i]);
+ final int userId = userIds.get(i);
+ final ArrayMap<Boolean, ArraySet<String>> approvedLists = mApproved.get(userId);
if (approvedLists != null) {
final int N = approvedLists.size();
for (int j = 0; j < N; j++) {
@@ -865,67 +857,125 @@
}
}
}
+ return componentsByUser;
+ }
- final ArrayList<ManagedServiceInfo> removableBoundServices = new ArrayList<>();
- final SparseArray<Set<ComponentName>> toAdd = new SparseArray<>();
-
- synchronized (mMutex) {
- // Rebind to non-system services if user switched
- for (ManagedServiceInfo service : mServices) {
- if (!service.isSystem && !service.isGuest(this)) {
- removableBoundServices.add(service);
- }
- }
-
- mEnabledServicesForCurrentProfiles.clear();
- mEnabledServicesPackageNames.clear();
-
- for (int i = 0; i < nUserIds; ++i) {
- // decode the list of components
- final ArraySet<ComponentName> userComponents = componentsByUser.get(userIds[i]);
- if (null == userComponents) {
- toAdd.put(userIds[i], new ArraySet<>());
- continue;
- }
-
- final Set<ComponentName> add = new HashSet<>(userComponents);
- add.removeAll(mSnoozingForCurrentProfiles);
-
- toAdd.put(userIds[i], add);
-
- mEnabledServicesForCurrentProfiles.addAll(userComponents);
-
- for (int j = 0; j < userComponents.size(); j++) {
- final ComponentName component = userComponents.valueAt(j);
- mEnabledServicesPackageNames.add(component.getPackageName());
- }
- }
- }
-
- for (ManagedServiceInfo info : removableBoundServices) {
- final ComponentName component = info.component;
- final int oldUser = info.userid;
- final Set<ComponentName> allowedComponents = toAdd.get(info.userid);
- if (allowedComponents != null) {
- if (allowedComponents.contains(component) && !forceRebind) {
- // Already bound, don't need to bind again.
- allowedComponents.remove(component);
- } else {
- // No longer allowed to be bound, or must rebind.
- Slog.v(TAG, "disabling " + getCaption() + " for user "
- + oldUser + ": " + component);
- unregisterService(component, oldUser);
- }
- }
- }
+ @GuardedBy("mMutex")
+ protected void populateComponentsToBind(SparseArray<Set<ComponentName>> componentsToBind,
+ final IntArray activeUsers,
+ SparseArray<ArraySet<ComponentName>> approvedComponentsByUser) {
+ mEnabledServicesForCurrentProfiles.clear();
+ mEnabledServicesPackageNames.clear();
+ final int nUserIds = activeUsers.size();
for (int i = 0; i < nUserIds; ++i) {
- final Set<ComponentName> add = toAdd.get(userIds[i]);
+ // decode the list of components
+ final int userId = activeUsers.get(i);
+ final ArraySet<ComponentName> userComponents = approvedComponentsByUser.get(userId);
+ if (null == userComponents) {
+ componentsToBind.put(userId, new ArraySet<>());
+ continue;
+ }
+
+ final Set<ComponentName> add = new HashSet<>(userComponents);
+ add.removeAll(mSnoozingForCurrentProfiles);
+
+ componentsToBind.put(userId, add);
+
+ mEnabledServicesForCurrentProfiles.addAll(userComponents);
+
+ for (int j = 0; j < userComponents.size(); j++) {
+ final ComponentName component = userComponents.valueAt(j);
+ mEnabledServicesPackageNames.add(component.getPackageName());
+ }
+ }
+ }
+
+ @GuardedBy("mMutex")
+ protected Set<ManagedServiceInfo> getRemovableConnectedServices() {
+ final Set<ManagedServiceInfo> removableBoundServices = new ArraySet<>();
+ for (ManagedServiceInfo service : mServices) {
+ if (!service.isSystem && !service.isGuest(this)) {
+ removableBoundServices.add(service);
+ }
+ }
+ return removableBoundServices;
+ }
+
+ protected void populateComponentsToUnbind(
+ boolean forceRebind,
+ Set<ManagedServiceInfo> removableBoundServices,
+ SparseArray<Set<ComponentName>> allowedComponentsToBind,
+ SparseArray<Set<ComponentName>> componentsToUnbind) {
+ for (ManagedServiceInfo info : removableBoundServices) {
+ final Set<ComponentName> allowedComponents = allowedComponentsToBind.get(info.userid);
+ if (allowedComponents != null) {
+ if (forceRebind || !allowedComponents.contains(info.component)) {
+ Set<ComponentName> toUnbind =
+ componentsToUnbind.get(info.userid, new ArraySet<>());
+ toUnbind.add(info.component);
+ componentsToUnbind.put(info.userid, toUnbind);
+ }
+ }
+ }
+ }
+
+ /**
+ * Called whenever packages change, the user switches, or the secure setting
+ * is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
+ */
+ protected void rebindServices(boolean forceRebind, int userToRebind) {
+ if (DEBUG) Slog.d(TAG, "rebindServices " + forceRebind + " " + userToRebind);
+ IntArray userIds = mUserProfiles.getCurrentProfileIds();
+ if (userToRebind != USER_ALL) {
+ userIds = new IntArray(1);
+ userIds.add(userToRebind);
+ }
+
+ final SparseArray<Set<ComponentName>> componentsToBind = new SparseArray<>();
+ final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
+
+ synchronized (mMutex) {
+ final SparseArray<ArraySet<ComponentName>> approvedComponentsByUser =
+ getAllowedComponents(userIds);
+ final Set<ManagedServiceInfo> removableBoundServices = getRemovableConnectedServices();
+
+ // Filter approvedComponentsByUser to collect all of the components that are allowed
+ // for the currently active user(s).
+ populateComponentsToBind(componentsToBind, userIds, approvedComponentsByUser);
+
+ // For every current non-system connection, disconnect services that are no longer
+ // approved, or ALL services if we are force rebinding
+ populateComponentsToUnbind(
+ forceRebind, removableBoundServices, componentsToBind, componentsToUnbind);
+ }
+
+ unbindFromServices(componentsToUnbind);
+ bindToServices(componentsToBind);
+ }
+
+ protected void unbindFromServices(SparseArray<Set<ComponentName>> componentsToUnbind) {
+ for (int i = 0; i < componentsToUnbind.size(); i++) {
+ final int userId = componentsToUnbind.keyAt(i);
+ final Set<ComponentName> removableComponents = componentsToUnbind.get(userId);
+ for (ComponentName cn : removableComponents) {
+ // No longer allowed to be bound, or must rebind.
+ Slog.v(TAG, "disabling " + getCaption() + " for user " + userId + ": " + cn);
+ unregisterService(cn, userId);
+ }
+ }
+ }
+
+ // Attempt to bind to services, skipping those that cannot be found or lack the permission.
+ private void bindToServices(SparseArray<Set<ComponentName>> componentsToBind) {
+ for (int i = 0; i < componentsToBind.size(); i++) {
+ final int userId = componentsToBind.keyAt(i);
+ final Set<ComponentName> add = componentsToBind.get(userId);
for (ComponentName component : add) {
try {
ServiceInfo info = mPm.getServiceInfo(component,
PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userIds[i]);
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
if (info == null) {
Slog.w(TAG, "Not binding " + getCaption() + " service " + component
+ ": service not found");
@@ -937,15 +987,13 @@
continue;
}
Slog.v(TAG,
- "enabling " + getCaption() + " for " + userIds[i] + ": " + component);
- registerService(component, userIds[i]);
+ "enabling " + getCaption() + " for " + userId + ": " + component);
+ registerService(component, userId);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
}
-
- mLastSeenProfileIds = userIds;
}
/**
@@ -1022,7 +1070,7 @@
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
- Slog.v(TAG, getCaption() + " service connected: " + name);
+ Slog.v(TAG, userid + " " + getCaption() + " service connected: " + name);
boolean added = false;
ManagedServiceInfo info = null;
synchronized (mMutex) {
@@ -1044,12 +1092,12 @@
@Override
public void onServiceDisconnected(ComponentName name) {
- Slog.v(TAG, getCaption() + " connection lost: " + name);
+ Slog.v(TAG, userid + " " + getCaption() + " connection lost: " + name);
}
@Override
public void onBindingDied(ComponentName name) {
- Slog.w(TAG, getCaption() + " binding died: " + name);
+ Slog.w(TAG, userid + " " + getCaption() + " binding died: " + name);
synchronized (mMutex) {
unbindService(this, name, userid);
if (!mServicesRebinding.contains(servicesBindingTag)) {
@@ -1061,8 +1109,8 @@
}
}, ON_BINDING_DIED_REBIND_DELAY_MS);
} else {
- Slog.v(TAG, getCaption() + " not rebinding as "
- + "a previous rebind attempt was made: " + name);
+ Slog.v(TAG, getCaption() + " not rebinding in user " + userid
+ + " as a previous rebind attempt was made: " + name);
}
}
}
@@ -1072,7 +1120,8 @@
getBindFlags(),
new UserHandle(userid))) {
mServicesBound.remove(servicesBindingTag);
- Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent);
+ Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent
+ + " in user " + userid);
return;
}
} catch (SecurityException ex) {
@@ -1236,9 +1285,9 @@
if (!isEnabledForCurrentProfiles()) {
return false;
}
- if (this.userid == UserHandle.USER_ALL) return true;
+ if (this.userid == USER_ALL) return true;
if (this.isSystem) return true;
- if (nid == UserHandle.USER_ALL || nid == this.userid) return true;
+ if (nid == USER_ALL || nid == this.userid) return true;
return supportsProfiles()
&& mUserProfiles.isCurrentProfile(nid)
&& isPermittedForProfile(nid);
@@ -1284,6 +1333,24 @@
Binder.restoreCallingIdentity(identity);
}
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ManagedServiceInfo that = (ManagedServiceInfo) o;
+ return userid == that.userid
+ && isSystem == that.isSystem
+ && targetSdkVersion == that.targetSdkVersion
+ && Objects.equals(service, that.service)
+ && Objects.equals(component, that.component)
+ && Objects.equals(connection, that.connection);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(service, component, userid, isSystem, connection, targetSdkVersion);
+ }
}
/** convenience method for looking in mEnabledServicesForCurrentProfiles */
@@ -1309,12 +1376,15 @@
}
}
- public int[] getCurrentProfileIds() {
+ /**
+ * Returns the currently active users (generally one user and its work profile).
+ */
+ public IntArray getCurrentProfileIds() {
synchronized (mCurrentProfiles) {
- int[] users = new int[mCurrentProfiles.size()];
+ IntArray users = new IntArray(mCurrentProfiles.size());
final int N = mCurrentProfiles.size();
for (int i = 0; i < N; ++i) {
- users[i] = mCurrentProfiles.keyAt(i);
+ users.add(mCurrentProfiles.keyAt(i));
}
return users;
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2e2a3c1..e53eeb0 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -178,6 +178,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.IntArray;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -1566,7 +1567,7 @@
filter.addAction(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_USER_UNLOCKED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
- getContext().registerReceiver(mIntentReceiver, filter);
+ getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null);
IntentFilter pkgFilter = new IntentFilter();
pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
@@ -1698,10 +1699,10 @@
UserHandle.getUserId(uid), REASON_CHANNEL_BANNED,
null);
if (isUidSystemOrPhone(uid)) {
- int[] profileIds = mUserProfiles.getCurrentProfileIds();
- int N = profileIds.length;
+ IntArray profileIds = mUserProfiles.getCurrentProfileIds();
+ int N = profileIds.size();
for (int i = 0; i < N; i++) {
- int profileId = profileIds[i];
+ int profileId = profileIds.get(i);
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
profileId, REASON_CHANNEL_BANNED,
null);
@@ -4402,22 +4403,12 @@
}
protected boolean isBlocked(NotificationRecord r, NotificationUsageStats usageStats) {
- final String pkg = r.sbn.getPackageName();
- final int callingUid = r.sbn.getUid();
-
- final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
- if (isPackageSuspended) {
- Slog.e(TAG, "Suppressing notification from package due to package "
- + "suspended by administrator.");
- usageStats.registerSuspendedByAdmin(r);
- return isPackageSuspended;
- }
- final boolean isBlocked = isBlocked(r);
- if (isBlocked) {
+ if (isBlocked(r)) {
Slog.e(TAG, "Suppressing notification from package by user request.");
usageStats.registerBlocked(r);
+ return true;
}
- return isBlocked;
+ return false;
}
private boolean isBlocked(NotificationRecord r) {
@@ -4691,7 +4682,11 @@
return;
}
- r.setHidden(isPackageSuspendedLocked(r));
+ final boolean isPackageSuspended = isPackageSuspendedLocked(r);
+ r.setHidden(isPackageSuspended);
+ if (isPackageSuspended) {
+ mUsageStats.registerSuspendedByAdmin(r);
+ }
NotificationRecord old = mNotificationsByKey.get(key);
final StatusBarNotification n = r.sbn;
final Notification notification = n.getNotification();
@@ -6697,7 +6692,9 @@
@Override
public void onUserUnlocked(int user) {
if (DEBUG) Slog.d(TAG, "onUserUnlocked u=" + user);
- rebindServices(true);
+ // force rebind the assistant, as it might be keeping its own state in user locked
+ // storage
+ rebindServices(true, user);
}
protected void onNotificationsSeenLocked(ArrayList<NotificationRecord> records) {
@@ -6922,7 +6919,6 @@
if (!oldSbnVisible && !sbnVisible) {
continue;
}
-
// If the notification is hidden, don't notifyPosted listeners targeting < P.
// Instead, those listeners will receive notifyPosted when the notification is
// unhidden.
@@ -7356,7 +7352,7 @@
new String[]{pkg});
final String action = suspend ? Intent.ACTION_PACKAGES_SUSPENDED
- : Intent.ACTION_PACKAGES_UNSUSPENDED;
+ : Intent.ACTION_PACKAGES_UNSUSPENDED;
final Intent intent = new Intent(action);
intent.putExtras(extras);
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index a178a52..2b581d6 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -15,17 +15,8 @@
*/
package com.android.server.notification;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
import android.annotation.NonNull;
import android.app.AlarmManager;
-import android.app.Notification;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -37,9 +28,18 @@
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
+import android.util.IntArray;
import android.util.Log;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -105,12 +105,12 @@
protected @NonNull List<NotificationRecord> getSnoozed() {
List<NotificationRecord> snoozedForUser = new ArrayList<>();
- int[] userIds = mUserProfiles.getCurrentProfileIds();
+ IntArray userIds = mUserProfiles.getCurrentProfileIds();
if (userIds != null) {
- final int N = userIds.length;
+ final int N = userIds.size();
for (int i = 0; i < N; i++) {
final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs =
- mSnoozedNotifications.get(userIds[i]);
+ mSnoozedNotifications.get(userIds.get(i));
if (snoozedPkgs != null) {
final int M = snoozedPkgs.size();
for (int j = 0; j < M; j++) {
@@ -179,7 +179,7 @@
protected boolean cancel(int userId, boolean includeCurrentProfiles) {
int[] userIds = {userId};
if (includeCurrentProfiles) {
- userIds = mUserProfiles.getCurrentProfileIds();
+ userIds = mUserProfiles.getCurrentProfileIds().toArray();
}
final int N = userIds.length;
for (int i = 0; i < N; i++) {
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 5bf323a..3b11525 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -1049,7 +1049,7 @@
(other != null && other.getComponentName() != null)
? other.getComponentName().getPackageName() : "?";
// if we're installing over the same already-installed package, this is ok
- if (otherPackageName != pkg.packageName) {
+ if (!otherPackageName.equals(pkg.packageName)) {
throw new PackageManagerException(
INSTALL_FAILED_CONFLICTING_PROVIDER,
"Can't install because provider name " + names[j]
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index bca3ca7..5810e30 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -53,16 +53,13 @@
private final static String TAG = "OTADexopt";
private final static boolean DEBUG_DEXOPT = true;
- // The synthetic library dependencies denoting "no checks."
- private final static String[] NO_LIBRARIES =
- new String[] { PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK };
-
// The amount of "available" (free - low threshold) space necessary at the start of an OTA to
// not bulk-delete unused apps' odex files.
private final static long BULK_DELETE_THRESHOLD = 1024 * 1024 * 1024; // 1GB.
private final Context mContext;
private final PackageManagerService mPackageManagerService;
+ private final MetricsLogger metricsLogger;
// TODO: Evaluate the need for WeakReferences here.
@@ -95,6 +92,7 @@
public OtaDexoptService(Context context, PackageManagerService packageManagerService) {
this.mContext = context;
this.mPackageManagerService = packageManagerService;
+ metricsLogger = new MetricsLogger();
}
public static OtaDexoptService main(Context context,
@@ -286,8 +284,8 @@
throws InstallerException {
final StringBuilder builder = new StringBuilder();
- // The current version.
- builder.append("9 ");
+ // The current version. For v10, see b/115993344.
+ builder.append("10 ");
builder.append("dexopt");
@@ -336,11 +334,6 @@
collectingInstaller, mPackageManagerService.mInstallLock, mContext);
String[] libraryDependencies = pkg.usesLibraryFiles;
- if (pkg.isSystem()) {
- // For system apps, we want to avoid classpaths checks.
- libraryDependencies = NO_LIBRARIES;
- }
-
optimizer.performDexOpt(pkg, libraryDependencies,
null /* ISAs */,
@@ -445,24 +438,22 @@
private void performMetricsLogging() {
long finalTime = System.nanoTime();
- MetricsLogger.histogram(mContext, "ota_dexopt_available_space_before_mb",
+ metricsLogger.histogram("ota_dexopt_available_space_before_mb",
inMegabytes(availableSpaceBefore));
- MetricsLogger.histogram(mContext, "ota_dexopt_available_space_after_bulk_delete_mb",
+ metricsLogger.histogram("ota_dexopt_available_space_after_bulk_delete_mb",
inMegabytes(availableSpaceAfterBulkDelete));
- MetricsLogger.histogram(mContext, "ota_dexopt_available_space_after_dexopt_mb",
+ metricsLogger.histogram("ota_dexopt_available_space_after_dexopt_mb",
inMegabytes(availableSpaceAfterDexopt));
- MetricsLogger.histogram(mContext, "ota_dexopt_num_important_packages",
- importantPackageCount);
- MetricsLogger.histogram(mContext, "ota_dexopt_num_other_packages", otherPackageCount);
+ metricsLogger.histogram("ota_dexopt_num_important_packages", importantPackageCount);
+ metricsLogger.histogram("ota_dexopt_num_other_packages", otherPackageCount);
- MetricsLogger.histogram(mContext, "ota_dexopt_num_commands", dexoptCommandCountTotal);
- MetricsLogger.histogram(mContext, "ota_dexopt_num_commands_executed",
- dexoptCommandCountExecuted);
+ metricsLogger.histogram("ota_dexopt_num_commands", dexoptCommandCountTotal);
+ metricsLogger.histogram("ota_dexopt_num_commands_executed", dexoptCommandCountExecuted);
final int elapsedTimeSeconds =
(int) TimeUnit.NANOSECONDS.toSeconds(finalTime - otaDexoptTimeStart);
- MetricsLogger.histogram(mContext, "ota_dexopt_time_s", elapsedTimeSeconds);
+ metricsLogger.histogram("ota_dexopt_time_s", elapsedTimeSeconds);
}
private static class OTADexoptPackageDexOptimizer extends
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 d9eb7e8..3c9dd63 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -946,7 +946,7 @@
}
private boolean isSystemPackage(String packageName) {
- return isSystemPackage(getSystemPackageInfo(packageName));
+ return isSystemPackage(getPackageInfo(packageName));
}
private boolean isSystemPackage(PackageInfo pkg) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 350d6b6..91fd8d0 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -125,6 +125,7 @@
import static android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION;
import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
+
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
@@ -631,9 +632,11 @@
InputConsumer mInputConsumer = null;
- private final WindowFrames mWindowFrames = new WindowFrames();
- private static final Rect mTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
- private static final Rect mTmpRect = new Rect();
+ private static final Rect sTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
+ private static final Rect sTmpRect = new Rect();
+ private static final Rect sTmpDockedFrame = new Rect();
+ private static final Rect sTmpNavFrame = new Rect();
+ private static final Rect sTmpLastParentFrame = new Rect();
WindowState mTopFullscreenOpaqueWindowState;
WindowState mTopFullscreenOpaqueOrDimmingWindowState;
@@ -3543,8 +3546,6 @@
return 0;
} else if (mHasFeatureLeanback && interceptBugreportGestureTv(keyCode, down)) {
return -1;
- } else if (mHasFeatureLeanback && interceptAccessibilityGestureTv(keyCode, down)) {
- return -1;
} else if (keyCode == KeyEvent.KEYCODE_ALL_APPS) {
if (!down) {
mHandler.removeMessages(MSG_HANDLE_ALL_APPS);
@@ -4310,9 +4311,6 @@
mDockLayer = 0x10000000;
mStatusBarLayer = -1;
- mWindowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
- mWindowFrames.setParentFrameWasClippedByDisplayCutout(false);
-
if (displayFrames.mDisplayId == DEFAULT_DISPLAY) {
// For purposes of putting out fake window up to steal focus, we will
// drive nav being hidden only by whether it is requested.
@@ -4379,12 +4377,8 @@
return;
}
- mTmpRect.setEmpty();
- mWindowFrames.setFrames(displayFrames.mDock /* parentFrame */,
- displayFrames.mDock /* displayFrame */, displayFrames.mDock /* overscanFrame */,
- displayFrames.mDock /* contentFrame */, displayFrames.mDock /* visibleFrame */,
- mTmpRect /* decorFrame */, displayFrames.mDock /* stableFrame */,
- displayFrames.mDock /* outsetFrame */);
+ sTmpRect.setEmpty();
+ sTmpDockedFrame.set(displayFrames.mDock);
final int displayId = displayFrames.mDisplayId;
final Rect dockFrame = displayFrames.mDock;
@@ -4398,7 +4392,13 @@
continue;
}
- w.computeFrameLw(mWindowFrames);
+ w.getWindowFrames().setFrames(sTmpDockedFrame /* parentFrame */,
+ sTmpDockedFrame /* displayFrame */, sTmpDockedFrame /* overscanFrame */,
+ sTmpDockedFrame /* contentFrame */, sTmpDockedFrame /* visibleFrame */,
+ sTmpRect /* decorFrame */, sTmpDockedFrame /* stableFrame */,
+ sTmpDockedFrame /* outsetFrame */);
+ w.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
+ w.computeFrameLw();
final Rect frame = w.getFrameLw();
if (frame.left <= 0 && frame.top <= 0) {
@@ -4450,17 +4450,17 @@
return false;
}
// apply any navigation bar insets
- mTmpRect.setEmpty();
- mWindowFrames.setFrames(displayFrames.mUnrestricted /* parentFrame */,
+ sTmpRect.setEmpty();
+ mStatusBar.getWindowFrames().setFrames(displayFrames.mUnrestricted /* parentFrame */,
displayFrames.mUnrestricted /* displayFrame */,
displayFrames.mStable /* overscanFrame */, displayFrames.mStable /* contentFrame */,
- displayFrames.mStable /* visibleFrame */, mTmpRect /* decorFrame */,
+ displayFrames.mStable /* visibleFrame */, sTmpRect /* decorFrame */,
displayFrames.mStable /* stableFrame */, displayFrames.mStable /* outsetFrame */);
-
+ mStatusBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
mStatusBarLayer = mStatusBar.getSurfaceLayer();
// Let the status bar determine its size.
- mStatusBar.computeFrameLw(mWindowFrames);
+ mStatusBar.computeFrameLw();
// For layout, the status bar is always at the top with our fixed height.
displayFrames.mStable.top = displayFrames.mUnrestricted.top
@@ -4470,11 +4470,11 @@
displayFrames.mDisplayCutoutSafe.top);
// Tell the bar controller where the collapsed status bar content is
- mTmpRect.set(mStatusBar.getContentFrameLw());
- mTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
- mTmpRect.top = mStatusBar.getContentFrameLw().top; // Ignore top display cutout inset
- mTmpRect.bottom = displayFrames.mStable.top; // Use collapsed status bar size
- mStatusBarController.setContentFrame(mTmpRect);
+ sTmpRect.set(mStatusBar.getContentFrameLw());
+ sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
+ sTmpRect.top = mStatusBar.getContentFrameLw().top; // Ignore top display cutout inset
+ sTmpRect.bottom = displayFrames.mStable.top; // Use collapsed status bar size
+ mStatusBarController.setContentFrame(sTmpRect);
boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
boolean statusBarTranslucent = (sysui
@@ -4514,7 +4514,7 @@
return false;
}
- final Rect navigationFrame = mWindowFrames.mParentFrame;
+ final Rect navigationFrame = sTmpNavFrame;
boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
// Force the navigation bar to its appropriate place and size. We need to do this directly,
// instead of relying on it to bubble up from the nav bar, because this needs to change
@@ -4525,7 +4525,7 @@
final Rect dockFrame = displayFrames.mDock;
mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation);
- final Rect cutoutSafeUnrestricted = mTmpRect;
+ final Rect cutoutSafeUnrestricted = sTmpRect;
cutoutSafeUnrestricted.set(displayFrames.mUnrestricted);
cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
@@ -4607,15 +4607,15 @@
displayFrames.mContent.set(dockFrame);
mStatusBarLayer = mNavigationBar.getSurfaceLayer();
// And compute the final frame.
- mTmpRect.setEmpty();
- mWindowFrames.setFrames(navigationFrame /* parentFrame */,
+ sTmpRect.setEmpty();
+ mNavigationBar.getWindowFrames().setFrames(navigationFrame /* parentFrame */,
navigationFrame /* displayFrame */, navigationFrame /* overscanFrame */,
displayFrames.mDisplayCutoutSafe /* contentFrame */,
- navigationFrame /* visibleFrame */, mTmpRect /* decorFrame */,
+ navigationFrame /* visibleFrame */, sTmpRect /* decorFrame */,
navigationFrame /* stableFrame */,
displayFrames.mDisplayCutoutSafe /* outsetFrame */);
-
- mNavigationBar.computeFrameLw(mWindowFrames);
+ mNavigationBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
+ mNavigationBar.computeFrameLw();
mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw());
if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame);
@@ -4746,17 +4746,20 @@
final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(null, attrs);
final int sysUiFl = requestedSysUiFl | getImpliedSysUiFlagsForLayout(attrs);
- final Rect pf = mWindowFrames.mParentFrame;
- final Rect df = mWindowFrames.mDisplayFrame;
- final Rect of = mWindowFrames.mOverscanFrame;
- final Rect cf = mWindowFrames.mContentFrame;
- final Rect vf = mWindowFrames.mVisibleFrame;
- final Rect dcf = mWindowFrames.mDecorFrame;
- final Rect sf = mWindowFrames.mStableFrame;
+ final WindowFrames windowFrames = win.getWindowFrames();
+
+ windowFrames.setHasOutsets(false);
+ sTmpLastParentFrame.set(windowFrames.mParentFrame);
+ final Rect pf = windowFrames.mParentFrame;
+ final Rect df = windowFrames.mDisplayFrame;
+ final Rect of = windowFrames.mOverscanFrame;
+ final Rect cf = windowFrames.mContentFrame;
+ final Rect vf = windowFrames.mVisibleFrame;
+ final Rect dcf = windowFrames.mDecorFrame;
+ final Rect sf = windowFrames.mStableFrame;
dcf.setEmpty();
- mWindowFrames.mOutsetFrame.setEmpty();
- mWindowFrames.setParentFrameWasClippedByDisplayCutout(false);
- mWindowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
+ windowFrames.setParentFrameWasClippedByDisplayCutout(false);
+ windowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
final boolean hasNavBar = (isDefaultDisplay && mDefaultDisplayPolicy.hasNavigationBar()
&& mNavigationBar != null && mNavigationBar.isVisibleLw());
@@ -4776,7 +4779,7 @@
cf.set(displayFrames.mDock);
of.set(displayFrames.mDock);
df.set(displayFrames.mDock);
- pf.set(displayFrames.mDock);
+ windowFrames.mParentFrame.set(displayFrames.mDock);
// IM dock windows layout below the nav bar...
pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom;
// ...with content insets above the nav bar
@@ -4878,9 +4881,7 @@
? displayFrames.mRestricted.bottom
: displayFrames.mUnrestricted.bottom;
- if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
- "Laying out status bar window: (%d,%d - %d,%d)",
- pf.left, pf.top, pf.right, pf.bottom));
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out status bar window: " + pf);
} else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
&& type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
// Asking to layout into the overscan region, so give it that pure
@@ -4952,17 +4953,13 @@
pf.bottom = df.bottom = of.bottom = cf.bottom =
displayFrames.mRestricted.bottom;
}
- if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
- "Laying out IN_SCREEN status bar window: (%d,%d - %d,%d)",
- pf.left, pf.top, pf.right, pf.bottom));
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out IN_SCREEN status bar window: " + pf);
} else if (type == TYPE_NAVIGATION_BAR || type == TYPE_NAVIGATION_BAR_PANEL) {
// The navigation bar has Real Ultimate Power.
of.set(displayFrames.mUnrestricted);
df.set(displayFrames.mUnrestricted);
pf.set(displayFrames.mUnrestricted);
- if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
- "Laying out navigation bar window: (%d,%d - %d,%d)",
- pf.left, pf.top, pf.right, pf.bottom));
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out navigation bar window: " + pf);
} else if ((type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_SCREENSHOT)
&& ((fl & FLAG_FULLSCREEN) != 0)) {
// Fullscreen secure system overlays get what they ask for. Screenshot region
@@ -5089,7 +5086,7 @@
// Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in
// the cutout safe zone.
if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
- final Rect displayCutoutSafeExceptMaybeBars = mTmpDisplayCutoutSafeExceptMaybeBarsRect;
+ final Rect displayCutoutSafeExceptMaybeBars = sTmpDisplayCutoutSafeExceptMaybeBarsRect;
displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe);
if (layoutInScreen && layoutInsetDecor && !requestedFullscreen
&& cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
@@ -5124,9 +5121,9 @@
// They will later be cropped or shifted using the displayFrame in WindowState,
// which prevents overlap with the DisplayCutout.
if (!attachedInParent && !floatingInScreenWindow) {
- mTmpRect.set(pf);
+ sTmpRect.set(pf);
pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
- mWindowFrames.setParentFrameWasClippedByDisplayCutout(!mTmpRect.equals(pf));
+ windowFrames.setParentFrameWasClippedByDisplayCutout(!sTmpRect.equals(pf));
}
// Make sure that NO_LIMITS windows clipped to the display don't extend under the
// cutout.
@@ -5154,8 +5151,9 @@
// apply the outsets to floating dialogs, because they wouldn't make sense there.
final boolean useOutsets = shouldUseOutsets(attrs, fl);
if (isDefaultDisplay && useOutsets) {
- final Rect osf = mWindowFrames.mOutsetFrame;
+ final Rect osf = windowFrames.mOutsetFrame;
osf.set(cf.left, cf.top, cf.right, cf.bottom);
+ windowFrames.setHasOutsets(true);
int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
if (outset > 0) {
int rotation = displayFrames.mRotation;
@@ -5182,9 +5180,13 @@
+ " cf=" + cf.toShortString() + " vf=" + vf.toShortString()
+ " dcf=" + dcf.toShortString()
+ " sf=" + sf.toShortString()
- + " osf=" + mWindowFrames.mOutsetFrame.toShortString());
+ + " osf=" + windowFrames.mOutsetFrame.toShortString() + " " + win);
- win.computeFrameLw(mWindowFrames);
+ if (!sTmpLastParentFrame.equals(pf)) {
+ windowFrames.setContentChanged(true);
+ }
+
+ win.computeFrameLw();
// Dock windows carve out the bottom of the screen, so normal windows
// can't appear underneath them.
if (type == TYPE_INPUT_METHOD && win.isVisibleLw()
@@ -6037,6 +6039,22 @@
}
}
+ // Intercept the Accessibility keychord for TV (DPAD_DOWN + Back) before the keyevent is
+ // processed through interceptKeyEventBeforeDispatch since Talkback may consume this event
+ // before it has a chance to reach that method.
+ if (mHasFeatureLeanback) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ case KeyEvent.KEYCODE_BACK: {
+ boolean handled = interceptAccessibilityGestureTv(keyCode, down);
+ if (handled) {
+ result &= ~ACTION_PASS_TO_USER;
+ }
+ break;
+ }
+ }
+ }
+
if (useHapticFeedback) {
performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false,
"Virtual Key - Press");
diff --git a/services/core/java/com/android/server/policy/TEST_MAPPING b/services/core/java/com/android/server/policy/TEST_MAPPING
new file mode 100644
index 0000000..e212b04
--- /dev/null
+++ b/services/core/java/com/android/server/policy/TEST_MAPPING
@@ -0,0 +1,50 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.policy."
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "android.support.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "WmTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.policy."
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.policy."
+ }
+ ]
+ },
+ {
+ "name": "WmTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.policy."
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index b55adeb..1fcdd63 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -63,12 +63,10 @@
import static java.lang.annotation.RetentionPolicy.SOURCE;
-import android.Manifest;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
-import android.content.pm.ActivityInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -95,7 +93,6 @@
import com.android.server.wm.DisplayFrames;
import com.android.server.wm.DisplayRotation;
import com.android.server.wm.WindowFrames;
-import com.android.server.wm.utils.WmDisplayCutout;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -201,10 +198,8 @@
* getFrame() if so desired. Must be called with the window manager
* lock held.
*
- * @param windowFrames Container for all the window frames that affect how the window is
- * laid out.
*/
- public void computeFrameLw(WindowFrames windowFrames);
+ public void computeFrameLw();
/**
* Retrieve the current frame of the window that has been assigned by
@@ -477,6 +472,11 @@
* Writes {@link com.android.server.wm.IdentifierProto} to stream.
*/
void writeIdentifierToProto(ProtoOutputStream proto, long fieldId);
+
+ /**
+ * @return The {@link WindowFrames} associated with this {@link WindowState}
+ */
+ WindowFrames getWindowFrames();
}
/**
@@ -1167,7 +1167,6 @@
default void layoutWindowLw(
WindowState win, WindowState attached, DisplayFrames displayFrames) {}
-
/**
* Return the layout hints for a newly added window. These values are computed on the
* most recent layout, so they are not guaranteed to be correct.
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 5981ab0..68e636a 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -49,6 +49,7 @@
import android.util.Slog;
import android.util.StatsLog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -75,7 +76,8 @@
* tell the system when we go to sleep so that it can lock the keyguard if needed.
* </p>
*/
-final class Notifier {
+@VisibleForTesting
+public class Notifier {
private static final String TAG = "PowerManagerNotifier";
private static final boolean DEBUG = false;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index bdf12ca..9f6b3dd 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -64,7 +64,6 @@
import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
import android.provider.Settings;
-import android.provider.Settings.Global;
import android.provider.Settings.SettingNotFoundException;
import android.service.dreams.DreamManagerInternal;
import android.service.vr.IVrManager;
@@ -84,7 +83,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.server.EventLogTags;
import com.android.server.LockGuard;
@@ -230,6 +228,10 @@
private final BatterySaverController mBatterySaverController;
private final BatterySaverStateMachine mBatterySaverStateMachine;
private final BatterySavingStats mBatterySavingStats;
+ private final BinderService mBinderService;
+ private final LocalService mLocalService;
+ private final NativeWrapper mNativeWrapper;
+ private final Injector mInjector;
private LightsManager mLightsManager;
private BatteryManagerInternal mBatteryManagerInternal;
@@ -636,10 +638,76 @@
}
}
+ /**
+ * Wrapper around the static-native methods of PowerManagerService.
+ *
+ * This class exists to allow us to mock static native methods in our tests. If mocking static
+ * methods becomes easier than this in the future, we can delete this class.
+ */
+ @VisibleForTesting
+ public static class NativeWrapper {
+ /** Wrapper for PowerManager.nativeInit */
+ public void nativeInit(PowerManagerService service) {
+ service.nativeInit();
+ }
+
+ /** Wrapper for PowerManager.nativeAcquireSuspectBlocker */
+ public void nativeAcquireSuspendBlocker(String name) {
+ PowerManagerService.nativeAcquireSuspendBlocker(name);
+ }
+
+ /** Wrapper for PowerManager.nativeReleaseSuspendBlocker */
+ public void nativeReleaseSuspendBlocker(String name) {
+ PowerManagerService.nativeReleaseSuspendBlocker(name);
+ }
+
+ /** Wrapper for PowerManager.nativeSetInteractive */
+ public void nativeSetInteractive(boolean enable) {
+ PowerManagerService.nativeSetInteractive(enable);
+ }
+
+ /** Wrapper for PowerManager.nativeSetAutoSuspend */
+ public void nativeSetAutoSuspend(boolean enable) {
+ PowerManagerService.nativeSetAutoSuspend(enable);
+ }
+
+ /** Wrapper for PowerManager.nativeSendPowerHint */
+ public void nativeSendPowerHint(int hintId, int data) {
+ PowerManagerService.nativeSendPowerHint(hintId, data);
+ }
+
+ /** Wrapper for PowerManager.nativeSetFeature */
+ public void nativeSetFeature(int featureId, int data) {
+ PowerManagerService.nativeSetFeature(featureId, data);
+ }
+ }
+
+ @VisibleForTesting
+ static class Injector {
+ Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
+ SuspendBlocker suspendBlocker, WindowManagerPolicy policy) {
+ return new Notifier(looper, context, batteryStats, suspendBlocker, policy);
+ }
+
+ SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) {
+ SuspendBlocker suspendBlocker = service.new SuspendBlockerImpl(name);
+ service.mSuspendBlockers.add(suspendBlocker);
+ return suspendBlocker;
+ }
+
+ BatterySaverPolicy createBatterySaverPolicy(
+ Object lock, Context context, BatterySavingStats batterySavingStats) {
+ return new BatterySaverPolicy(lock, context, batterySavingStats);
+ }
+
+ NativeWrapper createNativeWrapper() {
+ return new NativeWrapper();
+ }
+ }
+
final Constants mConstants;
private native void nativeInit();
-
private static native void nativeAcquireSuspendBlocker(String name);
private static native void nativeReleaseSuspendBlocker(String name);
private static native void nativeSetInteractive(boolean enable);
@@ -648,8 +716,19 @@
private static native void nativeSetFeature(int featureId, int data);
public PowerManagerService(Context context) {
+ this(context, new Injector());
+ }
+
+ @VisibleForTesting
+ PowerManagerService(Context context, Injector injector) {
super(context);
+
mContext = context;
+ mBinderService = new BinderService();
+ mLocalService = new LocalService();
+ mNativeWrapper = injector.createNativeWrapper();
+ mInjector = injector;
+
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
mHandlerThread.start();
@@ -658,57 +737,40 @@
mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
mBatterySavingStats = new BatterySavingStats(mLock);
- mBatterySaverPolicy = new BatterySaverPolicy(mLock, mContext, mBatterySavingStats);
+ mBatterySaverPolicy =
+ mInjector.createBatterySaverPolicy(mLock, mContext, mBatterySavingStats);
mBatterySaverController = new BatterySaverController(mLock, mContext,
- BackgroundThread.get().getLooper(), mBatterySaverPolicy, mBatterySavingStats);
+ BackgroundThread.get().getLooper(), mBatterySaverPolicy,
+ mBatterySavingStats);
mBatterySaverStateMachine = new BatterySaverStateMachine(
mLock, mContext, mBatterySaverController);
synchronized (mLock) {
- mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.WakeLocks");
- mDisplaySuspendBlocker = createSuspendBlockerLocked("PowerManagerService.Display");
- mDisplaySuspendBlocker.acquire();
- mHoldingDisplaySuspendBlocker = true;
+ mWakeLockSuspendBlocker =
+ mInjector.createSuspendBlocker(this, "PowerManagerService.WakeLocks");
+ mDisplaySuspendBlocker =
+ mInjector.createSuspendBlocker(this, "PowerManagerService.Display");
+ if (mDisplaySuspendBlocker != null) {
+ mDisplaySuspendBlocker.acquire();
+ mHoldingDisplaySuspendBlocker = true;
+ }
mHalAutoSuspendModeEnabled = false;
mHalInteractiveModeEnabled = true;
mWakefulness = WAKEFULNESS_AWAKE;
-
sQuiescent = SystemProperties.get(SYSTEM_PROPERTY_QUIESCENT, "0").equals("1");
- nativeInit();
- nativeSetAutoSuspend(false);
- nativeSetInteractive(true);
- nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, 0);
+ mNativeWrapper.nativeInit(this);
+ mNativeWrapper.nativeSetAutoSuspend(false);
+ mNativeWrapper.nativeSetInteractive(true);
+ mNativeWrapper.nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, 0);
}
}
- @VisibleForTesting
- PowerManagerService(Context context, BatterySaverPolicy batterySaverPolicy) {
- super(context);
-
- mContext = context;
- mHandlerThread = new ServiceThread(TAG,
- Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
- mHandlerThread.start();
- mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
- mConstants = new Constants(mHandler);
- mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
- mDisplaySuspendBlocker = null;
- mWakeLockSuspendBlocker = null;
-
- mBatterySavingStats = new BatterySavingStats(mLock);
- mBatterySaverPolicy = batterySaverPolicy;
- mBatterySaverController = new BatterySaverController(mLock, context,
- BackgroundThread.getHandler().getLooper(), batterySaverPolicy, mBatterySavingStats);
- mBatterySaverStateMachine = new BatterySaverStateMachine(
- mLock, mContext, mBatterySaverController);
- }
-
@Override
public void onStart() {
- publishBinderService(Context.POWER_SERVICE, new BinderService());
- publishLocalService(PowerManagerInternal.class, new LocalService());
+ publishBinderService(Context.POWER_SERVICE, mBinderService);
+ publishLocalService(PowerManagerInternal.class, mLocalService);
Watchdog.getInstance().addMonitor(this);
Watchdog.getInstance().addThread(mHandler);
@@ -752,11 +814,13 @@
// The notifier runs on the system server's main looper so as not to interfere
// with the animations and other critical functions of the power manager.
mBatteryStats = BatteryStatsService.getService();
- mNotifier = new Notifier(Looper.getMainLooper(), mContext, mBatteryStats,
- createSuspendBlockerLocked("PowerManagerService.Broadcasts"), mPolicy);
+ mNotifier = mInjector.createNotifier(Looper.getMainLooper(), mContext, mBatteryStats,
+ mInjector.createSuspendBlocker(this, "PowerManagerService.Broadcasts"),
+ mPolicy);
mWirelessChargerDetector = new WirelessChargerDetector(sensorManager,
- createSuspendBlockerLocked("PowerManagerService.WirelessChargerDetector"),
+ mInjector.createSuspendBlocker(
+ this, "PowerManagerService.WirelessChargerDetector"),
mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
@@ -824,7 +888,7 @@
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.DEVICE_DEMO_MODE),
false, mSettingsObserver, UserHandle.USER_SYSTEM);
- IVrManager vrManager = (IVrManager) getBinderService(Context.VR_SERVICE);
+ IVrManager vrManager = IVrManager.Stub.asInterface(getBinderService(Context.VR_SERVICE));
if (vrManager != null) {
try {
vrManager.registerListener(mVrStateCallbacks);
@@ -927,7 +991,8 @@
UserHandle.USER_CURRENT) != 0;
if (doubleTapWakeEnabled != mDoubleTapWakeEnabled) {
mDoubleTapWakeEnabled = doubleTapWakeEnabled;
- nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, mDoubleTapWakeEnabled ? 1 : 0);
+ mNativeWrapper.nativeSetFeature(
+ POWER_FEATURE_DOUBLE_TAP_TO_WAKE, mDoubleTapWakeEnabled ? 1 : 0);
}
}
@@ -1509,6 +1574,11 @@
}
}
+ @VisibleForTesting
+ int getWakefulness() {
+ return mWakefulness;
+ }
+
/**
* Logs the time the device would have spent awake before user activity timeout,
* had the system not been told the user was inactive.
@@ -2640,7 +2710,7 @@
mHalAutoSuspendModeEnabled = enable;
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setHalAutoSuspend(" + enable + ")");
try {
- nativeSetAutoSuspend(enable);
+ mNativeWrapper.nativeSetAutoSuspend(enable);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
@@ -2655,7 +2725,7 @@
mHalInteractiveModeEnabled = enable;
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setHalInteractive(" + enable + ")");
try {
- nativeSetInteractive(enable);
+ mNativeWrapper.nativeSetInteractive(enable);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
@@ -3127,7 +3197,7 @@
break;
}
- nativeSendPowerHint(hintId, data);
+ mNativeWrapper.nativeSendPowerHint(hintId, data);
}
/**
@@ -3718,12 +3788,6 @@
proto.flush();
}
- private SuspendBlocker createSuspendBlockerLocked(String name) {
- SuspendBlocker suspendBlocker = new SuspendBlockerImpl(name);
- mSuspendBlockers.add(suspendBlocker);
- return suspendBlocker;
- }
-
private void incrementBootCount() {
synchronized (mLock) {
int count;
@@ -4022,7 +4086,7 @@
Slog.wtf(TAG, "Suspend blocker \"" + mName
+ "\" was finalized without being released!");
mReferenceCount = 0;
- nativeReleaseSuspendBlocker(mName);
+ mNativeWrapper.nativeReleaseSuspendBlocker(mName);
Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0);
}
} finally {
@@ -4039,7 +4103,7 @@
Slog.d(TAG, "Acquiring suspend blocker \"" + mName + "\".");
}
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0);
- nativeAcquireSuspendBlocker(mName);
+ mNativeWrapper.nativeAcquireSuspendBlocker(mName);
}
}
}
@@ -4052,7 +4116,7 @@
if (DEBUG_SPEW) {
Slog.d(TAG, "Releasing suspend blocker \"" + mName + "\".");
}
- nativeReleaseSuspendBlocker(mName);
+ mNativeWrapper.nativeReleaseSuspendBlocker(mName);
Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0);
} else if (mReferenceCount < 0) {
Slog.wtf(TAG, "Suspend blocker \"" + mName
@@ -4090,7 +4154,8 @@
}
}
- private final class BinderService extends IPowerManager.Stub {
+ @VisibleForTesting
+ final class BinderService extends IPowerManager.Stub {
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
@@ -4592,6 +4657,16 @@
}
@VisibleForTesting
+ BinderService getBinderServiceInstance() {
+ return mBinderService;
+ }
+
+ @VisibleForTesting
+ LocalService getLocalServiceInstance() {
+ return mLocalService;
+ }
+
+ @VisibleForTesting
// lastRebootReasonProperty argument to permit testing
int getLastShutdownReasonInternal(String lastRebootReasonProperty) {
String line = SystemProperties.get(lastRebootReasonProperty);
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index 180f343..9f69702 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -23,11 +23,11 @@
import android.os.SharedMemory;
import android.system.ErrnoException;
import android.system.Os;
+import android.util.Pair;
+import android.util.Slog;
import android.util.apk.ApkSignatureVerifier;
import android.util.apk.ByteBufferFactory;
import android.util.apk.SignatureNotFoundException;
-import android.util.Pair;
-import android.util.Slog;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -85,7 +85,7 @@
*/
public static byte[] generateFsverityRootHash(@NonNull String apkPath)
throws NoSuchAlgorithmException, DigestException, IOException {
- return ApkSignatureVerifier.generateFsverityRootHash(apkPath);
+ return ApkSignatureVerifier.generateApkVerityRootHash(apkPath);
}
/**
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 2091899..5e3fe0a 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -32,6 +32,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.hardware.fingerprint.FingerprintManager;
import android.net.NetworkStats;
import android.net.wifi.IWifiManager;
import android.net.wifi.WifiActivityEnergyInfo;
@@ -122,7 +123,7 @@
public static final String CONFIG_DIR = "/data/misc/stats-service";
static final String TAG = "StatsCompanionService";
- static final boolean DEBUG = false;
+ static final boolean DEBUG = true;
public static final int CODE_DATA_BROADCAST = 1;
public static final int CODE_SUBSCRIBER_BROADCAST = 1;
@@ -253,34 +254,38 @@
@Override
public void sendSubscriberBroadcast(IBinder intentSenderBinder, long configUid, long configKey,
- long subscriptionId, long subscriptionRuleId,
- String[] cookies,
- StatsDimensionsValue dimensionsValue) {
+ long subscriptionId, long subscriptionRuleId, String[] cookies,
+ StatsDimensionsValue dimensionsValue) {
enforceCallingPermission();
IntentSender intentSender = new IntentSender(intentSenderBinder);
- Intent intent = new Intent()
- .putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid)
- .putExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, configKey)
- .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId)
- .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID, subscriptionRuleId)
- .putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue);
+ Intent intent =
+ new Intent()
+ .putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid)
+ .putExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, configKey)
+ .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId)
+ .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID, subscriptionRuleId)
+ .putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue);
ArrayList<String> cookieList = new ArrayList<>(cookies.length);
- for (String cookie : cookies) { cookieList.add(cookie); }
+ for (String cookie : cookies) {
+ cookieList.add(cookie);
+ }
intent.putStringArrayListExtra(
StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES, cookieList);
if (DEBUG) {
- Slog.d(TAG, String.format(
- "Statsd sendSubscriberBroadcast with params {%d %d %d %d %s %s}",
- configUid, configKey, subscriptionId, subscriptionRuleId,
- Arrays.toString(cookies), dimensionsValue));
+ Slog.d(TAG,
+ String.format("Statsd sendSubscriberBroadcast with params {%d %d %d %d %s %s}",
+ configUid, configKey, subscriptionId, subscriptionRuleId,
+ Arrays.toString(cookies),
+ dimensionsValue));
}
try {
intentSender.sendIntent(mContext, CODE_SUBSCRIBER_BROADCAST, intent, null, null);
} catch (IntentSender.SendIntentException e) {
- Slog.w(TAG, "Unable to send using IntentSender from uid " + configUid
- + "; presumably it had been cancelled.");
+ Slog.w(TAG,
+ "Unable to send using IntentSender from uid " + configUid
+ + "; presumably it had been cancelled.");
}
}
@@ -317,7 +322,7 @@
// Add in all the apps for every user/profile.
for (UserInfo profile : users) {
List<PackageInfo> pi =
- pm.getInstalledPackagesAsUser(PackageManager.MATCH_KNOWN_PACKAGES, profile.id);
+ pm.getInstalledPackagesAsUser(PackageManager.MATCH_KNOWN_PACKAGES, profile.id);
for (int j = 0; j < pi.size(); j++) {
if (pi.get(j).applicationInfo != null) {
uids.add(pi.get(j).applicationInfo.uid);
@@ -402,8 +407,9 @@
public final static class PullingAlarmListener implements OnAlarmListener {
@Override
public void onAlarm() {
- if (DEBUG)
+ if (DEBUG) {
Slog.d(TAG, "Time to poll something.");
+ }
synchronized (sStatsdLock) {
if (sStatsd == null) {
Slog.w(TAG, "Could not access statsd to inform it of pulling alarm firing.");
@@ -422,8 +428,9 @@
public final static class PeriodicAlarmListener implements OnAlarmListener {
@Override
public void onAlarm() {
- if (DEBUG)
+ if (DEBUG) {
Slog.d(TAG, "Time to trigger periodic alarm.");
+ }
synchronized (sStatsdLock) {
if (sStatsd == null) {
Slog.w(TAG, "Could not access statsd to inform it of periodic alarm firing.");
@@ -459,7 +466,7 @@
return;
}
try {
- sStatsd.informDeviceShutdown();
+ sStatsd.informDeviceShutdown();
} catch (Exception e) {
Slog.w(TAG, "Failed to inform statsd of a shutdown event.", e);
}
@@ -498,9 +505,11 @@
@Override // Binder call
public void setAlarmForSubscriberTriggering(long timestampMs) {
enforceCallingPermission();
- if (DEBUG)
- Slog.d(TAG, "Setting periodic alarm in about " +
- (timestampMs - SystemClock.elapsedRealtime()));
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Setting periodic alarm in about " + (timestampMs
+ - SystemClock.elapsedRealtime()));
+ }
final long callingToken = Binder.clearCallingIdentity();
try {
// using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
@@ -515,8 +524,9 @@
@Override // Binder call
public void cancelAlarmForSubscriberTriggering() {
enforceCallingPermission();
- if (DEBUG)
+ if (DEBUG) {
Slog.d(TAG, "Cancelling periodic alarm");
+ }
final long callingToken = Binder.clearCallingIdentity();
try {
mAlarmManager.cancel(mPeriodicAlarmListener);
@@ -546,8 +556,9 @@
@Override // Binder call
public void cancelPullingAlarm() {
enforceCallingPermission();
- if (DEBUG)
+ if (DEBUG) {
Slog.d(TAG, "Cancelling pulling alarm");
+ }
final long callingToken = Binder.clearCallingIdentity();
try {
mAlarmManager.cancel(mPullingAlarmListener);
@@ -560,10 +571,11 @@
int tag, List<StatsLogEventWrapper> ret, NetworkStats stats, boolean withFGBG) {
int size = stats.size();
long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ long wallClockNanos = SystemClock.currentTimeMicro() * 1000L;
NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
for (int j = 0; j < size; j++) {
stats.getValues(j, entry);
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tag, withFGBG ? 6 : 5);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tag, elapsedNanos, wallClockNanos);
e.writeInt(entry.uid);
if (withFGBG) {
e.writeInt(entry.set);
@@ -639,14 +651,15 @@
return null;
}
- private void pullKernelWakelock(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullKernelWakelock(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
final KernelWakelockStats wakelockStats =
mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
String name = ent.getKey();
KernelWakelockStats.Entry kws = ent.getValue();
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 4);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeString(name);
e.writeInt(kws.mCount);
e.writeInt(kws.mVersion);
@@ -655,7 +668,9 @@
}
}
- private void pullWifiBytesTransfer(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullWifiBytesTransfer(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
long token = Binder.clearCallingIdentity();
try {
// TODO: Consider caching the following call to get BatteryStatsInternal.
@@ -667,7 +682,8 @@
NetworkStatsFactory nsf = new NetworkStatsFactory();
// Combine all the metrics per Uid into one record.
NetworkStats stats =
- nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null)
+ nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
+ null)
.groupedByUid();
addNetworkStats(tagId, pulledData, stats, false);
} catch (java.io.IOException e) {
@@ -677,7 +693,9 @@
}
}
- private void pullWifiBytesTransferByFgBg(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullWifiBytesTransferByFgBg(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
long token = Binder.clearCallingIdentity();
try {
BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
@@ -687,7 +705,8 @@
}
NetworkStatsFactory nsf = new NetworkStatsFactory();
NetworkStats stats = rollupNetworkStatsByFGBG(
- nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null));
+ nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
+ null));
addNetworkStats(tagId, pulledData, stats, true);
} catch (java.io.IOException e) {
Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e);
@@ -696,7 +715,9 @@
}
}
- private void pullMobileBytesTransfer(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullMobileBytesTransfer(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
long token = Binder.clearCallingIdentity();
try {
BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
@@ -707,7 +728,8 @@
NetworkStatsFactory nsf = new NetworkStatsFactory();
// Combine all the metrics per Uid into one record.
NetworkStats stats =
- nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null)
+ nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
+ null)
.groupedByUid();
addNetworkStats(tagId, pulledData, stats, false);
} catch (java.io.IOException e) {
@@ -717,12 +739,14 @@
}
}
- private void pullBluetoothBytesTransfer(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullBluetoothBytesTransfer(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
BluetoothActivityEnergyInfo info = pullBluetoothData();
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
if (info.getUidTraffic() != null) {
for (UidTraffic traffic : info.getUidTraffic()) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+ wallClockNanos);
e.writeInt(traffic.getUid());
e.writeLong(traffic.getRxBytes());
e.writeLong(traffic.getTxBytes());
@@ -731,7 +755,9 @@
}
}
- private void pullMobileBytesTransferByFgBg(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullMobileBytesTransferByFgBg(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
long token = Binder.clearCallingIdentity();
try {
BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
@@ -741,7 +767,8 @@
}
NetworkStatsFactory nsf = new NetworkStatsFactory();
NetworkStats stats = rollupNetworkStatsByFGBG(
- nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null));
+ nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
+ null));
addNetworkStats(tagId, pulledData, stats, true);
} catch (java.io.IOException e) {
Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e);
@@ -750,13 +777,15 @@
}
}
- private void pullCpuTimePerFreq(int tagId, List<StatsLogEventWrapper> pulledData) {
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ private void pullCpuTimePerFreq(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readAbsolute();
if (clusterTimeMs != null) {
for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+ wallClockNanos);
e.writeInt(cluster);
e.writeInt(speed);
e.writeLong(clusterTimeMs[speed]);
@@ -766,10 +795,11 @@
}
}
- private void pullKernelUidCpuTime(int tagId, List<StatsLogEventWrapper> pulledData) {
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ private void pullKernelUidCpuTime(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
mKernelUidCpuTimeReader.readAbsolute((uid, userTimeUs, systemTimeUs) -> {
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(uid);
e.writeLong(userTimeUs);
e.writeLong(systemTimeUs);
@@ -777,12 +807,14 @@
});
}
- private void pullKernelUidCpuFreqTime(int tagId, List<StatsLogEventWrapper> pulledData) {
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ private void pullKernelUidCpuFreqTime(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
mKernelUidCpuFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> {
for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) {
- if(cpuFreqTimeMs[freqIndex] != 0) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
+ if (cpuFreqTimeMs[freqIndex] != 0) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+ wallClockNanos);
e.writeInt(uid);
e.writeInt(freqIndex);
e.writeLong(cpuFreqTimeMs[freqIndex]);
@@ -792,11 +824,13 @@
});
}
- private void pullKernelUidCpuClusterTime(int tagId, List<StatsLogEventWrapper> pulledData) {
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ private void pullKernelUidCpuClusterTime(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
mKernelUidCpuClusterTimeReader.readAbsolute((uid, cpuClusterTimesMs) -> {
for (int i = 0; i < cpuClusterTimesMs.length; i++) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+ wallClockNanos);
e.writeInt(uid);
e.writeInt(i);
e.writeLong(cpuClusterTimesMs[i]);
@@ -805,17 +839,20 @@
});
}
- private void pullKernelUidCpuActiveTime(int tagId, List<StatsLogEventWrapper> pulledData) {
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ private void pullKernelUidCpuActiveTime(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
mKernelUidCpuActiveTimeReader.readAbsolute((uid, cpuActiveTimesMs) -> {
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 2);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(uid);
- e.writeLong((long)cpuActiveTimesMs);
+ e.writeLong((long) cpuActiveTimesMs);
pulledData.add(e);
});
}
- private void pullWifiActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullWifiActivityInfo(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
long token = Binder.clearCallingIdentity();
if (mWifiManager == null) {
mWifiManager =
@@ -826,7 +863,8 @@
SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
mWifiManager.requestActivityInfo(wifiReceiver);
final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
- StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 6);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+ wallClockNanos);
e.writeLong(wifiInfo.getTimeStamp());
e.writeInt(wifiInfo.getStackState());
e.writeLong(wifiInfo.getControllerTxTimeMillis());
@@ -835,14 +873,18 @@
e.writeLong(wifiInfo.getControllerEnergyUsed());
pulledData.add(e);
} catch (RemoteException e) {
- Slog.e(TAG, "Pulling wifiManager for wifi controller activity energy info has error", e);
+ Slog.e(TAG,
+ "Pulling wifiManager for wifi controller activity energy info has error",
+ e);
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
- private void pullModemActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullModemActivityInfo(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
long token = Binder.clearCallingIdentity();
if (mTelephony == null) {
mTelephony = TelephonyManager.from(mContext);
@@ -851,7 +893,7 @@
SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
mTelephony.requestModemActivityInfo(modemReceiver);
final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
- StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 10);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeLong(modemInfo.getTimestamp());
e.writeLong(modemInfo.getSleepTimeMillis());
e.writeLong(modemInfo.getIdleTimeMillis());
@@ -866,9 +908,11 @@
}
}
- private void pullBluetoothActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullBluetoothActivityInfo(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
BluetoothActivityEnergyInfo info = pullBluetoothData();
- StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 6);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeLong(info.getTimeStamp());
e.writeInt(info.getBluetoothStackState());
e.writeLong(info.getControllerTxTimeMillis());
@@ -881,7 +925,8 @@
private synchronized BluetoothActivityEnergyInfo pullBluetoothData() {
final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter != null) {
- SynchronousResultReceiver bluetoothReceiver = new SynchronousResultReceiver("bluetooth");
+ SynchronousResultReceiver bluetoothReceiver = new SynchronousResultReceiver(
+ "bluetooth");
adapter.requestControllerActivityEnergyInfo(bluetoothReceiver);
return awaitControllerInfo(bluetoothReceiver);
} else {
@@ -890,25 +935,29 @@
}
}
- private void pullSystemElapsedRealtime(int tagId, List<StatsLogEventWrapper> pulledData) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 1);
+ private void pullSystemElapsedRealtime(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeLong(SystemClock.elapsedRealtime());
pulledData.add(e);
}
- private void pullSystemUpTime(int tagId, List<StatsLogEventWrapper> pulledData) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 1);
+ private void pullSystemUpTime(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeLong(SystemClock.uptimeMillis());
pulledData.add(e);
}
- private void pullProcessMemoryState(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullProcessMemoryState(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
List<ProcessMemoryState> processMemoryStates =
- LocalServices.getService(ActivityManagerInternal.class)
- .getMemoryStateForProcesses();
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ LocalServices.getService(
+ ActivityManagerInternal.class).getMemoryStateForProcesses();
for (ProcessMemoryState processMemoryState : processMemoryStates) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 8 /* fields */);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(processMemoryState.uid);
e.writeString(processMemoryState.processName);
e.writeInt(processMemoryState.oomScore);
@@ -921,7 +970,9 @@
}
}
- private void pullBinderCallsStats(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullBinderCallsStats(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
BinderCallsStatsService.Internal binderStats =
LocalServices.getService(BinderCallsStatsService.Internal.class);
if (binderStats == null) {
@@ -930,9 +981,8 @@
List<ExportedCallStat> callStats = binderStats.getExportedCallStats();
binderStats.reset();
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
for (ExportedCallStat callStat : callStats) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 13 /* fields */);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(callStat.uid);
e.writeString(callStat.className);
e.writeString(callStat.methodName);
@@ -950,7 +1000,9 @@
}
}
- private void pullBinderCallsStatsExceptions(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullBinderCallsStatsExceptions(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
BinderCallsStatsService.Internal binderStats =
LocalServices.getService(BinderCallsStatsService.Internal.class);
if (binderStats == null) {
@@ -960,26 +1012,26 @@
ArrayMap<String, Integer> exceptionStats = binderStats.getExportedExceptionStats();
// TODO: decouple binder calls exceptions with the rest of the binder calls data so that we
// can reset the exception stats.
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
for (Entry<String, Integer> entry : exceptionStats.entrySet()) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 2 /* fields */);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeString(entry.getKey());
e.writeInt(entry.getValue());
pulledData.add(e);
}
}
- private void pullLooperStats(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullLooperStats(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
LooperStats looperStats = LocalServices.getService(LooperStats.class);
if (looperStats == null) {
return;
}
List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ looperStats.reset();
for (LooperStats.ExportedEntry entry : entries) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 9 /* fields */);
- e.writeLong(0); // uid collection not implemented yet
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(1000); // uid collection not implemented yet
e.writeString(entry.handlerClassName);
e.writeString(entry.threadName);
e.writeString(entry.messageName);
@@ -988,11 +1040,13 @@
e.writeLong(entry.recordedMessageCount);
e.writeLong(entry.totalLatencyMicros);
e.writeLong(entry.cpuUsageMicros);
+ e.writeBoolean(entry.isInteractive);
pulledData.add(e);
}
}
- private void pullDiskStats(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullDiskStats(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
// Run a quick-and-dirty performance test: write 512 bytes
byte[] junk = new byte[512];
for (int i = 0; i < junk.length; i++) junk[i] = (byte) i; // Write nonzero bytes
@@ -1039,41 +1093,40 @@
}
// Add info pulledData.
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeLong(latency);
e.writeBoolean(fileBased);
e.writeInt(writeSpeed);
pulledData.add(e);
}
- private void pullDirectoryUsage(int tagId, List<StatsLogEventWrapper> pulledData) {
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ private void pullDirectoryUsage(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
StatFs statFsData = new StatFs(Environment.getDataDirectory().getAbsolutePath());
StatFs statFsSystem = new StatFs(Environment.getRootDirectory().getAbsolutePath());
StatFs statFsCache = new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__DATA);
e.writeLong(statFsData.getAvailableBytes());
e.writeLong(statFsData.getTotalBytes());
pulledData.add(e);
- e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__CACHE);
e.writeLong(statFsCache.getAvailableBytes());
e.writeLong(statFsCache.getTotalBytes());
pulledData.add(e);
- e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__SYSTEM);
e.writeLong(statFsSystem.getAvailableBytes());
e.writeLong(statFsSystem.getTotalBytes());
pulledData.add(e);
}
- private void pullAppSize(int tagId, List<StatsLogEventWrapper> pulledData) {
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ private void pullAppSize(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
try {
String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH);
JSONObject json = new JSONObject(jsonStr);
@@ -1091,7 +1144,7 @@
}
for (int i = 0; i < length; i++) {
StatsLogEventWrapper e =
- new StatsLogEventWrapper(elapsedNanos, tagId, 5 /* fields */);
+ new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeString(pkg_names.getString(i));
e.writeLong(app_sizes.optLong(i, -1L));
e.writeLong(app_data_sizes.optLong(i, -1L));
@@ -1104,62 +1157,62 @@
}
}
- private void pullCategorySize(int tagId, List<StatsLogEventWrapper> pulledData) {
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ private void pullCategorySize(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
try {
String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH);
JSONObject json = new JSONObject(jsonStr);
long cacheTime = json.optLong(DiskStatsFileLogger.LAST_QUERY_TIMESTAMP_KEY, -1L);
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_SIZE);
e.writeLong(json.optLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY, -1L));
e.writeLong(cacheTime);
pulledData.add(e);
- e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_DATA_SIZE);
e.writeLong(json.optLong(DiskStatsFileLogger.APP_DATA_SIZE_AGG_KEY, -1L));
e.writeLong(cacheTime);
pulledData.add(e);
- e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_CACHE_SIZE);
e.writeLong(json.optLong(DiskStatsFileLogger.APP_CACHE_AGG_KEY, -1L));
e.writeLong(cacheTime);
pulledData.add(e);
- e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__PHOTOS);
e.writeLong(json.optLong(DiskStatsFileLogger.PHOTOS_KEY, -1L));
e.writeLong(cacheTime);
pulledData.add(e);
- e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__VIDEOS);
e.writeLong(json.optLong(DiskStatsFileLogger.VIDEOS_KEY, -1L));
e.writeLong(cacheTime);
pulledData.add(e);
- e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__AUDIO);
e.writeLong(json.optLong(DiskStatsFileLogger.AUDIO_KEY, -1L));
e.writeLong(cacheTime);
pulledData.add(e);
- e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__DOWNLOADS);
e.writeLong(json.optLong(DiskStatsFileLogger.DOWNLOADS_KEY, -1L));
e.writeLong(cacheTime);
pulledData.add(e);
- e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__SYSTEM);
e.writeLong(json.optLong(DiskStatsFileLogger.SYSTEM_KEY, -1L));
e.writeLong(cacheTime);
pulledData.add(e);
- e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__OTHER);
e.writeLong(json.optLong(DiskStatsFileLogger.MISC_KEY, -1L));
e.writeLong(cacheTime);
@@ -1169,110 +1222,139 @@
}
}
+ private void pullNumFingerprints(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ FingerprintManager fingerprintManager = mContext.getSystemService(FingerprintManager.class);
+ if (fingerprintManager == null) {
+ return;
+ }
+ UserManager userManager = mContext.getSystemService(UserManager.class);
+ if (userManager == null) {
+ return;
+ }
+ final long token = Binder.clearCallingIdentity();
+ for (UserInfo user : userManager.getUsers()) {
+ final int userId = user.getUserHandle().getIdentifier();
+ final int numFingerprints = fingerprintManager.getEnrolledFingerprints(userId).size();
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(userId);
+ e.writeInt(numFingerprints);
+ pulledData.add(e);
+ }
+ Binder.restoreCallingIdentity(token);
+ }
+
/**
* Pulls various data.
*/
@Override // Binder call
public StatsLogEventWrapper[] pullData(int tagId) {
enforceCallingPermission();
- if (DEBUG)
+ if (DEBUG) {
Slog.d(TAG, "Pulling " + tagId);
+ }
List<StatsLogEventWrapper> ret = new ArrayList<>();
+ long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ long wallClockNanos = SystemClock.currentTimeMicro() * 1000L;
switch (tagId) {
case StatsLog.WIFI_BYTES_TRANSFER: {
- pullWifiBytesTransfer(tagId, ret);
+ pullWifiBytesTransfer(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.MOBILE_BYTES_TRANSFER: {
- pullMobileBytesTransfer(tagId, ret);
+ pullMobileBytesTransfer(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: {
- pullWifiBytesTransferByFgBg(tagId, ret);
+ pullWifiBytesTransferByFgBg(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: {
- pullMobileBytesTransferByFgBg(tagId, ret);
+ pullMobileBytesTransferByFgBg(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.BLUETOOTH_BYTES_TRANSFER: {
- pullBluetoothBytesTransfer(tagId, ret);
+ pullBluetoothBytesTransfer(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.KERNEL_WAKELOCK: {
- pullKernelWakelock(tagId, ret);
+ pullKernelWakelock(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.CPU_TIME_PER_FREQ: {
- pullCpuTimePerFreq(tagId, ret);
+ pullCpuTimePerFreq(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.CPU_TIME_PER_UID: {
- pullKernelUidCpuTime(tagId, ret);
+ pullKernelUidCpuTime(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.CPU_TIME_PER_UID_FREQ: {
- pullKernelUidCpuFreqTime(tagId, ret);
+ pullKernelUidCpuFreqTime(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.CPU_CLUSTER_TIME: {
- pullKernelUidCpuClusterTime(tagId, ret);
+ pullKernelUidCpuClusterTime(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.CPU_ACTIVE_TIME: {
- pullKernelUidCpuActiveTime(tagId, ret);
+ pullKernelUidCpuActiveTime(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.WIFI_ACTIVITY_INFO: {
- pullWifiActivityInfo(tagId, ret);
+ pullWifiActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.MODEM_ACTIVITY_INFO: {
- pullModemActivityInfo(tagId, ret);
+ pullModemActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.BLUETOOTH_ACTIVITY_INFO: {
- pullBluetoothActivityInfo(tagId, ret);
+ pullBluetoothActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.SYSTEM_UPTIME: {
- pullSystemUpTime(tagId, ret);
+ pullSystemUpTime(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.SYSTEM_ELAPSED_REALTIME: {
- pullSystemElapsedRealtime(tagId, ret);
+ pullSystemElapsedRealtime(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.PROCESS_MEMORY_STATE: {
- pullProcessMemoryState(tagId, ret);
+ pullProcessMemoryState(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.BINDER_CALLS: {
- pullBinderCallsStats(tagId, ret);
+ pullBinderCallsStats(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.BINDER_CALLS_EXCEPTIONS: {
- pullBinderCallsStatsExceptions(tagId, ret);
+ pullBinderCallsStatsExceptions(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.LOOPER_STATS: {
- pullLooperStats(tagId, ret);
+ pullLooperStats(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.DISK_STATS: {
- pullDiskStats(tagId, ret);
+ pullDiskStats(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.DIRECTORY_USAGE: {
- pullDirectoryUsage(tagId, ret);
+ pullDirectoryUsage(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.APP_SIZE: {
- pullAppSize(tagId, ret);
+ pullAppSize(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.CATEGORY_SIZE: {
- pullCategorySize(tagId, ret);
+ pullCategorySize(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.NUM_FINGERPRINTS: {
+ pullNumFingerprints(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
default:
@@ -1484,7 +1566,8 @@
// Thermal event received from vendor thermal management subsystem
private static final class ThermalEventListener extends IThermalEventListener.Stub {
- @Override public void notifyThrottling(boolean isThrottling, Temperature temp) {
+ @Override
+ public void notifyThrottling(boolean isThrottling, Temperature temp) {
StatsLog.write(StatsLog.THERMAL_THROTTLING, temp.getType(),
isThrottling ? 1 : 0, temp.getValue());
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 03a4d8e..6aca464 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -150,7 +150,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ToBooleanFunction;
-import com.android.internal.view.IInputMethodClient;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.utils.RotationCache;
import com.android.server.wm.utils.WmDisplayCutout;
@@ -409,6 +408,9 @@
private InputMonitor mInputMonitor;
+ /** Caches the value whether told display manager that we have content. */
+ private boolean mLastHasContent;
+
/**
* The input method window for this display.
*/
@@ -569,7 +571,7 @@
if (!w.mLayoutAttached) {
if (mTmpInitial) {
//Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
- w.mContentChanged = false;
+ w.resetContentChanged();
}
if (w.mAttrs.type == TYPE_DREAM) {
// Don't layout windows behind a dream, so that if it does stuff like hide
@@ -614,7 +616,7 @@
|| w.mLayoutNeeded) {
if (mTmpInitial) {
//Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
- w.mContentChanged = false;
+ w.resetContentChanged();
}
w.mLayoutNeeded = false;
w.prelayout();
@@ -697,7 +699,7 @@
final WindowStateAnimator winAnimator = w.mWinAnimator;
//Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
- w.mContentChanged = false;
+ w.resetContentChanged();
// Moved from updateWindowsAndWallpaperLocked().
if (w.mHasSurface) {
@@ -2946,7 +2948,7 @@
}
}
- boolean inputMethodClientHasFocus(IInputMethodClient client) {
+ boolean isInputMethodClientFocus(int uid, int pid) {
final WindowState imFocus = computeImeTarget(false /* updateImeTarget */);
if (imFocus == null) {
return false;
@@ -2958,17 +2960,13 @@
Slog.i(TAG_WM, "Last focus: " + mService.mLastFocus);
}
- final IInputMethodClient imeClient = imFocus.mSession.mClient;
-
if (DEBUG_INPUT_METHOD) {
- Slog.i(TAG_WM, "IM target client: " + imeClient);
- if (imeClient != null) {
- Slog.i(TAG_WM, "IM target client binder: " + imeClient.asBinder());
- Slog.i(TAG_WM, "Requesting client binder: " + client.asBinder());
- }
+ Slog.i(TAG_WM, "IM target uid/pid: " + imFocus.mSession.mUid
+ + "/" + imFocus.mSession.mPid);
+ Slog.i(TAG_WM, "Requesting client uid/pid: " + uid + "/" + pid);
}
- return imeClient != null && imeClient.asBinder() == client.asBinder();
+ return imFocus.mSession.mUid == uid && imFocus.mSession.mPid == pid;
}
boolean hasSecureWindowOnScreen() {
@@ -3092,8 +3090,9 @@
forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
prepareSurfaces();
+ mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
mService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
- mTmpApplySurfaceChangesTransactionState.displayHasContent,
+ mLastHasContent,
mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
mTmpApplySurfaceChangesTransactionState.preferredModeId,
true /* inTraversal, must call performTraversalInTrans... below */);
@@ -4267,4 +4266,11 @@
InputMonitor getInputMonitor() {
return mInputMonitor;
}
+
+ /**
+ * @return Cached value whether we told display manager that we have content.
+ */
+ boolean getLastHasContent() {
+ return mLastHasContent;
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 685c444..bd82553 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -21,6 +21,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -732,10 +733,13 @@
boolean shouldUpdateOrientationListener = false;
// Configure rotation suggestions.
- final int showRotationSuggestions = Settings.Secure.getIntForUser(resolver,
- Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
- Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT,
- UserHandle.USER_CURRENT);
+ final int showRotationSuggestions =
+ ActivityManager.isLowRamDeviceStatic()
+ ? Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DISABLED
+ : Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
+ Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT,
+ UserHandle.USER_CURRENT);
if (mShowRotationSuggestions != showRotationSuggestions) {
mShowRotationSuggestions = showRotationSuggestions;
shouldUpdateOrientationListener = true;
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index f9a71d3..acc9c03 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -38,7 +38,6 @@
import android.os.Parcel;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.Trace;
import android.os.UserHandle;
import android.util.MergedConfiguration;
@@ -56,10 +55,6 @@
import android.view.WindowManager;
import com.android.internal.os.logging.MetricsLoggerWrapper;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodClient;
-import com.android.server.LocalServices;
-import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.WindowManagerService.H;
import java.io.PrintWriter;
@@ -73,8 +68,6 @@
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final WindowManagerService mService;
final IWindowSessionCallback mCallback;
- final IInputMethodClient mClient;
- final InputMethodManagerInternal mInputMethodManagerInternal;
final int mUid;
final int mPid;
private final String mStringName;
@@ -95,17 +88,9 @@
private String mPackageName;
private String mRelayoutTag;
- public Session(WindowManagerService service, IWindowSessionCallback callback,
- IInputMethodClient client, IInputContext inputContext) {
+ public Session(WindowManagerService service, IWindowSessionCallback callback) {
mService = service;
mCallback = callback;
- mClient = client;
- // Depending on the timing when Session object gets called and SystemServer#mFactoryTestMode
- // this could be null, right?
- final InputMethodManagerInternal immInternal =
- LocalServices.getService(InputMethodManagerInternal.class);
- mInputMethodManagerInternal =
- immInternal != null ? immInternal : InputMethodManagerInternal.NOP;
mUid = Binder.getCallingUid();
mPid = Binder.getCallingPid();
mLastReportedAnimatorScale = service.getCurrentAnimatorScale();
@@ -134,12 +119,11 @@
sb.append("}");
mStringName = sb.toString();
- mInputMethodManagerInternal.addClient(client, inputContext, mUid, mPid);
try {
- client.asBinder().linkToDeath(this, 0);
+ mCallback.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
// The caller has died, so we can just forget about this.
- mInputMethodManagerInternal.removeClient(client);
+ // Hmmm, should we call killSessionLocked()??
}
}
@@ -159,9 +143,8 @@
@Override
public void binderDied() {
- mInputMethodManagerInternal.removeClient(mClient);
synchronized(mService.mWindowMap) {
- mClient.asBinder().unlinkToDeath(this, 0);
+ mCallback.asBinder().unlinkToDeath(this, 0);
mClientDead = true;
killSessionLocked();
}
diff --git a/services/core/java/com/android/server/wm/TEST_MAPPING b/services/core/java/com/android/server/wm/TEST_MAPPING
index c99329a..0c9a14b 100644
--- a/services/core/java/com/android/server/wm/TEST_MAPPING
+++ b/services/core/java/com/android/server/wm/TEST_MAPPING
@@ -21,7 +21,7 @@
"include-annotation": "android.platform.test.annotations.Presubmit"
},
{
- "exclude-annotation": "android.support.test.filters.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
},
@@ -35,7 +35,7 @@
"include-annotation": "android.platform.test.annotations.Presubmit"
},
{
- "exclude-annotation": "android.support.test.filters.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index cbe7d9d..20a874b 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -36,7 +36,6 @@
import android.graphics.Rect;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayCutout;
-import android.view.WindowManager;
import com.android.server.wm.utils.InsetUtils;
import com.android.server.wm.utils.WmDisplayCutout;
@@ -191,6 +190,10 @@
private final Rect mTmpRect = new Rect();
+ private boolean mHasOutsets;
+
+ private boolean mContentChanged;
+
public WindowFrames() {
}
@@ -237,11 +240,9 @@
* Calculates the outsets for this windowFrame. The outsets are calculated by the area between
* the {@link #mOutsetFrame} and the {@link #mContentFrame}. If there are no outsets, then
* {@link #mOutsets} is set to empty.
- *
- * @param hasOutsets Whether this frame has outsets.
*/
- void calculateOutsets(boolean hasOutsets) {
- if (hasOutsets) {
+ void calculateOutsets() {
+ if (mHasOutsets) {
InsetUtils.insetsBetweenFrames(mOutsetFrame, mContentFrame, mOutsets);
} else {
mOutsets.setEmpty();
@@ -249,7 +250,8 @@
}
/**
- * Calculate the insets for the type {@link WindowManager.LayoutParams#TYPE_DOCK_DIVIDER}
+ * Calculate the insets for the type
+ * {@link android.view.WindowManager.LayoutParams#TYPE_DOCK_DIVIDER}
*
* @param cutoutInsets The insets for the cutout.
*/
@@ -367,6 +369,28 @@
mLastContentInsets.set(-1, -1, -1, -1);
}
+ /**
+ * Sets whether the frame has outsets.
+ */
+ public void setHasOutsets(boolean hasOutsets) {
+ mHasOutsets = hasOutsets;
+ }
+
+ /**
+ * Sets whether the content has changed. This means that either the size or parent frame has
+ * changed.
+ */
+ public void setContentChanged(boolean contentChanged) {
+ mContentChanged = contentChanged;
+ }
+
+ /**
+ * @see #setContentChanged(boolean)
+ */
+ boolean hasContentChanged() {
+ return mContentChanged;
+ }
+
public void writeToProto(@NonNull ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
mParentFrame.writeToProto(proto, PARENT_FRAME);
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 793ce60..57cae39 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -30,7 +30,6 @@
import android.view.MagnificationSpec;
import android.view.WindowInfo;
-import com.android.internal.view.IInputMethodClient;
import com.android.server.input.InputManagerService;
import com.android.server.policy.WindowManagerPolicy;
@@ -444,9 +443,14 @@
public abstract boolean isUidFocused(int uid);
/**
- * Returns {@code true} if a process that is identified by {@code client} has IME focus.
+ * Checks whether the specified process has IME focus or not.
+ *
+ * @param uid UID of the process to be queried
+ * @param pid PID of the process to be queried
+ * @return {@code true} if a process that is identified by {@code uid} and {@code pid} has IME
+ * focus
*/
- public abstract boolean inputMethodClientHasFocus(IInputMethodClient client);
+ public abstract boolean isInputMethodClientFocus(int uid, int pid);
/**
* Return the display Id for given window.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2ed09ae..71ce1d9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -232,8 +232,6 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.LatencyTracker;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.WindowManagerPolicyThread;
import com.android.server.AnimationThread;
import com.android.server.DisplayThread;
@@ -5037,12 +5035,8 @@
// -------------------------------------------------------------
@Override
- public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
- IInputContext inputContext) {
- if (client == null) throw new IllegalArgumentException("null client");
- if (inputContext == null) throw new IllegalArgumentException("null inputContext");
- Session session = new Session(this, callback, client, inputContext);
- return session;
+ public IWindowSession openSession(IWindowSessionCallback callback) {
+ return new Session(this, callback);
}
@Override
@@ -5075,9 +5069,7 @@
throw new SecurityException("Must hold permission " +
android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
- if (displayId != DEFAULT_DISPLAY) {
- throw new IllegalArgumentException("Can only set the default display");
- }
+
final long ident = Binder.clearCallingIdentity();
try {
synchronized(mWindowMap) {
@@ -5110,9 +5102,7 @@
throw new SecurityException("Must hold permission " +
android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
- if (displayId != DEFAULT_DISPLAY) {
- throw new IllegalArgumentException("Can only set the default display");
- }
+
final long ident = Binder.clearCallingIdentity();
try {
synchronized(mWindowMap) {
@@ -5192,9 +5182,7 @@
throw new SecurityException("Must hold permission " +
android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
- if (displayId != DEFAULT_DISPLAY) {
- throw new IllegalArgumentException("Can only set the default display");
- }
+
final long ident = Binder.clearCallingIdentity();
try {
synchronized(mWindowMap) {
@@ -5241,9 +5229,6 @@
throw new SecurityException("Must hold permission " +
android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
- if (displayId != DEFAULT_DISPLAY) {
- throw new IllegalArgumentException("Can only set the default display");
- }
final int targetUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, true, "setForcedDisplayDensityForUser",
@@ -5272,9 +5257,6 @@
throw new SecurityException("Must hold permission " +
android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
- if (displayId != DEFAULT_DISPLAY) {
- throw new IllegalArgumentException("Can only set the default display");
- }
final int callingUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, true, "clearForcedDisplayDensityForUser",
@@ -7411,32 +7393,32 @@
}
}
- public boolean inputMethodClientHasFocus(IInputMethodClient client) {
- boolean hasFocus;
- synchronized (mWindowMap) {
- // Check all displays if any input method window has focus.
- for (int i = mRoot.mChildren.size() - 1; i >= 0; --i) {
- final DisplayContent displayContent = mRoot.mChildren.get(i);
- if (displayContent.inputMethodClientHasFocus(client)) {
+ @Override
+ public boolean isInputMethodClientFocus(int uid, int pid) {
+ synchronized (mWindowMap) {
+ // Check all displays if any input method window has focus.
+ for (int i = mRoot.mChildren.size() - 1; i >= 0; --i) {
+ final DisplayContent displayContent = mRoot.mChildren.get(i);
+ if (displayContent.isInputMethodClientFocus(uid, pid)) {
+ return true;
+ }
+ }
+
+ // Okay, how about this... what is the current focus?
+ // It seems in some cases we may not have moved the IM
+ // target window, such as when it was in a pop-up window,
+ // so let's also look at the current focus. (An example:
+ // go to Gmail, start searching so the keyboard goes up,
+ // press home. Sometimes the IME won't go down.)
+ // Would be nice to fix this more correctly, but it's
+ // way at the end of a release, and this should be good enough.
+ if (mCurrentFocus != null && mCurrentFocus.mSession.mUid == uid
+ && mCurrentFocus.mSession.mPid == pid) {
return true;
}
}
-
- // Okay, how about this... what is the current focus?
- // It seems in some cases we may not have moved the IM
- // target window, such as when it was in a pop-up window,
- // so let's also look at the current focus. (An example:
- // go to Gmail, start searching so the keyboard goes up,
- // press home. Sometimes the IME won't go down.)
- // Would be nice to fix this more correctly, but it's
- // way at the end of a release, and this should be good enough.
- if (mCurrentFocus != null && mCurrentFocus.mSession.mClient != null
- && mCurrentFocus.mSession.mClient.asBinder() == client.asBinder()) {
- return true;
- }
+ return false;
}
- return false;
- }
@Override
public int getDisplayIdForWindow(IBinder windowToken) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 6e0ccfd..831418b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -20,7 +20,6 @@
import android.graphics.Point;
import android.graphics.Rect;
-import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ShellCommand;
import android.os.UserHandle;
@@ -28,10 +27,6 @@
import android.view.Display;
import android.view.IWindowManager;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -87,21 +82,48 @@
return -1;
}
+ private int getDisplayId(String opt) {
+ int displayId = Display.DEFAULT_DISPLAY;
+ String option = "-d".equals(opt) ? opt : getNextOption();
+ if (option != null && "-d".equals(option)) {
+ try {
+ displayId = Integer.parseInt(getNextArgRequired());
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad number " + e);
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println("Error: " + e);
+ }
+ }
+ return displayId;
+ }
+
+ private void printInitialDisplaySize(PrintWriter pw , int displayId) {
+ final Point initialSize = new Point();
+ final Point baseSize = new Point();
+
+ try {
+ mInterface.getInitialDisplaySize(displayId, initialSize);
+ mInterface.getBaseDisplaySize(displayId, baseSize);
+ pw.println("Physical size: " + initialSize.x + "x" + initialSize.y);
+ if (!initialSize.equals(baseSize)) {
+ pw.println("Override size: " + baseSize.x + "x" + baseSize.y);
+ }
+ } catch (RemoteException e) {
+ // Can't call getInitialDisplaySize() on IWindowManager or
+ // Can't call getBaseDisplaySize() on IWindowManager
+ pw.println("Remote exception: " + e);
+ }
+ }
+
private int runDisplaySize(PrintWriter pw) throws RemoteException {
String size = getNextArg();
int w, h;
+ final int displayId = getDisplayId(size);
if (size == null) {
- Point initialSize = new Point();
- Point baseSize = new Point();
- try {
- mInterface.getInitialDisplaySize(Display.DEFAULT_DISPLAY, initialSize);
- mInterface.getBaseDisplaySize(Display.DEFAULT_DISPLAY, baseSize);
- pw.println("Physical size: " + initialSize.x + "x" + initialSize.y);
- if (!initialSize.equals(baseSize)) {
- pw.println("Override size: " + baseSize.x + "x" + baseSize.y);
- }
- } catch (RemoteException e) {
- }
+ printInitialDisplaySize(pw, displayId);
+ return 0;
+ } else if ("-d".equals(size)) {
+ printInitialDisplaySize(pw, displayId);
return 0;
} else if ("reset".equals(size)) {
w = h = -1;
@@ -114,8 +136,8 @@
String wstr = size.substring(0, div);
String hstr = size.substring(div+1);
try {
- w = parseDimension(wstr);
- h = parseDimension(hstr);
+ w = parseDimension(wstr, displayId);
+ h = parseDimension(hstr, displayId);
} catch (NumberFormatException e) {
getErrPrintWriter().println("Error: bad number " + e);
return -1;
@@ -123,27 +145,38 @@
}
if (w >= 0 && h >= 0) {
- // TODO(multidisplay): For now Configuration only applies to main screen.
- mInterface.setForcedDisplaySize(Display.DEFAULT_DISPLAY, w, h);
+ mInterface.setForcedDisplaySize(displayId, w, h);
} else {
- mInterface.clearForcedDisplaySize(Display.DEFAULT_DISPLAY);
+ mInterface.clearForcedDisplaySize(displayId);
}
return 0;
}
+ private void printInitialDisplayDensity(PrintWriter pw , int displayId) {
+ try {
+ final int initialDensity = mInterface.getInitialDisplayDensity(displayId);
+ final int baseDensity = mInterface.getBaseDisplayDensity(displayId);
+ pw.println("Physical density: " + initialDensity);
+ if (initialDensity != baseDensity) {
+ pw.println("Override density: " + baseDensity);
+ }
+ } catch (RemoteException e) {
+ // Can't call getInitialDisplayDensity() on IWindowManager or
+ // Can't call getBaseDisplayDensity() on IWindowManager
+ pw.println("Remote exception: " + e);
+ }
+ }
+
private int runDisplayDensity(PrintWriter pw) throws RemoteException {
String densityStr = getNextArg();
int density;
+ final int displayId = getDisplayId(densityStr);
+
if (densityStr == null) {
- try {
- int initialDensity = mInterface.getInitialDisplayDensity(Display.DEFAULT_DISPLAY);
- int baseDensity = mInterface.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
- pw.println("Physical density: " + initialDensity);
- if (initialDensity != baseDensity) {
- pw.println("Override density: " + baseDensity);
- }
- } catch (RemoteException e) {
- }
+ printInitialDisplayDensity(pw, displayId);
+ return 0;
+ } else if ("-d".equals(densityStr)) {
+ printInitialDisplayDensity(pw, displayId);
return 0;
} else if ("reset".equals(densityStr)) {
density = -1;
@@ -161,11 +194,10 @@
}
if (density > 0) {
- // TODO(multidisplay): For now Configuration only applies to main screen.
- mInterface.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, density,
+ mInterface.setForcedDisplayDensityForUser(displayId, density,
UserHandle.USER_CURRENT);
} else {
- mInterface.clearForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY,
+ mInterface.clearForcedDisplayDensityForUser(displayId,
UserHandle.USER_CURRENT);
}
return 0;
@@ -174,6 +206,7 @@
private int runDisplayOverscan(PrintWriter pw) throws RemoteException {
String overscanStr = getNextArgRequired();
Rect rect = new Rect();
+ final int displayId = getDisplayId(overscanStr);
if ("reset".equals(overscanStr)) {
rect.set(0, 0, 0, 0);
} else {
@@ -190,17 +223,16 @@
rect.bottom = Integer.parseInt(matcher.group(4));
}
- mInterface.setOverscan(Display.DEFAULT_DISPLAY, rect.left, rect.top, rect.right,
- rect.bottom);
+ mInterface.setOverscan(displayId, rect.left, rect.top, rect.right, rect.bottom);
return 0;
}
private int runDisplayScaling(PrintWriter pw) throws RemoteException {
String scalingStr = getNextArgRequired();
if ("auto".equals(scalingStr)) {
- mInterface.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 0);
+ mInterface.setForcedDisplayScalingMode(getDisplayId(scalingStr), 0);
} else if ("off".equals(scalingStr)) {
- mInterface.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 1);
+ mInterface.setForcedDisplayScalingMode(getDisplayId(scalingStr), 1);
} else {
getErrPrintWriter().println("Error: scaling must be 'auto' or 'off'");
return -1;
@@ -213,14 +245,14 @@
return 0;
}
- private int parseDimension(String s) throws NumberFormatException {
+ private int parseDimension(String s, int displayId) throws NumberFormatException {
if (s.endsWith("px")) {
return Integer.parseInt(s.substring(0, s.length() - 2));
}
if (s.endsWith("dp")) {
int density;
try {
- density = mInterface.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
+ density = mInterface.getBaseDisplayDensity(displayId);
} catch (RemoteException e) {
density = DisplayMetrics.DENSITY_DEFAULT;
}
@@ -236,14 +268,14 @@
pw.println("Window manager (window) commands:");
pw.println(" help");
pw.println(" Print this help text.");
- pw.println(" size [reset|WxH|WdpxHdp]");
+ pw.println(" size [reset|WxH|WdpxHdp] [-d DISPLAY_ID]");
pw.println(" Return or override display size.");
pw.println(" width and height in pixels unless suffixed with 'dp'.");
- pw.println(" density [reset|DENSITY]");
+ pw.println(" density [reset|DENSITY] [-d DISPLAY_ID]");
pw.println(" Return or override display density.");
- pw.println(" overscan [reset|LEFT,TOP,RIGHT,BOTTOM]");
+ pw.println(" overscan [reset|LEFT,TOP,RIGHT,BOTTOM] [-d DISPLAY ID]");
pw.println(" Set overscan area for display.");
- pw.println(" scaling [off|auto]");
+ pw.println(" scaling [off|auto] [-d DISPLAY_ID]");
pw.println(" Set display scaling mode.");
pw.println(" dismiss-keyguard");
pw.println(" Dismiss the keyguard, prompting user for auth if necessary.");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5272b66..f1ddda7 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -359,8 +359,6 @@
*/
private final Rect mInsetFrame = new Rect();
- boolean mContentChanged;
-
// If a window showing a wallpaper: the requested offset for the
// wallpaper; if a wallpaper window: the currently applied offset.
float mWallpaperX = -1;
@@ -787,7 +785,7 @@
}
@Override
- public void computeFrameLw(WindowFrames windowFrames) {
+ public void computeFrameLw() {
if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
// This window is being replaced and either already got information that it's being
// removed or we are still waiting for some information. Because of this we don't
@@ -796,8 +794,6 @@
return;
}
mHaveFrame = true;
- mWindowFrames.setParentFrameWasClippedByDisplayCutout(
- windowFrames.parentFrameWasClippedByDisplayCutout());
final Task task = getTask();
final boolean inFullscreenContainer = inFullscreenContainer();
@@ -827,10 +823,9 @@
final int layoutYDiff;
if (inFullscreenContainer || layoutInParentFrame()) {
// We use the parent frame as the containing frame for fullscreen and child windows
- mWindowFrames.mContainingFrame.set(windowFrames.mParentFrame);
- mWindowFrames.mDisplayFrame.set(windowFrames.mDisplayFrame);
- layoutDisplayFrame = windowFrames.mDisplayFrame;
- layoutContainingFrame = windowFrames.mParentFrame;
+ mWindowFrames.mContainingFrame.set(mWindowFrames.mParentFrame);
+ layoutDisplayFrame = mWindowFrames.mDisplayFrame;
+ layoutContainingFrame = mWindowFrames.mParentFrame;
layoutXDiff = 0;
layoutYDiff = 0;
} else {
@@ -849,17 +844,17 @@
// IME is up and obscuring this window. Adjust the window position so it is visible.
if (imeWin != null && imeWin.isVisibleNow() && isInputMethodTarget()) {
if (inFreeformWindowingMode() && mWindowFrames.mContainingFrame.bottom
- > windowFrames.mContentFrame.bottom) {
+ > mWindowFrames.mContentFrame.bottom) {
// In freeform we want to move the top up directly.
// TODO: Investigate why this is contentFrame not parentFrame.
mWindowFrames.mContainingFrame.top -= mWindowFrames.mContainingFrame.bottom
- - windowFrames.mContentFrame.bottom;
+ - mWindowFrames.mContentFrame.bottom;
} else if (!inPinnedWindowingMode() && mWindowFrames.mContainingFrame.bottom
- > windowFrames.mParentFrame.bottom) {
+ > mWindowFrames.mParentFrame.bottom) {
// But in docked we want to behave like fullscreen and behave as if the task
// were given smaller bounds for the purposes of layout. Skip adjustments for
// the pinned stack, they are handled separately in the PinnedStackController.
- mWindowFrames.mContainingFrame.bottom = windowFrames.mParentFrame.bottom;
+ mWindowFrames.mContainingFrame.bottom = mWindowFrames.mParentFrame.bottom;
}
}
@@ -868,7 +863,7 @@
// if it wasn't set already. No need to intersect it with the (visible)
// "content frame" since it is allowed to be outside the visible desktop.
if (mWindowFrames.mContainingFrame.isEmpty()) {
- mWindowFrames.mContainingFrame.set(windowFrames.mContentFrame);
+ mWindowFrames.mContainingFrame.set(mWindowFrames.mContentFrame);
}
}
@@ -878,10 +873,11 @@
// PIP edge case: When going from pinned to fullscreen, we apply a
// tempInsetFrame for the full task - but we're still at the start of the animation.
// To prevent a jump if there's a letterbox, restrict to the parent frame.
- mInsetFrame.intersectUnchecked(windowFrames.mParentFrame);
- mWindowFrames.mContainingFrame.intersectUnchecked(windowFrames.mParentFrame);
+ mInsetFrame.intersectUnchecked(mWindowFrames.mParentFrame);
+ mWindowFrames.mContainingFrame.intersectUnchecked(mWindowFrames.mParentFrame);
}
+ layoutDisplayFrame = new Rect(mWindowFrames.mDisplayFrame);
mWindowFrames.mDisplayFrame.set(mWindowFrames.mContainingFrame);
layoutXDiff =
!mInsetFrame.isEmpty() ? mInsetFrame.left - mWindowFrames.mContainingFrame.left
@@ -892,41 +888,24 @@
layoutContainingFrame =
!mInsetFrame.isEmpty() ? mInsetFrame : mWindowFrames.mContainingFrame;
mTmpRect.set(0, 0, dc.getDisplayInfo().logicalWidth, dc.getDisplayInfo().logicalHeight);
- subtractInsets(mWindowFrames.mDisplayFrame, layoutContainingFrame,
- windowFrames.mDisplayFrame, mTmpRect);
+ subtractInsets(mWindowFrames.mDisplayFrame, layoutContainingFrame, layoutDisplayFrame,
+ mTmpRect);
if (!layoutInParentFrame()) {
subtractInsets(mWindowFrames.mContainingFrame, layoutContainingFrame,
- windowFrames.mParentFrame, mTmpRect);
- subtractInsets(mInsetFrame, layoutContainingFrame, windowFrames.mParentFrame,
+ mWindowFrames.mParentFrame, mTmpRect);
+ subtractInsets(mInsetFrame, layoutContainingFrame, mWindowFrames.mParentFrame,
mTmpRect);
}
- layoutDisplayFrame = windowFrames.mDisplayFrame;
layoutDisplayFrame.intersect(layoutContainingFrame);
}
final int pw = mWindowFrames.mContainingFrame.width();
final int ph = mWindowFrames.mContainingFrame.height();
- if (!mWindowFrames.mParentFrame.equals(windowFrames.mParentFrame)) {
- //Slog.i(TAG_WM, "Window " + this + " content frame from " + mParentFrame
- // + " to " + parentFrame);
- mWindowFrames.mParentFrame.set(windowFrames.mParentFrame);
- mContentChanged = true;
- }
if (mRequestedWidth != mLastRequestedWidth || mRequestedHeight != mLastRequestedHeight) {
mLastRequestedWidth = mRequestedWidth;
mLastRequestedHeight = mRequestedHeight;
- mContentChanged = true;
- }
-
- mWindowFrames.mOverscanFrame.set(windowFrames.mOverscanFrame);
- mWindowFrames.mContentFrame.set(windowFrames.mContentFrame);
- mWindowFrames.mVisibleFrame.set(windowFrames.mVisibleFrame);
- mWindowFrames.mDecorFrame.set(windowFrames.mDecorFrame);
- mWindowFrames.mStableFrame.set(windowFrames.mStableFrame);
- final boolean hasOutsets = !windowFrames.mOutsetFrame.isEmpty();
- if (hasOutsets) {
- mWindowFrames.mOutsetFrame.set(windowFrames.mOutsetFrame);
+ mWindowFrames.setContentChanged(true);
}
final int fw = mWindowFrames.mFrame.width();
@@ -935,7 +914,7 @@
applyGravityAndUpdateFrame(layoutContainingFrame, layoutDisplayFrame);
// Calculate the outsets before the content frame gets shrinked to the window frame.
- mWindowFrames.calculateOutsets(hasOutsets);
+ mWindowFrames.calculateOutsets();
// Make sure the content and visible frames are inside of the
// final window frame.
@@ -997,7 +976,7 @@
}
if (mAttrs.type == TYPE_DOCK_DIVIDER) {
- final WmDisplayCutout c = windowFrames.mDisplayCutout.calculateRelativeTo(
+ final WmDisplayCutout c = mWindowFrames.mDisplayCutout.calculateRelativeTo(
mWindowFrames.mDisplayFrame);
mWindowFrames.calculateDockedDividerInsets(c.getDisplayCutout().getSafeInsets());
} else {
@@ -1006,7 +985,7 @@
}
mWindowFrames.setDisplayCutout(
- windowFrames.mDisplayCutout.calculateRelativeTo(mWindowFrames.mFrame));
+ mWindowFrames.mDisplayCutout.calculateRelativeTo(mWindowFrames.mFrame));
// Offset the actual frame by the amount layout frame is off.
mWindowFrames.offsetFrames(-layoutXDiff, -layoutYDiff);
@@ -1733,7 +1712,7 @@
* sense to call from performLayoutAndPlaceSurfacesLockedInner().)
*/
private boolean hasMoved() {
- return mHasSurface && (mContentChanged || mMovedByResize)
+ return mHasSurface && (mWindowFrames.hasContentChanged() || mMovedByResize)
&& !mAnimatingExit
&& (mWindowFrames.mFrame.top != mWindowFrames.mLastFrame.top
|| mWindowFrames.mFrame.left != mWindowFrames.mLastFrame.left)
@@ -4762,6 +4741,15 @@
return mWindowFrames.mVisibleInsets;
}
+ @Override
+ public WindowFrames getWindowFrames() {
+ return mWindowFrames;
+ }
+
+ void resetContentChanged() {
+ mWindowFrames.setContentChanged(false);
+ }
+
private final class MoveAnimationSpec implements AnimationSpec {
private final long mDuration;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index fec8039..979149a 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1176,15 +1176,17 @@
if (mIsWallpaper) {
w.dispatchWallpaperVisibility(true);
}
- // This draw means the difference between unique content and mirroring.
- // Run another pass through performLayout to set mHasContent in the
- // LogicalDisplay.
- mAnimator.setPendingLayoutChanges(w.getDisplayId(),
- FINISH_LAYOUT_REDO_ANIM);
- if (DEBUG_LAYOUT_REPEATS) {
- mService.mWindowPlacerLocked.debugLayoutRepeats(
- "showSurfaceRobustlyLocked " + w,
- mAnimator.getPendingLayoutChanges(w.getDisplayId()));
+ if (!w.getDisplayContent().getLastHasContent()) {
+ // This draw means the difference between unique content and mirroring.
+ // Run another pass through performLayout to set mHasContent in the
+ // LogicalDisplay.
+ mAnimator.setPendingLayoutChanges(w.getDisplayId(),
+ FINISH_LAYOUT_REDO_ANIM);
+ if (DEBUG_LAYOUT_REPEATS) {
+ mService.mWindowPlacerLocked.debugLayoutRepeats(
+ "showSurfaceRobustlyLocked " + w,
+ mAnimator.getPendingLayoutChanges(w.getDisplayId()));
+ }
}
} else {
w.setOrientationChanging(false);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 0ca0835..e76afa3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2588,12 +2588,32 @@
ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy)
throws SecurityException {
+ return getActiveAdminOrCheckPermissionForCallerLocked(who,
+ reqPolicy, /* permission= */ null);
+ }
+
+ /**
+ * Finds an active admin for the caller then checks {@code permission} if admin check failed.
+ *
+ * @return an active admin or {@code null} if there is no active admin but
+ * {@code permission} is granted
+ * @throws SecurityException if caller neither has an active admin nor {@code permission}
+ */
+ @Nullable
+ ActiveAdmin getActiveAdminOrCheckPermissionForCallerLocked(
+ ComponentName who,
+ int reqPolicy,
+ @Nullable String permission) throws SecurityException {
ensureLocked();
final int callingUid = mInjector.binderGetCallingUid();
ActiveAdmin result = getActiveAdminWithPolicyForUidLocked(who, reqPolicy, callingUid);
if (result != null) {
return result;
+ } else if (permission != null
+ && (mContext.checkCallingPermission(permission)
+ == PackageManager.PERMISSION_GRANTED)) {
+ return null;
}
if (who != null) {
@@ -2605,7 +2625,7 @@
if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) {
throw new SecurityException("Admin " + admin.info.getComponent()
- + " does not own the device");
+ + " does not own the device");
}
if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) {
throw new SecurityException("Admin " + admin.info.getComponent()
@@ -2621,20 +2641,39 @@
+ admin.info.getTagForPolicy(reqPolicy));
} else {
throw new SecurityException("No active admin owned by uid "
- + mInjector.binderGetCallingUid() + " for policy #" + reqPolicy);
+ + callingUid + " for policy #" + reqPolicy);
}
}
ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy, boolean parent)
throws SecurityException {
+ return getActiveAdminOrCheckPermissionForCallerLocked(
+ who, reqPolicy, parent, /* permission= */ null);
+ }
+
+ /**
+ * Finds an active admin for the caller then checks {@code permission} if admin check failed.
+ *
+ * @return an active admin or {@code null} if there is no active admin but
+ * {@code permission} is granted
+ * @throws SecurityException if caller neither has an active admin nor {@code permission}
+ */
+ @Nullable
+ ActiveAdmin getActiveAdminOrCheckPermissionForCallerLocked(
+ ComponentName who,
+ int reqPolicy,
+ boolean parent,
+ @Nullable String permission) throws SecurityException {
ensureLocked();
if (parent) {
enforceManagedProfile(mInjector.userHandleGetCallingUserId(),
"call APIs on the parent profile");
}
- ActiveAdmin admin = getActiveAdminForCallerLocked(who, reqPolicy);
+ ActiveAdmin admin = getActiveAdminOrCheckPermissionForCallerLocked(
+ who, reqPolicy, permission);
return parent ? admin.getParentActiveAdmin() : admin;
}
+
/**
* Find the admin for the component and userId bit of the uid, then check
* the admin's uid matches the uid.
@@ -4744,10 +4783,15 @@
preN = getTargetSdk(admin.info.getPackageName(),
userHandle) <= android.os.Build.VERSION_CODES.M;
} else {
- // Otherwise, make sure the caller has any active admin with the right policy.
- admin = getActiveAdminForCallerLocked(null,
- DeviceAdminInfo.USES_POLICY_RESET_PASSWORD);
- preN = getTargetSdk(admin.info.getPackageName(),
+ // Otherwise, make sure the caller has any active admin with the right policy or
+ // the required permission.
+ admin = getActiveAdminOrCheckPermissionForCallerLocked(
+ null,
+ DeviceAdminInfo.USES_POLICY_RESET_PASSWORD,
+ android.Manifest.permission.RESET_PASSWORD);
+ // Cannot be preN if admin is null because an exception would have been
+ // thrown before getting here
+ preN = admin == null ? false : getTargetSdk(admin.info.getPackageName(),
userHandle) <= android.os.Build.VERSION_CODES.M;
// As of N, password resetting to empty/null is not allowed anymore.
@@ -4763,9 +4807,9 @@
// As of N, password cannot be changed by the admin if it is already set.
if (isLockScreenSecureUnchecked(userHandle)) {
if (!preN) {
- throw new SecurityException("Admin cannot change current password");
+ throw new SecurityException("Cannot change current password");
} else {
- Slog.e(LOG_TAG, "Admin cannot change current password");
+ Slog.e(LOG_TAG, "Cannot change current password");
return false;
}
}
@@ -5136,31 +5180,37 @@
final int callingUserId = mInjector.userHandleGetCallingUserId();
synchronized (getLockObject()) {
- // This API can only be called by an active device admin,
- // so try to retrieve it to check that the caller is one.
- final ActiveAdmin admin = getActiveAdminForCallerLocked(
- null, DeviceAdminInfo.USES_POLICY_FORCE_LOCK, parent);
-
+ // Make sure the caller has any active admin with the right policy or
+ // the required permission.
+ final ActiveAdmin admin = getActiveAdminOrCheckPermissionForCallerLocked(
+ null,
+ DeviceAdminInfo.USES_POLICY_FORCE_LOCK,
+ parent,
+ android.Manifest.permission.LOCK_DEVICE);
final long ident = mInjector.binderClearCallingIdentity();
try {
- final ComponentName adminComponent = admin.info.getComponent();
- // Evict key
- if ((flags & DevicePolicyManager.FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY) != 0) {
- enforceManagedProfile(
- callingUserId, "set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY");
- if (!isProfileOwner(adminComponent, callingUserId)) {
- throw new SecurityException("Only profile owner admins can set "
- + "FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY");
+ final ComponentName adminComponent = admin == null ?
+ null : admin.info.getComponent();
+ if (adminComponent != null) {
+ // For Profile Owners only, callers with only permission not allowed.
+ if ((flags & DevicePolicyManager.FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY) != 0) {
+ // Evict key
+ enforceManagedProfile(
+ callingUserId, "set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY");
+ if (!isProfileOwner(adminComponent, callingUserId)) {
+ throw new SecurityException("Only profile owner admins can set "
+ + "FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY");
+ }
+ if (parent) {
+ throw new IllegalArgumentException(
+ "Cannot set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY for the parent");
+ }
+ if (!mInjector.storageManagerIsFileBasedEncryptionEnabled()) {
+ throw new UnsupportedOperationException(
+ "FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY only applies to FBE devices");
+ }
+ mUserManager.evictCredentialEncryptionKey(callingUserId);
}
- if (parent) {
- throw new IllegalArgumentException(
- "Cannot set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY for the parent");
- }
- if (!mInjector.storageManagerIsFileBasedEncryptionEnabled()) {
- throw new UnsupportedOperationException(
- "FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY only applies to FBE devices");
- }
- mUserManager.evictCredentialEncryptionKey(callingUserId);
}
// Lock all users unless this is a managed profile with a separate challenge
@@ -5179,7 +5229,7 @@
mInjector.getTrustManager().setDeviceLockedForUser(userToLock, true);
}
- if (SecurityLog.isLoggingEnabled()) {
+ if (SecurityLog.isLoggingEnabled() && adminComponent != null) {
final int affectedUserId =
parent ? getProfileParentId(callingUserId) : callingUserId;
SecurityLog.writeEvent(SecurityLog.TAG_REMOTE_LOCK,
@@ -5969,7 +6019,7 @@
success = mUserManagerInternal.removeUserEvenWhenDisallowed(userId);
if (!success) {
Slog.w(LOG_TAG, "Couldn't remove user " + userId);
- } else if (isManagedProfile(userId)) {
+ } else if (isManagedProfile(userId) && !TextUtils.isEmpty(wipeReasonForUser)) {
sendWipeProfileNotification(wipeReasonForUser);
}
} catch (RemoteException re) {
@@ -5984,7 +6034,6 @@
if (!mHasFeature) {
return;
}
- Preconditions.checkStringNotEmpty(wipeReasonForUser, "wipeReasonForUser is null or empty");
enforceFullCrossUsersPermission(mInjector.userHandleGetCallingUserId());
final ActiveAdmin admin;
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index 2338744..ffc7fa2 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -69,7 +69,6 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityRecordTests extends ActivityTestsBase {
- private ActivityTaskManagerService mService;
private TestActivityStack mStack;
private TaskRecord mTask;
private ActivityRecord mActivity;
@@ -79,10 +78,10 @@
public void setUp() throws Exception {
super.setUp();
- mService = createActivityTaskManagerService();
- mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ setupActivityTaskManagerService();
+ mStack = mSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- mTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build();
+ mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
mActivity = new ActivityBuilder(mService).setTask(mTask).build();
}
@@ -189,17 +188,13 @@
@Test
public void testCanBeLaunchedOnDisplay() throws Exception {
- testSupportsLaunchingResizeable(false /*taskPresent*/, true /*taskResizeable*/,
- true /*activityResizeable*/, true /*expected*/);
+ mService.mSupportsMultiWindow = true;
+ final ActivityRecord activity = new ActivityBuilder(mService).build();
- testSupportsLaunchingResizeable(false /*taskPresent*/, true /*taskResizeable*/,
- false /*activityResizeable*/, false /*expected*/);
-
- testSupportsLaunchingResizeable(true /*taskPresent*/, false /*taskResizeable*/,
- true /*activityResizeable*/, false /*expected*/);
-
- testSupportsLaunchingResizeable(true /*taskPresent*/, true /*taskResizeable*/,
- false /*activityResizeable*/, true /*expected*/);
+ // An activity can be launched on default display.
+ assertTrue(activity.canBeLaunchedOnDisplay(DEFAULT_DISPLAY));
+ // An activity cannot be launched on a non-existent display.
+ assertFalse(activity.canBeLaunchedOnDisplay(DEFAULT_DISPLAY + 1));
}
@Test
@@ -230,26 +225,4 @@
assertNull(mActivity.pendingOptions);
assertNotNull(activity2.pendingOptions);
}
-
- private void testSupportsLaunchingResizeable(boolean taskPresent, boolean taskResizeable,
- boolean activityResizeable, boolean expected) {
- mService.mSupportsMultiWindow = true;
-
- final TaskRecord task = taskPresent
- ? new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build() : null;
-
- if (task != null) {
- task.setResizeMode(taskResizeable ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE);
- }
-
- final ActivityRecord record = new ActivityBuilder(mService).setTask(task).build();
- record.info.resizeMode = activityResizeable
- ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE;
-
- record.canBeLaunchedOnDisplay(DEFAULT_DISPLAY);
-
-
- verify(mService.mStackSupervisor, times(1)).canPlaceEntityOnDisplay(anyInt(), eq(expected),
- anyInt(), anyInt(), eq(record.info));
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 1aa80c8..0345a81 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -64,8 +64,6 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityStackSupervisorTests extends ActivityTestsBase {
- private ActivityTaskManagerService mService;
- private ActivityStackSupervisor mSupervisor;
private ActivityStack mFullscreenStack;
@Before
@@ -73,8 +71,7 @@
public void setUp() throws Exception {
super.setUp();
- mService = createActivityTaskManagerService();
- mSupervisor = mService.mStackSupervisor;
+ setupActivityTaskManagerService();
mFullscreenStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 95f8fd1..ab814ee 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -60,8 +60,6 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityStackTests extends ActivityTestsBase {
- private ActivityTaskManagerService mService;
- private ActivityStackSupervisor mSupervisor;
private ActivityDisplay mDefaultDisplay;
private ActivityStack mStack;
private TaskRecord mTask;
@@ -71,9 +69,8 @@
public void setUp() throws Exception {
super.setUp();
- mService = createActivityTaskManagerService();
- mSupervisor = mService.mStackSupervisor;
- mDefaultDisplay = mService.mStackSupervisor.getDefaultDisplay();
+ setupActivityTaskManagerService();
+ mDefaultDisplay = mSupervisor.getDefaultDisplay();
mStack = mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index d032eb5..749403e 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -32,26 +32,32 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
+import static com.android.server.am.ActivityDisplay.POSITION_BOTTOM;
import static com.android.server.am.ActivityManagerService.ANIMATE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyObject;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.ActivityOptions;
import android.app.IApplicationThread;
+import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.WindowLayout;
@@ -65,28 +71,6 @@
import android.service.voice.IVoiceInteractionSession;
import android.view.Gravity;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
-import static com.android.server.am.ActivityManagerService.ANIMATE;
-import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyObject;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.times;
-
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -106,7 +90,6 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityStarterTests extends ActivityTestsBase {
- private ActivityTaskManagerService mService;
private ActivityStarter mStarter;
private ActivityStartController mController;
private ActivityMetricsLogger mActivityMetricsLogger;
@@ -130,7 +113,7 @@
@Override
public void setUp() throws Exception {
super.setUp();
- mService = createActivityTaskManagerService();
+ setupActivityTaskManagerService();
mController = mock(ActivityStartController.class);
mActivityMetricsLogger = mock(ActivityMetricsLogger.class);
clearInvocations(mActivityMetricsLogger);
@@ -323,7 +306,22 @@
}
}
- private ActivityStarter prepareStarter(int launchFlags) {
+ private ActivityStarter prepareStarter(@Intent.Flags int launchFlags) {
+ return prepareStarter(launchFlags, true /* mockGetLaunchStack */);
+ }
+
+ /**
+ * Creates a {@link ActivityStarter} with default parameters and necessary mocks.
+ *
+ * @param launchFlags The intent flags to launch activity.
+ * @param mockGetLaunchStack Whether to mock {@link ActivityStackSupervisor#getLaunchStack} for
+ * always launching to the testing stack. Set to false when allowing
+ * the activity can be launched to any stack that is decided by real
+ * implementation.
+ * @return A {@link ActivityStarter} with default setup.
+ */
+ private ActivityStarter prepareStarter(@Intent.Flags int launchFlags,
+ boolean mockGetLaunchStack) {
// always allow test to start activity.
doReturn(true).when(mService.mStackSupervisor).checkStartAnyActivityPermission(
any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
@@ -343,11 +341,13 @@
// return task when created.
doReturn(task).when(factory).create(any(), anyInt(), any(), any(), any(), any());
- // direct starter to use spy stack.
- doReturn(stack).when(mService.mStackSupervisor)
- .getLaunchStack(any(), any(), any(), anyBoolean());
- doReturn(stack).when(mService.mStackSupervisor)
- .getLaunchStack(any(), any(), any(), anyBoolean(), anyInt());
+ if (mockGetLaunchStack) {
+ // Direct starter to use spy stack.
+ doReturn(stack).when(mService.mStackSupervisor)
+ .getLaunchStack(any(), any(), any(), anyBoolean());
+ doReturn(stack).when(mService.mStackSupervisor)
+ .getLaunchStack(any(), any(), any(), anyBoolean(), anyInt());
+ }
// Set up mock package manager internal and make sure no unmocked methods are called
PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class,
@@ -546,4 +546,84 @@
eq(ActivityBuilder.getDefaultComponent().getPackageName()), anyInt(), anyBoolean(),
any(), eq(false));
}
+
+ /**
+ * This test ensures that when starting an existing single task activity on secondary display
+ * which is not the top focused display, it should deliver new intent to the activity and not
+ * create a new stack.
+ */
+ @Test
+ public void testDeliverIntentToTopActivityOfNonTopDisplay() {
+ final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
+ false /* mockGetLaunchStack */);
+
+ // Create a secondary display at bottom.
+ final TestActivityDisplay secondaryDisplay = spy(addNewActivityDisplayAt(POSITION_BOTTOM));
+ final ActivityStack stack = secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+ // Create an activity record on the top of secondary display.
+ final ComponentName componentName = ComponentName.createRelative(
+ DEFAULT_COMPONENT_PACKAGE_NAME,
+ DEFAULT_COMPONENT_PACKAGE_NAME + ".ReusableActivity");
+ final TaskRecord taskRecord = new TaskBuilder(mSupervisor)
+ .setComponent(componentName)
+ .setStack(stack)
+ .build();
+ final ActivityRecord topActivityOnSecondaryDisplay = new ActivityBuilder(mService)
+ .setComponent(componentName)
+ .setLaunchMode(LAUNCH_SINGLE_TASK)
+ .setTask(taskRecord)
+ .build();
+
+ // Put an activity on default display as the top focused activity.
+ new ActivityBuilder(mService).setCreateTask(true).build();
+
+ // Start activity with the same intent as {@code topActivityOnSecondaryDisplay}
+ // on secondary display.
+ final ActivityOptions options = ActivityOptions.makeBasic()
+ .setLaunchDisplayId(secondaryDisplay.mDisplayId);
+ final int result = starter.setReason("testDeliverIntentToTopActivityOfNonTopDisplay")
+ .setIntent(topActivityOnSecondaryDisplay.intent)
+ .setActivityOptions(options.toBundle())
+ .execute();
+
+ // Ensure result is delivering intent to top.
+ assertEquals(START_DELIVERED_TO_TOP, result);
+
+ // Ensure secondary display only creates one stack.
+ verify(secondaryDisplay, times(1)).createStack(anyInt(), anyInt(), anyBoolean());
+ }
+
+ /**
+ * This test ensures that a reused top activity in the top focused stack is able to be
+ * reparented to another display.
+ */
+ @Test
+ public void testReparentTopFocusedActivityToSecondaryDisplay() {
+ final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
+ false /* mockGetLaunchStack */);
+
+ // Create a secondary display at bottom.
+ final TestActivityDisplay secondaryDisplay = addNewActivityDisplayAt(POSITION_BOTTOM);
+ secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+ true /* onTop */);
+
+ // Put an activity on default display as the top focused activity.
+ final ActivityRecord topActivity = new ActivityBuilder(mService)
+ .setCreateTask(true)
+ .setLaunchMode(LAUNCH_SINGLE_TASK)
+ .build();
+
+ // Start activity with the same intent as {@code topActivity} on secondary display.
+ final ActivityOptions options = ActivityOptions.makeBasic()
+ .setLaunchDisplayId(secondaryDisplay.mDisplayId);
+ starter.setReason("testReparentTopFocusedActivityToSecondaryDisplay")
+ .setIntent(topActivity.intent)
+ .setActivityOptions(options.toBundle())
+ .execute();
+
+ // Ensure the activity is moved to secondary display.
+ assertEquals(secondaryDisplay, topActivity.getDisplay());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index bb8e5c5..9d09f5c 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -22,6 +22,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
@@ -56,11 +57,14 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerGlobal;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
import android.testing.DexmakerShareClassLoaderRule;
+import android.view.Display;
+import android.view.DisplayInfo;
import androidx.test.InstrumentationRegistry;
@@ -86,6 +90,8 @@
public class ActivityTestsBase {
private static boolean sOneTimeSetupDone = false;
+ private static int sNextDisplayId = DEFAULT_DISPLAY + 1;
+
@Rule
public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
new DexmakerShareClassLoaderRule();
@@ -93,6 +99,9 @@
private final Context mContext = InstrumentationRegistry.getContext();
private HandlerThread mHandlerThread;
+ ActivityTaskManagerService mService;
+ ActivityStackSupervisor mSupervisor;
+
// Default package name
static final String DEFAULT_COMPONENT_PACKAGE_NAME = "com.foo";
@@ -122,6 +131,11 @@
return atm;
}
+ void setupActivityTaskManagerService() {
+ mService = createActivityTaskManagerService();
+ mSupervisor = mService.mStackSupervisor;
+ }
+
ActivityManagerService createActivityManagerService() {
final TestActivityTaskManagerService atm =
spy(new TestActivityTaskManagerService(mContext));
@@ -134,6 +148,18 @@
return am;
}
+ /** Creates a {@link TestActivityDisplay}. */
+ TestActivityDisplay createNewActivityDisplay() {
+ return TestActivityDisplay.create(mSupervisor, sNextDisplayId++);
+ }
+
+ /** Creates and adds a {@link TestActivityDisplay} to supervisor at the given position. */
+ TestActivityDisplay addNewActivityDisplayAt(int position) {
+ final TestActivityDisplay display = createNewActivityDisplay();
+ mSupervisor.addChild(display, position);
+ return display;
+ }
+
void setupActivityManagerService(
TestActivityManagerService am, TestActivityTaskManagerService atm) {
atm.setActivityManagerService(am);
@@ -173,6 +199,7 @@
private boolean mCreateTask;
private ActivityStack mStack;
private int mActivityFlags;
+ private int mLaunchMode;
ActivityBuilder(ActivityTaskManagerService service) {
mService = service;
@@ -198,6 +225,11 @@
return this;
}
+ ActivityBuilder setLaunchMode(int launchMode) {
+ mLaunchMode = launchMode;
+ return this;
+ }
+
ActivityBuilder setStack(ActivityStack stack) {
mStack = stack;
return this;
@@ -233,6 +265,7 @@
aInfo.applicationInfo.packageName = mComponent.getPackageName();
aInfo.applicationInfo.uid = mUid;
aInfo.flags |= mActivityFlags;
+ aInfo.launchMode = mLaunchMode;
final ActivityRecord activity = new ActivityRecord(mService, null /* caller */,
0 /* launchedFromPid */, 0, null, intent, null,
@@ -500,7 +533,7 @@
@Override
public void initialize() {
super.initialize();
- mDisplay = spy(new TestActivityDisplay(this, DEFAULT_DISPLAY));
+ mDisplay = spy(TestActivityDisplay.create(this, DEFAULT_DISPLAY));
addChild(mDisplay, ActivityDisplay.POSITION_TOP);
}
@@ -516,10 +549,20 @@
}
protected static class TestActivityDisplay extends ActivityDisplay {
-
private final ActivityStackSupervisor mSupervisor;
- TestActivityDisplay(ActivityStackSupervisor supervisor, int displayId) {
- super(supervisor, displayId);
+
+ static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId) {
+ if (displayId == DEFAULT_DISPLAY) {
+ return new TestActivityDisplay(supervisor,
+ supervisor.mDisplayManager.getDisplay(displayId));
+ }
+ final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
+ new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
+ return new TestActivityDisplay(supervisor, display);
+ }
+
+ TestActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
+ super(supervisor, display);
// Normally this comes from display-properties as exposed by WM. Without that, just
// hard-code to FULLSCREEN for tests.
setWindowingMode(WINDOWING_MODE_FULLSCREEN);
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 5195214..70cfad1 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -870,8 +870,8 @@
@Override
public void initialize() {
super.initialize();
- mDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY);
- mOtherDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY);
+ mDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY);
+ mOtherDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY + 1);
addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
addChild(mDisplay, ActivityDisplay.POSITION_TOP);
}
diff --git a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
index d56c6a6..aa3046f 100644
--- a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
@@ -51,7 +51,6 @@
public class RunningTasksTest extends ActivityTestsBase {
private Context mContext = InstrumentationRegistry.getContext();
- private ActivityTaskManagerService mService;
private RunningTasks mRunningTasks;
@@ -60,21 +59,20 @@
public void setUp() throws Exception {
super.setUp();
- mService = createActivityTaskManagerService();
+ setupActivityTaskManagerService();
mRunningTasks = new RunningTasks();
}
@Test
public void testCollectTasksByLastActiveTime() throws Exception {
// Create a number of stacks with tasks (of incrementing active time)
- final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
final ArrayList<ActivityDisplay> displays = new ArrayList<>();
- final ActivityDisplay display = new TestActivityDisplay(supervisor, DEFAULT_DISPLAY);
+ final ActivityDisplay display = TestActivityDisplay.create(mSupervisor, DEFAULT_DISPLAY);
displays.add(display);
final int numStacks = 2;
for (int stackIndex = 0; stackIndex < numStacks; stackIndex++) {
- final ActivityStack stack = new TestActivityStack(display, stackIndex, supervisor,
+ final ActivityStack stack = new TestActivityStack(display, stackIndex, mSupervisor,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true);
display.addChild(stack, POSITION_BOTTOM);
}
diff --git a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
index b238e43..d34f951 100644
--- a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
+++ b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
@@ -30,7 +30,7 @@
public class FakeWindowState implements WindowManagerPolicy.WindowState {
- private WindowFrames windowFrames;
+ private WindowFrames mWindowFrames = new WindowFrames();
public WindowManager.LayoutParams attrs;
public int displayId;
@@ -53,41 +53,40 @@
}
@Override
- public void computeFrameLw(WindowFrames windowFrames) {
- this.windowFrames = windowFrames;
+ public void computeFrameLw() {
}
@Override
public Rect getFrameLw() {
- return windowFrames.mParentFrame;
+ return mWindowFrames.mParentFrame;
}
@Override
public Rect getDisplayFrameLw() {
- return windowFrames.mDisplayFrame;
+ return mWindowFrames.mDisplayFrame;
}
@Override
public Rect getOverscanFrameLw() {
- return windowFrames.mOverscanFrame;
+ return mWindowFrames.mOverscanFrame;
}
@Override
public Rect getContentFrameLw() {
- return windowFrames.mContentFrame;
+ return mWindowFrames.mContentFrame;
}
@Override
public Rect getVisibleFrameLw() {
- return windowFrames.mVisibleFrame;
+ return mWindowFrames.mVisibleFrame;
}
public Rect getStableFrame() {
- return windowFrames.mStableFrame;
+ return mWindowFrames.mStableFrame;
}
public Rect getDecorFrame() {
- return windowFrames.mDecorFrame;
+ return mWindowFrames.mDecorFrame;
}
@Override
@@ -252,6 +251,11 @@
}
@Override
+ public WindowFrames getWindowFrames() {
+ return mWindowFrames;
+ }
+
+ @Override
public boolean isInputMethodTarget() {
return false;
}
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 5039e42..8ac2930 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -21,18 +21,32 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.app.ActivityManagerInternal;
+import android.content.Context;
+import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.os.BatteryManagerInternal;
+import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerSaveState;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
-import com.android.server.power.batterysaver.BatterySaverController;
+import com.android.internal.app.IBatteryStats;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.lights.LightsManager;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.power.PowerManagerService.Injector;
+import com.android.server.power.PowerManagerService.NativeWrapper;
+import com.android.server.power.batterysaver.BatterySavingStats;
import org.junit.Rule;
import org.mockito.Mock;
@@ -47,11 +61,19 @@
private static final boolean BATTERY_SAVER_ENABLED = true;
private static final String TEST_LAST_REBOOT_PROPERTY = "test.sys.boot.reason";
- private @Mock BatterySaverPolicy mBatterySaverPolicy;
+ private @Mock BatterySaverPolicy mBatterySaverPolicyMock;
+ private @Mock LightsManager mLightsManagerMock;
+ private @Mock DisplayManagerInternal mDisplayManagerInternalMock;
+ private @Mock BatteryManagerInternal mBatteryManagerInternalMock;
+ private @Mock ActivityManagerInternal mActivityManagerInternalMock;
+ private @Mock PowerManagerService.NativeWrapper mNativeWrapperMock;
+ private @Mock Notifier mNotifierMock;
private PowerManagerService mService;
private PowerSaveState mPowerSaveState;
private DisplayPowerRequest mDisplayPowerRequest;
+
+
@Rule
public void setUp() throws Exception {
super.setUp();
@@ -61,11 +83,51 @@
.setBatterySaverEnabled(BATTERY_SAVER_ENABLED)
.setBrightnessFactor(BRIGHTNESS_FACTOR)
.build();
- when(mBatterySaverPolicy.getBatterySaverPolicy(
+ when(mBatterySaverPolicyMock.getBatterySaverPolicy(
eq(PowerManager.ServiceType.SCREEN_BRIGHTNESS), anyBoolean()))
.thenReturn(mPowerSaveState);
+
mDisplayPowerRequest = new DisplayPowerRequest();
- mService = new PowerManagerService(getContext(), mBatterySaverPolicy);
+ addLocalServiceMock(LightsManager.class, mLightsManagerMock);
+ addLocalServiceMock(DisplayManagerInternal.class, mDisplayManagerInternalMock);
+ addLocalServiceMock(BatteryManagerInternal.class, mBatteryManagerInternalMock);
+ addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock);
+
+ mService = new PowerManagerService(getContext(), new Injector() {
+ Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
+ SuspendBlocker suspendBlocker, WindowManagerPolicy policy) {
+ return mNotifierMock;
+ }
+
+ SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) {
+ return mock(SuspendBlocker.class);
+ }
+
+ BatterySaverPolicy createBatterySaverPolicy(
+ Object lock, Context context, BatterySavingStats batterySavingStats) {
+ return mBatterySaverPolicyMock;
+ }
+
+ NativeWrapper createNativeWrapper() {
+ return mNativeWrapperMock;
+ }
+ });
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ LocalServices.removeServiceForTest(LightsManager.class);
+ LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+ LocalServices.removeServiceForTest(BatteryManagerInternal.class);
+ LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+ }
+
+ /**
+ * Creates a mock and registers it to {@link LocalServices}.
+ */
+ private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
+ LocalServices.removeServiceForTest(clazz);
+ LocalServices.addService(clazz, mock);
}
@SmallTest
@@ -110,6 +172,25 @@
mService.setVrModeEnabled(false);
assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
DisplayPowerRequest.POLICY_BRIGHT);
+ }
+ @SmallTest
+ public void testWakefulnessAwake_InitialValue() throws Exception {
+ assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE);
+ }
+
+ @SmallTest
+ public void testWakefulnessSleep_NoDozeSleepFlag() throws Exception {
+ // Start with AWAKE state
+ assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE);
+
+ mService.systemReady(null);
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ // Take a nap with a flag.
+ mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(),
+ PowerManager.GO_TO_SLEEP_REASON_APPLICATION, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
+
+ assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index e648230..0886729 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -202,8 +202,8 @@
// When mFrame extends past cf, the content insets are
// the difference between mFrame and ContentFrame. Visible
// and stable frames work the same way.
- final WindowFrames windowFrames = new WindowFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
- w.computeFrameLw(windowFrames);
+ w.getWindowFrames().setFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
+ w.computeFrameLw();
assertFrame(w, 0, 0, 1000, 1000);
assertContentInset(w, 0, topContentInset, 0, bottomContentInset);
assertVisibleInset(w, 0, topVisibleInset, 0, bottomVisibleInset);
@@ -217,7 +217,7 @@
w.mAttrs.width = 100; w.mAttrs.height = 100; //have to clear MATCH_PARENT
w.mRequestedWidth = 100;
w.mRequestedHeight = 100;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, 100, 100, 200, 200);
assertContentInset(w, 0, 0, 0, 0);
// In this case the frames are shrunk to the window frame.
@@ -238,8 +238,8 @@
// Here the window has FILL_PARENT, FILL_PARENT
// so we expect it to fill the entire available frame.
- final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf);
- w.computeFrameLw(windowFrames);
+ w.getWindowFrames().setFrames(pf, pf, pf, pf, pf, pf, pf, pf);
+ w.computeFrameLw();
assertFrame(w, 0, 0, 1000, 1000);
// It can select various widths and heights within the bounds.
@@ -247,14 +247,14 @@
// and we use mRequestedWidth/mRequestedHeight
w.mAttrs.width = 300;
w.mAttrs.height = 300;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
// Explicit width and height without requested width/height
// gets us nothing.
assertFrame(w, 0, 0, 0, 0);
w.mRequestedWidth = 300;
w.mRequestedHeight = 300;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
// With requestedWidth/Height we can freely choose our size within the
// parent bounds.
assertFrame(w, 0, 0, 300, 300);
@@ -267,14 +267,14 @@
w.mRequestedWidth = -1;
w.mAttrs.width = 100;
w.mAttrs.height = 100;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, 0, 0, 100, 100);
w.mAttrs.flags = 0;
// But sizes too large will be clipped to the containing frame
w.mRequestedWidth = 1200;
w.mRequestedHeight = 1200;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, 0, 0, 1000, 1000);
// Before they are clipped though windows will be shifted
@@ -282,7 +282,7 @@
w.mAttrs.y = 300;
w.mRequestedWidth = 1000;
w.mRequestedHeight = 1000;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, 0, 0, 1000, 1000);
// If there is room to move around in the parent frame the window will be shifted according
@@ -292,16 +292,16 @@
w.mRequestedWidth = 300;
w.mRequestedHeight = 300;
w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, 700, 0, 1000, 300);
w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, 700, 700, 1000, 1000);
// Window specified x and y are interpreted as offsets in the opposite
// direction of gravity
w.mAttrs.x = 100;
w.mAttrs.y = 100;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, 600, 600, 900, 900);
}
@@ -322,8 +322,9 @@
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
- final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect);
- w.computeFrameLw(windowFrames);
+ final WindowFrames windowFrames = w.getWindowFrames();
+ windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect);
+ w.computeFrameLw();
// For non fullscreen tasks the containing frame is based off the
// task bounds not the parent frame.
assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
@@ -336,7 +337,7 @@
final int cfBottom = logicalHeight / 2;
final Rect cf = new Rect(0, 0, cfRight, cfBottom);
windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
int contentInsetRight = taskRight - cfRight;
int contentInsetBottom = taskBottom - cfBottom;
@@ -354,7 +355,7 @@
final int insetBottom = insetTop + (taskBottom - taskTop);
task.mInsetBounds.set(insetLeft, insetTop, insetRight, insetBottom);
windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
contentInsetRight = insetRight - cfRight;
contentInsetBottom = insetBottom - cfBottom;
@@ -384,13 +385,14 @@
// We use a decor content frame with insets to produce cropping.
Rect dcf = new Rect(cf);
- final WindowFrames windowFrames = new WindowFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
- w.computeFrameLw(windowFrames);
+ final WindowFrames windowFrames = w.getWindowFrames();
+ windowFrames.setFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
+ w.computeFrameLw();
assertPolicyCrop(w, 0, cf.top, logicalWidth, cf.bottom);
windowFrames.mDecorFrame.setEmpty();
// Likewise with no decor frame we would get no crop
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertPolicyCrop(w, 0, 0, logicalWidth, logicalHeight);
// Now we set up a window which doesn't fill the entire decor frame.
@@ -404,7 +406,7 @@
w.mAttrs.height = logicalHeight / 2;
w.mRequestedWidth = logicalWidth / 2;
w.mRequestedHeight = logicalHeight / 2;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
// Normally the crop is shrunk from the decor frame
// to the computed window frame.
@@ -437,8 +439,9 @@
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
- final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect);
- w.computeFrameLw(windowFrames);
+ final WindowFrames windowFrames = w.getWindowFrames();
+ windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect);
+ w.computeFrameLw();
// For non fullscreen tasks the containing frame is based off the
// task bounds not the parent frame.
assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
@@ -455,7 +458,7 @@
pf.set(0, 0, logicalWidth, logicalHeight);
task.mFullscreenForTest = true;
windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, cf.left, cf.top, cf.right, cf.bottom);
assertContentFrame(w, cf);
assertContentInset(w, 0, 0, 0, 0);
@@ -473,9 +476,10 @@
final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height());
- final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf);
+ final WindowFrames windowFrames = w.getWindowFrames();
+ windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf);
windowFrames.setDisplayCutout(cutout);
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50);
assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0);
@@ -497,9 +501,10 @@
final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height());
- final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf);
+ final WindowFrames windowFrames = w.getWindowFrames();
+ windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf);
windowFrames.setDisplayCutout(cutout);
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50);
assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index b43d9a6..6af3ea7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -391,14 +391,14 @@
@Test
public void testDisplayCutoutIsCalculatedRelativeToFrame() {
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
- WindowFrames wf = new WindowFrames();
+ WindowFrames wf = app.getWindowFrames();
wf.mParentFrame.set(7, 10, 185, 380);
wf.mDisplayFrame.set(wf.mParentFrame);
final DisplayCutout cutout = new DisplayCutout(new Rect(0, 15, 0, 22),
Arrays.asList(new Rect(95, 0, 105, 15), new Rect(95, 378, 105, 400)));
wf.setDisplayCutout(new WmDisplayCutout(cutout, new Size(200, 400)));
- app.computeFrameLw(wf);
+ app.computeFrameLw();
assertThat(app.getWmDisplayCutout().getDisplayCutout(), is(cutout.inset(7, 10, 5, 20)));
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 95d4a15..659c6e7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -20,6 +20,7 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Matchers.any;
@@ -34,6 +35,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.ServiceConnection;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -47,6 +49,9 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IntArray;
+import android.util.SparseArray;
import android.util.Xml;
import com.android.internal.util.FastXmlSerializer;
@@ -68,7 +73,9 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
+import java.util.Set;
public class ManagedServicesTest extends UiServiceTestCase {
@@ -113,7 +120,12 @@
when(mUm.getUserInfo(eq(user.id))).thenReturn(user);
}
when(mUm.getUsers()).thenReturn(users);
- when(mUserProfiles.getCurrentProfileIds()).thenReturn(new int[] {0, 10, 11, 12});
+ IntArray profileIds = new IntArray();
+ profileIds.add(0);
+ profileIds.add(11);
+ profileIds.add(10);
+ profileIds.add(12);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
mExpectedPrimaryPackages = new ArrayMap<>();
mExpectedPrimaryPackages.put(0, "this.is.a.package.name:another.package");
@@ -165,12 +177,12 @@
@Test
public void testBackupAndRestore_migration_preO() throws Exception {
- ArrayMap backupPrimaryPackages = new ArrayMap<>();
+ ArrayMap<Integer, String> backupPrimaryPackages = new ArrayMap<>();
backupPrimaryPackages.put(0, "backup.0:backup:0a");
backupPrimaryPackages.put(10, "10.backup");
backupPrimaryPackages.put(11, "eleven");
backupPrimaryPackages.put(12, "");
- ArrayMap backupPrimaryComponentNames = new ArrayMap<>();
+ ArrayMap<Integer, String> backupPrimaryComponentNames = new ArrayMap<>();
backupPrimaryComponentNames.put(0, "backup.first/whatever:a/b");
backupPrimaryComponentNames.put(10, "again/M1");
backupPrimaryComponentNames.put(11, "orange/youglad:itisnot/banana");
@@ -179,11 +191,11 @@
backupPrimary.put(APPROVAL_BY_PACKAGE, backupPrimaryPackages);
backupPrimary.put(APPROVAL_BY_COMPONENT, backupPrimaryComponentNames);
- ArrayMap backupSecondaryComponentNames = new ArrayMap<>();
+ ArrayMap<Integer, String> backupSecondaryComponentNames = new ArrayMap<>();
backupSecondaryComponentNames.put(0, "secondary.1/component.Name");
backupSecondaryComponentNames.put(10,
"this.is.another.package.backup/with.Component:component.backup/2");
- ArrayMap backupSecondaryPackages = new ArrayMap<>();
+ ArrayMap<Integer, String> backupSecondaryPackages = new ArrayMap<>();
backupSecondaryPackages.put(0, "");
backupSecondaryPackages.put(10,
"this.is.another.package.backup:package.backup");
@@ -368,7 +380,7 @@
serializer.endDocument();
serializer.flush();
- for (int userId : mUserProfiles.getCurrentProfileIds()) {
+ for (int userId : mUserProfiles.getCurrentProfileIds().toArray()) {
List<String> expected =
stringToList(mExpectedPrimary.get(approvalLevel).get(userId));
List<String> actual = stringToList(Settings.Secure.getStringForUser(
@@ -633,7 +645,7 @@
}
@Test
- public void testGetAllowedComponents() throws Exception {
+ public void testGetAllowedComponentsByUser() throws Exception {
ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
APPROVAL_BY_COMPONENT);
loadXml(service);
@@ -708,6 +720,145 @@
assertTrue(services.isSameUser(service, 10));
}
+ @Test
+ public void testGetAllowedComponents() throws Exception {
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+ loadXml(service);
+
+ SparseArray<ArraySet<ComponentName>> expected = new SparseArray<>();
+
+ ArraySet<ComponentName> expected10 = new ArraySet<>();
+ expected10.add(ComponentName.unflattenFromString("this.is.another.package/M1"));
+ expected10.add(ComponentName.unflattenFromString("this.is.another.package/with.Component"));
+ expected10.add(ComponentName.unflattenFromString("component/2"));
+ expected10.add(ComponentName.unflattenFromString("package/component2"));
+ expected.put(10, expected10);
+ ArraySet<ComponentName> expected0 = new ArraySet<>();
+ expected0.add(ComponentName.unflattenFromString("secondary/component.Name"));
+ expected0.add(ComponentName.unflattenFromString("this.is.a.package.name/Ba"));
+ expected0.add(ComponentName.unflattenFromString("another.package/B1"));
+ expected.put(0, expected0);
+ ArraySet<ComponentName> expected12 = new ArraySet<>();
+ expected12.add(ComponentName.unflattenFromString("bananas!/Bananas!"));
+ expected.put(12, expected12);
+ expected.put(11, new ArraySet<>());
+
+ SparseArray<ArraySet<ComponentName>> actual =
+ service.getAllowedComponents(mUserProfiles.getCurrentProfileIds());
+
+ assertContentsInAnyOrder(expected, actual);
+ }
+
+ @Test
+ public void testPopulateComponentsToUnbind_forceRebind() {
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+
+ IInterface iInterface = mock(IInterface.class);
+ when(iInterface.asBinder()).thenReturn(mock(IBinder.class));
+
+ ManagedServices.ManagedServiceInfo service0 = service.new ManagedServiceInfo(
+ iInterface, ComponentName.unflattenFromString("a/a"), 0, false,
+ mock(ServiceConnection.class), 26);
+ ManagedServices.ManagedServiceInfo service10 = service.new ManagedServiceInfo(
+ iInterface, ComponentName.unflattenFromString("b/b"), 10, false,
+ mock(ServiceConnection.class), 26);
+ Set<ManagedServices.ManagedServiceInfo> removableBoundServices = new ArraySet<>();
+ removableBoundServices.add(service0);
+ removableBoundServices.add(service10);
+
+ SparseArray<Set<ComponentName>> allowedComponentsToBind = new SparseArray<>();
+ Set<ComponentName> allowed0 = new ArraySet<>();
+ allowed0.add(ComponentName.unflattenFromString("a/a"));
+ allowedComponentsToBind.put(0, allowed0);
+ Set<ComponentName> allowed10 = new ArraySet<>();
+ allowed10.add(ComponentName.unflattenFromString("b/b"));
+ allowedComponentsToBind.put(10, allowed10);
+
+ SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
+
+ service.populateComponentsToUnbind(true, removableBoundServices, allowedComponentsToBind,
+ componentsToUnbind);
+
+ assertEquals(2, componentsToUnbind.size());
+ assertTrue(componentsToUnbind.get(0).contains(ComponentName.unflattenFromString("a/a")));
+ assertTrue(componentsToUnbind.get(10).contains(ComponentName.unflattenFromString("b/b")));
+ }
+
+ @Test
+ public void testPopulateComponentsToUnbind() {
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+
+ IInterface iInterface = mock(IInterface.class);
+ when(iInterface.asBinder()).thenReturn(mock(IBinder.class));
+
+ ManagedServices.ManagedServiceInfo service0 = service.new ManagedServiceInfo(
+ iInterface, ComponentName.unflattenFromString("a/a"), 0, false,
+ mock(ServiceConnection.class), 26);
+ ManagedServices.ManagedServiceInfo service0a = service.new ManagedServiceInfo(
+ iInterface, ComponentName.unflattenFromString("c/c"), 0, false,
+ mock(ServiceConnection.class), 26);
+ ManagedServices.ManagedServiceInfo service10 = service.new ManagedServiceInfo(
+ iInterface, ComponentName.unflattenFromString("b/b"), 10, false,
+ mock(ServiceConnection.class), 26);
+ Set<ManagedServices.ManagedServiceInfo> removableBoundServices = new ArraySet<>();
+ removableBoundServices.add(service0);
+ removableBoundServices.add(service0a);
+ removableBoundServices.add(service10);
+
+ SparseArray<Set<ComponentName>> allowedComponentsToBind = new SparseArray<>();
+ Set<ComponentName> allowed0 = new ArraySet<>();
+ allowed0.add(ComponentName.unflattenFromString("a/a"));
+ allowedComponentsToBind.put(0, allowed0);
+ Set<ComponentName> allowed10 = new ArraySet<>();
+ allowed10.add(ComponentName.unflattenFromString("b/b"));
+ allowedComponentsToBind.put(10, allowed10);
+
+ SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
+
+ service.populateComponentsToUnbind(false, removableBoundServices, allowedComponentsToBind,
+ componentsToUnbind);
+
+ assertEquals(1, componentsToUnbind.size());
+ assertEquals(1, componentsToUnbind.get(0).size());
+ assertTrue(componentsToUnbind.get(0).contains(ComponentName.unflattenFromString("c/c")));
+ }
+
+ @Test
+ public void populateComponentsToBind() {
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+
+ SparseArray<ArraySet<ComponentName>> approvedComponentsByUser = new SparseArray<>();
+ ArraySet<ComponentName> allowed0 = new ArraySet<>();
+ allowed0.add(ComponentName.unflattenFromString("a/a"));
+ approvedComponentsByUser.put(0, allowed0);
+ ArraySet<ComponentName> allowed10 = new ArraySet<>();
+ allowed10.add(ComponentName.unflattenFromString("b/b"));
+ allowed10.add(ComponentName.unflattenFromString("c/c"));
+ approvedComponentsByUser.put(10, allowed10);
+ ArraySet<ComponentName> allowed15 = new ArraySet<>();
+ allowed15.add(ComponentName.unflattenFromString("d/d"));
+ approvedComponentsByUser.put(15, allowed15);
+
+ IntArray users = new IntArray();
+ users.add(10);
+ users.add(0);
+
+ SparseArray<Set<ComponentName>> componentsToBind = new SparseArray<>();
+
+ service.populateComponentsToBind(componentsToBind, users, approvedComponentsByUser);
+
+ assertEquals(2, componentsToBind.size());
+ assertEquals(1, componentsToBind.get(0).size());
+ assertTrue(componentsToBind.get(0).contains(ComponentName.unflattenFromString("a/a")));
+ assertEquals(2, componentsToBind.get(10).size());
+ assertTrue(componentsToBind.get(10).contains(ComponentName.unflattenFromString("b/b")));
+ assertTrue(componentsToBind.get(10).contains(ComponentName.unflattenFromString("c/c")));
+ }
+
private void loadXml(ManagedServices service) throws Exception {
final StringBuffer xml = new StringBuffer();
xml.append("<" + service.getConfig().xmlTag + ">\n");
@@ -775,7 +926,8 @@
ManagedServices.ENABLED_SERVICES_SEPARATOR)));
}
- private void assertContentsInAnyOrder(List<?> expected, List<?> actual) {
+ private void assertContentsInAnyOrder(Collection<?> expected, Collection<?> actual) {
+ assertNotNull(actual);
assertEquals(expected.size(), actual.size());
for (Object o : expected) {
@@ -787,6 +939,21 @@
}
}
+ private void assertContentsInAnyOrder(SparseArray<ArraySet<ComponentName>> expected,
+ SparseArray<ArraySet<ComponentName>> actual) throws Exception {
+ assertEquals(expected.size(), actual.size());
+
+ for (int i = 0; i < expected.size(); i++) {
+ int key = expected.keyAt(i);
+ assertTrue(actual.indexOfKey(key) >= 0);
+ try {
+ assertContentsInAnyOrder(expected.valueAt(i), actual.get(key));
+ } catch (Throwable t) {
+ throw new Exception("Error validating " + key, t);
+ }
+ }
+ }
+
private void verifyExpectedBoundEntries(ManagedServices service, boolean primary)
throws Exception {
ArrayMap<Integer, String> verifyMap = primary ? mExpectedPrimary.get(service.mApprovalLevel)
@@ -920,7 +1087,7 @@
@Override
protected boolean checkType(IInterface service) {
- return false;
+ return true;
}
@Override
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index f9a4f78..1de1e4e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -33,6 +33,7 @@
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.os.UserManager;
+import android.util.IntArray;
import android.util.Xml;
import com.android.internal.util.FastXmlSerializer;
@@ -103,7 +104,12 @@
}
when(mUm.getUsers()).thenReturn(users);
when(mUm.getUsers(anyBoolean())).thenReturn(users);
- when(mUserProfiles.getCurrentProfileIds()).thenReturn(new int[] {0, 10, 11, 12});
+ IntArray profileIds = new IntArray();
+ profileIds.add(0);
+ profileIds.add(11);
+ profileIds.add(10);
+ profileIds.add(12);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 519b3ae..4344285 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -519,8 +519,9 @@
NotificationChannel channel = new NotificationChannel("id", "name",
IMPORTANCE_HIGH);
NotificationRecord r = generateNotificationRecord(channel);
- assertTrue(mService.isBlocked(r, mUsageStats));
- verify(mUsageStats, times(1)).registerSuspendedByAdmin(eq(r));
+
+ // isBlocked is only used for user blocking, not app suspension
+ assertFalse(mService.isBlocked(r, mUsageStats));
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 88c6fcf..b955e56 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -32,6 +32,7 @@
import android.service.notification.StatusBarNotification;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.IntArray;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -235,18 +236,22 @@
mSnoozeHelper.snooze(r2, 1000);
mSnoozeHelper.snooze(r3, 1000);
mSnoozeHelper.snooze(r4, 1000);
- when(mUserProfiles.getCurrentProfileIds()).thenReturn(
- new int[] {UserHandle.USER_SYSTEM});
+ IntArray profileIds = new IntArray();
+ profileIds.add(UserHandle.USER_SYSTEM);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
assertEquals(3, mSnoozeHelper.getSnoozed().size());
- when(mUserProfiles.getCurrentProfileIds()).thenReturn(
- new int[] {UserHandle.USER_CURRENT});
+ profileIds = new IntArray();
+ profileIds.add(UserHandle.USER_CURRENT);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
assertEquals(1, mSnoozeHelper.getSnoozed().size());
}
@Test
public void testGetSnoozedByUser_managedProfiles() throws Exception {
- when(mUserProfiles.getCurrentProfileIds()).thenReturn(
- new int[] {UserHandle.USER_SYSTEM, UserHandle.USER_CURRENT});
+ IntArray profileIds = new IntArray();
+ profileIds.add(UserHandle.USER_CURRENT);
+ profileIds.add(UserHandle.USER_SYSTEM);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM);
@@ -273,8 +278,9 @@
@Test
public void repostGroupSummary_repostsSummary() throws Exception {
- when(mUserProfiles.getCurrentProfileIds()).thenReturn(
- new int[] {UserHandle.USER_SYSTEM});
+ IntArray profileIds = new IntArray();
+ profileIds.add(UserHandle.USER_SYSTEM);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
NotificationRecord r = getNotificationRecord(
"pkg", 1, "one", UserHandle.SYSTEM, "group1", true);
NotificationRecord r2 = getNotificationRecord(
diff --git a/services/tests/wmtests/src/com/android/server/policy/DummyPolicyTests.java b/services/tests/wmtests/src/com/android/server/policy/DummyPolicyTests.java
new file mode 100644
index 0000000..03fb123
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/policy/DummyPolicyTests.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.policy;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+
+import androidx.test.filters.FlakyTest;
+
+/**
+ * Dummy test for com.android.server.policy.
+ * TODO(b/113800711): Remove this class once the actual tests are moved from servicestests.
+ */
+public class DummyPolicyTests {
+
+ @Presubmit
+ @Test
+ public void preSubmitTest() {}
+
+ @FlakyTest
+ @Presubmit
+ @Test
+ public void flakyPreSubmitTest() {}
+
+ @Test
+ public void postSubmitTest() {}
+
+ @FlakyTest
+ @Test
+ public void flakyPostSubmitTest() {}
+}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index c5d6dc7..99ad1f4 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -19,6 +19,8 @@
import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+
+import com.android.internal.app.IVoiceActionCheckCallback;
import com.android.server.wm.ActivityTaskManagerInternal;
import android.app.AppGlobals;
import android.content.ComponentName;
@@ -1128,6 +1130,27 @@
}
}
+ @Override
+ public void getActiveServiceSupportedActions(List<String> voiceActions,
+ IVoiceActionCheckCallback callback) {
+ enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
+ synchronized (this) {
+ if (mImpl == null) {
+ try {
+ callback.onComplete(null);
+ } catch (RemoteException e) {
+ }
+ return;
+ }
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ mImpl.getActiveServiceSupportedActions(voiceActions, callback);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+ }
+
public void onSessionShown() {
synchronized (this) {
final int size = mVoiceInteractionSessionListeners.beginBroadcast();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 57e9f66..61d7d6c 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -25,6 +25,8 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
+
+import com.android.internal.app.IVoiceActionCheckCallback;
import com.android.server.wm.ActivityTaskManagerInternal;
import android.app.IActivityManager;
import android.app.IActivityTaskManager;
@@ -57,6 +59,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConnection.Callback {
final static String TAG = "VoiceInteractionServiceManager";
@@ -177,6 +180,23 @@
activityTokens);
}
+ public void getActiveServiceSupportedActions(List<String> commands,
+ IVoiceActionCheckCallback callback) {
+ if (mService == null) {
+ Slog.w(TAG, "Not bound to voice interaction service " + mComponent);
+ try {
+ callback.onComplete(null);
+ } catch (RemoteException e) {
+ }
+ return;
+ }
+ try {
+ mService.getActiveServiceSupportedActions(commands, callback);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException while calling getActiveServiceSupportedActions", e);
+ }
+ }
+
public boolean hideSessionLocked() {
if (mActiveSession != null) {
return mActiveSession.hideLocked();
diff --git a/startop/scripts/app_startup/force_compiler_filter b/startop/scripts/app_startup/force_compiler_filter
index 78e915b..08f983d 100755
--- a/startop/scripts/app_startup/force_compiler_filter
+++ b/startop/scripts/app_startup/force_compiler_filter
@@ -100,36 +100,6 @@
fi
}
-get_activity_name() {
- local package="$1"
- local action_key="android.intent.action.MAIN:"
-
- local activity_line="$(adb shell cmd package query-activities --brief -a android.intent.action.MAIN -c android.intent.category.LAUNCHER | grep "$package")"
- verbose_print $activity_line
- IFS="/" read -a array <<< "$activity_line"
- local activity_name="${array[1]}"
- echo "$activity_name"
- #adb shell am start "$package/$activity_name"
-}
-
-remote_pidof() {
- local procname="$1"
- adb shell ps | grep "$procname" | awk '{print $2;}'
-}
-
-remote_pkill() {
- local procname="$1"
- shift
-
- local the_pids=$(remote_pidof "$procname")
- local pid
-
- for pid in $the_pids; do
- verbose_print adb shell kill "$@" "$pid"
- adb shell kill "$@" "$pid"
- done
-}
-
force_package_compilation() {
local arg_compiler_filter="$1"
local arg_package="$2"
@@ -150,13 +120,13 @@
# screen needs to be unlocked in order to run an app
"$DIR"/unlock_screen
- am_output="$(adb shell am start -S -W "$package"/"$activity")"
+ local output=$("$DIR"/launch_application "$package" "$activity")
if [[ $? -ne 0 ]]; then
- echo "am start failed" >&2
+ echo "launching application failed" >&2
exit 1
fi
- verbose_print "$am_output"
+ verbose_print "$output"
# give some time for app startup to complete.
# this is supposed to be an upper bound for measuring startup time.
sleep "$wait_time"
diff --git a/startop/scripts/app_startup/launch_application b/startop/scripts/app_startup/launch_application
index bc4ec51..8a68e50 100755
--- a/startop/scripts/app_startup/launch_application
+++ b/startop/scripts/app_startup/launch_application
@@ -20,6 +20,12 @@
launch_application() {
local package="$1"
local activity="$2"
+
+ # if there's any $s inside of the activity name, it needs to be escaped to \$.
+ # example '.app.honeycomb.Shell$HomeActivity'
+ # if the $ is not escaped, adb shell will try to evaluate $HomeActivity to a variable.
+ activity=${activity//\$/\\$}
+
local am_output="$(adb shell am start -S -W "$package"/"$activity")"
verbose_print adb shell am start -S -W "$package"/"$activity"
if [[ $? -ne 0 ]]; then
diff --git a/startop/scripts/app_startup/lib/common b/startop/scripts/app_startup/lib/common
index 4d5a53e..043d855 100755
--- a/startop/scripts/app_startup/lib/common
+++ b/startop/scripts/app_startup/lib/common
@@ -12,3 +12,42 @@
echo "$@" >&2
fi
}
+
+remote_pidof() {
+ local procname="$1"
+ adb shell ps | grep "$procname" | awk '{print $2;}'
+}
+
+remote_pkill() {
+ local procname="$1"
+ shift
+
+ local the_pids=$(remote_pidof "$procname")
+ local pid
+
+ for pid in $the_pids; do
+ verbose_print adb shell kill "$@" "$pid"
+ adb shell kill "$@" "$pid"
+ done
+}
+
+get_activity_name() {
+ local package="$1"
+ local action_key="android.intent.action.MAIN:"
+
+ # Example query-activities output being parsed:
+ #
+ # Activity #14:
+ # priority=0 preferredOrder=0 match=0x108000 specificIndex=-1 isDefault=true
+ # com.google.android.videos/com.google.android.youtube.videos.EntryPoint
+ # Activity #15:
+ # priority=0 preferredOrder=0 match=0x108000 specificIndex=-1 isDefault=true
+ # com.google.android.youtube/.app.honeycomb.Shell$HomeActivity
+
+ # Given package 'com.google.android.youtube' return '.app.honeycomb.Shell$HomeActivity'
+
+ local activity_line="$(adb shell cmd package query-activities --brief -a android.intent.action.MAIN -c android.intent.category.LAUNCHER | grep "$package/")"
+ IFS="/" read -a array <<< "$activity_line"
+ local activity_name="${array[1]}"
+ echo "$activity_name"
+}
diff --git a/startop/scripts/app_startup/run_app_with_prefetch b/startop/scripts/app_startup/run_app_with_prefetch
index ce63ff9..56bffa8 100755
--- a/startop/scripts/app_startup/run_app_with_prefetch
+++ b/startop/scripts/app_startup/run_app_with_prefetch
@@ -111,18 +111,6 @@
echo "$@"
}
-get_activity_name() {
- local package="$1"
- local action_key="android.intent.action.MAIN:"
-
- local activity_line="$(adb shell cmd package query-activities --brief -a android.intent.action.MAIN -c android.intent.category.LAUNCHER | grep "$package")"
- #echo $activity_line
- IFS="/" read -a array <<< "$activity_line"
- local activity_name="${array[1]}"
- echo "$activity_name"
- #adb shell am start "$package/$activity_name"
-}
-
find_package_path() {
local pkg="$1"
@@ -133,11 +121,6 @@
echo "$res"
}
-remote_pkill() {
- local what="$1"
- adb shell "for i in $(pid $what); do kill \$i; done"
-}
-
# Main entry point
if [[ $# -eq 0 ]]; then
usage
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ffbe7d3..7506d00 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1070,6 +1070,16 @@
/**
* Indexes of SPN format strings in wfcSpnFormats and wfcDataSpnFormats.
+ *
+ * <p>Available options are:
+ * <ul>
+ * <li> 0: %s</li>
+ * <li> 1: %s Wi-Fi Calling</li>
+ * <li> 2: WLAN Call</li>
+ * <li> 3: %s WLAN Call</li>
+ * <li> 4: %s Wi-Fi</li>
+ * <li> 5: WiFi Calling | %s</li>
+ * <li> 6: %s VoWifi</li>
* @hide
*/
public static final String KEY_WFC_SPN_FORMAT_IDX_INT = "wfc_spn_format_idx_int";
@@ -1077,6 +1087,15 @@
public static final String KEY_WFC_DATA_SPN_FORMAT_IDX_INT = "wfc_data_spn_format_idx_int";
/**
+ * Use root locale when reading wfcSpnFormats.
+ *
+ * If true, then the root locale will always be used when reading wfcSpnFormats. This means the
+ * non localized version of wfcSpnFormats will be used.
+ * @hide
+ */
+ public static final String KEY_WFC_SPN_USE_ROOT_LOCALE = "wfc_spn_use_root_locale";
+
+ /**
* The Component Name of the activity that can setup the emergency addrees for WiFi Calling
* as per carrier requirement.
* @hide
@@ -2263,6 +2282,7 @@
sDefaults.putStringArray(KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY, null);
sDefaults.putInt(KEY_WFC_SPN_FORMAT_IDX_INT, 0);
sDefaults.putInt(KEY_WFC_DATA_SPN_FORMAT_IDX_INT, 0);
+ sDefaults.putBoolean(KEY_WFC_SPN_USE_ROOT_LOCALE, false);
sDefaults.putString(KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING, "");
sDefaults.putBoolean(KEY_CONFIG_WIFI_DISABLE_IN_ECBM, false);
sDefaults.putBoolean(KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
@@ -2272,7 +2292,7 @@
sDefaults.putBoolean(KEY_SUPPORT_DIRECT_FDN_DIALING_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL, false);
sDefaults.putBoolean(KEY_SKIP_CF_FAIL_TO_DISABLE_DIALOG_BOOL, false);
- sDefaults.putBoolean(KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL, false);
+ sDefaults.putBoolean(KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL, true);
// MMS defaults
sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index b01d419..e9423f7 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -60,6 +60,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -1811,6 +1812,19 @@
*/
@UnsupportedAppUsage
public static Resources getResourcesForSubId(Context context, int subId) {
+ return getResourcesForSubId(context, subId, false);
+ }
+
+ /**
+ * Returns the resources associated with Subscription.
+ * @param context Context object
+ * @param subId Subscription Id of Subscription who's resources are required
+ * @param useRootLocale if root locale should be used. Localized locale is used if false.
+ * @return Resources associated with Subscription.
+ * @hide
+ */
+ public static Resources getResourcesForSubId(Context context, int subId,
+ boolean useRootLocale) {
final SubscriptionInfo subInfo =
SubscriptionManager.from(context).getActiveSubscriptionInfo(subId);
@@ -1822,6 +1836,11 @@
newConfig.mnc = subInfo.getMnc();
if (newConfig.mnc == 0) newConfig.mnc = Configuration.MNC_ZERO;
}
+
+ if (useRootLocale) {
+ newConfig.setLocale(Locale.ROOT);
+ }
+
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
DisplayMetrics newMetrics = new DisplayMetrics();
newMetrics.setTo(metrics);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fa24796..b5d1f06 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -6217,36 +6217,25 @@
/**
* @deprecated Use {@link android.telecom.TelecomManager#endCall()} instead.
* @hide
+ * @removed
*/
@Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.CALL_PHONE)
public boolean endCall() {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null)
- return telephony.endCall();
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#endCall", e);
- }
return false;
}
/**
* @deprecated Use {@link android.telecom.TelecomManager#acceptRingingCall} instead
* @hide
+ * @removed
*/
@Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void answerRingingCall() {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null)
- telephony.answerRingingCall();
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#answerRingingCall", e);
- }
+
}
/**
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
index 6bf22a0..8015b07 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
@@ -33,6 +33,7 @@
import com.android.internal.telephony.SmsConstants;
import java.io.UnsupportedEncodingException;
+import java.util.Locale;
/**
* Parses a GSM or UMTS format SMS-CB message into an {@link SmsCbMessage} object. The class is
@@ -44,16 +45,34 @@
* Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5.
*/
private static final String[] LANGUAGE_CODES_GROUP_0 = {
- "de", "en", "it", "fr", "es", "nl", "sv", "da", "pt", "fi", "no", "el", "tr", "hu",
- "pl", null
+ Locale.GERMAN.getLanguage(), // German
+ Locale.ENGLISH.getLanguage(), // English
+ Locale.ITALIAN.getLanguage(), // Italian
+ Locale.FRENCH.getLanguage(), // French
+ new Locale("es").getLanguage(), // Spanish
+ new Locale("nl").getLanguage(), // Dutch
+ new Locale("sv").getLanguage(), // Swedish
+ new Locale("da").getLanguage(), // Danish
+ new Locale("pt").getLanguage(), // Portuguese
+ new Locale("fi").getLanguage(), // Finnish
+ new Locale("nb").getLanguage(), // Norwegian
+ new Locale("el").getLanguage(), // Greek
+ new Locale("tr").getLanguage(), // Turkish
+ new Locale("hu").getLanguage(), // Hungarian
+ new Locale("pl").getLanguage(), // Polish
+ null
};
/**
* Languages in the 0010xxxx DCS group as defined in 3GPP TS 23.038, section 5.
*/
private static final String[] LANGUAGE_CODES_GROUP_2 = {
- "cs", "he", "ar", "ru", "is", null, null, null, null, null, null, null, null, null,
- null, null
+ new Locale("cs").getLanguage(), // Czech
+ new Locale("he").getLanguage(), // Hebrew
+ new Locale("ar").getLanguage(), // Arabic
+ new Locale("ru").getLanguage(), // Russian
+ new Locale("is").getLanguage(), // Icelandic
+ null, null, null, null, null, null, null, null, null, null, null
};
private static final char CARRIAGE_RETURN = 0x0d;
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index 4790b75..6b3df94 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -93,11 +93,23 @@
* converted. If the array size is more than needed, the rest of array remains unchanged.
*/
public static void bcdToBytes(String bcd, byte[] bytes) {
+ bcdToBytes(bcd, bytes, 0);
+ }
+
+ /**
+ * Converts BCD string to bytes and put it into the given byte array.
+ *
+ * @param bcd This should have an even length. If not, an "0" will be appended to the string.
+ * @param bytes If the array size is less than needed, the rest of the BCD string isn't be
+ * converted. If the array size is more than needed, the rest of array remains unchanged.
+ * @param offset the offset into the bytes[] to fill the data
+ */
+ public static void bcdToBytes(String bcd, byte[] bytes, int offset) {
if (bcd.length() % 2 != 0) {
bcd += "0";
}
- int size = Math.min(bytes.length * 2, bcd.length());
- for (int i = 0, j = 0; i + 1 < size; i += 2, j++) {
+ int size = Math.min((bytes.length - offset) * 2, bcd.length());
+ for (int i = 0, j = offset; i + 1 < size; i += 2, j++) {
bytes[j] = (byte) (charToByte(bcd.charAt(i + 1)) << 4 | charToByte(bcd.charAt(i)));
}
}
@@ -125,6 +137,20 @@
}
/**
+ * Convert a 5 or 6 - digit PLMN string to a nibble-swizzled encoding as per 24.008 10.5.1.3
+ *
+ * @param plmn the PLMN to convert
+ * @param data a byte array for the output
+ * @param offset the offset into data to start writing
+ */
+ public static void stringToBcdPlmn(final String plmn, byte[] data, int offset) {
+ char digit6 = (plmn.length() > 5) ? plmn.charAt(5) : 'F';
+ data[offset] = (byte) (charToByte(plmn.charAt(1)) << 4 | charToByte(plmn.charAt(0)));
+ data[offset + 1] = (byte) (charToByte(digit6) << 4 | charToByte(plmn.charAt(2)));
+ data[offset + 2] = (byte) (charToByte(plmn.charAt(4)) << 4 | charToByte(plmn.charAt(3)));
+ }
+
+ /**
* Some fields (like ICC ID) in GSM SIMs are stored as nibble-swizzled BCH
*/
public static String
@@ -844,7 +870,7 @@
}
/**
- * Converts a character of [0-9a-aA-F] to its hex value in a byte. If the character is not a
+ * Converts a character of [0-9a-fA-F] to its hex value in a byte. If the character is not a
* hex number, 0 will be returned.
*/
private static byte charToByte(char c) {
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java
index 1b63874..6500428 100644
--- a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java
@@ -16,24 +16,27 @@
package com.android.tests.sysmem.host;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-
/**
* Critical user journeys with which to exercise the system, driven from the
* host.
*/
public class Cujs {
- private ITestDevice mDevice;
+ private Device mDevice;
- public Cujs(ITestDevice device) {
+ public Cujs(Device device) {
this.mDevice = device;
}
/**
* Runs the critical user journeys.
*/
- public void run() throws DeviceNotAvailableException {
+ public void run() throws TestException {
+ // Do an explicit GC in the system server process as part of the test
+ // case to reduce GC-related sources of noise.
+ // SIGUSR1 = 10 is the magic signal to trigger the GC.
+ int pid = mDevice.getPidForProcess("system_server");
+ mDevice.executeShellCommand("kill -10 " + pid);
+
// Invoke the Device Cujs instrumentation to run the cujs.
// TODO: Consider exercising the system in other interesting ways as
// well.
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Device.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Device.java
new file mode 100644
index 0000000..03503ce
--- /dev/null
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Device.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.sysmem.host;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+
+import java.util.InputMismatchException;
+import java.util.Scanner;
+
+/**
+ * Wrapper around ITestDevice exposing useful device functions.
+ */
+class Device {
+
+ private ITestDevice mDevice;
+
+ Device(ITestDevice device) {
+ mDevice = device;
+ }
+
+ /**
+ * Execute a shell command and return the output as a string.
+ */
+ public String executeShellCommand(String command) throws TestException {
+ try {
+ return mDevice.executeShellCommand(command);
+ } catch (DeviceNotAvailableException e) {
+ throw new TestException(e);
+ }
+ }
+
+ /**
+ * Enable adb root
+ */
+ public void enableAdbRoot() throws TestException {
+ try {
+ mDevice.enableAdbRoot();
+ } catch (DeviceNotAvailableException e) {
+ throw new TestException(e);
+ }
+ }
+
+ /**
+ * Returns the pid for the process with the given name.
+ */
+ public int getPidForProcess(String name) throws TestException {
+ String psout = executeShellCommand("ps -A -o PID,CMD");
+ Scanner sc = new Scanner(psout);
+ try {
+ // ps output is of the form:
+ // PID CMD
+ // 1 init
+ // 2 kthreadd
+ // ...
+ // 9693 ps
+ sc.nextLine();
+ while (sc.hasNextLine()) {
+ int pid = sc.nextInt();
+ String cmd = sc.next();
+
+ if (name.equals(cmd)) {
+ return pid;
+ }
+ }
+ } catch (InputMismatchException e) {
+ throw new TestException("unexpected ps output format: " + psout, e);
+ }
+
+ throw new TestException("failed to get pid for process " + name);
+ }
+}
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/MemoryTest.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/MemoryTest.java
index cfd598d..48b7f39 100644
--- a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/MemoryTest.java
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/MemoryTest.java
@@ -16,7 +16,6 @@
package com.android.tests.sysmem.host;
-import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
@@ -27,8 +26,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.IOException;
-
/**
* Runs a system memory test.
*/
@@ -44,8 +41,9 @@
private Cujs mCujs;
@Override
- public void setDevice(ITestDevice device) {
- mTestDevice = device;
+ public void setDevice(ITestDevice testDevice) {
+ mTestDevice = testDevice;
+ Device device = new Device(testDevice);
mMetrics = new Metrics(device, testMetrics, testLogs);
mCujs = new Cujs(device);
}
@@ -56,14 +54,13 @@
}
// Invoke a single iteration of running the cujs.
- private void runCujs() throws DeviceNotAvailableException {
+ private void runCujs() throws TestException {
mCujs.run();
mIterations++;
}
// Sample desired memory.
- private void sample()
- throws DeviceNotAvailableException, IOException, Metrics.MetricsException {
+ private void sample() throws TestException {
mMetrics.sample(String.format("%03d", mIterations));
}
@@ -71,7 +68,7 @@
* Runs the memory tests.
*/
@Test
- public void run() throws Exception {
+ public void run() throws TestException {
sample(); // Sample before running cujs
runCujs();
sample(); // Sample after first iteration of cujs
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java
index 70bc22c..b408a81 100644
--- a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java
@@ -16,8 +16,6 @@
package com.android.tests.sysmem.host;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.result.FileInputStreamSource;
import com.android.tradefed.result.LogDataType;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
@@ -34,24 +32,11 @@
*/
class Metrics {
- private ITestDevice mDevice;
+ private Device mDevice;
private TestMetrics mMetrics;
private TestLogData mLogs;
/**
- * Exception thrown in case of error sampling metrics.
- */
- public static class MetricsException extends Exception {
- MetricsException(String msg) {
- super(msg);
- }
-
- MetricsException(String msg, Exception cause) {
- super(msg, cause);
- }
- }
-
- /**
* Constructs a metrics instance that will output high level metrics and
* more detailed breakdowns using the given <code>metrics</code> and
* <code>logs</code> objects.
@@ -60,7 +45,7 @@
* @param metrics where to log the high level metrics when taking a sample
* @param logs where to log detailed breakdowns when taking a sample
*/
- Metrics(ITestDevice device, TestMetrics metrics, TestLogData logs) {
+ Metrics(Device device, TestMetrics metrics, TestLogData logs) {
this.mDevice = device;
this.mMetrics = metrics;
this.mLogs = logs;
@@ -69,43 +54,17 @@
/**
* Writes the given <code>text</code> to a log with the given label.
*/
- private void logText(String label, String text) throws IOException {
- File file = File.createTempFile(label, "txt");
- PrintStream ps = new PrintStream(file);
- ps.print(text);
- try (FileInputStreamSource dataStream = new FileInputStreamSource(file)) {
- mLogs.addTestLog(label, LogDataType.TEXT, dataStream);
- }
- }
-
- /**
- * Returns the pid for the process with the given name.
- */
- private int getPidForProcess(String name)
- throws DeviceNotAvailableException, IOException, MetricsException {
- String psout = mDevice.executeShellCommand("ps -A -o PID,CMD");
- Scanner sc = new Scanner(psout);
+ private void logText(String label, String text) throws TestException {
try {
- // ps output is of the form:
- // PID CMD
- // 1 init
- // 2 kthreadd
- // ...
- // 9693 ps
- sc.nextLine();
- while (sc.hasNextLine()) {
- int pid = sc.nextInt();
- String cmd = sc.next();
-
- if (name.equals(cmd)) {
- return pid;
- }
+ File file = File.createTempFile(label, "txt");
+ PrintStream ps = new PrintStream(file);
+ ps.print(text);
+ try (FileInputStreamSource dataStream = new FileInputStreamSource(file)) {
+ mLogs.addTestLog(label, LogDataType.TEXT, dataStream);
}
- } catch (InputMismatchException e) {
- throw new MetricsException("unexpected ps output format: " + psout, e);
+ } catch (IOException e) {
+ throw new TestException(e);
}
-
- throw new MetricsException("failed to get pid for process " + name);
}
/**
@@ -116,11 +75,11 @@
*
* @param label prefix to use for metrics and logs output for this sample.
*/
- void sample(String label) throws DeviceNotAvailableException, IOException, MetricsException {
+ void sample(String label) throws TestException {
// adb root access is required to get showmap
mDevice.enableAdbRoot();
- int pid = getPidForProcess("system_server");
+ int pid = mDevice.getPidForProcess("system_server");
// Read showmap for system server and add it as a test log
String showmap = mDevice.executeShellCommand("showmap " + pid);
@@ -146,7 +105,7 @@
mMetrics.addTestMetric(label + ".system_server.rss", Long.toString(rss));
mMetrics.addTestMetric(label + ".system_server.pss", Long.toString(pss));
} catch (InputMismatchException e) {
- throw new MetricsException("unexpected showmap format", e);
+ throw new TestException("unexpected showmap format", e);
}
// Run debuggerd -j to get GC stats for system server and add it as a
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/TestException.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/TestException.java
new file mode 100644
index 0000000..dc3b68f
--- /dev/null
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/TestException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.sysmem.host;
+
+/**
+ * Exception thrown in case of unexpected error encountered when executing the
+ * test.
+ */
+class TestException extends Exception {
+ TestException(Exception cause) {
+ super(cause);
+ }
+
+ TestException(String msg) {
+ super(msg);
+ }
+
+ TestException(String msg, Exception cause) {
+ super(msg, cause);
+ }
+}
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 2ecf25b..c02ca21 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -83,6 +83,7 @@
"compile/Pseudolocalizer.cpp",
"compile/XmlIdCollector.cpp",
"configuration/ConfigurationParser.cpp",
+ "dump/DumpManifest.cpp",
"filter/AbiFilter.cpp",
"filter/ConfigFilter.cpp",
"format/Archive.cpp",
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index a73d56c..a20b9b7 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -69,8 +69,8 @@
return {};
}
- io::ZeroCopyInputAdaptor adaptor(in.get());
- if (!pb_table.ParseFromZeroCopyStream(&adaptor)) {
+ io::ProtoInputStreamReader proto_reader(in.get());
+ if (!proto_reader.ReadMessage(&pb_table)) {
diag->Error(DiagMessage(source) << "failed to read " << kProtoResourceTablePath);
return {};
}
@@ -97,8 +97,8 @@
}
pb::XmlNode pb_node;
- io::ZeroCopyInputAdaptor manifest_adaptor(manifest_in.get());
- if (!pb_node.ParseFromZeroCopyStream(&manifest_adaptor)) {
+ io::ProtoInputStreamReader proto_reader(manifest_in.get());
+ if (!proto_reader.ReadMessage(&pb_node)) {
diag->Error(DiagMessage(source) << "failed to read proto " << kAndroidManifestPath);
return {};
}
@@ -255,7 +255,7 @@
}
std::unique_ptr<xml::XmlResource> LoadedApk::LoadXml(const std::string& file_path,
- IDiagnostics* diag) {
+ IDiagnostics* diag) const {
io::IFile* file = apk_->FindFile(file_path);
if (file == nullptr) {
diag->Error(DiagMessage() << "failed to find file");
@@ -270,9 +270,9 @@
return nullptr;
}
- io::ZeroCopyInputAdaptor adaptor(in.get());
pb::XmlNode pb_node;
- if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
+ io::ProtoInputStreamReader proto_reader(in.get());
+ if (!proto_reader.ReadMessage(&pb_node)) {
diag->Error(DiagMessage() << "failed to parse file as proto XML");
return nullptr;
}
@@ -317,8 +317,8 @@
std::unique_ptr<io::InputStream> manifest_in = manifest_file->OpenInputStream();
if (manifest_in != nullptr) {
pb::XmlNode pb_node;
- io::ZeroCopyInputAdaptor manifest_adaptor(manifest_in.get());
- if (pb_node.ParseFromZeroCopyStream(&manifest_adaptor)) {
+ io::ProtoInputStreamReader proto_reader(manifest_in.get());
+ if (!proto_reader.ReadMessage(&pb_node)) {
return ApkFormat::kProto;
}
}
diff --git a/tools/aapt2/LoadedApk.h b/tools/aapt2/LoadedApk.h
index dcb085a..84c57c1 100644
--- a/tools/aapt2/LoadedApk.h
+++ b/tools/aapt2/LoadedApk.h
@@ -111,7 +111,7 @@
IArchiveWriter* writer, xml::XmlResource* manifest = nullptr);
/** Loads the file as an xml document. */
- std::unique_ptr<xml::XmlResource> LoadXml(const std::string& file_path, IDiagnostics* diag);
+ std::unique_ptr<xml::XmlResource> LoadXml(const std::string& file_path, IDiagnostics* diag) const;
private:
DISALLOW_COPY_AND_ASSIGN(LoadedApk);
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 86b1f4c..954f1ed 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -170,8 +170,8 @@
}
pb::XmlNode pb_node;
- io::ZeroCopyInputAdaptor adaptor(in.get());
- if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
+ io::ProtoInputStreamReader proto_reader(in.get());
+ if (!proto_reader.ReadMessage(&pb_node)) {
context_->GetDiagnostics()->Error(DiagMessage(source_)
<< "failed to parse proto XML " << *file->path);
return false;
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index 5cb30b6..91e3977 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -223,6 +223,12 @@
return 1;
}
+ ResourceTable* table = loaded_apk->GetResourceTable();
+ if (!table) {
+ diag_->Error(DiagMessage() << "Failed to retrieve resource table.");
+ return 1;
+ }
+
io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
Printer printer(&fout);
@@ -233,7 +239,7 @@
// Insert the configurations into a set in order to keep every configuarion seen
std::set<ConfigDescription, decltype(compare)> configs(compare);
- for (auto& package : loaded_apk->GetResourceTable()->packages) {
+ for (auto& package : table->packages) {
for (auto& type : package->types) {
for (auto& entry : type->entries) {
for (auto& value : entry->values) {
@@ -267,10 +273,15 @@
return 1;
}
+ ResourceTable* table = loaded_apk->GetResourceTable();
+ if (!table) {
+ diag_->Error(DiagMessage() << "Failed to retrieve resource table.");
+ return 1;
+ }
+
// Load the run-time xml string pool using the flattened data
BigBuffer buffer(4096);
- StringPool::FlattenUtf8(&buffer, loaded_apk->GetResourceTable()->string_pool,
- context.GetDiagnostics());
+ StringPool::FlattenUtf8(&buffer, table->string_pool, context.GetDiagnostics());
auto data = buffer.to_string();
android::ResStringPool pool(data.data(), data.size(), false);
Debug::DumpResStringPool(&pool, &printer);
@@ -304,7 +315,13 @@
printer.Println("Binary APK");
}
- Debug::PrintTable(*loaded_apk->GetResourceTable(), print_options, &printer);
+ ResourceTable* table = loaded_apk->GetResourceTable();
+ if (!table) {
+ diag_->Error(DiagMessage() << "Failed to retrieve resource table.");
+ return 1;
+ }
+
+ Debug::PrintTable(*table, print_options, &printer);
}
return 0;
diff --git a/tools/aapt2/cmd/Dump.h b/tools/aapt2/cmd/Dump.h
index 0724d62..9ec820d 100644
--- a/tools/aapt2/cmd/Dump.h
+++ b/tools/aapt2/cmd/Dump.h
@@ -19,6 +19,7 @@
#include "Command.h"
#include "Debug.h"
+#include "dump/DumpManifest.h"
namespace aapt {
@@ -133,8 +134,10 @@
public:
explicit DumpCommand(IDiagnostics* diag) : Command("dump", "d"), diag_(diag) {
AddOptionalSubcommand(util::make_unique<DumpAPCCommand>(diag_));
+ AddOptionalSubcommand(util::make_unique<DumpBadgingCommand>(diag_));
AddOptionalSubcommand(util::make_unique<DumpConfigsCommand>(diag_));
AddOptionalSubcommand(util::make_unique<DumpPackageNameCommand>(diag_));
+ AddOptionalSubcommand(util::make_unique<DumpPermissionsCommand>(diag_));
AddOptionalSubcommand(util::make_unique<DumpStringsCommand>(diag_));
AddOptionalSubcommand(util::make_unique<DumpTableCommand>(diag_));
AddOptionalSubcommand(util::make_unique<DumpXmlStringsCommand>(diag_));
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 119f56a..13c1047 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -1842,9 +1842,15 @@
} else {
// Adjust the SplitConstraints so that their SDK version is stripped if it is less than or
// equal to the minSdk.
+ const size_t origConstraintSize = options_.split_constraints.size();
options_.split_constraints =
AdjustSplitConstraintsForMinSdk(context_->GetMinSdkVersion(), options_.split_constraints);
+ if (origConstraintSize != options_.split_constraints.size()) {
+ context_->GetDiagnostics()->Warn(DiagMessage()
+ << "requested to split resources prior to min sdk of "
+ << context_->GetMinSdkVersion());
+ }
TableSplitter table_splitter(options_.split_constraints, options_.table_splitter_options);
if (!table_splitter.VerifySplitConstraints(context_)) {
return 1;
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index c6c82b0..5862d31 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -73,6 +73,7 @@
}
*out_path = parts[0];
+ out_split->name = parts[1];
for (const StringPiece& config_str : util::Tokenize(parts[1], ',')) {
ConfigDescription config;
if (!ConfigDescription::Parse(config_str, &config)) {
@@ -119,12 +120,15 @@
for (const SplitConstraints& constraints : split_constraints) {
SplitConstraints constraint;
for (const ConfigDescription& config : constraints.configs) {
- if (config.sdkVersion <= min_sdk) {
- constraint.configs.insert(config.CopyWithoutSdkVersion());
- } else {
- constraint.configs.insert(config);
+ const ConfigDescription &configToInsert = (config.sdkVersion <= min_sdk)
+ ? config.CopyWithoutSdkVersion()
+ : config;
+ // only add the config if it actually selects something
+ if (configToInsert != ConfigDescription::DefaultConfig()) {
+ constraint.configs.insert(configToInsert);
}
}
+ constraint.name = constraints.name;
adjusted_constraints.push_back(std::move(constraint));
}
return adjusted_constraints;
diff --git a/tools/aapt2/cmd/Util_test.cpp b/tools/aapt2/cmd/Util_test.cpp
index b9fb5b2..158ef29 100644
--- a/tools/aapt2/cmd/Util_test.cpp
+++ b/tools/aapt2/cmd/Util_test.cpp
@@ -22,6 +22,10 @@
#include "test/Test.h"
namespace aapt {
+#define EXPECT_CONFIG_EQ(constraints, config) \
+ EXPECT_EQ(constraints.configs.size(), 1); \
+ EXPECT_EQ(*constraints.configs.begin(), config); \
+ constraints.configs.clear();
TEST(UtilTest, SplitNamesAreSanitized) {
AppInfo app_info{"com.pkg"};
@@ -84,4 +88,287 @@
EXPECT_EQ(compiled_version_code_major->value.data, 0x61);
}
+
+TEST (UtilTest, ParseSplitParameter) {
+ IDiagnostics* diagnostics = test::ContextBuilder().Build().get()->GetDiagnostics();
+ std::string path;
+ SplitConstraints constraints;
+ ConfigDescription expected_configuration;
+
+ // ========== Test IMSI ==========
+ // mcc: 'mcc[0-9]{3}'
+ // mnc: 'mnc[0-9]{1,3}'
+ ASSERT_TRUE(ParseSplitParameter(":mcc310",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setMcc(0x0136)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":mcc310-mnc004",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setMcc(0x0136)
+ .setMnc(0x0004)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":mcc310-mnc000",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setMcc(0x0136)
+ .setMnc(0xFFFF)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ // ========== Test LOCALE ==========
+ // locale: '[a-z]{2,3}(-r[a-z]{2})?'
+ // locale: 'b+[a-z]{2,3}(+[a-z[0-9]]{2})?'
+ ASSERT_TRUE(ParseSplitParameter(":es",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setLanguage(0x6573)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":fr-rCA",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setLanguage(0x6672)
+ .setCountry(0x4341)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":b+es+419",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setLanguage(0x6573)
+ .setCountry(0xA424)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ // ========== Test SCREEN_TYPE ==========
+ // orientation: '(port|land|square)'
+ // touchscreen: '(notouch|stylus|finger)'
+ // density" '(anydpi|nodpi|ldpi|mdpi|tvdpi|hdpi|xhdpi|xxhdpi|xxxhdpi|[0-9]*dpi)'
+ ASSERT_TRUE(ParseSplitParameter(":square",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setOrientation(0x03)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":stylus",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setTouchscreen(0x02)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":xxxhdpi",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setDensity(0x0280)
+ .setSdkVersion(0x0004) // version [any density requires donut]
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":land-xhdpi-finger",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setOrientation(0x02)
+ .setTouchscreen(0x03)
+ .setDensity(0x0140)
+ .setSdkVersion(0x0004) // version [any density requires donut]
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ // ========== Test INPUT ==========
+ // keyboard: '(nokeys|qwerty|12key)'
+ // navigation: '(nonav|dpad|trackball|wheel)'
+ // inputFlags: '(keysexposed|keyshidden|keyssoft)'
+ // inputFlags: '(navexposed|navhidden)'
+ ASSERT_TRUE(ParseSplitParameter(":qwerty",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setKeyboard(0x02)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":dpad",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setNavigation(0x02)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":keyssoft-navhidden",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setInputFlags(0x0B)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":keyshidden-nokeys-navexposed-trackball",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setKeyboard(0x01)
+ .setNavigation(0x03)
+ .setInputFlags(0x06)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ // ========== Test SCREEN_SIZE ==========
+ // screenWidth/screenHeight: '[0-9]+x[0-9]+'
+ ASSERT_TRUE(ParseSplitParameter(":1920x1080",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setScreenWidth(0x0780)
+ .setScreenHeight(0x0438)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ // ========== Test VERSION ==========
+ // version 'v[0-9]+'
+
+ // ========== Test SCREEN_CONFIG ==========
+ // screenLayout [direction]: '(ldltr|ldrtl)'
+ // screenLayout [size]: '(small|normal|large|xlarge)'
+ // screenLayout [long]: '(long|notlong)'
+ // uiMode [type]: '(desk|car|television|appliance|watch|vrheadset)'
+ // uiMode [night]: '(night|notnight)'
+ // smallestScreenWidthDp: 'sw[0-9]dp'
+ ASSERT_TRUE(ParseSplitParameter(":ldrtl",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setScreenLayout(0x80)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":small",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setScreenLayout(0x01)
+ .setSdkVersion(0x0004) // screenLayout (size) requires donut
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":notlong",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setScreenLayout(0x10)
+ .setSdkVersion(0x0004) // screenLayout (long) requires donut
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":ldltr-normal-long",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setScreenLayout(0x62)
+ .setSdkVersion(0x0004) // screenLayout (size|long) requires donut
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":car",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setUiMode(0x03)
+ .setSdkVersion(0x0008) // uiMode requires froyo
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":vrheadset",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setUiMode(0x07)
+ .setSdkVersion(0x001A) // uiMode 'vrheadset' requires oreo
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":television-night",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setUiMode(0x24)
+ .setSdkVersion(0x0008) // uiMode requires froyo
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":sw1920dp",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setSmallestScreenWidthDp(0x0780)
+ .setSdkVersion(0x000D) // smallestScreenWidthDp requires honeycomb mr2
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ // ========== Test SCREEN_SIZE_DP ==========
+ // screenWidthDp: 'w[0-9]dp'
+ // screenHeightDp: 'h[0-9]dp'
+ ASSERT_TRUE(ParseSplitParameter(":w1920dp",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setScreenWidthDp(0x0780)
+ .setSdkVersion(0x000D) // screenWidthDp requires honeycomb mr2
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":h1080dp",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setScreenHeightDp(0x0438)
+ .setSdkVersion(0x000D) // screenHeightDp requires honeycomb mr2
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ // ========== Test SCREEN_CONFIG_2 ==========
+ // screenLayout2: '(round|notround)'
+ // colorMode: '(widecg|nowidecg)'
+ // colorMode: '(highhdr|lowdr)'
+ ASSERT_TRUE(ParseSplitParameter(":round",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setScreenLayout2(0x02)
+ .setSdkVersion(0x0017) // screenLayout2 (round) requires marshmallow
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":widecg-highdr",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setColorMode(0x0A)
+ .setSdkVersion(0x001A) // colorMode (hdr|colour gamut) requires oreo
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+}
+
+TEST (UtilTest, AdjustSplitConstraintsForMinSdk) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+
+ IDiagnostics* diagnostics = context.get()->GetDiagnostics();
+ std::vector<SplitConstraints> test_constraints;
+ std::string path;
+
+ test_constraints.push_back({});
+ ASSERT_TRUE(ParseSplitParameter(":v7",
+ diagnostics, &path, &test_constraints.back()));
+ test_constraints.push_back({});
+ ASSERT_TRUE(ParseSplitParameter(":xhdpi",
+ diagnostics, &path, &test_constraints.back()));
+ EXPECT_EQ(test_constraints.size(), 2);
+ EXPECT_EQ(test_constraints[0].name, "v7");
+ EXPECT_EQ(test_constraints[0].configs.size(), 1);
+ EXPECT_NE(*test_constraints[0].configs.begin(), ConfigDescription::DefaultConfig());
+ EXPECT_EQ(test_constraints[1].name, "xhdpi");
+ EXPECT_EQ(test_constraints[1].configs.size(), 1);
+ EXPECT_NE(*test_constraints[0].configs.begin(), ConfigDescription::DefaultConfig());
+
+ auto adjusted_contraints = AdjustSplitConstraintsForMinSdk(26, test_constraints);
+ EXPECT_EQ(adjusted_contraints.size(), 2);
+ EXPECT_EQ(adjusted_contraints[0].name, "v7");
+ EXPECT_EQ(adjusted_contraints[0].configs.size(), 0);
+ EXPECT_EQ(adjusted_contraints[1].name, "xhdpi");
+ EXPECT_EQ(adjusted_contraints[1].configs.size(), 1);
+ EXPECT_NE(*adjusted_contraints[1].configs.begin(), ConfigDescription::DefaultConfig());
+}
+
} // namespace aapt
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
new file mode 100644
index 0000000..2c356d1
--- /dev/null
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -0,0 +1,2197 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DumpManifest.h"
+
+#include "LoadedApk.h"
+#include "SdkConstants.h"
+#include "ValueVisitor.h"
+#include "io/File.h"
+#include "io/FileStream.h"
+#include "process/IResourceTableConsumer.h"
+#include "xml/XmlDom.h"
+
+using ::android::base::StringPrintf;
+
+namespace aapt {
+
+/**
+ * These are attribute resource constants for the platform, as found in android.R.attr.
+ */
+enum {
+ LABEL_ATTR = 0x01010001,
+ ICON_ATTR = 0x01010002,
+ NAME_ATTR = 0x01010003,
+ PERMISSION_ATTR = 0x01010006,
+ EXPORTED_ATTR = 0x01010010,
+ GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
+ RESOURCE_ATTR = 0x01010025,
+ DEBUGGABLE_ATTR = 0x0101000f,
+ VALUE_ATTR = 0x01010024,
+ VERSION_CODE_ATTR = 0x0101021b,
+ VERSION_NAME_ATTR = 0x0101021c,
+ SCREEN_ORIENTATION_ATTR = 0x0101001e,
+ MIN_SDK_VERSION_ATTR = 0x0101020c,
+ MAX_SDK_VERSION_ATTR = 0x01010271,
+ REQ_TOUCH_SCREEN_ATTR = 0x01010227,
+ REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
+ REQ_HARD_KEYBOARD_ATTR = 0x01010229,
+ REQ_NAVIGATION_ATTR = 0x0101022a,
+ REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
+ TARGET_SDK_VERSION_ATTR = 0x01010270,
+ TEST_ONLY_ATTR = 0x01010272,
+ ANY_DENSITY_ATTR = 0x0101026c,
+ GL_ES_VERSION_ATTR = 0x01010281,
+ SMALL_SCREEN_ATTR = 0x01010284,
+ NORMAL_SCREEN_ATTR = 0x01010285,
+ LARGE_SCREEN_ATTR = 0x01010286,
+ XLARGE_SCREEN_ATTR = 0x010102bf,
+ REQUIRED_ATTR = 0x0101028e,
+ INSTALL_LOCATION_ATTR = 0x010102b7,
+ SCREEN_SIZE_ATTR = 0x010102ca,
+ SCREEN_DENSITY_ATTR = 0x010102cb,
+ REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
+ COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
+ LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
+ PUBLIC_KEY_ATTR = 0x010103a6,
+ CATEGORY_ATTR = 0x010103e8,
+ BANNER_ATTR = 0x10103f2,
+ ISGAME_ATTR = 0x10103f4,
+ REQUIRED_FEATURE_ATTR = 0x1010557,
+ REQUIRED_NOT_FEATURE_ATTR = 0x1010558,
+ COMPILE_SDK_VERSION_ATTR = 0x01010572,
+ COMPILE_SDK_VERSION_CODENAME_ATTR = 0x01010573,
+};
+
+const std::string& kAndroidNamespace = "http://schemas.android.com/apk/res/android";
+
+/** Retrieves the attribute of the element with the specified attribute resource id. */
+static xml::Attribute* FindAttribute(xml::Element *el, uint32_t resd_id) {
+ for (auto& a : el->attributes) {
+ if (a.compiled_attribute && a.compiled_attribute.value().id) {
+ if (a.compiled_attribute.value().id.value() == resd_id) {
+ return std::move(&a);
+ }
+ }
+ }
+ return nullptr;
+}
+
+/** Retrieves the attribute of the element that has the specified namespace and attribute name. */
+static xml::Attribute* FindAttribute(xml::Element *el, const std::string &package,
+ const std::string &name) {
+ return el->FindAttribute(package, name);
+}
+
+class CommonFeatureGroup;
+
+class ManifestExtractor {
+ public:
+ struct Options {
+ /** Include meta information from <meta-data> elements in the output. */
+ bool include_meta_data = false;
+
+ /** Only output permission information. */
+ bool only_permissions = false;
+ };
+
+ explicit ManifestExtractor(LoadedApk* apk, ManifestExtractor::Options options)
+ : apk_(apk), options_(options) { }
+
+ class Element {
+ public:
+ Element() = default;
+ virtual ~Element() = default;
+
+ static std::unique_ptr<Element> Inflate(ManifestExtractor* extractor, xml::Element* el);
+
+ /** Writes out the extracted contents of the element. */
+ virtual void Print(text::Printer& printer) { }
+
+ /** Adds an element to the list of children of the element. */
+ void AddChild(std::unique_ptr<Element>& child) { children_.push_back(std::move(child)); }
+
+ /** Retrieves the list of children of the element. */
+ const std::vector<std::unique_ptr<Element>>& children() const {
+ return children_;
+ }
+
+ /** Retrieves the extracted xml element tag. */
+ const std::string tag() const {
+ return tag_;
+ }
+
+ protected:
+ ManifestExtractor* extractor() const {
+ return extractor_;
+ }
+
+ /** Retrieves and stores the information extracted from the xml element. */
+ virtual void Extract(xml::Element* el) { }
+
+ /*
+ * Retrieves a configuration value of the resource entry that best matches the specified
+ * configuration.
+ */
+ static Value* BestConfigValue(ResourceEntry* entry,
+ const ConfigDescription& match) {
+ if (!entry) {
+ return nullptr;
+ }
+
+ // Determine the config that best matches the desired config
+ ResourceConfigValue* best_value = nullptr;
+ for (auto& value : entry->values) {
+ if (!value->config.match(match)) {
+ continue;
+ }
+
+ if (best_value != nullptr) {
+ if (!value->config.isBetterThan(best_value->config, &match)) {
+ if (value->config.compare(best_value->config) != 0) {
+ continue;
+ }
+ }
+ }
+
+ best_value = value.get();
+ }
+
+ // The entry has no values
+ if (!best_value) {
+ return nullptr;
+ }
+
+ return best_value->value.get();
+ }
+
+ /** Retrieves the resource assigned to the specified resource id if one exists. */
+ Value* FindValueById(const ResourceTable* table, const ResourceId& res_id,
+ const ConfigDescription& config = DummyConfig()) {
+ if (table) {
+ for (auto& package : table->packages) {
+ if (package->id && package->id.value() == res_id.package_id()) {
+ for (auto& type : package->types) {
+ if (type->id && type->id.value() == res_id.type_id()) {
+ for (auto& entry : type->entries) {
+ if (entry->id && entry->id.value() == res_id.entry_id()) {
+ if (auto value = BestConfigValue(entry.get(), config)) {
+ return value;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return nullptr;
+ }
+
+ /** Attempts to resolve the reference to a non-reference value. */
+ Value* ResolveReference(Reference* ref, const ConfigDescription& config = DummyConfig()) {
+ const int kMaxIterations = 40;
+ int i = 0;
+ while (ref && ref->id && i++ < kMaxIterations) {
+ auto table = extractor_->apk_->GetResourceTable();
+ if (auto value = FindValueById(table, ref->id.value(), config)) {
+ if (ValueCast<Reference>(value)) {
+ ref = ValueCast<Reference>(value);
+ } else {
+ return value;
+ }
+ }
+ }
+ return nullptr;
+ }
+
+ /**
+ * Retrieves the integer value of the attribute . If the value of the attribute is a reference,
+ * this will attempt to resolve the reference to an integer value.
+ **/
+ int32_t* GetAttributeInteger(xml::Attribute* attr,
+ const ConfigDescription& config = DummyConfig()) {
+ if (attr != nullptr) {
+ if (attr->compiled_value) {
+ // Resolve references using the dummy configuration
+ Value* value = attr->compiled_value.get();
+ if (ValueCast<Reference>(value)) {
+ value = ResolveReference(ValueCast<Reference>(value), config);
+ } else {
+ value = attr->compiled_value.get();
+ }
+ // Retrieve the integer data if possible
+ if (value != nullptr) {
+ if (BinaryPrimitive* intValue = ValueCast<BinaryPrimitive>(value)) {
+ return (int32_t*) &intValue->value.data;
+ }
+ }
+ }
+ }
+ return nullptr;
+ }
+
+ /**
+ * A version of GetAttributeInteger that returns a default integer if the attribute does not
+ * exist or cannot be resolved to an integer value.
+ **/
+ int32_t GetAttributeIntegerDefault(xml::Attribute* attr, int32_t def,
+ const ConfigDescription& config = DummyConfig()) {
+ auto value = GetAttributeInteger(attr, config);
+ if (value) {
+ return *value;
+ }
+ return def;
+ }
+
+ /**
+ * Retrieves the string value of the attribute. If the value of the attribute is a reference,
+ * this will attempt to resolve the reference to a string value.
+ **/
+ const std::string* GetAttributeString(xml::Attribute* attr,
+ const ConfigDescription& config = DummyConfig()) {
+ if (attr != nullptr) {
+ if (attr->compiled_value) {
+ // Resolve references using the dummy configuration
+ Value* value = attr->compiled_value.get();
+ if (ValueCast<Reference>(value)) {
+ value = ResolveReference(ValueCast<Reference>(value), config);
+ } else {
+ value = attr->compiled_value.get();
+ }
+
+ // Retrieve the string data of the value if possible
+ if (value != nullptr) {
+ if (String* intValue = ValueCast<String>(value)) {
+ return &(*intValue->value);
+ } else if (RawString* rawValue = ValueCast<RawString>(value)) {
+ return &(*rawValue->value);
+ } else if (FileReference* strValue = ValueCast<FileReference>(value)) {
+ return &(*strValue->path);
+ }
+ }
+ }
+ return &attr->value;
+ }
+ return nullptr;
+ }
+
+ /**
+ * A version of GetAttributeString that returns a default string if the attribute does not
+ * exist or cannot be resolved to an string value.
+ **/
+ std::string GetAttributeStringDefault(xml::Attribute* attr, std::string def,
+ const ConfigDescription& config = DummyConfig()) {
+ auto value = GetAttributeString(attr, config);
+ if (value) {
+ return *value;
+ }
+ return def;
+ }
+
+ private:
+ ManifestExtractor* extractor_;
+ std::vector<std::unique_ptr<Element>> children_;
+ std::string tag_;
+ };
+
+ friend Element;
+
+ /** Creates a default configuration used to retrieve resources. */
+ static ConfigDescription DummyConfig() {
+ ConfigDescription config;
+ config.orientation = android::ResTable_config::ORIENTATION_PORT;
+ config.density = android::ResTable_config::DENSITY_MEDIUM;
+ config.sdkVersion = 10000; // Very high.
+ config.screenWidthDp = 320;
+ config.screenHeightDp = 480;
+ config.smallestScreenWidthDp = 320;
+ config.screenLayout |= android::ResTable_config::SCREENSIZE_NORMAL;
+ return config;
+ }
+
+ bool Dump(text::Printer& printer, IDiagnostics* diag);
+
+ /** Recursively visit the xml element tree and return a processed badging element tree. */
+ std::unique_ptr<Element> Visit(xml::Element* element);
+
+ /** Raises the target sdk value if the min target is greater than the current target. */
+ void RaiseTargetSdk(int32_t min_target) {
+ if (min_target > target_sdk_) {
+ target_sdk_ = min_target;
+ }
+ }
+
+ /**
+ * Retrieves the default feature group that features are added into when <uses-feature>
+ * are not in a <feature-group> element.
+ **/
+ CommonFeatureGroup* GetCommonFeatureGroup() {
+ return commonFeatureGroup_.get();
+ }
+
+ /**
+ * Retrieves a mapping of density values to Configurations for retrieving resources that would be
+ * used for that density setting.
+ **/
+ const std::map<uint16_t, ConfigDescription> densities() const {
+ return densities_;
+ }
+
+ /**
+ * Retrieves a mapping of locale BCP 47 strings to Configurations for retrieving resources that
+ * would be used for that locale setting.
+ **/
+ const std::map<std::string, ConfigDescription> locales() const {
+ return locales_;
+ }
+
+ /** Retrieves the current stack of parent during data extraction. */
+ const std::vector<Element*> parent_stack() const {
+ return parent_stack_;
+ }
+
+ int32_t target_sdk() const {
+ return target_sdk_;
+ }
+
+ LoadedApk* const apk_;
+ const Options options_;
+
+ private:
+ std::unique_ptr<CommonFeatureGroup> commonFeatureGroup_ = util::make_unique<CommonFeatureGroup>();
+ std::map<std::string, ConfigDescription> locales_;
+ std::map<uint16_t, ConfigDescription> densities_;
+ std::vector<Element*> parent_stack_;
+ int32_t target_sdk_ = 0;
+};
+
+template<typename T> T* ElementCast(ManifestExtractor::Element* element);
+
+/** Recurs through the children of the specified root in depth-first order. */
+static void ForEachChild(ManifestExtractor::Element* root,
+ std::function<void(ManifestExtractor::Element*)> f) {
+ for (auto& child : root->children()) {
+ f(child.get());
+ ForEachChild(child.get(), f);
+ }
+}
+
+/**
+ * Checks the element and its recursive children for an element that makes the specified
+ * conditional function return true. Returns the first element that makes the conditional function
+ * return true.
+ **/
+static ManifestExtractor::Element* FindElement(ManifestExtractor::Element* root,
+ std::function<bool(ManifestExtractor::Element*)> f) {
+ if (f(root)) {
+ return root;
+ }
+ for (auto& child : root->children()) {
+ if (auto b2 = FindElement(child.get(), f)) {
+ return b2;
+ }
+ }
+ return nullptr;
+}
+
+/** Represents the <manifest> elements **/
+class Manifest : public ManifestExtractor::Element {
+ public:
+ Manifest() = default;
+ std::string package;
+ int32_t versionCode;
+ std::string versionName;
+ const std::string* split = nullptr;
+ const std::string* platformVersionName = nullptr;
+ const std::string* platformVersionCode = nullptr;
+ const int32_t* compilesdkVersion = nullptr;
+ const std::string* compilesdkVersionCodename = nullptr;
+ const int32_t* installLocation = nullptr;
+
+ void Extract(xml::Element* manifest) override {
+ package = GetAttributeStringDefault(FindAttribute(manifest, {}, "package"), "");
+ versionCode = GetAttributeIntegerDefault(FindAttribute(manifest, VERSION_CODE_ATTR), 0);
+ versionName = GetAttributeStringDefault(FindAttribute(manifest, VERSION_NAME_ATTR), "");
+ split = GetAttributeString(FindAttribute(manifest, {}, "split"));
+
+ // Extract the platform build info
+ platformVersionName = GetAttributeString(FindAttribute(manifest, {},
+ "platformBuildVersionName"));
+ platformVersionCode = GetAttributeString(FindAttribute(manifest, {},
+ "platformBuildVersionCode"));
+
+ // Extract the compile sdk info
+ compilesdkVersion = GetAttributeInteger(FindAttribute(manifest, COMPILE_SDK_VERSION_ATTR));
+ compilesdkVersionCodename = GetAttributeString(
+ FindAttribute(manifest, COMPILE_SDK_VERSION_CODENAME_ATTR));
+ installLocation = GetAttributeInteger(FindAttribute(manifest, INSTALL_LOCATION_ATTR));
+ }
+
+ void Print(text::Printer& printer) override {
+ printer.Print(StringPrintf("package: name='%s' ", package.data()));
+ printer.Print(StringPrintf("versionCode='%s' ",
+ (versionCode > 0) ? std::to_string(versionCode).data() : ""));
+ printer.Print(StringPrintf("versionName='%s'", versionName.data()));
+
+ if (split) {
+ printer.Print(StringPrintf(" split='%s'", split->data()));
+ }
+ if (platformVersionName) {
+ printer.Print(StringPrintf(" platformBuildVersionName='%s'", platformVersionName->data()));
+ }
+ if (platformVersionCode) {
+ printer.Print(StringPrintf(" platformBuildVersionCode='%s'", platformVersionCode->data()));
+ }
+ if (compilesdkVersion) {
+ printer.Print(StringPrintf(" compileSdkVersion='%d'", *compilesdkVersion));
+ }
+ if (compilesdkVersionCodename) {
+ printer.Print(StringPrintf(" compileSdkVersionCodename='%s'",
+ compilesdkVersionCodename->data()));
+ }
+ printer.Print("\n");
+
+ if (installLocation) {
+ switch (*installLocation) {
+ case 0:
+ printer.Print("install-location:'auto'\n");
+ break;
+ case 1:
+ printer.Print("install-location:'internalOnly'\n");
+ break;
+ case 2:
+ printer.Print("install-location:'preferExternal'\n");
+ break;
+ default:
+ break;
+ }
+ }
+ }
+};
+
+/** Represents <application> elements. **/
+class Application : public ManifestExtractor::Element {
+ public:
+ Application() = default;
+ std::string label;
+ std::string icon;
+ std::string banner;
+ int32_t is_game;
+ int32_t debuggable;
+ int32_t test_only;
+ bool has_multi_arch;
+
+ /** Mapping from locales to app names. */
+ std::map<std::string, std::string> locale_labels;
+
+ /** Mapping from densities to app icons. */
+ std::map<uint16_t, std::string> density_icons;
+
+ void Extract(xml::Element* element) override {
+ label = GetAttributeStringDefault(FindAttribute(element, LABEL_ATTR), "");
+ icon = GetAttributeStringDefault(FindAttribute(element, ICON_ATTR), "");
+ test_only = GetAttributeIntegerDefault(FindAttribute(element, TEST_ONLY_ATTR), 0);
+ banner = GetAttributeStringDefault(FindAttribute(element, BANNER_ATTR), "");
+ is_game = GetAttributeIntegerDefault(FindAttribute(element, ISGAME_ATTR), 0);
+ debuggable = GetAttributeIntegerDefault(FindAttribute(element, DEBUGGABLE_ATTR), 0);
+
+ // We must search by name because the multiArch flag hasn't been API
+ // frozen yet.
+ has_multi_arch = (GetAttributeIntegerDefault(
+ FindAttribute(element, kAndroidNamespace, "multiArch"), 0) != 0);
+
+ // Retrieve the app names for every locale the app supports
+ auto attr = FindAttribute(element, LABEL_ATTR);
+ for (auto& config : extractor()->locales()) {
+ if (auto label = GetAttributeString(attr, config.second)) {
+ if (label) {
+ locale_labels.insert(std::make_pair(config.first, *label));
+ }
+ }
+ }
+
+ // Retrieve the icons for the densities the app supports
+ attr = FindAttribute(element, ICON_ATTR);
+ for (auto& config : extractor()->densities()) {
+ if (auto resource = GetAttributeString(attr, config.second)) {
+ if (resource) {
+ density_icons.insert(std::make_pair(config.first, *resource));
+ }
+ }
+ }
+ }
+
+ void Print(text::Printer& printer) override {
+ // Print the labels for every locale
+ for (auto p : locale_labels) {
+ if (p.first.empty()) {
+ printer.Print(StringPrintf("application-label:'%s'\n",
+ android::ResTable::normalizeForOutput(p.second.data())
+ .c_str()));
+ } else {
+ printer.Print(StringPrintf("application-label-%s:'%s'\n", p.first.data(),
+ android::ResTable::normalizeForOutput(p.second.data())
+ .c_str()));
+ }
+ }
+
+ // Print the icon paths for every density
+ for (auto p : density_icons) {
+ printer.Print(StringPrintf("application-icon-%d:'%s'\n", p.first, p.second.data()));
+ }
+
+ // Print the application info
+ printer.Print(StringPrintf("application: label='%s' ",
+ android::ResTable::normalizeForOutput(label.data()).c_str()));
+ printer.Print(StringPrintf("icon='%s'", icon.data()));
+ if (!banner.empty()) {
+ printer.Print(StringPrintf(" banner='%s'", banner.data()));
+ }
+ printer.Print("\n");
+
+ if (test_only != 0) {
+ printer.Print(StringPrintf("testOnly='%d'\n", test_only));
+ }
+ if (is_game != 0) {
+ printer.Print("application-isGame\n");
+ }
+ if (debuggable != 0) {
+ printer.Print("application-debuggable\n");
+ }
+ }
+};
+
+/** Represents <uses-sdk> elements. **/
+class UsesSdkBadging : public ManifestExtractor::Element {
+ public:
+ UsesSdkBadging() = default;
+ const int32_t* min_sdk = nullptr;
+ const std::string* min_sdk_name = nullptr;
+ const int32_t* max_sdk = nullptr;
+ const int32_t* target_sdk = nullptr;
+ const std::string* target_sdk_name = nullptr;
+
+ void Extract(xml::Element* element) override {
+ min_sdk = GetAttributeInteger(FindAttribute(element, MIN_SDK_VERSION_ATTR));
+ min_sdk_name = GetAttributeString(FindAttribute(element, MIN_SDK_VERSION_ATTR));
+ max_sdk = GetAttributeInteger(FindAttribute(element, MAX_SDK_VERSION_ATTR));
+ target_sdk = GetAttributeInteger(FindAttribute(element, TARGET_SDK_VERSION_ATTR));
+ target_sdk_name = GetAttributeString(FindAttribute(element, TARGET_SDK_VERSION_ATTR));
+
+ // Detect the target sdk of the element
+ if ((min_sdk_name && *min_sdk_name == "Donut")
+ || (target_sdk_name && *target_sdk_name == "Donut")) {
+ extractor()->RaiseTargetSdk(4);
+ }
+ if (min_sdk) {
+ extractor()->RaiseTargetSdk(*min_sdk);
+ }
+ if (target_sdk) {
+ extractor()->RaiseTargetSdk(*target_sdk);
+ }
+ }
+
+ void Print(text::Printer& printer) override {
+ if (min_sdk) {
+ printer.Print(StringPrintf("sdkVersion:'%d'\n", *min_sdk));
+ } else if (min_sdk_name) {
+ printer.Print(StringPrintf("sdkVersion:'%s'\n", min_sdk_name->data()));
+ }
+ if (max_sdk) {
+ printer.Print(StringPrintf("maxSdkVersion:'%d'\n", *max_sdk));
+ }
+ if (target_sdk) {
+ printer.Print(StringPrintf("targetSdkVersion:'%d'\n", *target_sdk));
+ } else if (target_sdk_name) {
+ printer.Print(StringPrintf("targetSdkVersion:'%s'\n", target_sdk_name->data()));
+ }
+ }
+};
+
+/** Represents <uses-configuration> elements. **/
+class UsesConfiguarion : public ManifestExtractor::Element {
+ public:
+ UsesConfiguarion() = default;
+ int32_t req_touch_screen = 0;
+ int32_t req_keyboard_type = 0;
+ int32_t req_hard_keyboard = 0;
+ int32_t req_navigation = 0;
+ int32_t req_five_way_nav = 0;
+
+ void Extract(xml::Element* element) override {
+ req_touch_screen = GetAttributeIntegerDefault(
+ FindAttribute(element, REQ_TOUCH_SCREEN_ATTR), 0);
+ req_keyboard_type = GetAttributeIntegerDefault(
+ FindAttribute(element, REQ_KEYBOARD_TYPE_ATTR), 0);
+ req_hard_keyboard = GetAttributeIntegerDefault(
+ FindAttribute(element, REQ_HARD_KEYBOARD_ATTR), 0);
+ req_navigation = GetAttributeIntegerDefault(
+ FindAttribute(element, REQ_NAVIGATION_ATTR), 0);
+ req_five_way_nav = GetAttributeIntegerDefault(
+ FindAttribute(element, REQ_FIVE_WAY_NAV_ATTR), 0);
+ }
+
+ void Print(text::Printer& printer) override {
+ printer.Print("uses-configuration:");
+ if (req_touch_screen != 0) {
+ printer.Print(StringPrintf(" reqTouchScreen='%d'", req_touch_screen));
+ }
+ if (req_keyboard_type != 0) {
+ printer.Print(StringPrintf(" reqKeyboardType='%d'", req_keyboard_type));
+ }
+ if (req_hard_keyboard != 0) {
+ printer.Print(StringPrintf(" reqHardKeyboard='%d'", req_hard_keyboard));
+ }
+ if (req_navigation != 0) {
+ printer.Print(StringPrintf(" reqNavigation='%d'", req_navigation));
+ }
+ if (req_five_way_nav != 0) {
+ printer.Print(StringPrintf(" reqFiveWayNav='%d'", req_five_way_nav));
+ }
+ printer.Print("\n");
+ }
+};
+
+/** Represents <supports-screen> elements. **/
+class SupportsScreen : public ManifestExtractor::Element {
+ public:
+ SupportsScreen() = default;
+ int32_t small_screen = 1;
+ int32_t normal_screen = 1;
+ int32_t large_screen = 1;
+ int32_t xlarge_screen = 1;
+ int32_t any_density = 1;
+ int32_t requires_smallest_width_dp = 0;
+ int32_t compatible_width_limit_dp = 0;
+ int32_t largest_width_limit_dp = 0;
+
+ void Extract(xml::Element* element) override {
+ small_screen = GetAttributeIntegerDefault(FindAttribute(element, SMALL_SCREEN_ATTR), 1);
+ normal_screen = GetAttributeIntegerDefault(FindAttribute(element, NORMAL_SCREEN_ATTR), 1);
+ large_screen = GetAttributeIntegerDefault(FindAttribute(element, LARGE_SCREEN_ATTR), 1);
+ xlarge_screen = GetAttributeIntegerDefault(FindAttribute(element, XLARGE_SCREEN_ATTR), 1);
+ any_density = GetAttributeIntegerDefault(FindAttribute(element, ANY_DENSITY_ATTR), 1);
+
+ requires_smallest_width_dp = GetAttributeIntegerDefault(
+ FindAttribute(element, REQUIRES_SMALLEST_WIDTH_DP_ATTR), 0);
+ compatible_width_limit_dp = GetAttributeIntegerDefault(
+ FindAttribute(element, COMPATIBLE_WIDTH_LIMIT_DP_ATTR), 0);
+ largest_width_limit_dp = GetAttributeIntegerDefault(
+ FindAttribute(element, LARGEST_WIDTH_LIMIT_DP_ATTR), 0);
+
+ // For modern apps, if screen size buckets haven't been specified
+ // but the new width ranges have, then infer the buckets from them.
+ if (small_screen > 0 && normal_screen > 0 && large_screen > 0 && xlarge_screen > 0
+ && requires_smallest_width_dp > 0) {
+ int32_t compat_width = (compatible_width_limit_dp > 0) ? compatible_width_limit_dp
+ : requires_smallest_width_dp;
+ small_screen = (requires_smallest_width_dp <= 240 && compat_width >= 240) ? -1 : 0;
+ normal_screen = (requires_smallest_width_dp <= 320 && compat_width >= 320) ? -1 : 0;
+ large_screen = (requires_smallest_width_dp <= 480 && compat_width >= 480) ? -1 : 0;
+ xlarge_screen = (requires_smallest_width_dp <= 720 && compat_width >= 720) ? -1 : 0;
+ }
+ }
+
+ void PrintScreens(text::Printer& printer, int32_t target_sdk) {
+ int32_t small_screen_temp = small_screen;
+ int32_t normal_screen_temp = normal_screen;
+ int32_t large_screen_temp = large_screen;
+ int32_t xlarge_screen_temp = xlarge_screen;
+ int32_t any_density_temp = any_density;
+
+ // Determine default values for any unspecified screen sizes,
+ // based on the target SDK of the package. As of 4 (donut)
+ // the screen size support was introduced, so all default to
+ // enabled.
+ if (small_screen_temp > 0) {
+ small_screen_temp = target_sdk >= 4 ? -1 : 0;
+ }
+ if (normal_screen_temp > 0) {
+ normal_screen_temp = -1;
+ }
+ if (large_screen_temp > 0) {
+ large_screen_temp = target_sdk >= 4 ? -1 : 0;
+ }
+ if (xlarge_screen_temp > 0) {
+ // Introduced in Gingerbread.
+ xlarge_screen_temp = target_sdk >= 9 ? -1 : 0;
+ }
+ if (any_density_temp > 0) {
+ any_density_temp = (target_sdk >= 4 || requires_smallest_width_dp > 0
+ || compatible_width_limit_dp > 0) ? -1 : 0;
+ }
+
+ // Print the formatted screen info
+ printer.Print("supports-screens:");
+ if (small_screen_temp != 0) {
+ printer.Print(" 'small'");
+ }
+ if (normal_screen_temp != 0) {
+ printer.Print(" 'normal'");
+ }
+ if (large_screen_temp != 0) {
+ printer.Print(" 'large'");
+ }
+ if (xlarge_screen_temp != 0) {
+ printer.Print(" 'xlarge'");
+ }
+ printer.Print("\n");
+ printer.Print(StringPrintf("supports-any-density: '%s'\n",
+ (any_density_temp ) ? "true" : "false"));
+ if (requires_smallest_width_dp > 0) {
+ printer.Print(StringPrintf("requires-smallest-width:'%d'\n", requires_smallest_width_dp));
+ }
+ if (compatible_width_limit_dp > 0) {
+ printer.Print(StringPrintf("compatible-width-limit:'%d'\n", compatible_width_limit_dp));
+ }
+ if (largest_width_limit_dp > 0) {
+ printer.Print(StringPrintf("largest-width-limit:'%d'\n", largest_width_limit_dp));
+ }
+ }
+};
+
+/** Represents <feature-group> elements. **/
+class FeatureGroup : public ManifestExtractor::Element {
+ public:
+ FeatureGroup() = default;
+ std::string label;
+ int32_t open_gles_version = 0;
+
+ void Extract(xml::Element* element) override {
+ label = GetAttributeStringDefault(FindAttribute(element, LABEL_ATTR), "");
+ }
+
+ virtual void PrintGroup(text::Printer& printer) {
+ printer.Print(StringPrintf("feature-group: label='%s'\n", label.data()));
+ if (open_gles_version > 0) {
+ printer.Print(StringPrintf(" uses-gl-es: '0x%x'\n", open_gles_version));
+ }
+
+ for (auto feature : features_) {
+ printer.Print(StringPrintf(" uses-feature%s: name='%s'",
+ (feature.second.required ? "" : "-not-required"),
+ feature.first.data()));
+ if (feature.second.version > 0) {
+ printer.Print(StringPrintf(" version='%d'", feature.second.version));
+ }
+ printer.Print("\n");
+ }
+ }
+
+ /** Adds a feature to the feature group. */
+ void AddFeature(const std::string& name, bool required = true, int32_t version = -1) {
+ features_.insert(std::make_pair(name, Feature{ required, version }));
+ if (required) {
+ if (name == "android.hardware.camera.autofocus" ||
+ name == "android.hardware.camera.flash") {
+ AddFeature("android.hardware.camera", true);
+ } else if (name == "android.hardware.location.gps" ||
+ name == "android.hardware.location.network") {
+ AddFeature("android.hardware.location", true);
+ } else if (name == "android.hardware.faketouch.multitouch") {
+ AddFeature("android.hardware.faketouch", true);
+ } else if (name == "android.hardware.faketouch.multitouch.distinct" ||
+ name == "android.hardware.faketouch.multitouch.jazzhands") {
+ AddFeature("android.hardware.faketouch.multitouch", true);
+ AddFeature("android.hardware.faketouch", true);
+ } else if (name == "android.hardware.touchscreen.multitouch") {
+ AddFeature("android.hardware.touchscreen", true);
+ } else if (name == "android.hardware.touchscreen.multitouch.distinct" ||
+ name == "android.hardware.touchscreen.multitouch.jazzhands") {
+ AddFeature("android.hardware.touchscreen.multitouch", true);
+ AddFeature("android.hardware.touchscreen", true);
+ } else if (name == "android.hardware.opengles.aep") {
+ const int kOpenGLESVersion31 = 0x00030001;
+ if (kOpenGLESVersion31 > open_gles_version) {
+ open_gles_version = kOpenGLESVersion31;
+ }
+ }
+ }
+ }
+
+ /** Returns true if the feature group has the given feature. */
+ virtual bool HasFeature(const std::string& name) {
+ return features_.find(name) != features_.end();
+ }
+
+ /** Merges the features of another feature group into this group. */
+ void Merge(FeatureGroup* group) {
+ open_gles_version = std::max(open_gles_version, group->open_gles_version);
+ for (auto& feature : group->features_) {
+ features_.insert(feature);
+ }
+ }
+
+ protected:
+ struct Feature {
+ public:
+ bool required = false;
+ int32_t version = -1;
+ };
+
+ /* Mapping of feature names to their properties. */
+ std::map<std::string, Feature> features_;
+};
+
+/**
+ * Represents the default feature group for the application if no <feature-group> elements are
+ * present in the manifest.
+ **/
+class CommonFeatureGroup : public FeatureGroup {
+ public:
+ CommonFeatureGroup() = default;
+ void PrintGroup(text::Printer& printer) override {
+ FeatureGroup::PrintGroup(printer);
+
+ // Also print the implied features
+ for (auto feature : implied_features_) {
+ if (features_.find(feature.first) == features_.end()) {
+ const char* sdk23 = feature.second.implied_from_sdk_k23 ? "-sdk-23" : "";
+ printer.Print(StringPrintf(" uses-feature%s: name='%s'\n", sdk23, feature.first.data()));
+ printer.Print(StringPrintf(" uses-implied-feature%s: name='%s' reason='", sdk23,
+ feature.first.data()));
+
+ // Print the reasons as a sentence
+ size_t count = 0;
+ for (auto reason : feature.second.reasons) {
+ printer.Print(reason);
+ if (count + 2 < feature.second.reasons.size()) {
+ printer.Print(", ");
+ } else if (count + 1 < feature.second.reasons.size()) {
+ printer.Print(", and ");
+ }
+ count++;
+ }
+ printer.Print("'\n");
+ }
+ }
+ }
+
+ /** Returns true if the feature group has the given feature. */
+ bool HasFeature(const std::string& name) override {
+ return FeatureGroup::HasFeature(name)
+ || implied_features_.find(name) != implied_features_.end();
+ }
+
+ /** Adds a feature to a set of implied features not explicitly requested in the manifest. */
+ void addImpliedFeature(const std::string& name, const std::string& reason, bool sdk23 = false) {
+ auto entry = implied_features_.find(name);
+ if (entry == implied_features_.end()) {
+ implied_features_.insert(std::make_pair(name, ImpliedFeature(sdk23)));
+ entry = implied_features_.find(name);
+ }
+
+ // A non-sdk 23 implied feature takes precedence.
+ if (entry->second.implied_from_sdk_k23 && !sdk23) {
+ entry->second.implied_from_sdk_k23 = false;
+ }
+
+ entry->second.reasons.insert(reason);
+ }
+
+ /**
+ * Adds a feature to a set of implied features for all features that are implied by the presence
+ * of the permission.
+ **/
+ void addImpliedFeaturesForPermission(int32_t targetSdk, const std::string& name, bool sdk23) {
+ if (name == "android.permission.CAMERA") {
+ addImpliedFeature("android.hardware.camera",
+ StringPrintf("requested %s permission", name.data()),
+ sdk23);
+
+ } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
+ if (targetSdk < SDK_LOLLIPOP) {
+ addImpliedFeature("android.hardware.location.gps",
+ StringPrintf("requested %s permission", name.data()),
+ sdk23);
+ addImpliedFeature("android.hardware.location.gps",
+ StringPrintf("targetSdkVersion < %d", SDK_LOLLIPOP),
+ sdk23);
+ }
+ addImpliedFeature("android.hardware.location",
+ StringPrintf("requested %s permission", name.data()),
+ sdk23);
+
+ } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
+ if (targetSdk < SDK_LOLLIPOP) {
+ addImpliedFeature("android.hardware.location.network",
+ StringPrintf("requested %s permission", name.data()),
+ sdk23);
+ addImpliedFeature("android.hardware.location.network",
+ StringPrintf("targetSdkVersion < %d", SDK_LOLLIPOP),
+ sdk23);
+ }
+ addImpliedFeature("android.hardware.location",
+ StringPrintf("requested %s permission", name.data()),
+ sdk23);
+
+ } else if (name == "android.permission.ACCESS_MOCK_LOCATION" ||
+ name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
+ name == "android.permission.INSTALL_LOCATION_PROVIDER") {
+ addImpliedFeature("android.hardware.location",
+ StringPrintf("requested %s permission", name.data()),
+ sdk23);
+
+ } else if (name == "android.permission.BLUETOOTH" ||
+ name == "android.permission.BLUETOOTH_ADMIN") {
+ if (targetSdk > SDK_DONUT) {
+ addImpliedFeature("android.hardware.bluetooth",
+ StringPrintf("requested %s permission", name.data()),
+ sdk23);
+ addImpliedFeature("android.hardware.bluetooth",
+ StringPrintf("targetSdkVersion > %d", SDK_DONUT),
+ sdk23);
+ }
+
+ } else if (name == "android.permission.RECORD_AUDIO") {
+ addImpliedFeature("android.hardware.microphone",
+ StringPrintf("requested %s permission", name.data()),
+ sdk23);
+
+ } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
+ name == "android.permission.CHANGE_WIFI_STATE" ||
+ name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
+ addImpliedFeature("android.hardware.wifi",
+ StringPrintf("requested %s permission", name.data()),
+ sdk23);
+
+ } else if (name == "android.permission.CALL_PHONE" ||
+ name == "android.permission.CALL_PRIVILEGED" ||
+ name == "android.permission.MODIFY_PHONE_STATE" ||
+ name == "android.permission.PROCESS_OUTGOING_CALLS" ||
+ name == "android.permission.READ_SMS" ||
+ name == "android.permission.RECEIVE_SMS" ||
+ name == "android.permission.RECEIVE_MMS" ||
+ name == "android.permission.RECEIVE_WAP_PUSH" ||
+ name == "android.permission.SEND_SMS" ||
+ name == "android.permission.WRITE_APN_SETTINGS" ||
+ name == "android.permission.WRITE_SMS") {
+ addImpliedFeature("android.hardware.telephony",
+ "requested a telephony permission",
+ sdk23);
+ }
+ }
+
+ private:
+ /**
+ * Represents a feature that has been automatically added due to a pre-requisite or for some
+ * other reason.
+ */
+ struct ImpliedFeature {
+ explicit ImpliedFeature(bool sdk23 = false) : implied_from_sdk_k23(sdk23) {}
+
+ /** List of human-readable reasons for why this feature was implied. */
+ std::set<std::string> reasons;
+
+ // Was this implied by a permission from SDK 23 (<uses-permission-sdk-23 />)
+ bool implied_from_sdk_k23;
+ };
+
+ /* Mapping of implied feature names to their properties. */
+ std::map<std::string, ImpliedFeature> implied_features_;
+};
+
+/** Represents <uses-feature> elements. **/
+class UsesFeature : public ManifestExtractor::Element {
+ public:
+ UsesFeature() = default;
+ void Extract(xml::Element* element) override {
+ const std::string* name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+ int32_t* gl = GetAttributeInteger(FindAttribute(element, GL_ES_VERSION_ATTR));
+ bool required = GetAttributeIntegerDefault(
+ FindAttribute(element, REQUIRED_ATTR), true) != 0;
+ int32_t version = GetAttributeIntegerDefault(
+ FindAttribute(element, kAndroidNamespace, "version"), 0);
+
+ // Add the feature to the parent feature group element if one exists; otherwise, add it to the
+ // common feature group
+ FeatureGroup* feature_group = ElementCast<FeatureGroup>(extractor()->parent_stack()[0]);
+ if (!feature_group) {
+ feature_group = extractor()->GetCommonFeatureGroup();
+ } else {
+ // All features in side of <feature-group> elements are required.
+ required = true;
+ }
+
+ if (name) {
+ feature_group->AddFeature(*name, required, version);
+ } else if (gl) {
+ feature_group->open_gles_version = std::max(feature_group->open_gles_version, *gl);
+ }
+ }
+};
+
+/** Represents <uses-permission> elements. **/
+class UsesPermission : public ManifestExtractor::Element {
+ public:
+ UsesPermission() = default;
+ std::string name;
+ std::string requiredFeature;
+ std::string requiredNotFeature;
+ int32_t required = true;
+ int32_t maxSdkVersion = -1;
+
+ void Extract(xml::Element* element) override {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ requiredFeature = GetAttributeStringDefault(
+ FindAttribute(element, REQUIRED_FEATURE_ATTR), "");
+ requiredNotFeature = GetAttributeStringDefault(
+ FindAttribute(element, REQUIRED_NOT_FEATURE_ATTR), "");
+ required = GetAttributeIntegerDefault(FindAttribute(element, REQUIRED_ATTR), 1);
+ maxSdkVersion = GetAttributeIntegerDefault(
+ FindAttribute(element, MAX_SDK_VERSION_ATTR), -1);
+
+ if (!name.empty()) {
+ CommonFeatureGroup* common = extractor()->GetCommonFeatureGroup();
+ common->addImpliedFeaturesForPermission(extractor()->target_sdk(), name, false);
+ }
+ }
+
+ void Print(text::Printer& printer) override {
+ if (!name.empty()) {
+ printer.Print(StringPrintf("uses-permission: name='%s'", name.data()));
+ if (maxSdkVersion >= 0) {
+ printer.Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion));
+ }
+ if (!requiredFeature.empty()) {
+ printer.Print(StringPrintf(" requiredFeature='%s'", requiredFeature.data()));
+ }
+ if (!requiredNotFeature.empty()) {
+ printer.Print(StringPrintf(" requiredNotFeature='%s'", requiredNotFeature.data()));
+ }
+ printer.Print("\n");
+ if (required == 0) {
+ printer.Print(StringPrintf("optional-permission: name='%s'", name.data()));
+ if (maxSdkVersion >= 0) {
+ printer.Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion));
+ }
+ printer.Print("\n");
+ }
+ }
+ }
+
+ void PrintImplied(text::Printer& printer, const std::string& reason) {
+ printer.Print(StringPrintf("uses-implied-permission: name='%s'", name.data()));
+ if (maxSdkVersion >= 0) {
+ printer.Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion));
+ }
+ printer.Print(StringPrintf(" reason='%s'\n", reason.data()));
+ }
+};
+
+/** Represents <uses-permission-sdk-23> elements. **/
+class UsesPermissionSdk23 : public ManifestExtractor::Element {
+ public:
+ UsesPermissionSdk23() = default;
+ const std::string* name = nullptr;
+ const int32_t* maxSdkVersion = nullptr;
+
+ void Extract(xml::Element* element) override {
+ name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+ maxSdkVersion = GetAttributeInteger(FindAttribute(element, MAX_SDK_VERSION_ATTR));
+
+ if (name) {
+ CommonFeatureGroup* common = extractor()->GetCommonFeatureGroup();
+ common->addImpliedFeaturesForPermission(extractor()->target_sdk(), *name, true);
+ }
+ }
+
+ void Print(text::Printer& printer) override {
+ if (name) {
+ printer.Print(StringPrintf("uses-permission-sdk-23: name='%s'", name->data()));
+ if (maxSdkVersion) {
+ printer.Print(StringPrintf(" maxSdkVersion='%d'", *maxSdkVersion));
+ }
+ printer.Print("\n");
+ }
+ }
+};
+
+/** Represents <permission> elements. These elements are only printing when dumping permissions. **/
+class Permission : public ManifestExtractor::Element {
+ public:
+ Permission() = default;
+ std::string name;
+
+ void Extract(xml::Element* element) override {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ }
+
+ void Print(text::Printer& printer) override {
+ if (extractor()->options_.only_permissions && !name.empty()) {
+ printer.Print(StringPrintf("permission: %s\n", name.data()));
+ }
+ }
+};
+
+/** Represents <activity> elements. **/
+class Activity : public ManifestExtractor::Element {
+ public:
+ Activity() = default;
+ std::string name;
+ std::string icon;
+ std::string label;
+ std::string banner;
+
+ bool has_component_ = false;
+ bool has_launcher_category = false;
+ bool has_leanback_launcher_category = false;
+ bool has_main_action = false;
+
+ void Extract(xml::Element* element) override {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ label = GetAttributeStringDefault(FindAttribute(element, LABEL_ATTR), "");
+ icon = GetAttributeStringDefault(FindAttribute(element, ICON_ATTR), "");
+ banner = GetAttributeStringDefault(FindAttribute(element, BANNER_ATTR), "");
+
+ // Retrieve the package name from the manifest
+ std::string package;
+ for (auto& parent : extractor()->parent_stack()) {
+ if (auto manifest = ElementCast<Manifest>(parent)) {
+ package = manifest->package;
+ break;
+ }
+ }
+
+ // Fully qualify the activity name
+ ssize_t idx = name.find(".");
+ if (idx == 0) {
+ name = package + name;
+ } else if (idx < 0) {
+ name = package + "." + name;
+ }
+
+ auto orientation = GetAttributeInteger(FindAttribute(element, SCREEN_ORIENTATION_ATTR));
+ if (orientation) {
+ CommonFeatureGroup* common = extractor()->GetCommonFeatureGroup();
+ int orien = *orientation;
+ if (orien == 0 || orien == 6 || orien == 8) {
+ // Requests landscape, sensorLandscape, or reverseLandscape.
+ common->addImpliedFeature("android.hardware.screen.landscape",
+ "one or more activities have specified a landscape orientation",
+ false);
+ } else if (orien == 1 || orien == 7 || orien == 9) {
+ // Requests portrait, sensorPortrait, or reversePortrait.
+ common->addImpliedFeature("android.hardware.screen.portrait",
+ "one or more activities have specified a portrait orientation",
+ false);
+ }
+ }
+ }
+
+ void Print(text::Printer& printer) override {
+ // Print whether the activity has the HOME category and a the MAIN action
+ if (has_main_action && has_launcher_category) {
+ printer.Print("launchable-activity:");
+ if (!name.empty()) {
+ printer.Print(StringPrintf(" name='%s' ", name.data()));
+ }
+ printer.Print(StringPrintf(" label='%s' icon='%s'\n",
+ android::ResTable::normalizeForOutput(label.data()).c_str(),
+ icon.data()));
+ }
+
+ // Print wether the activity has the HOME category and a the MAIN action
+ if (has_leanback_launcher_category) {
+ printer.Print("leanback-launchable-activity:");
+ if (!name.empty()) {
+ printer.Print(StringPrintf(" name='%s' ", name.data()));
+ }
+ printer.Print(StringPrintf(" label='%s' icon='%s' banner='%s'\n",
+ android::ResTable::normalizeForOutput(label.data()).c_str(),
+ icon.data(), banner.data()));
+ }
+ }
+};
+
+/** Represents <intent-filter> elements. */
+class IntentFilter : public ManifestExtractor::Element {
+ public:
+ IntentFilter() = default;
+};
+
+/** Represents <category> elements. */
+class Category : public ManifestExtractor::Element {
+ public:
+ Category() = default;
+ std::string component = "";
+
+ void Extract(xml::Element* element) override {
+ const std::string* category = GetAttributeString(FindAttribute(element, NAME_ATTR));
+
+ auto parent_stack = extractor()->parent_stack();
+ if (category && ElementCast<IntentFilter>(parent_stack[0])
+ && ElementCast<Activity>(parent_stack[1])) {
+ Activity* activity = ElementCast<Activity>(parent_stack[1]);
+
+ if (*category == "android.intent.category.LAUNCHER") {
+ activity->has_launcher_category = true;
+ } else if (*category == "android.intent.category.LEANBACK_LAUNCHER") {
+ activity->has_leanback_launcher_category = true;
+ } else if (*category == "android.intent.category.HOME") {
+ component = "launcher";
+ }
+ }
+ }
+};
+
+/**
+ * Represents <provider> elements. The elements may have an <intent-filter> which may have <action>
+ * elements nested within.
+ **/
+class Provider : public ManifestExtractor::Element {
+ public:
+ Provider() = default;
+ bool has_required_saf_attributes = false;
+
+ void Extract(xml::Element* element) override {
+ const int32_t* exported = GetAttributeInteger(FindAttribute(element, EXPORTED_ATTR));
+ const int32_t* grant_uri_permissions = GetAttributeInteger(
+ FindAttribute(element, GRANT_URI_PERMISSIONS_ATTR));
+ const std::string* permission = GetAttributeString(
+ FindAttribute(element, PERMISSION_ATTR));
+
+ has_required_saf_attributes = ((exported && *exported != 0)
+ && (grant_uri_permissions && *grant_uri_permissions != 0)
+ && (permission && *permission == "android.permission.MANAGE_DOCUMENTS"));
+ }
+};
+
+/** Represents <receiver> elements. **/
+class Receiver : public ManifestExtractor::Element {
+ public:
+ Receiver() = default;
+ const std::string* permission = nullptr;
+ bool has_component = false;
+
+ void Extract(xml::Element* element) override {
+ permission = GetAttributeString(FindAttribute(element, PERMISSION_ATTR));
+ }
+};
+
+/**Represents <service> elements. **/
+class Service : public ManifestExtractor::Element {
+ public:
+ Service() = default;
+ const std::string* permission = nullptr;
+ bool has_component = false;
+
+ void Extract(xml::Element* element) override {
+ permission = GetAttributeString(FindAttribute(element, PERMISSION_ATTR));
+ }
+};
+
+/** Represents <uses-library> elements. **/
+class UsesLibrary : public ManifestExtractor::Element {
+ public:
+ UsesLibrary() = default;
+ std::string name;
+ int required;
+
+ void Extract(xml::Element* element) override {
+ auto parent_stack = extractor()->parent_stack();
+ if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ required = GetAttributeIntegerDefault(FindAttribute(element, REQUIRED_ATTR), 1);
+ }
+ }
+
+ void Print(text::Printer& printer) override {
+ if (!name.empty()) {
+ printer.Print(StringPrintf("uses-library%s:'%s'\n",
+ (required == 0) ? "-not-required" : "", name.data()));
+ }
+ }
+};
+
+/**
+ * Represents <meta-data> elements. These tags are only printed when a flag is passed in to
+ * explicitly enable meta data printing.
+ **/
+class MetaData : public ManifestExtractor::Element {
+ public:
+ MetaData() = default;
+ std::string name;
+ const std::string* value;
+ const int* value_int;
+ const std::string* resource;
+ const int* resource_int;
+
+ void Extract(xml::Element* element) override {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ value = GetAttributeString(FindAttribute(element, VALUE_ATTR));
+ value_int = GetAttributeInteger(FindAttribute(element, VALUE_ATTR));
+ resource = GetAttributeString(FindAttribute(element, RESOURCE_ATTR));
+ resource_int = GetAttributeInteger(FindAttribute(element, RESOURCE_ATTR));
+ }
+
+ void Print(text::Printer& printer) override {
+ if (extractor()->options_.include_meta_data && !name.empty()) {
+ printer.Print(StringPrintf("meta-data: name='%s' ", name.data()));
+ if (value) {
+ printer.Print(StringPrintf("value='%s' ", value->data()));
+ } else if (value_int) {
+ printer.Print(StringPrintf("value='%d' ", *value_int));
+ } else {
+ if (resource) {
+ printer.Print(StringPrintf("resource='%s' ", resource->data()));
+ } else if (resource_int) {
+ printer.Print(StringPrintf("resource='%d' ", *resource_int));
+ }
+ }
+ printer.Print("\n");
+ }
+ }
+};
+
+/**
+ * Represents <action> elements. Detects the presence of certain activity, provider, receiver, and
+ * service components.
+ **/
+class Action : public ManifestExtractor::Element {
+ public:
+ Action() = default;
+ std::string component = "";
+
+ void Extract(xml::Element* element) override {
+ auto parent_stack = extractor()->parent_stack();
+ std::string action = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+
+ if (ElementCast<IntentFilter>(parent_stack[0])) {
+ if (ElementCast<Activity>(parent_stack[1])) {
+ // Detects the presence of a particular type of activity.
+ Activity* activity = ElementCast<Activity>(parent_stack[1]);
+ auto map = std::map<std::string, std::string>({
+ { "android.intent.action.MAIN" , "main" },
+ { "android.intent.action.VIDEO_CAMERA" , "camera" },
+ { "android.intent.action.STILL_IMAGE_CAMERA_SECURE" , "camera-secure" },
+ });
+
+ auto entry = map.find(action);
+ if (entry != map.end()) {
+ component = entry->second;
+ activity->has_component_ = true;
+ }
+
+ if (action == "android.intent.action.MAIN") {
+ activity->has_main_action = true;
+ }
+
+ } else if (ElementCast<Receiver>(parent_stack[1])) {
+ // Detects the presence of a particular type of receiver. If the action requires a
+ // permission, then the receiver element is checked for the permission.
+ Receiver* receiver = ElementCast<Receiver>(parent_stack[1]);
+ auto map = std::map<std::string, std::string>({
+ { "android.appwidget.action.APPWIDGET_UPDATE" , "app-widget" },
+ { "android.app.action.DEVICE_ADMIN_ENABLED" , "device-admin" },
+ });
+
+ auto permissions = std::map<std::string, std::string>({
+ { "android.app.action.DEVICE_ADMIN_ENABLED" , "android.permission.BIND_DEVICE_ADMIN" },
+ });
+
+ auto entry = map.find(action);
+ auto permission = permissions.find(action);
+ if (entry != map.end() && (permission == permissions.end()
+ || (receiver->permission && permission->second == *receiver->permission))) {
+ receiver->has_component = true;
+ component = entry->second;
+ }
+
+ } else if (ElementCast<Service>(parent_stack[1])) {
+ // Detects the presence of a particular type of service. If the action requires a
+ // permission, then the service element is checked for the permission.
+ Service* service = ElementCast<Service>(parent_stack[1]);
+ auto map = std::map<std::string, std::string>({
+ { "android.view.InputMethod" , "ime" },
+ { "android.service.wallpaper.WallpaperService" , "wallpaper" },
+ { "android.accessibilityservice.AccessibilityService" , "accessibility" },
+ { "android.printservice.PrintService" , "print-service" },
+ { "android.nfc.cardemulation.action.HOST_APDU_SERVICE" , "host-apdu" },
+ { "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE" , "offhost-apdu" },
+ { "android.service.notification.NotificationListenerService" ,"notification-listener" },
+ { "android.service.dreams.DreamService" , "dream" },
+ });
+
+ auto permissions = std::map<std::string, std::string>({
+ { "android.accessibilityservice.AccessibilityService" ,
+ "android.permission.BIND_ACCESSIBILITY_SERVICE" },
+ { "android.printservice.PrintService" , "android.permission.BIND_PRINT_SERVICE" },
+ { "android.nfc.cardemulation.action.HOST_APDU_SERVICE" ,
+ "android.permission.BIND_NFC_SERVICE" },
+ { "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE" ,
+ "android.permission.BIND_NFC_SERVICE" },
+ { "android.service.notification.NotificationListenerService" ,
+ "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" },
+ { "android.service.dreams.DreamService" , "android.permission.BIND_DREAM_SERVICE" },
+ });
+
+ auto entry = map.find(action);
+ auto permission = permissions.find(action);
+ if (entry != map.end() && (permission == permissions.end()
+ || (service->permission && permission->second == *service->permission))) {
+ service->has_component= true;
+ component = entry->second;
+ }
+
+ } else if (ElementCast<Provider>(parent_stack[1])) {
+ // Detects the presence of a particular type of receiver. If the provider requires a
+ // permission, then the provider element is checked for the permission.
+ // Detect whether this action
+ Provider* provider = ElementCast<Provider>(parent_stack[1]);
+ if (action == "android.content.action.DOCUMENTS_PROVIDER"
+ && provider->has_required_saf_attributes) {
+ component = "document-provider";
+ }
+ }
+ }
+
+ // Represents a searchable interface
+ if (action == "android.intent.action.SEARCH") {
+ component = "search";
+ }
+ }
+};
+
+/**
+ * Represents <supports-input> elements. The element may have <input-type> elements nested within.
+ **/
+class SupportsInput : public ManifestExtractor::Element {
+ public:
+ SupportsInput() = default;
+ std::vector<std::string> inputs;
+
+ void Print(text::Printer& printer) override {
+ const size_t size = inputs.size();
+ if (size > 0) {
+ printer.Print("supports-input: '");
+ for (size_t i = 0; i < size; i++) {
+ printer.Print(StringPrintf("value='%s' ", inputs[i].data()));
+ }
+ printer.Print("\n");
+ }
+ }
+};
+
+/** Represents <input-type> elements. **/
+class InputType : public ManifestExtractor::Element {
+ public:
+ InputType() = default;
+ void Extract(xml::Element* element) override {
+ auto name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+ auto parent_stack = extractor()->parent_stack();
+
+ // Add the input to the set of supported inputs
+ if (name && ElementCast<SupportsInput>(parent_stack[0])) {
+ SupportsInput* supports = ElementCast<SupportsInput>(parent_stack[0]);
+ supports->inputs.push_back(*name);
+ }
+ }
+};
+
+/** Represents <original-package> elements. **/
+class OriginalPackage : public ManifestExtractor::Element {
+ public:
+ OriginalPackage() = default;
+ const std::string* name = nullptr;
+
+ void Extract(xml::Element* element) override {
+ name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+ }
+
+ void Print(text::Printer& printer) override {
+ if (name) {
+ printer.Print(StringPrintf("original-package:'%s'\n", name->data()));
+ }
+ }
+};
+
+/** * Represents <package-verifier> elements. **/
+class PackageVerifier : public ManifestExtractor::Element {
+ public:
+ PackageVerifier() = default;
+ const std::string* name = nullptr;
+ const std::string* public_key = nullptr;
+
+ void Extract(xml::Element* element) override {
+ name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+ public_key = GetAttributeString(FindAttribute(element, PUBLIC_KEY_ATTR));
+ }
+
+ void Print(text::Printer& printer) override {
+ if (name && public_key) {
+ printer.Print(StringPrintf("package-verifier: name='%s' publicKey='%s'\n",
+ name->data(), public_key->data()));
+ }
+ }
+};
+
+/** Represents <uses-package> elements. **/
+class UsesPackage : public ManifestExtractor::Element {
+ public:
+ UsesPackage() = default;
+ const std::string* name = nullptr;
+
+ void Extract(xml::Element* element) override {
+ name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+ }
+
+ void Print(text::Printer& printer) override {
+ if (name) {
+ printer.Print(StringPrintf("uses-package:'%s'\n", name->data()));
+ }
+ }
+};
+
+/** Represents <screen> elements found in <compatible-screens> elements. */
+class Screen : public ManifestExtractor::Element {
+ public:
+ Screen() = default;
+ const int32_t* size = nullptr;
+ const int32_t* density = nullptr;
+
+ void Extract(xml::Element* element) override {
+ size = GetAttributeInteger(FindAttribute(element, SCREEN_SIZE_ATTR));
+ density = GetAttributeInteger(FindAttribute(element, SCREEN_DENSITY_ATTR));
+ }
+};
+
+/**
+ * Represents <compatible-screens> elements. These elements have <screen> elements nested within
+ * that each denote a supported screen size and screen density.
+ **/
+class CompatibleScreens : public ManifestExtractor::Element {
+ public:
+ CompatibleScreens() = default;
+ void Print(text::Printer& printer) override {
+ printer.Print("compatible-screens:");
+
+ bool first = true;
+ ForEachChild(this, [&printer, &first](ManifestExtractor::Element* el){
+ if (auto screen = ElementCast<Screen>(el)) {
+ if (first) {
+ first = false;
+ } else {
+ printer.Print(",");
+ }
+
+ if (screen->size && screen->density) {
+ printer.Print(StringPrintf("'%d/%d'", *screen->size, *screen->density));
+ }
+ }
+ });
+ printer.Print("\n");
+ }
+};
+
+/** Represents <supports-gl-texture> elements. **/
+class SupportsGlTexture : public ManifestExtractor::Element {
+ public:
+ SupportsGlTexture() = default;
+ const std::string* name = nullptr;
+
+ void Extract(xml::Element* element) override {
+ name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+ }
+
+ void Print(text::Printer& printer) override {
+ if (name) {
+ printer.Print(StringPrintf("supports-gl-texture:'%s'\n", name->data()));
+ }
+ }
+};
+
+/** Recursively prints the extracted badging element. */
+static void Print(ManifestExtractor::Element* el, text::Printer& printer) {
+ el->Print(printer);
+ for (auto &child : el->children()) {
+ Print(child.get(), printer);
+ }
+}
+
+bool ManifestExtractor::Dump(text::Printer& printer, IDiagnostics* diag) {
+ // Load the manifest
+ std::unique_ptr<xml::XmlResource> doc = apk_->LoadXml("AndroidManifest.xml", diag);
+ if (doc == nullptr) {
+ diag->Error(DiagMessage() << "failed to find AndroidManifest.xml");
+ return false;
+ }
+
+ xml::Element* element = doc->root.get();
+ if (element->name != "manifest") {
+ diag->Error(DiagMessage() << "manifest does not start with <manifest> tag");
+ return false;
+ }
+
+ // Print only the <uses-permission>, <uses-permission-sdk23>, and <permission> elements if
+ // printing only permission elements is requested
+ if (options_.only_permissions) {
+ std::unique_ptr<ManifestExtractor::Element> manifest_element =
+ ManifestExtractor::Element::Inflate(this, element);
+
+ if (auto manifest = ElementCast<Manifest>(manifest_element.get())) {
+ for (xml::Element* child : element->GetChildElements()) {
+ if (child->name == "uses-permission" || child->name == "uses-permission-sdk-23"
+ || child->name == "permission") {
+ auto permission_element = ManifestExtractor::Element::Inflate(this, child);
+ manifest->AddChild(permission_element);
+ }
+ }
+
+ printer.Print(StringPrintf("package: %s\n", manifest->package.data()));
+ ForEachChild(manifest, [&printer](ManifestExtractor::Element* el) -> void {
+ el->Print(printer);
+ });
+
+ return true;
+ }
+
+ return false;
+ }
+
+ // Collect information about the resource configurations
+ if (apk_->GetResourceTable()) {
+ for (auto &package : apk_->GetResourceTable()->packages) {
+ for (auto &type : package->types) {
+ for (auto &entry : type->entries) {
+ for (auto &value : entry->values) {
+ std::string locale_str = value->config.GetBcp47LanguageTag();
+
+ // Collect all the unique locales of the apk
+ if (locales_.find(locale_str) == locales_.end()) {
+ ConfigDescription config = ManifestExtractor::DummyConfig();
+ config.setBcp47Locale(locale_str.data());
+ locales_.insert(std::make_pair(locale_str, config));
+ }
+
+ // Collect all the unique density of the apk
+ uint16_t density = (value->config.density == 0) ? (uint16_t) 160
+ : value->config.density;
+ if (densities_.find(density) == densities_.end()) {
+ ConfigDescription config = ManifestExtractor::DummyConfig();
+ config.density = density;
+ densities_.insert(std::make_pair(density, config));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Extract badging information
+ auto root = Visit(element);
+
+ // Print the elements in order seen
+ Print(root.get(), printer);
+
+ /** Recursively checks the extracted elements for the specified permission. **/
+ auto FindPermission = [&](ManifestExtractor::Element* root,
+ const std::string& name) -> ManifestExtractor::Element* {
+ return FindElement(root, [&](ManifestExtractor::Element* el) -> bool {
+ if (UsesPermission* permission = ElementCast<UsesPermission>(el)) {
+ return permission->name == name;
+ }
+ return false;
+ });
+ };
+
+ auto PrintPermission = [&printer](const std::string& name, const std::string& reason,
+ int32_t max_sdk_version) -> void {
+ auto permission = util::make_unique<UsesPermission>();
+ permission->name = name;
+ permission->maxSdkVersion = max_sdk_version;
+ permission->Print(printer);
+ permission->PrintImplied(printer, reason);
+ };
+
+ // Implied permissions
+ // Pre-1.6 implicitly granted permission compatibility logic
+ CommonFeatureGroup* common_feature_group = GetCommonFeatureGroup();
+ bool insert_write_external = false;
+ auto write_external_permission = ElementCast<UsesPermission>(
+ FindPermission(root.get(), "android.permission.WRITE_EXTERNAL_STORAGE"));
+
+ if (target_sdk() < 4) {
+ if (!write_external_permission) {
+ PrintPermission("android.permission.WRITE_EXTERNAL_STORAGE", "targetSdkVersion < 4", -1);
+ insert_write_external = true;
+ }
+
+ if (!FindPermission(root.get(), "android.permission.READ_PHONE_STATE")) {
+ PrintPermission("android.permission.READ_PHONE_STATE", "targetSdkVersion < 4", -1);
+ }
+ }
+
+ // If the application has requested WRITE_EXTERNAL_STORAGE, we will
+ // force them to always take READ_EXTERNAL_STORAGE as well. We always
+ // do this (regardless of target API version) because we can't have
+ // an app with write permission but not read permission.
+ auto read_external = FindPermission(root.get(), "android.permission.READ_EXTERNAL_STORAGE");
+ if (!read_external && (insert_write_external || write_external_permission)) {
+ PrintPermission("android.permission.READ_EXTERNAL_STORAGE",
+ "requested WRITE_EXTERNAL_STORAGE",
+ (write_external_permission) ? write_external_permission->maxSdkVersion : -1);
+ }
+
+ // Pre-JellyBean call log permission compatibility.
+ if (target_sdk() < 16) {
+ if (!FindPermission(root.get(), "android.permission.READ_CALL_LOG")
+ && FindPermission(root.get(), "android.permission.READ_CONTACTS")) {
+ PrintPermission("android.permission.READ_CALL_LOG",
+ "targetSdkVersion < 16 and requested READ_CONTACTS", -1);
+ }
+
+ if (!FindPermission(root.get(), "android.permission.WRITE_CALL_LOG")
+ && FindPermission(root.get(), "android.permission.WRITE_CONTACTS")) {
+ PrintPermission("android.permission.WRITE_CALL_LOG",
+ "targetSdkVersion < 16 and requested WRITE_CONTACTS", -1);
+ }
+ }
+
+ // If the app hasn't declared the touchscreen as a feature requirement (either
+ // directly or implied, required or not), then the faketouch feature is implied.
+ if (!common_feature_group->HasFeature("android.hardware.touchscreen")) {
+ common_feature_group->addImpliedFeature("android.hardware.faketouch",
+ "default feature for all apps", false);
+ }
+
+ // Only print the common feature group if no feature group is defined
+ std::vector<FeatureGroup*> feature_groups;
+ ForEachChild(root.get(), [&feature_groups](ManifestExtractor::Element* el) -> void {
+ if (auto feature_group = ElementCast<FeatureGroup>(el)) {
+ feature_groups.push_back(feature_group);
+ }
+ });
+
+ if (feature_groups.empty()) {
+ common_feature_group->PrintGroup(printer);
+ } else {
+ // Merge the common feature group into the feature group
+ for (auto& feature_group : feature_groups) {
+ feature_group->open_gles_version = std::max(feature_group->open_gles_version,
+ common_feature_group->open_gles_version);
+ feature_group->Merge(common_feature_group);
+ feature_group->PrintGroup(printer);
+ }
+ };
+
+ // Collect the component types of the application
+ std::set<std::string> components;
+ ForEachChild(root.get(), [&components](ManifestExtractor::Element* el) -> void {
+ if (ElementCast<Action>(el)) {
+ auto action = ElementCast<Action>(el);
+ if (!action->component.empty()) {
+ components.insert(action->component);
+ return;
+ }
+ }
+
+ if (ElementCast<Category>(el)) {
+ auto category = ElementCast<Category>(el);
+ if (!category->component.empty()) {
+ components.insert(category->component);
+ return;
+ }
+ }
+ });
+
+ // Check for the payment component
+ auto apk = apk_;
+ ForEachChild(root.get(), [&apk, &components, &diag](ManifestExtractor::Element* el) -> void {
+ if (auto service = ElementCast<Service>(el)) {
+ auto host_apdu_action = ElementCast<Action>(FindElement(service,
+ [&](ManifestExtractor::Element* el) -> bool {
+ if (auto action = ElementCast<Action>(el)) {
+ return (action->component == "host-apdu");
+ }
+ return false;
+ }));
+
+ auto offhost_apdu_action = ElementCast<Action>(FindElement(service,
+ [&](ManifestExtractor::Element* el) -> bool {
+ if (auto action = ElementCast<Action>(el)) {
+ return (action->component == "offhost-apdu");
+ }
+ return false;
+ }));
+
+ ForEachChild(service, [&apk, &components, &diag, &host_apdu_action,
+ &offhost_apdu_action](ManifestExtractor::Element* el) -> void {
+ if (auto meta_data = ElementCast<MetaData>(el)) {
+ if ((meta_data->name == "android.nfc.cardemulation.host_apdu_service" && host_apdu_action)
+ || (meta_data->name == "android.nfc.cardemulation.off_host_apdu_service"
+ && offhost_apdu_action)) {
+
+ // Attempt to load the resource file
+ if (!meta_data->resource) {
+ return;
+ }
+ auto resource = apk->LoadXml(*meta_data->resource, diag);
+ if (!resource) {
+ return;
+ }
+
+ // Look for the payment category on an <aid-group> element
+ auto& root = resource.get()->root;
+ if ((host_apdu_action && root->name == "host-apdu-service")
+ || (offhost_apdu_action && root->name == "offhost-apdu-service")) {
+
+ for (auto& child : root->GetChildElements()) {
+ if (child->name == "aid-group") {
+ auto category = FindAttribute(child, CATEGORY_ATTR);
+ if (category && category->value == "payment") {
+ components.insert("payment");
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+ }
+ });
+
+ // Print the components types if they are present
+ auto PrintComponent = [&components, &printer](const std::string& component) -> void {
+ if (components.find(component) != components.end()) {
+ printer.Print(StringPrintf("provides-component:'%s'\n", component.data()));
+ }
+ };
+
+ PrintComponent("app-widget");
+ PrintComponent("device-admin");
+ PrintComponent("ime");
+ PrintComponent("wallpaper");
+ PrintComponent("accessibility");
+ PrintComponent("print-service");
+ PrintComponent("payment");
+ PrintComponent("search");
+ PrintComponent("document-provider");
+ PrintComponent("launcher");
+ PrintComponent("notification-listener");
+ PrintComponent("dream");
+ PrintComponent("camera");
+ PrintComponent("camera-secure");
+
+ // Print presence of main activity
+ if (components.find("main") != components.end()) {
+ printer.Print("main\n");
+ }
+
+ // Print presence of activities, recivers, and services with no special components
+ FindElement(root.get(), [&printer](ManifestExtractor::Element* el) -> bool {
+ if (auto activity = ElementCast<Activity>(el)) {
+ if (!activity->has_component_) {
+ printer.Print("other-activities\n");
+ return true;
+ }
+ }
+ return false;
+ });
+
+ FindElement(root.get(), [&printer](ManifestExtractor::Element* el) -> bool {
+ if (auto receiver = ElementCast<Receiver>(el)) {
+ if (!receiver->has_component) {
+ printer.Print("other-receivers\n");
+ return true;
+ }
+ }
+ return false;
+ });
+
+ FindElement(root.get(), [&printer](ManifestExtractor::Element* el) -> bool {
+ if (auto service = ElementCast<Service>(el)) {
+ if (!service->has_component) {
+ printer.Print("other-services\n");
+ return true;
+ }
+ }
+ return false;
+ });
+
+ // Print the supported screens
+ SupportsScreen* screen = ElementCast<SupportsScreen>(FindElement(root.get(),
+ [&](ManifestExtractor::Element* el) -> bool {
+ return ElementCast<SupportsScreen>(el) != nullptr;
+ }));
+
+ if (screen) {
+ screen->PrintScreens(printer, target_sdk_);
+ } else {
+ // Print the default supported screens
+ SupportsScreen default_screens;
+ default_screens.PrintScreens(printer, target_sdk_);
+ }
+
+ // Print all the unique locales of the apk
+ printer.Print("locales:");
+ for (auto& config : locales_) {
+ if (config.first.empty()) {
+ printer.Print(" '--_--'");
+ } else {
+ printer.Print(StringPrintf(" '%s'", config.first.data()));
+ }
+ }
+ printer.Print("\n");
+
+ // Print all the densities locales of the apk
+ printer.Print("densities:");
+ for (auto& config : densities_) {
+ printer.Print(StringPrintf(" '%d'", config.first));
+ }
+ printer.Print("\n");
+
+ // Print the supported architectures of the app
+ std::set<std::string> architectures;
+ auto it = apk_->GetFileCollection()->Iterator();
+ while (it->HasNext()) {
+ auto file_path = it->Next()->GetSource().path;
+
+
+ size_t pos = file_path.find("lib/");
+ if (pos != std::string::npos) {
+ file_path = file_path.substr(pos + 4);
+ pos = file_path.find("/");
+ if (pos != std::string::npos) {
+ file_path = file_path.substr(0, pos);
+ }
+
+ architectures.insert(file_path);
+ }
+ }
+
+ // Determine if the application has multiArch supports
+ auto has_multi_arch = FindElement(root.get(), [&](ManifestExtractor::Element* el) -> bool {
+ if (auto application = ElementCast<Application>(el)) {
+ return application->has_multi_arch;
+ }
+ return false;
+ });
+
+ bool output_alt_native_code = false;
+ // A multiArch package is one that contains 64-bit and
+ // 32-bit versions of native code and expects 3rd-party
+ // apps to load these native code libraries. Since most
+ // 64-bit systems also support 32-bit apps, the apps
+ // loading this multiArch package's code may be either
+ if (has_multi_arch) {
+ // If this is a multiArch package, report the 64-bit
+ // version only. Then as a separate entry, report the
+ // rest.
+ //
+ // If we report the 32-bit architecture, this APK will
+ // be installed on a 32-bit device, causing a large waste
+ // of bandwidth and disk space. This assumes that
+ // the developer of the multiArch package has also
+ // made a version that is 32-bit only.
+ const std::string kIntel64 = "x86_64";
+ const std::string kArm64 = "arm64-v8a";
+
+ auto arch = architectures.find(kIntel64);
+ if (arch == architectures.end()) {
+ arch = architectures.find(kArm64);
+ }
+
+ if (arch != architectures.end()) {
+ printer.Print(StringPrintf("native-code: '%s'\n", arch->data()));
+ architectures.erase(arch);
+ output_alt_native_code = true;
+ }
+ }
+
+ if (architectures.size() > 0) {
+ if (output_alt_native_code) {
+ printer.Print("alt-");
+ }
+ printer.Print("native-code:");
+ for (auto& arch : architectures) {
+ printer.Print(StringPrintf(" '%s'", arch.data()));
+ }
+ printer.Print("\n");
+ }
+
+ return true;
+}
+
+/**
+ * Returns the element casted to the type if the element is of that type. Otherwise, returns a null
+ * pointer.
+ **/
+template<typename T>
+T* ElementCast(ManifestExtractor::Element* element) {
+ if (element == nullptr) {
+ return nullptr;
+ }
+
+ const std::unordered_map<std::string, bool> kTagCheck = {
+ {"action", std::is_base_of<Action, T>::value},
+ {"activity", std::is_base_of<Activity, T>::value},
+ {"application", std::is_base_of<Application, T>::value},
+ {"category", std::is_base_of<Category, T>::value},
+ {"compatible-screens", std::is_base_of<CompatibleScreens, T>::value},
+ {"feature-group", std::is_base_of<FeatureGroup, T>::value},
+ {"input-type", std::is_base_of<InputType, T>::value},
+ {"intent-filter", std::is_base_of<IntentFilter, T>::value},
+ {"meta-data", std::is_base_of<MetaData, T>::value},
+ {"manifest", std::is_base_of<Manifest, T>::value},
+ {"original-package", std::is_base_of<OriginalPackage, T>::value},
+ {"package-verifier", std::is_base_of<PackageVerifier, T>::value},
+ {"permission", std::is_base_of<Permission, T>::value},
+ {"provider", std::is_base_of<Provider, T>::value},
+ {"receiver", std::is_base_of<Receiver, T>::value},
+ {"screen", std::is_base_of<Screen, T>::value},
+ {"service", std::is_base_of<Service, T>::value},
+ {"supports-gl-texture", std::is_base_of<SupportsGlTexture, T>::value},
+ {"supports-input", std::is_base_of<SupportsInput, T>::value},
+ {"supports-screens", std::is_base_of<SupportsScreen, T>::value},
+ {"uses-configuration", std::is_base_of<UsesConfiguarion, T>::value},
+ {"uses-feature", std::is_base_of<UsesFeature, T>::value},
+ {"uses-permission", std::is_base_of<UsesPermission, T>::value},
+ {"uses-permission-sdk-23", std::is_base_of<UsesPermissionSdk23, T>::value},
+ {"uses-library", std::is_base_of<UsesLibrary, T>::value},
+ {"uses-package", std::is_base_of<UsesPackage, T>::value},
+ {"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value},
+ };
+
+ auto check = kTagCheck.find(element->tag());
+ if (check != kTagCheck.end() && check->second) {
+ return static_cast<T*>(element);
+ }
+ return nullptr;
+}
+
+template<typename T>
+std::unique_ptr<T> CreateType() {
+ return std::move(util::make_unique<T>());
+}
+
+std::unique_ptr<ManifestExtractor::Element> ManifestExtractor::Element::Inflate(
+ ManifestExtractor* extractor, xml::Element* el) {
+ const std::unordered_map<std::string,
+ std::function<std::unique_ptr<ManifestExtractor::Element>()>>
+ kTagCheck = {
+ {"action", &CreateType<Action>},
+ {"activity", &CreateType<Activity>},
+ {"application", &CreateType<Application>},
+ {"category", &CreateType<Category>},
+ {"compatible-screens", &CreateType<CompatibleScreens>},
+ {"feature-group", &CreateType<FeatureGroup>},
+ {"input-type", &CreateType<InputType>},
+ {"intent-filter",&CreateType<IntentFilter>},
+ {"manifest", &CreateType<Manifest>},
+ {"meta-data", &CreateType<MetaData>},
+ {"original-package", &CreateType<OriginalPackage>},
+ {"package-verifier", &CreateType<PackageVerifier>},
+ {"permission", &CreateType<Permission>},
+ {"provider", &CreateType<Provider>},
+ {"receiver", &CreateType<Receiver>},
+ {"screen", &CreateType<Screen>},
+ {"service", &CreateType<Service>},
+ {"supports-gl-texture", &CreateType<SupportsGlTexture>},
+ {"supports-input", &CreateType<SupportsInput>},
+ {"supports-screens", &CreateType<SupportsScreen>},
+ {"uses-configuration", &CreateType<UsesConfiguarion>},
+ {"uses-feature", &CreateType<UsesFeature>},
+ {"uses-permission", &CreateType<UsesPermission>},
+ {"uses-permission-sdk-23", &CreateType<UsesPermissionSdk23>},
+ {"uses-library", &CreateType<UsesLibrary>},
+ {"uses-package", &CreateType<UsesPackage>},
+ {"uses-sdk", &CreateType<UsesSdkBadging>},
+ };
+
+ // Attempt to map the xml tag to a element inflater
+ std::unique_ptr<ManifestExtractor::Element> element;
+ auto check = kTagCheck.find(el->name);
+ if (check != kTagCheck.end()) {
+ element = check->second();
+ } else {
+ element = util::make_unique<ManifestExtractor::Element>();
+ }
+
+ element->extractor_ = extractor;
+ element->tag_ = el->name;
+ element->Extract(el);
+ return element;
+}
+
+std::unique_ptr<ManifestExtractor::Element> ManifestExtractor::Visit(xml::Element* el) {
+ auto element = ManifestExtractor::Element::Inflate(this, el);
+ parent_stack_.insert(parent_stack_.begin(), element.get());
+
+ // Process the element and recursively visit the children
+ for (xml::Element* child : el->GetChildElements()) {
+ auto v = Visit(child);
+ element->AddChild(v);
+ }
+
+ parent_stack_.erase(parent_stack_.begin());
+ return element;
+}
+
+// Use a smaller buffer so that there is less latency for dumping to stdout.
+constexpr size_t kStdOutBufferSize = 1024u;
+
+int DumpBadgingCommand::Action(const std::vector<std::string>& args) {
+ if (args.size() < 1) {
+ diag_->Error(DiagMessage() << "No dump apk specified.");
+ return 1;
+ }
+
+ ManifestExtractor::Options options;
+ options.include_meta_data = include_metadata_;
+
+ io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
+ text::Printer printer(&fout);
+ for (auto apk : args) {
+ auto loaded_apk = LoadedApk::LoadApkFromPath(apk, diag_);
+ if (!loaded_apk) {
+ return 1;
+ }
+
+ ManifestExtractor extractor(loaded_apk.get(), options);
+ extractor.Dump(printer, diag_);
+ }
+
+ return 0;
+}
+
+int DumpPermissionsCommand::Action(const std::vector<std::string>& args) {
+ if (args.size() < 1) {
+ diag_->Error(DiagMessage() << "No dump apk specified.");
+ return 1;
+ }
+
+ ManifestExtractor::Options options;
+ options.only_permissions = true;
+
+ io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
+ text::Printer printer(&fout);
+ for (auto apk : args) {
+ auto loaded_apk = LoadedApk::LoadApkFromPath(apk, diag_);
+ if (!loaded_apk) {
+ return 1;
+ }
+
+ ManifestExtractor extractor(loaded_apk.get(), options);
+ extractor.Dump(printer, diag_);
+ }
+
+ return 0;
+}
+
+} // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/dump/DumpManifest.h b/tools/aapt2/dump/DumpManifest.h
new file mode 100644
index 0000000..a70be53
--- /dev/null
+++ b/tools/aapt2/dump/DumpManifest.h
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT2_DUMP_MANIFEST_H
+#define AAPT2_DUMP_MANIFEST_H
+
+#include <Diagnostics.h>
+#include <ValueVisitor.h>
+#include <io/ZipArchive.h>
+
+
+#include "cmd/Command.h"
+#include "process/IResourceTableConsumer.h"
+#include "text/Printer.h"
+#include "xml/XmlDom.h"
+
+namespace aapt {
+
+class DumpBadgingCommand : public Command {
+ public:
+ explicit DumpBadgingCommand(IDiagnostics* diag) : Command("badging"), diag_(diag) {
+ SetDescription("Print information extracted from the manifest of the APK.");
+ AddOptionalSwitch("--include-meta-data", "Include meta-data information.",
+ &include_metadata_);
+ }
+
+ int Action(const std::vector<std::string>& args) override;
+
+ private:
+ IDiagnostics* diag_;
+ bool include_metadata_ = false;
+};
+
+class DumpPermissionsCommand : public Command {
+ public:
+ explicit DumpPermissionsCommand(IDiagnostics* diag) : Command("permissions"), diag_(diag) {
+ SetDescription("Print the permissions extracted from the manifest of the APK.");
+ }
+
+ int Action(const std::vector<std::string>& args) override;
+
+ private:
+ IDiagnostics* diag_;
+};
+
+}// namespace aapt
+
+#endif //AAPT2_DUMP_MANIFEST_H
\ No newline at end of file
diff --git a/tools/aapt2/io/Util.h b/tools/aapt2/io/Util.h
index b07fb53..5f978a8 100644
--- a/tools/aapt2/io/Util.h
+++ b/tools/aapt2/io/Util.h
@@ -20,6 +20,7 @@
#include <string>
#include "google/protobuf/message_lite.h"
+#include "google/protobuf/io/coded_stream.h"
#include "format/Archive.h"
#include "io/File.h"
@@ -122,6 +123,23 @@
io::InputStream* in_;
};
+class ProtoInputStreamReader {
+ public:
+ explicit ProtoInputStreamReader(io::InputStream* in) : in_(in) { }
+
+ /** Deserializes a MessageLite proto from the current position in the input stream.*/
+ template <typename T> bool ReadMessage(T *message_lite) {
+ ZeroCopyInputAdaptor adapter(in_);
+ google::protobuf::io::CodedInputStream coded_stream(&adapter);
+ coded_stream.SetTotalBytesLimit(std::numeric_limits<int32_t>::max(),
+ coded_stream.BytesUntilTotalBytesLimit());
+ return message_lite->ParseFromCodedStream(&coded_stream);
+ }
+
+ private:
+ io::InputStream* in_;
+};
+
} // namespace io
} // namespace aapt
diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp
index e991743..b5c33062 100644
--- a/tools/aapt2/split/TableSplitter.cpp
+++ b/tools/aapt2/split/TableSplitter.cpp
@@ -154,6 +154,12 @@
bool TableSplitter::VerifySplitConstraints(IAaptContext* context) {
bool error = false;
for (size_t i = 0; i < split_constraints_.size(); i++) {
+ if (split_constraints_[i].configs.size() == 0) {
+ // For now, treat this as a warning. We may consider aborting processing.
+ context->GetDiagnostics()->Warn(DiagMessage()
+ << "no configurations for constraint '"
+ << split_constraints_[i].name << "'");
+ }
for (size_t j = i + 1; j < split_constraints_.size(); j++) {
for (const ConfigDescription& config : split_constraints_[i].configs) {
if (split_constraints_[j].configs.find(config) != split_constraints_[j].configs.end()) {
diff --git a/tools/aapt2/split/TableSplitter.h b/tools/aapt2/split/TableSplitter.h
index 6aec257..ed24bc39 100644
--- a/tools/aapt2/split/TableSplitter.h
+++ b/tools/aapt2/split/TableSplitter.h
@@ -30,6 +30,7 @@
struct SplitConstraints {
std::set<ConfigDescription> configs;
+ std::string name;
};
struct TableSplitterOptions {
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index fd5262a..be6e510 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -204,6 +204,112 @@
configuration::PostProcessingConfiguration config_;
};
+class ConfigDescriptionBuilder {
+ public:
+ ConfigDescriptionBuilder() = default;
+
+ ConfigDescriptionBuilder& setMcc(uint16_t mcc) {
+ config_.mcc = mcc;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setMnc(uint16_t mnc) {
+ config_.mnc = mnc;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setLanguage(uint16_t language) {
+ config_.language[0] = language >> 8;
+ config_.language[1] = language & 0xff;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setCountry(uint16_t country) {
+ config_.country[0] = country >> 8;
+ config_.country[1] = country & 0xff;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setOrientation(uint8_t orientation) {
+ config_.orientation = orientation;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setTouchscreen(uint8_t touchscreen) {
+ config_.touchscreen = touchscreen;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setDensity(uint16_t density) {
+ config_.density = density;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setKeyboard(uint8_t keyboard) {
+ config_.keyboard = keyboard;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setNavigation(uint8_t navigation) {
+ config_.navigation = navigation;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setInputFlags(uint8_t inputFlags) {
+ config_.inputFlags = inputFlags;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setInputPad0(uint8_t inputPad0) {
+ config_.inputPad0 = inputPad0;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setScreenWidth(uint16_t screenWidth) {
+ config_.screenWidth = screenWidth;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setScreenHeight(uint16_t screenHeight) {
+ config_.screenHeight = screenHeight;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setSdkVersion(uint16_t sdkVersion) {
+ config_.sdkVersion = sdkVersion;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setMinorVersion(uint16_t minorVersion) {
+ config_.minorVersion = minorVersion;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setScreenLayout(uint8_t screenLayout) {
+ config_.screenLayout = screenLayout;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setUiMode(uint8_t uiMode) {
+ config_.uiMode = uiMode;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setSmallestScreenWidthDp(uint16_t smallestScreenWidthDp) {
+ config_.smallestScreenWidthDp = smallestScreenWidthDp;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setScreenWidthDp(uint16_t screenWidthDp) {
+ config_.screenWidthDp = screenWidthDp;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setScreenHeightDp(uint16_t screenHeightDp) {
+ config_.screenHeightDp = screenHeightDp;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setScreenLayout2(uint8_t screenLayout2) {
+ config_.screenLayout2 = screenLayout2;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setColorMode(uint8_t colorMode) {
+ config_.colorMode = colorMode;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setScreenConfigPad2(uint16_t screenConfigPad2) {
+ config_.screenConfigPad2 = screenConfigPad2;
+ return *this;
+ }
+ ConfigDescription Build() {
+ return config_;
+ }
+
+ private:
+ ConfigDescription config_;
+};
+
} // namespace test
} // namespace aapt
diff --git a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
index bb02dec..b4dfac6 100644
--- a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
+++ b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
@@ -127,7 +127,7 @@
public static final int OSU_STATUS_SERVICE_PROVIDER_VERIFIED = 5;
/**
- * The status code for provisioning flow to indicate starting the SOAP exchange.
+ * The status code for provisioning flow to indicate starting the first SOAP exchange.
*/
public static final int OSU_STATUS_INIT_SOAP_EXCHANGE = 6;
@@ -142,6 +142,11 @@
public static final int OSU_STATUS_REDIRECT_RESPONSE_RECEIVED = 8;
/**
+ * The status code for provisioning flow to indicate starting the second SOAP exchange.
+ */
+ public static final int OSU_STATUS_SECOND_SOAP_EXCHANGE = 9;
+
+ /**
* Provisioning status for OSU failure
*
* @param status indicates error condition