Merge "Update StatsLogProcessor to handle BinaryPushStateChanged"
diff --git a/Android.bp b/Android.bp
index dbef611..5796fb1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -402,7 +402,7 @@
"ext",
"unsupportedappusage",
"framework-media-stubs-systemapi",
- "framework_mediaprovider_stubs",
+ "framework-mediaprovider-stubs-systemapi",
"framework-tethering",
"framework-telephony-stubs",
],
@@ -491,14 +491,11 @@
apex_available: ["//apex_available:platform"],
visibility: [
"//frameworks/base",
- // TODO(b/144149403) remove the below lines
+ // TODO(b/147128803) remove the below lines
"//frameworks/base/apex/appsearch/framework",
"//frameworks/base/apex/blobstore/framework",
"//frameworks/base/apex/jobscheduler/framework",
- "//frameworks/base/apex/permission/framework",
"//frameworks/base/apex/statsd/service",
- "//frameworks/base/telephony",
- "//frameworks/opt/net/wifi/service",
],
}
@@ -517,9 +514,9 @@
installable: false, // this lib is a build-only library
static_libs: [
"framework-minus-apex",
- "framework-media-stubs-systemapi",
- "framework_mediaprovider_stubs",
"framework-appsearch", // TODO(b/146218515): should be framework-appsearch-stubs
+ "framework-media-stubs-systemapi",
+ "framework-mediaprovider-stubs-systemapi",
"framework-permission-stubs-systemapi",
"framework-sdkextensions-stubs-systemapi",
// TODO(b/146167933): Use framework-statsd-stubs instead.
diff --git a/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp b/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp
index e4ab823..22daa8e 100644
--- a/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp
+++ b/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp
@@ -46,15 +46,15 @@
}
}
-static status_pull_atom_return_t pullAtomCallback(int32_t atomTag, pulled_stats_event_list* data,
- void* /*cookie*/) {
+static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atomTag, AStatsEventList* data,
+ void* /*cookie*/) {
sNumPulls++;
sleep_for(std::chrono::milliseconds(sLatencyMillis));
for (int i = 0; i < sAtomsPerPull; i++) {
- stats_event* event = add_stats_event_to_pull_data(data);
- stats_event_set_atom_id(event, atomTag);
- stats_event_write_int64(event, (int64_t) sNumPulls);
- stats_event_build(event);
+ AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+ AStatsEvent_setAtomId(event, atomTag);
+ AStatsEvent_writeInt64(event, (int64_t) sNumPulls);
+ AStatsEvent_build(event);
}
return sPullReturnVal;
}
@@ -71,11 +71,12 @@
sLatencyMillis = latencyMillis;
sAtomsPerPull = atomsPerPull;
sNumPulls = 0;
- pull_atom_metadata metadata = {.cool_down_ns = coolDownNs,
- .timeout_ns = timeoutNs,
- .additive_fields = nullptr,
- .additive_fields_size = 0};
- register_stats_pull_atom_callback(sAtomTag, &pullAtomCallback, &metadata, nullptr);
+ AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
+ AStatsManager_PullAtomMetadata_setCoolDownNs(metadata, coolDownNs);
+ AStatsManager_PullAtomMetadata_setTimeoutNs(metadata, timeoutNs);
+
+ AStatsManager_registerPullAtomCallback(sAtomTag, &pullAtomCallback, metadata, nullptr);
+ AStatsManager_PullAtomMetadata_release(metadata);
}
extern "C"
@@ -83,6 +84,6 @@
Java_com_android_internal_os_statsd_libstats_LibStatsPullTests_unregisterStatsPuller(
JNIEnv* /*env*/, jobject /* this */, jint /*atomTag*/)
{
- unregister_stats_pull_atom_callback(sAtomTag);
+ AStatsManager_unregisterPullAtomCallback(sAtomTag);
}
-} // namespace
\ No newline at end of file
+} // namespace
diff --git a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java
index dbd636d..e119b4c 100644
--- a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java
+++ b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java
@@ -71,7 +71,6 @@
*/
@Before
public void setup() {
-// Debug.waitForDebugger();
mContext = InstrumentationRegistry.getTargetContext();
assertThat(InstrumentationRegistry.getInstrumentation()).isNotNull();
sPullReturnValue = StatsManager.PULL_SUCCESS;
diff --git a/api/current.txt b/api/current.txt
index cab81ad..87a5cd7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5919,6 +5919,7 @@
method public long[] getVibrationPattern();
method public boolean hasUserSetImportance();
method public boolean hasUserSetSound();
+ method public boolean isImportantConversation();
method public void setAllowBubbles(boolean);
method public void setBypassDnd(boolean);
method public void setConversationId(@Nullable String, @Nullable String);
@@ -6031,14 +6032,19 @@
public static class NotificationManager.Policy implements android.os.Parcelable {
ctor public NotificationManager.Policy(int, int, int);
ctor public NotificationManager.Policy(int, int, int, int);
+ ctor public NotificationManager.Policy(int, int, int, int, int);
method public int describeContents();
method public static String priorityCategoriesToString(int);
method public static String prioritySendersToString(int);
method public static String suppressedEffectsToString(int);
method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CONVERSATION_SENDERS_ANYONE = 1; // 0x1
+ field public static final int CONVERSATION_SENDERS_IMPORTANT = 2; // 0x2
+ field public static final int CONVERSATION_SENDERS_NONE = 3; // 0x3
field @NonNull public static final android.os.Parcelable.Creator<android.app.NotificationManager.Policy> CREATOR;
field public static final int PRIORITY_CATEGORY_ALARMS = 32; // 0x20
field public static final int PRIORITY_CATEGORY_CALLS = 8; // 0x8
+ field public static final int PRIORITY_CATEGORY_CONVERSATIONS = 256; // 0x100
field public static final int PRIORITY_CATEGORY_EVENTS = 2; // 0x2
field public static final int PRIORITY_CATEGORY_MEDIA = 64; // 0x40
field public static final int PRIORITY_CATEGORY_MESSAGES = 4; // 0x4
@@ -6059,6 +6065,7 @@
field public static final int SUPPRESSED_EFFECT_STATUS_BAR = 32; // 0x20
field public final int priorityCallSenders;
field public final int priorityCategories;
+ field public final int priorityConversationSenders;
field public final int priorityMessageSenders;
field public final int suppressedVisualEffects;
}
@@ -7140,6 +7147,7 @@
field public static final int KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS = 8; // 0x8
field public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 1; // 0x1
field public static final int LEAVE_ALL_SYSTEM_APPS_ENABLED = 16; // 0x10
+ field public static final int LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK = 64; // 0x40
field public static final int LOCK_TASK_FEATURE_GLOBAL_ACTIONS = 16; // 0x10
field public static final int LOCK_TASK_FEATURE_HOME = 4; // 0x4
field public static final int LOCK_TASK_FEATURE_KEYGUARD = 32; // 0x20
@@ -43776,12 +43784,14 @@
method public int getPriorityCallSenders();
method public int getPriorityCategoryAlarms();
method public int getPriorityCategoryCalls();
+ method public int getPriorityCategoryConversations();
method public int getPriorityCategoryEvents();
method public int getPriorityCategoryMedia();
method public int getPriorityCategoryMessages();
method public int getPriorityCategoryReminders();
method public int getPriorityCategoryRepeatCallers();
method public int getPriorityCategorySystem();
+ method public int getPriorityConversationSenders();
method public int getPriorityMessageSenders();
method public int getVisualEffectAmbient();
method public int getVisualEffectBadge();
@@ -43791,6 +43801,10 @@
method public int getVisualEffectPeek();
method public int getVisualEffectStatusBar();
method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CONVERSATION_SENDERS_ANYONE = 1; // 0x1
+ field public static final int CONVERSATION_SENDERS_IMPORTANT = 2; // 0x2
+ field public static final int CONVERSATION_SENDERS_NONE = 3; // 0x3
+ field public static final int CONVERSATION_SENDERS_UNSET = 0; // 0x0
field @NonNull public static final android.os.Parcelable.Creator<android.service.notification.ZenPolicy> CREATOR;
field public static final int PEOPLE_TYPE_ANYONE = 1; // 0x1
field public static final int PEOPLE_TYPE_CONTACTS = 2; // 0x2
@@ -43807,6 +43821,7 @@
method @NonNull public android.service.notification.ZenPolicy.Builder allowAlarms(boolean);
method @NonNull public android.service.notification.ZenPolicy.Builder allowAllSounds();
method @NonNull public android.service.notification.ZenPolicy.Builder allowCalls(int);
+ method @NonNull public android.service.notification.ZenPolicy.Builder allowConversations(int);
method @NonNull public android.service.notification.ZenPolicy.Builder allowEvents(boolean);
method @NonNull public android.service.notification.ZenPolicy.Builder allowMedia(boolean);
method @NonNull public android.service.notification.ZenPolicy.Builder allowMessages(int);
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index b4259474..90531b1 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -1,151 +1,4 @@
// Signature format: 2.0
-package android.app.timedetector {
-
- public final class PhoneTimeSuggestion implements android.os.Parcelable {
- method public void addDebugInfo(@NonNull String);
- method public void addDebugInfo(@NonNull java.util.List<java.lang.String>);
- method public int describeContents();
- method @NonNull public java.util.List<java.lang.String> getDebugInfo();
- method public int getSlotIndex();
- method @Nullable public android.os.TimestampedValue<java.lang.Long> getUtcTime();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.app.timedetector.PhoneTimeSuggestion> CREATOR;
- }
-
- public static final class PhoneTimeSuggestion.Builder {
- ctor public PhoneTimeSuggestion.Builder(int);
- method @NonNull public android.app.timedetector.PhoneTimeSuggestion.Builder addDebugInfo(@NonNull String);
- method @NonNull public android.app.timedetector.PhoneTimeSuggestion build();
- method @NonNull public android.app.timedetector.PhoneTimeSuggestion.Builder setUtcTime(@Nullable android.os.TimestampedValue<java.lang.Long>);
- }
-
- public interface TimeDetector {
- method @RequiresPermission("android.permission.SUGGEST_PHONE_TIME_AND_ZONE") public void suggestPhoneTime(@NonNull android.app.timedetector.PhoneTimeSuggestion);
- }
-
-}
-
-package android.app.timezonedetector {
-
- public final class PhoneTimeZoneSuggestion implements android.os.Parcelable {
- method public void addDebugInfo(@NonNull String);
- method public void addDebugInfo(@NonNull java.util.List<java.lang.String>);
- method @NonNull public static android.app.timezonedetector.PhoneTimeZoneSuggestion createEmptySuggestion(int, @NonNull String);
- method public int describeContents();
- method @NonNull public java.util.List<java.lang.String> getDebugInfo();
- method public int getMatchType();
- method public int getQuality();
- method public int getSlotIndex();
- method @Nullable public String getZoneId();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.app.timezonedetector.PhoneTimeZoneSuggestion> CREATOR;
- field public static final int MATCH_TYPE_EMULATOR_ZONE_ID = 4; // 0x4
- field public static final int MATCH_TYPE_NA = 0; // 0x0
- field public static final int MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET = 3; // 0x3
- field public static final int MATCH_TYPE_NETWORK_COUNTRY_ONLY = 2; // 0x2
- field public static final int MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY = 5; // 0x5
- field public static final int QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS = 3; // 0x3
- field public static final int QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET = 2; // 0x2
- field public static final int QUALITY_NA = 0; // 0x0
- field public static final int QUALITY_SINGLE_ZONE = 1; // 0x1
- }
-
- public static final class PhoneTimeZoneSuggestion.Builder {
- ctor public PhoneTimeZoneSuggestion.Builder(int);
- method @NonNull public android.app.timezonedetector.PhoneTimeZoneSuggestion.Builder addDebugInfo(@NonNull String);
- method @NonNull public android.app.timezonedetector.PhoneTimeZoneSuggestion build();
- method @NonNull public android.app.timezonedetector.PhoneTimeZoneSuggestion.Builder setMatchType(int);
- method @NonNull public android.app.timezonedetector.PhoneTimeZoneSuggestion.Builder setQuality(int);
- method @NonNull public android.app.timezonedetector.PhoneTimeZoneSuggestion.Builder setZoneId(@Nullable String);
- }
-
- public interface TimeZoneDetector {
- method @RequiresPermission("android.permission.SUGGEST_PHONE_TIME_AND_ZONE") public void suggestPhoneTimeZone(@NonNull android.app.timezonedetector.PhoneTimeZoneSuggestion);
- }
-
-}
-
-package android.os {
-
- public final class TimestampedValue<T> implements android.os.Parcelable {
- ctor public TimestampedValue(long, @Nullable T);
- method public int describeContents();
- method public long getReferenceTimeMillis();
- method @Nullable public T getValue();
- method public static long referenceTimeDifference(@NonNull android.os.TimestampedValue<?>, @NonNull android.os.TimestampedValue<?>);
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.os.TimestampedValue<?>> CREATOR;
- }
-
-}
-
-package android.timezone {
-
- public final class CountryTimeZones {
- method @Nullable public android.icu.util.TimeZone getDefaultTimeZone();
- method @Nullable public String getDefaultTimeZoneId();
- method @NonNull public java.util.List<android.timezone.CountryTimeZones.TimeZoneMapping> getEffectiveTimeZoneMappingsAt(long);
- method public boolean hasUtcZone(long);
- method public boolean isDefaultTimeZoneBoosted();
- method @Nullable public android.timezone.CountryTimeZones.OffsetResult lookupByOffsetWithBias(int, @Nullable Boolean, @Nullable Integer, long, @Nullable android.icu.util.TimeZone);
- method public boolean matchesCountryCode(@NonNull String);
- }
-
- public static final class CountryTimeZones.OffsetResult {
- ctor public CountryTimeZones.OffsetResult(@NonNull android.icu.util.TimeZone, boolean);
- method @NonNull public android.icu.util.TimeZone getTimeZone();
- method public boolean isOnlyMatch();
- }
-
- public static final class CountryTimeZones.TimeZoneMapping {
- method @NonNull public android.icu.util.TimeZone getTimeZone();
- method @NonNull public String getTimeZoneId();
- }
-
- public final class TelephonyLookup {
- method @NonNull public static android.timezone.TelephonyLookup getInstance();
- method @Nullable public android.timezone.TelephonyNetworkFinder getTelephonyNetworkFinder();
- }
-
- public final class TelephonyNetwork {
- method @NonNull public String getCountryIsoCode();
- method @NonNull public String getMcc();
- method @NonNull public String getMnc();
- }
-
- public final class TelephonyNetworkFinder {
- method @Nullable public android.timezone.TelephonyNetwork findNetworkByMccMnc(@NonNull String, @NonNull String);
- }
-
- public final class TimeZoneFinder {
- method @Nullable public String getIanaVersion();
- method @NonNull public static android.timezone.TimeZoneFinder getInstance();
- method @Nullable public android.timezone.CountryTimeZones lookupCountryTimeZones(@NonNull String);
- }
-
- public final class TzDataSetVersion {
- method public static int currentFormatMajorVersion();
- method public static int currentFormatMinorVersion();
- method public int getFormatMajorVersion();
- method public int getFormatMinorVersion();
- method public int getRevision();
- method @NonNull public String getRulesVersion();
- method public static boolean isCompatibleWithThisDevice(android.timezone.TzDataSetVersion);
- method @NonNull public static android.timezone.TzDataSetVersion read() throws java.io.IOException, android.timezone.TzDataSetVersion.TzDataSetException;
- }
-
- public static final class TzDataSetVersion.TzDataSetException extends java.lang.Exception {
- ctor public TzDataSetVersion.TzDataSetException(String);
- ctor public TzDataSetVersion.TzDataSetException(String, Throwable);
- }
-
- public final class ZoneInfoDb {
- method @NonNull public static android.timezone.ZoneInfoDb getInstance();
- method @NonNull public String getVersion();
- }
-
-}
-
package android.util {
public final class Log {
diff --git a/api/system-current.txt b/api/system-current.txt
index dc126d8..b229506 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -218,7 +218,6 @@
field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
field public static final String SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON = "android.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON";
- field public static final String SUGGEST_PHONE_TIME_AND_ZONE = "android.permission.SUGGEST_PHONE_TIME_AND_ZONE";
field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
field public static final String SYSTEM_CAMERA = "android.permission.SYSTEM_CAMERA";
field public static final String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
@@ -4318,7 +4317,7 @@
}
public final class AudioRecordingConfiguration implements android.os.Parcelable {
- method public int getClientUid();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getClientUid();
}
public class HwAudioSource {
@@ -7518,9 +7517,6 @@
method @Deprecated public boolean isNoInternetAccessExpected();
method @Deprecated public void setIpConfiguration(@Nullable android.net.IpConfiguration);
method @Deprecated public void setNetworkSelectionStatus(@NonNull android.net.wifi.WifiConfiguration.NetworkSelectionStatus);
- field @Deprecated public static final int AP_BAND_2GHZ = 0; // 0x0
- field @Deprecated public static final int AP_BAND_5GHZ = 1; // 0x1
- field @Deprecated public static final int AP_BAND_ANY = -1; // 0xffffffff
field @Deprecated public static final int INVALID_NETWORK_ID = -1; // 0xffffffff
field @Deprecated public static final int METERED_OVERRIDE_METERED = 1; // 0x1
field @Deprecated public static final int METERED_OVERRIDE_NONE = 0; // 0x0
@@ -7530,7 +7526,6 @@
field @Deprecated public static final int RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA = 17; // 0x11
field @Deprecated public static final int RECENT_FAILURE_NONE = 0; // 0x0
field @Deprecated public boolean allowAutojoin;
- field @Deprecated public int apBand;
field @Deprecated public int carrierId;
field @Deprecated public String creatorName;
field @Deprecated public int creatorUid;
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index 2f1889c..a57e178 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -200,8 +200,6 @@
MutableBareField: android.net.wifi.WifiConfiguration#allowAutojoin:
-MutableBareField: android.net.wifi.WifiConfiguration#apBand:
-
MutableBareField: android.net.wifi.WifiConfiguration#carrierId:
MutableBareField: android.net.wifi.WifiConfiguration#fromWifiNetworkSpecifier:
diff --git a/api/test-current.txt b/api/test-current.txt
index 9472ce2..d3e7ea1 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -416,6 +416,7 @@
method public void setFgServiceShown(boolean);
method public void setImportanceLockedByCriticalDeviceFunction(boolean);
method public void setImportanceLockedByOEM(boolean);
+ method public void setImportantConversation(boolean);
method public void setOriginalImportance(int);
}
@@ -885,6 +886,7 @@
method public abstract boolean arePermissionsIndividuallyControlled();
method @Nullable public String getContentCaptureServicePackageName();
method @Nullable @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL") public abstract String getDefaultBrowserPackageNameAsUser(int);
+ method @Nullable public String getDefaultTextClassifierPackageName();
method @Nullable public String getIncidentReportApproverPackageName();
method public abstract int getInstallReason(@NonNull String, @NonNull android.os.UserHandle);
method @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
@@ -894,6 +896,7 @@
method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS", "android.permission.GET_RUNTIME_PERMISSIONS"}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @NonNull public abstract String getServicesSystemSharedLibraryPackageName();
method @NonNull public abstract String getSharedSystemSharedLibraryPackageName();
+ method @Nullable public String getSystemTextClassifierPackageName();
method @Nullable public String[] getTelephonyPackageNames();
method @Nullable public String getWellbeingPackageName();
method @RequiresPermission("android.permission.GRANT_RUNTIME_PERMISSIONS") public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 286fc69..e6cc1da 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -111,7 +111,7 @@
],
cflags: [
- "-DNEW_ENCODING_SCHEME",
+ // "-DNEW_ENCODING_SCHEME",
],
local_include_dirs: [
diff --git a/cmds/statsd/benchmark/log_event_benchmark.cpp b/cmds/statsd/benchmark/log_event_benchmark.cpp
index 30dfe32..8b68743 100644
--- a/cmds/statsd/benchmark/log_event_benchmark.cpp
+++ b/cmds/statsd/benchmark/log_event_benchmark.cpp
@@ -23,14 +23,14 @@
namespace statsd {
static size_t createAndParseStatsEvent(uint8_t* msg) {
- struct stats_event* event = stats_event_obtain();
- stats_event_set_atom_id(event, 100);
- stats_event_write_int32(event, 2);
- stats_event_write_float(event, 2.0);
- stats_event_build(event);
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
+ AStatsEvent_writeInt32(event, 2);
+ AStatsEvent_writeFloat(event, 2.0);
+ AStatsEvent_build(event);
size_t size;
- uint8_t* buf = stats_event_get_buffer(event, &size);
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
memcpy(msg, buf, size);
return size;
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 3be1ef8..183d741 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -202,7 +202,8 @@
DocsUIStartupMsReported docs_ui_startup_ms = 111 [(module) = "docsui"];
DocsUIUserActionReported docs_ui_user_action_reported = 112 [(module) = "docsui"];
WifiEnabledStateChanged wifi_enabled_state_changed = 113 [(module) = "framework"];
- WifiRunningStateChanged wifi_running_state_changed = 114 [(module) = "framework"];
+ WifiRunningStateChanged wifi_running_state_changed = 114
+ [(module) = "framework", deprecated = true];
AppCompacted app_compacted = 115 [(module) = "framework"];
NetworkDnsEventReported network_dns_event_reported = 116 [(module) = "resolv"];
DocsUIPickerLaunchedFromReported docs_ui_picker_launched_from_reported =
@@ -1258,6 +1259,8 @@
}
/**
+ * This atom is deprecated starting in R.
+ *
* Logs when an app causes Wifi to run. In this context, 'to run' means to use Wifi Client Mode.
* TODO: Include support for Hotspot, perhaps by using an extra field to denote 'mode'.
* Note that Wifi Scanning is monitored separately in WifiScanStateChanged.
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 656743b..e8fc603 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -35,6 +35,34 @@
using std::string;
using std::vector;
+// stats_event.h socket types. Keep in sync.
+/* ERRORS */
+#define ERROR_NO_TIMESTAMP 0x1
+#define ERROR_NO_ATOM_ID 0x2
+#define ERROR_OVERFLOW 0x4
+#define ERROR_ATTRIBUTION_CHAIN_TOO_LONG 0x8
+#define ERROR_TOO_MANY_KEY_VALUE_PAIRS 0x10
+#define ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD 0x20
+#define ERROR_INVALID_ANNOTATION_ID 0x40
+#define ERROR_ANNOTATION_ID_TOO_LARGE 0x80
+#define ERROR_TOO_MANY_ANNOTATIONS 0x100
+#define ERROR_TOO_MANY_FIELDS 0x200
+#define ERROR_INVALID_VALUE_TYPE 0x400
+#define ERROR_STRING_NOT_NULL_TERMINATED 0x800
+
+/* TYPE IDS */
+#define INT32_TYPE 0x00
+#define INT64_TYPE 0x01
+#define STRING_TYPE 0x02
+#define LIST_TYPE 0x03
+#define FLOAT_TYPE 0x04
+#define BOOL_TYPE 0x05
+#define BYTE_ARRAY_TYPE 0x06
+#define OBJECT_TYPE 0x07
+#define KEY_VALUE_PAIRS_TYPE 0x08
+#define ATTRIBUTION_CHAIN_TYPE 0x09
+#define ERROR_TYPE 0x0F
+
// Msg is expected to begin at the start of the serialized atom -- it should not
// include the android_log_header_t or the StatsEventTag.
LogEvent::LogEvent(uint8_t* msg, uint32_t len, int32_t uid, int32_t pid)
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 35b0396..f624e12 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -46,16 +46,16 @@
}
TEST(LogEventTest, TestPrimitiveParsing) {
- struct stats_event* event = stats_event_obtain();
- stats_event_set_atom_id(event, 100);
- stats_event_write_int32(event, 10);
- stats_event_write_int64(event, 0x123456789);
- stats_event_write_float(event, 2.0);
- stats_event_write_bool(event, true);
- stats_event_build(event);
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
+ AStatsEvent_writeInt32(event, 10);
+ AStatsEvent_writeInt64(event, 0x123456789);
+ AStatsEvent_writeFloat(event, 2.0);
+ AStatsEvent_writeBool(event, true);
+ AStatsEvent_build(event);
size_t size;
- uint8_t* buf = stats_event_get_buffer(event, &size);
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
EXPECT_TRUE(logEvent.isValid());
@@ -90,20 +90,20 @@
EXPECT_EQ(Type::INT, boolItem.mValue.getType()); // FieldValue does not support boolean type
EXPECT_EQ(1, boolItem.mValue.int_value);
- stats_event_release(event);
+ AStatsEvent_release(event);
}
TEST(LogEventTest, TestStringAndByteArrayParsing) {
- struct stats_event* event = stats_event_obtain();
- stats_event_set_atom_id(event, 100);
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
string str = "test";
- stats_event_write_string8(event, str.c_str());
- stats_event_write_byte_array(event, (uint8_t*)str.c_str(), str.length());
- stats_event_build(event);
+ AStatsEvent_writeString(event, str.c_str());
+ AStatsEvent_writeByteArray(event, (uint8_t*)str.c_str(), str.length());
+ AStatsEvent_build(event);
size_t size;
- uint8_t* buf = stats_event_get_buffer(event, &size);
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
EXPECT_TRUE(logEvent.isValid());
@@ -127,18 +127,18 @@
vector<uint8_t> expectedValue = {'t', 'e', 's', 't'};
EXPECT_EQ(expectedValue, storageItem.mValue.storage_value);
- stats_event_release(event);
+ AStatsEvent_release(event);
}
TEST(LogEventTest, TestEmptyString) {
- struct stats_event* event = stats_event_obtain();
- stats_event_set_atom_id(event, 100);
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
string empty = "";
- stats_event_write_string8(event, empty.c_str());
- stats_event_build(event);
+ AStatsEvent_writeString(event, empty.c_str());
+ AStatsEvent_build(event);
size_t size;
- uint8_t* buf = stats_event_get_buffer(event, &size);
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
EXPECT_TRUE(logEvent.isValid());
@@ -155,18 +155,18 @@
EXPECT_EQ(Type::STRING, item.mValue.getType());
EXPECT_EQ(empty, item.mValue.str_value);
- stats_event_release(event);
+ AStatsEvent_release(event);
}
TEST(LogEventTest, TestByteArrayWithNullCharacter) {
- struct stats_event* event = stats_event_obtain();
- stats_event_set_atom_id(event, 100);
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
uint8_t message[] = {'\t', 'e', '\0', 's', 't'};
- stats_event_write_byte_array(event, message, 5);
- stats_event_build(event);
+ AStatsEvent_writeByteArray(event, message, 5);
+ AStatsEvent_build(event);
size_t size;
- uint8_t* buf = stats_event_get_buffer(event, &size);
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
EXPECT_TRUE(logEvent.isValid());
@@ -184,79 +184,12 @@
vector<uint8_t> expectedValue(message, message + 5);
EXPECT_EQ(expectedValue, item.mValue.storage_value);
- stats_event_release(event);
-}
-
-TEST(LogEventTest, TestKeyValuePairs) {
- struct stats_event* event = stats_event_obtain();
- stats_event_set_atom_id(event, 100);
-
- struct key_value_pair pairs[4];
- pairs[0] = {.key = 0, .valueType = INT32_TYPE, .int32Value = 1};
- pairs[1] = {.key = 1, .valueType = INT64_TYPE, .int64Value = 0x123456789};
- pairs[2] = {.key = 2, .valueType = FLOAT_TYPE, .floatValue = 2.0};
- string str = "test";
- pairs[3] = {.key = 3, .valueType = STRING_TYPE, .stringValue = str.c_str()};
-
- stats_event_write_key_value_pairs(event, pairs, 4);
- stats_event_build(event);
-
- size_t size;
- uint8_t* buf = stats_event_get_buffer(event, &size);
-
- LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
- EXPECT_TRUE(logEvent.isValid());
- EXPECT_EQ(100, logEvent.GetTagId());
- EXPECT_EQ(1000, logEvent.GetUid());
- EXPECT_EQ(1001, logEvent.GetPid());
-
- const vector<FieldValue>& values = logEvent.getValues();
- EXPECT_EQ(8, values.size()); // 2 FieldValues per key-value pair
-
- // Check the keys first
- for (int i = 0; i < values.size() / 2; i++) {
- const FieldValue& item = values[2 * i];
- int32_t depth1Pos = i + 1;
- bool depth1Last = i == (values.size() / 2 - 1);
- Field expectedField = getField(100, {1, depth1Pos, 1}, 2, {true, depth1Last, false});
-
- EXPECT_EQ(expectedField, item.mField);
- EXPECT_EQ(Type::INT, item.mValue.getType());
- EXPECT_EQ(i, item.mValue.int_value);
- }
-
- // Check the values now
- // Note: pos[2] = index of type in KeyValuePair in atoms.proto
- const FieldValue& int32Item = values[1];
- Field expectedField = getField(100, {1, 1, 2}, 2, {true, false, true});
- EXPECT_EQ(expectedField, int32Item.mField);
- EXPECT_EQ(Type::INT, int32Item.mValue.getType());
- EXPECT_EQ(1, int32Item.mValue.int_value);
-
- const FieldValue& int64Item = values[3];
- expectedField = getField(100, {1, 2, 3}, 2, {true, false, true});
- EXPECT_EQ(expectedField, int64Item.mField);
- EXPECT_EQ(Type::LONG, int64Item.mValue.getType());
- EXPECT_EQ(0x123456789, int64Item.mValue.long_value);
-
- const FieldValue& floatItem = values[5];
- expectedField = getField(100, {1, 3, 5}, 2, {true, false, true});
- EXPECT_EQ(expectedField, floatItem.mField);
- EXPECT_EQ(Type::FLOAT, floatItem.mValue.getType());
- EXPECT_EQ(2.0, floatItem.mValue.float_value);
-
- const FieldValue& stringItem = values[7];
- expectedField = getField(100, {1, 4, 4}, 2, {true, true, true});
- EXPECT_EQ(expectedField, stringItem.mField);
- EXPECT_EQ(Type::STRING, stringItem.mValue.getType());
- EXPECT_EQ(str, stringItem.mValue.str_value);
-
- stats_event_release(event);
+ AStatsEvent_release(event);
}
TEST(LogEventTest, TestAttributionChain) {
- struct stats_event* event = stats_event_obtain();
- stats_event_set_atom_id(event, 100);
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
string tag1 = "tag1";
string tag2 = "tag2";
@@ -264,11 +197,11 @@
uint32_t uids[] = {1001, 1002};
const char* tags[] = {tag1.c_str(), tag2.c_str()};
- stats_event_write_attribution_chain(event, uids, tags, 2);
- stats_event_build(event);
+ AStatsEvent_writeAttributionChain(event, uids, tags, 2);
+ AStatsEvent_build(event);
size_t size;
- uint8_t* buf = stats_event_get_buffer(event, &size);
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
EXPECT_TRUE(logEvent.isValid());
@@ -305,7 +238,7 @@
EXPECT_EQ(Type::STRING, tag2Item.mValue.getType());
EXPECT_EQ(tag2, tag2Item.mValue.str_value);
- stats_event_release(event);
+ AStatsEvent_release(event);
}
#else // NEW_ENCODING_SCHEME
diff --git a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
index 2576cf5..a011692e 100644
--- a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
+++ b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
@@ -50,11 +50,11 @@
int64_t pullCoolDownNs;
std::thread pullThread;
-stats_event* createSimpleEvent(int64_t value) {
- stats_event* event = stats_event_obtain();
- stats_event_set_atom_id(event, pullTagId);
- stats_event_write_int64(event, value);
- stats_event_build(event);
+AStatsEvent* createSimpleEvent(int64_t value) {
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, pullTagId);
+ AStatsEvent_writeInt64(event, value);
+ AStatsEvent_build(event);
return event;
}
@@ -62,16 +62,16 @@
// Convert stats_events into StatsEventParcels.
std::vector<android::util::StatsEventParcel> parcels;
for (int i = 0; i < values.size(); i++) {
- stats_event* event = createSimpleEvent(values[i]);
+ AStatsEvent* event = createSimpleEvent(values[i]);
size_t size;
- uint8_t* buffer = stats_event_get_buffer(event, &size);
+ uint8_t* buffer = AStatsEvent_getBuffer(event, &size);
android::util::StatsEventParcel p;
// vector.assign() creates a copy, but this is inevitable unless
// stats_event.h/c uses a vector as opposed to a buffer.
p.buffer.assign(buffer, buffer + size);
parcels.push_back(std::move(p));
- stats_event_release(event);
+ AStatsEvent_release(event);
}
sleep_for(std::chrono::nanoseconds(pullDelayNs));
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 6e1890a..db09ee9 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -953,24 +953,24 @@
// Convert stats_events into StatsEventParcels.
std::vector<android::util::StatsEventParcel> parcels;
for (int i = 1; i < 3; i++) {
- stats_event* event = stats_event_obtain();
- stats_event_set_atom_id(event, atomTag);
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, atomTag);
std::string subsystemName = "subsystem_name_";
subsystemName = subsystemName + std::to_string(i);
- stats_event_write_string8(event, subsystemName.c_str());
- stats_event_write_string8(event, "subsystem_subname foo");
- stats_event_write_int64(event, /*count= */ i);
- stats_event_write_int64(event, /*time_millis= */ i * 100);
- stats_event_build(event);
+ AStatsEvent_writeString(event, subsystemName.c_str());
+ AStatsEvent_writeString(event, "subsystem_subname foo");
+ AStatsEvent_writeInt64(event, /*count= */ i);
+ AStatsEvent_writeInt64(event, /*time_millis= */ i * 100);
+ AStatsEvent_build(event);
size_t size;
- uint8_t* buffer = stats_event_get_buffer(event, &size);
+ uint8_t* buffer = AStatsEvent_getBuffer(event, &size);
android::util::StatsEventParcel p;
// vector.assign() creates a copy, but this is inevitable unless
// stats_event.h/c uses a vector as opposed to a buffer.
p.buffer.assign(buffer, buffer + size);
parcels.push_back(std::move(p));
- stats_event_release(event);
+ AStatsEvent_write(event);
}
resultReceiver->pullFinished(atomTag, /*success=*/true, parcels);
return binder::Status::ok();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 46f8669..861d41d 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -88,10 +88,15 @@
import java.util.function.Supplier;
/**
- * API for interacting with "application operation" tracking.
+ * AppOps are mappings of [package/uid, op-name] -> [mode]. The list of existing appops is defined
+ * by the system and cannot be amended by apps. Only system apps can change appop-modes.
*
- * <p>This API is not generally intended for third party application developers; most
- * features are only available to system applications.
+ * <p>Beside a mode the system tracks when an op was {@link #noteOp noted}. The tracked data can
+ * only be read by system components.
+ *
+ * <p>Installed apps can usually only listen to changes and events on their own ops. E.g.
+ * {@link AppOpsCollector} allows to get a callback each time an app called {@link #noteOp} or
+ * {@link #startOp} for an op belonging to the app.
*/
@SystemService(Context.APP_OPS_SERVICE)
public class AppOpsManager {
@@ -6774,6 +6779,9 @@
* succeeds, the last execution time of the operation for this app will be updated to
* the current time.
*
+ * <p>If this is a check that is not preceding the protected operation, use
+ * {@link #unsafeCheckOp} instead.
+ *
* @param op The operation to note. One of the OPSTR_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
@@ -6798,6 +6806,9 @@
* succeeds, the last execution time of the operation for this app will be updated to
* the current time.
*
+ * <p>If this is a check that is not preceding the protected operation, use
+ * {@link #unsafeCheckOp} instead.
+ *
* @param op The operation to note. One of the OP_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
@@ -7757,9 +7768,38 @@
/**
* Callback an app can choose to {@link #setNotedAppOpsCollector register} to monitor it's noted
- * appops.
+ * appops. I.e. each time any app calls {@link #noteOp} or {@link #startOp} one of the callback
+ * methods of this object is called.
*
* <p><b>Only appops related to dangerous permissions are collected.</b>
+ *
+ * <pre>
+ * setNotedAppOpsCollector(new AppOpsCollector() {
+ * ArraySet<Pair<String, String>> opsNotedForThisProcess = new ArraySet<>();
+ *
+ * private synchronized void addAccess(String op, String accessLocation) {
+ * // Ops are often noted when permission protected APIs were called.
+ * // In this case permissionToOp() allows to resolve the permission<->op
+ * opsNotedForThisProcess.add(new Pair(accessType, accessLocation));
+ * }
+ *
+ * public void onNoted(SyncNotedAppOp op) {
+ * // Accesses is currently happening, hence stack trace describes location of access
+ * addAccess(op.getOp(), Arrays.toString(Thread.currentThread().getStackTrace()));
+ * }
+ *
+ * public void onSelfNoted(SyncNotedAppOp op) {
+ * onNoted(op);
+ * }
+ *
+ * public void onAsyncNoted(AsyncNotedAppOp asyncOp) {
+ * // Stack trace is not useful for async ops as accessed happened on different thread
+ * addAccess(asyncOp.getOp(), asyncOp.getMessage());
+ * }
+ * });
+ * </pre>
+ *
+ * @see #setNotedAppOpsCollector
*/
public abstract static class AppOpsCollector {
/** Callback registered with the system. This will receive the async notes ops */
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 71cb4a4..cd05e2c 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -3229,18 +3229,18 @@
}
@Override
- public String getSystemTextClassifierPackageName() {
+ public String getDefaultTextClassifierPackageName() {
try {
- return mPM.getSystemTextClassifierPackageName();
+ return mPM.getDefaultTextClassifierPackageName();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
@Override
- public String[] getSystemTextClassifierPackages() {
+ public String getSystemTextClassifierPackageName() {
try {
- return mPM.getSystemTextClassifierPackages();
+ return mPM.getSystemTextClassifierPackageName();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl
index 80ba464..8c3180b 100644
--- a/core/java/android/app/IUiAutomationConnection.aidl
+++ b/core/java/android/app/IUiAutomationConnection.aidl
@@ -39,6 +39,7 @@
boolean injectInputEvent(in InputEvent event, boolean sync);
void syncInputTransactions();
boolean setRotation(int rotation);
+ Bitmap takeScreenshot(in Rect crop, int rotation);
boolean clearWindowContentFrameStats(int windowId);
WindowContentFrameStats getWindowContentFrameStats(int windowId);
void clearWindowAnimationFrameStats();
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 4b24e09..7212be8 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -31,7 +31,6 @@
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.text.TextUtils;
-import android.util.Log;
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.Preconditions;
@@ -105,6 +104,7 @@
private static final String ATT_ORIG_IMP = "orig_imp";
private static final String ATT_PARENT_CHANNEL = "parent";
private static final String ATT_CONVERSATION_ID = "conv_id";
+ private static final String ATT_IMP_CONVERSATION = "imp_conv";
private static final String ATT_DEMOTE = "dem";
private static final String DELIMITER = ",";
@@ -196,6 +196,7 @@
private String mParentId = null;
private String mConversationId = null;
private boolean mDemoted = false;
+ private boolean mImportantConvo = false;
/**
* Creates a notification channel.
@@ -263,6 +264,7 @@
mParentId = in.readString();
mConversationId = in.readString();
mDemoted = in.readBoolean();
+ mImportantConvo = in.readBoolean();
}
@Override
@@ -321,6 +323,7 @@
dest.writeString(mParentId);
dest.writeString(mConversationId);
dest.writeBoolean(mDemoted);
+ dest.writeBoolean(mImportantConvo);
}
/**
@@ -354,6 +357,14 @@
}
/**
+ * @hide
+ */
+ @TestApi
+ public void setImportantConversation(boolean importantConvo) {
+ mImportantConvo = importantConvo;
+ }
+
+ /**
* Allows users to block notifications sent through this channel, if this channel belongs to
* a package that is signed with the system signature. If the channel does not belong to a
* package that is signed with the system signature, this method does nothing.
@@ -601,6 +612,18 @@
}
/**
+ * Whether or not notifications in this conversation are considered important.
+ *
+ * <p>Important conversations may get special visual treatment, and might be able to bypass DND.
+ *
+ * <p>This is only valid for channels that represent conversations, that is, those with a valid
+ * {@link #getConversationId() conversation id}.
+ */
+ public boolean isImportantConversation() {
+ return mImportantConvo;
+ }
+
+ /**
* Returns the notification sound for this channel.
*/
public Uri getSound() {
@@ -852,6 +875,7 @@
setConversationId(parser.getAttributeValue(null, ATT_PARENT_CHANNEL),
parser.getAttributeValue(null, ATT_CONVERSATION_ID));
setDemoted(safeBool(parser, ATT_DEMOTE, false));
+ setImportantConversation(safeBool(parser, ATT_IMP_CONVERSATION, false));
}
@Nullable
@@ -985,6 +1009,9 @@
if (isDemoted()) {
out.attribute(null, ATT_DEMOTE, Boolean.toString(isDemoted()));
}
+ if (isImportantConversation()) {
+ out.attribute(null, ATT_IMP_CONVERSATION, Boolean.toString(isImportantConversation()));
+ }
// mImportanceLockedDefaultApp and mImportanceLockedByOEM have a different source of
// truth and so aren't written to this xml file
@@ -1145,7 +1172,8 @@
&& mOriginalImportance == that.mOriginalImportance
&& Objects.equals(getParentChannelId(), that.getParentChannelId())
&& Objects.equals(getConversationId(), that.getConversationId())
- && isDemoted() == that.isDemoted();
+ && isDemoted() == that.isDemoted()
+ && isImportantConversation() == that.isImportantConversation();
}
@Override
@@ -1156,7 +1184,7 @@
isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getGroup(),
getAudioAttributes(), isBlockableSystem(), mAllowBubbles,
mImportanceLockedByOEM, mImportanceLockedDefaultApp, mOriginalImportance,
- mParentId, mConversationId, mDemoted);
+ mParentId, mConversationId, mDemoted, mImportantConvo);
result = 31 * result + Arrays.hashCode(mVibration);
return result;
}
@@ -1204,7 +1232,8 @@
+ ", mOriginalImp=" + mOriginalImportance
+ ", mParent=" + mParentId
+ ", mConversationId=" + mConversationId
- + ", mDemoted=" + mDemoted;
+ + ", mDemoted=" + mDemoted
+ + ", mImportantConvo=" + mImportantConvo;
}
/** @hide */
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 1a8e15c..528b508 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -46,6 +46,7 @@
import android.service.notification.Condition;
import android.service.notification.StatusBarNotification;
import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenPolicy;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
@@ -1555,19 +1556,24 @@
public static final int PRIORITY_CATEGORY_MEDIA = 1 << 6;
/**System (catch-all for non-never suppressible sounds) are prioritized */
public static final int PRIORITY_CATEGORY_SYSTEM = 1 << 7;
+ /**
+ * Conversations are allowed through DND.
+ */
+ public static final int PRIORITY_CATEGORY_CONVERSATIONS = 1 << 8;
/**
* @hide
*/
public static final int[] ALL_PRIORITY_CATEGORIES = {
- PRIORITY_CATEGORY_ALARMS,
- PRIORITY_CATEGORY_MEDIA,
- PRIORITY_CATEGORY_SYSTEM,
- PRIORITY_CATEGORY_REMINDERS,
- PRIORITY_CATEGORY_EVENTS,
- PRIORITY_CATEGORY_MESSAGES,
- PRIORITY_CATEGORY_CALLS,
- PRIORITY_CATEGORY_REPEAT_CALLERS,
+ PRIORITY_CATEGORY_ALARMS,
+ PRIORITY_CATEGORY_MEDIA,
+ PRIORITY_CATEGORY_SYSTEM,
+ PRIORITY_CATEGORY_REMINDERS,
+ PRIORITY_CATEGORY_EVENTS,
+ PRIORITY_CATEGORY_MESSAGES,
+ PRIORITY_CATEGORY_CALLS,
+ PRIORITY_CATEGORY_REPEAT_CALLERS,
+ PRIORITY_CATEGORY_CONVERSATIONS,
};
/** Any sender is prioritized. */
@@ -1577,6 +1583,31 @@
/** Only starred contacts are prioritized. */
public static final int PRIORITY_SENDERS_STARRED = 2;
+
+ /** @hide */
+ @IntDef(prefix = { "CONVERSATION_SENDERS_" }, value = {
+ CONVERSATION_SENDERS_ANYONE,
+ CONVERSATION_SENDERS_IMPORTANT,
+ CONVERSATION_SENDERS_NONE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ConversationSenders {}
+ /**
+ * Used to indicate all conversations can bypass dnd.
+ */
+ public static final int CONVERSATION_SENDERS_ANYONE = ZenPolicy.CONVERSATION_SENDERS_ANYONE;
+
+ /**
+ * Used to indicate important conversations can bypass dnd.
+ */
+ public static final int CONVERSATION_SENDERS_IMPORTANT =
+ ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
+
+ /**
+ * Used to indicate no conversations can bypass dnd.
+ */
+ public static final int CONVERSATION_SENDERS_NONE = ZenPolicy.CONVERSATION_SENDERS_NONE;
+
/** Notification categories to prioritize. Bitmask of PRIORITY_CATEGORY_* constants. */
public final int priorityCategories;
@@ -1589,6 +1620,18 @@
public final int priorityMessageSenders;
/**
+ * Notification senders to prioritize for conversations. One of:
+ * {@link #CONVERSATION_SENDERS_NONE}, {@link #CONVERSATION_SENDERS_IMPORTANT},
+ * {@link #CONVERSATION_SENDERS_ANYONE}.
+ */
+ public final int priorityConversationSenders;
+
+ /**
+ * @hide
+ */
+ public static final int CONVERSATION_SENDERS_UNSET = -1;
+
+ /**
* @hide
*/
public static final int SUPPRESSED_EFFECTS_UNSET = -1;
@@ -1665,21 +1708,6 @@
SUPPRESSED_EFFECT_NOTIFICATION_LIST
};
- private static final int[] SCREEN_OFF_SUPPRESSED_EFFECTS = {
- SUPPRESSED_EFFECT_SCREEN_OFF,
- SUPPRESSED_EFFECT_FULL_SCREEN_INTENT,
- SUPPRESSED_EFFECT_LIGHTS,
- SUPPRESSED_EFFECT_AMBIENT,
- };
-
- private static final int[] SCREEN_ON_SUPPRESSED_EFFECTS = {
- SUPPRESSED_EFFECT_SCREEN_ON,
- SUPPRESSED_EFFECT_PEEK,
- SUPPRESSED_EFFECT_STATUS_BAR,
- SUPPRESSED_EFFECT_BADGE,
- SUPPRESSED_EFFECT_NOTIFICATION_LIST
- };
-
/**
* Visual effects to suppress for a notification that is filtered by Do Not Disturb mode.
* Bitmask of SUPPRESSED_EFFECT_* constants.
@@ -1718,17 +1746,16 @@
*/
public Policy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders) {
this(priorityCategories, priorityCallSenders, priorityMessageSenders,
- SUPPRESSED_EFFECTS_UNSET, STATE_UNSET);
+ SUPPRESSED_EFFECTS_UNSET, STATE_UNSET, CONVERSATION_SENDERS_UNSET);
}
/**
* Constructs a policy for Do Not Disturb priority mode behavior.
*
* <p>
- * Apps that target API levels below {@link Build.VERSION_CODES#P} cannot
+ * Apps that target API levels below {@link Build.VERSION_CODES#R} cannot
* change user-designated values to allow or disallow
- * {@link Policy#PRIORITY_CATEGORY_ALARMS}, {@link Policy#PRIORITY_CATEGORY_SYSTEM}, and
- * {@link Policy#PRIORITY_CATEGORY_MEDIA} from bypassing dnd.
+ * {@link Policy#PRIORITY_CATEGORY_CONVERSATIONS}, from bypassing dnd.
* <p>
* Additionally, apps that target API levels below {@link Build.VERSION_CODES#P} can
* only modify the {@link #SUPPRESSED_EFFECT_SCREEN_ON} and
@@ -1752,27 +1779,68 @@
*/
public Policy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders,
int suppressedVisualEffects) {
- this.priorityCategories = priorityCategories;
- this.priorityCallSenders = priorityCallSenders;
- this.priorityMessageSenders = priorityMessageSenders;
- this.suppressedVisualEffects = suppressedVisualEffects;
- this.state = STATE_UNSET;
+ this(priorityCategories, priorityCallSenders, priorityMessageSenders,
+ suppressedVisualEffects, STATE_UNSET, CONVERSATION_SENDERS_UNSET);
+ }
+
+ /**
+ * Constructs a policy for Do Not Disturb priority mode behavior.
+ *
+ * <p>
+ * Apps that target API levels below {@link Build.VERSION_CODES#P} cannot
+ * change user-designated values to allow or disallow
+ * {@link Policy#PRIORITY_CATEGORY_CONVERSATIONS} from bypassing dnd. If you do need
+ * to change them, use a {@link ZenPolicy} associated with an {@link AutomaticZenRule}
+ * instead of changing the global setting.
+ * <p>
+ * Apps that target API levels below {@link Build.VERSION_CODES#P} cannot
+ * change user-designated values to allow or disallow
+ * {@link Policy#PRIORITY_CATEGORY_ALARMS},
+ * {@link Policy#PRIORITY_CATEGORY_SYSTEM}, and
+ * {@link Policy#PRIORITY_CATEGORY_MEDIA} from bypassing dnd.
+ * <p>
+ * Additionally, apps that target API levels below {@link Build.VERSION_CODES#P} can
+ * only modify the {@link #SUPPRESSED_EFFECT_SCREEN_ON} and
+ * {@link #SUPPRESSED_EFFECT_SCREEN_OFF} bits of the suppressed visual effects field.
+ * All other suppressed effects will be ignored and reconstituted from the screen on
+ * and screen off values.
+ * <p>
+ * Apps that target {@link Build.VERSION_CODES#P} or above can set any
+ * suppressed visual effects. However, if any suppressed effects >
+ * {@link #SUPPRESSED_EFFECT_SCREEN_ON} are set, {@link #SUPPRESSED_EFFECT_SCREEN_ON}
+ * and {@link #SUPPRESSED_EFFECT_SCREEN_OFF} will be ignored and reconstituted from
+ * the more specific suppressed visual effect bits. Apps should migrate to targeting
+ * specific effects instead of the deprecated {@link #SUPPRESSED_EFFECT_SCREEN_ON} and
+ * {@link #SUPPRESSED_EFFECT_SCREEN_OFF} effects.
+ *
+ * @param priorityCategories bitmask of categories of notifications that can bypass DND.
+ * @param priorityCallSenders which callers can bypass DND.
+ * @param priorityMessageSenders which message senders can bypass DND.
+ * @param suppressedVisualEffects which visual interruptions should be suppressed from
+ * notifications that are filtered by DND.
+ */
+ public Policy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders,
+ int suppressedVisualEffects, int priorityConversationSenders) {
+ this(priorityCategories, priorityCallSenders, priorityMessageSenders,
+ suppressedVisualEffects, STATE_UNSET, priorityConversationSenders);
}
/** @hide */
public Policy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders,
- int suppressedVisualEffects, int state) {
+ int suppressedVisualEffects, int state, int priorityConversationSenders) {
this.priorityCategories = priorityCategories;
this.priorityCallSenders = priorityCallSenders;
this.priorityMessageSenders = priorityMessageSenders;
this.suppressedVisualEffects = suppressedVisualEffects;
this.state = state;
+ this.priorityConversationSenders = priorityConversationSenders;
}
+
/** @hide */
public Policy(Parcel source) {
this(source.readInt(), source.readInt(), source.readInt(), source.readInt(),
- source.readInt());
+ source.readInt(), source.readInt());
}
@Override
@@ -1782,6 +1850,7 @@
dest.writeInt(priorityMessageSenders);
dest.writeInt(suppressedVisualEffects);
dest.writeInt(state);
+ dest.writeInt(priorityConversationSenders);
}
@Override
@@ -1792,7 +1861,7 @@
@Override
public int hashCode() {
return Objects.hash(priorityCategories, priorityCallSenders, priorityMessageSenders,
- suppressedVisualEffects, state);
+ suppressedVisualEffects, state, priorityConversationSenders);
}
@Override
@@ -1805,7 +1874,8 @@
&& other.priorityMessageSenders == priorityMessageSenders
&& suppressedVisualEffectsEqual(suppressedVisualEffects,
other.suppressedVisualEffects)
- && other.state == this.state;
+ && other.state == this.state
+ && other.priorityConversationSenders == this.priorityConversationSenders;
}
private boolean suppressedVisualEffectsEqual(int suppressedEffects,
@@ -1867,6 +1937,8 @@
+ "priorityCategories=" + priorityCategoriesToString(priorityCategories)
+ ",priorityCallSenders=" + prioritySendersToString(priorityCallSenders)
+ ",priorityMessageSenders=" + prioritySendersToString(priorityMessageSenders)
+ + ",priorityConvSenders="
+ + conversationSendersToString(priorityConversationSenders)
+ ",suppressedVisualEffects="
+ suppressedEffectsToString(suppressedVisualEffects)
+ ",areChannelsBypassingDnd=" + (((state & STATE_CHANNELS_BYPASSING_DND) != 0)
@@ -2003,6 +2075,7 @@
case PRIORITY_CATEGORY_ALARMS: return "PRIORITY_CATEGORY_ALARMS";
case PRIORITY_CATEGORY_MEDIA: return "PRIORITY_CATEGORY_MEDIA";
case PRIORITY_CATEGORY_SYSTEM: return "PRIORITY_CATEGORY_SYSTEM";
+ case PRIORITY_CATEGORY_CONVERSATIONS: return "PRIORITY_CATEGORY_CONVERSATIONS";
default: return "PRIORITY_CATEGORY_UNKNOWN_" + priorityCategory;
}
}
@@ -2016,7 +2089,25 @@
}
}
- public static final @android.annotation.NonNull Parcelable.Creator<Policy> CREATOR = new Parcelable.Creator<Policy>() {
+ /**
+ * @hide
+ */
+ public static @NonNull String conversationSendersToString(int priorityConversationSenders) {
+ switch (priorityConversationSenders) {
+ case CONVERSATION_SENDERS_ANYONE:
+ return "anyone";
+ case CONVERSATION_SENDERS_IMPORTANT:
+ return "important";
+ case CONVERSATION_SENDERS_NONE:
+ return "none";
+ case CONVERSATION_SENDERS_UNSET:
+ return "unset";
+ }
+ return "invalidConversationType{" + priorityConversationSenders + "}";
+ }
+
+ public static final @android.annotation.NonNull Parcelable.Creator<Policy> CREATOR
+ = new Parcelable.Creator<Policy>() {
@Override
public Policy createFromParcel(Parcel in) {
return new Policy(in);
@@ -2054,6 +2145,11 @@
}
/** @hide **/
+ public boolean allowConversations() {
+ return (priorityCategories & PRIORITY_CATEGORY_CONVERSATIONS) != 0;
+ }
+
+ /** @hide **/
public boolean allowMessages() {
return (priorityCategories & PRIORITY_CATEGORY_MESSAGES) != 0;
}
@@ -2079,6 +2175,11 @@
}
/** @hide **/
+ public int allowConversationsFrom() {
+ return priorityConversationSenders;
+ }
+
+ /** @hide **/
public boolean showFullScreenIntents() {
return (suppressedVisualEffects & SUPPRESSED_EFFECT_FULL_SCREEN_INTENT) == 0;
}
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 35cf687..a9a06da 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -27,7 +27,10 @@
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.graphics.Region;
+import android.hardware.display.DisplayManagerGlobal;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
@@ -828,20 +831,39 @@
}
/**
- * Takes a screenshot of the default display and returns it by {@link Bitmap.Config#HARDWARE}
- * format.
+ * Takes a screenshot.
*
* @return The screenshot bitmap on success, null otherwise.
*/
public Bitmap takeScreenshot() {
- final int connectionId;
synchronized (mLock) {
throwIfNotConnectedLocked();
- connectionId = mConnectionId;
}
- // Calling out without a lock held.
- return AccessibilityInteractionClient.getInstance()
- .takeScreenshot(connectionId, Display.DEFAULT_DISPLAY);
+ Display display = DisplayManagerGlobal.getInstance()
+ .getRealDisplay(Display.DEFAULT_DISPLAY);
+ Point displaySize = new Point();
+ display.getRealSize(displaySize);
+
+ int rotation = display.getRotation();
+
+ // Take the screenshot
+ Bitmap screenShot = null;
+ try {
+ // Calling out without a lock held.
+ screenShot = mUiAutomationConnection.takeScreenshot(
+ new Rect(0, 0, displaySize.x, displaySize.y), rotation);
+ if (screenShot == null) {
+ return null;
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while taking screnshot!", re);
+ return null;
+ }
+
+ // Optimization
+ screenShot.setHasAlpha(false);
+
+ return screenShot;
}
/**
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 4fb9743..82e9881 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -21,6 +21,8 @@
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
import android.hardware.input.InputManager;
import android.os.Binder;
import android.os.IBinder;
@@ -178,6 +180,23 @@
}
@Override
+ public Bitmap takeScreenshot(Rect crop, int rotation) {
+ synchronized (mLock) {
+ throwIfCalledByNotTrustedUidLocked();
+ throwIfShutdownLocked();
+ throwIfNotConnectedLocked();
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ int width = crop.width();
+ int height = crop.height();
+ return SurfaceControl.screenshot(crop, width, height, rotation);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public boolean clearWindowContentFrameStats(int windowId) throws RemoteException {
synchronized (mLock) {
throwIfCalledByNotTrustedUidLocked();
@@ -430,8 +449,7 @@
info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
| AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
| AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
- | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS
- | AccessibilityServiceInfo.CAPABILITY_CAN_TAKE_SCREENSHOT);
+ | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS);
try {
// Calling out with a lock held is fine since if the system
// process is gone the client calling in will be killed.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 3676a9b..7599791 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2090,6 +2090,13 @@
public static final int LOCK_TASK_FEATURE_KEYGUARD = 1 << 5;
/**
+ * Enable blocking of non-whitelisted activities from being started into a locked task.
+ *
+ * @see #setLockTaskFeatures(ComponentName, int)
+ */
+ public static final int LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK = 1 << 6;
+
+ /**
* Flags supplied to {@link #setLockTaskFeatures(ComponentName, int)}.
*
* @hide
@@ -2102,7 +2109,8 @@
LOCK_TASK_FEATURE_HOME,
LOCK_TASK_FEATURE_OVERVIEW,
LOCK_TASK_FEATURE_GLOBAL_ACTIONS,
- LOCK_TASK_FEATURE_KEYGUARD
+ LOCK_TASK_FEATURE_KEYGUARD,
+ LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK
})
public @interface LockTaskFeature {}
@@ -3722,6 +3730,11 @@
* requires that you request both {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} and
* {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}}.
* <p>
+ * When this policy is set by a device owner, profile owner of an organization-owned device or
+ * an admin on the primary user, the device will be factory reset after too many incorrect
+ * password attempts. When set by a profile owner or an admin on a secondary user or a managed
+ * profile, only the corresponding user or profile will be wiped.
+ * <p>
* To implement any other policy (e.g. wiping data for a particular application only, erasing or
* revoking credentials, or reporting the failure to a server), you should implement
* {@link DeviceAdminReceiver#onPasswordFailed(Context, android.content.Intent)} instead. Do not
@@ -3790,10 +3803,12 @@
}
/**
- * Returns the profile with the smallest maximum failed passwords for wipe,
- * for the given user. So for primary user, it might return the primary or
- * a managed profile. For a secondary user, it would be the same as the
- * user passed in.
+ * Returns the user that will be wiped first when too many failed attempts are made to unlock
+ * user {@code userHandle}. That user is either the same as {@code userHandle} or belongs to the
+ * same profile group. When there is no such policy, returns {@code UserHandle.USER_NULL}.
+ * E.g. managed profile user may be wiped as a result of failed primary profile password
+ * attempts when using unified challenge. Primary user may be wiped as a result of failed
+ * password attempts on the managed profile on an organization-owned device.
* @hide Used only by Keyguard
*/
@RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
diff --git a/core/java/android/app/timedetector/PhoneTimeSuggestion.java b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
index 16288e8..0133a44 100644
--- a/core/java/android/app/timedetector/PhoneTimeSuggestion.java
+++ b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.TimestampedValue;
@@ -51,11 +50,9 @@
*
* @hide
*/
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final class PhoneTimeSuggestion implements Parcelable {
/** @hide */
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final @NonNull Parcelable.Creator<PhoneTimeSuggestion> CREATOR =
new Parcelable.Creator<PhoneTimeSuggestion>() {
public PhoneTimeSuggestion createFromParcel(Parcel in) {
@@ -188,7 +185,6 @@
*
* @hide
*/
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final class Builder {
private final int mSlotIndex;
@Nullable private TimestampedValue<Long> mUtcTime;
diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java
index 2412fb3..df4f513 100644
--- a/core/java/android/app/timedetector/TimeDetector.java
+++ b/core/java/android/app/timedetector/TimeDetector.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.os.SystemClock;
@@ -29,7 +28,6 @@
*
* @hide
*/
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@SystemService(Context.TIME_DETECTOR_SERVICE)
public interface TimeDetector {
diff --git a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java
index 0544ccd..9147b44 100644
--- a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java
+++ b/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java
@@ -19,7 +19,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -57,11 +56,9 @@
*
* @hide
*/
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final class PhoneTimeZoneSuggestion implements Parcelable {
/** @hide */
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@NonNull
public static final Creator<PhoneTimeZoneSuggestion> CREATOR =
new Creator<PhoneTimeZoneSuggestion>() {
@@ -297,7 +294,6 @@
*
* @hide
*/
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final class Builder {
private final int mSlotIndex;
@Nullable private String mZoneId;
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
index b4f6087..6a3953e 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
@@ -27,7 +26,6 @@
*
* @hide
*/
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@SystemService(Context.TIME_ZONE_DETECTOR_SERVICE)
public interface TimeZoneDetector {
@@ -49,7 +47,6 @@
*
* @hide
*/
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@RequiresPermission(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE)
void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion timeZoneSuggestion);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 93126b8..6552d1b 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -679,9 +679,9 @@
boolean hasUidSigningCertificate(int uid, in byte[] signingCertificate, int flags);
- String getSystemTextClassifierPackageName();
+ String getDefaultTextClassifierPackageName();
- String[] getSystemTextClassifierPackages();
+ String getSystemTextClassifierPackageName();
String getAttentionServicePackageName();
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 6d5e8fb..51d5c3f6 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -7602,14 +7602,15 @@
}
/**
- * @return the system defined text classifier package name, or null if there's none.
+ * @return the default text classifier package name, or null if there's none.
*
* @hide
*/
@Nullable
- public String getSystemTextClassifierPackageName() {
+ @TestApi
+ public String getDefaultTextClassifierPackageName() {
throw new UnsupportedOperationException(
- "getSystemTextClassifierPackageName not implemented in subclass");
+ "getDefaultTextClassifierPackageName not implemented in subclass");
}
/**
@@ -7617,10 +7618,11 @@
*
* @hide
*/
- @NonNull
- public String[] getSystemTextClassifierPackages() {
+ @Nullable
+ @TestApi
+ public String getSystemTextClassifierPackageName() {
throw new UnsupportedOperationException(
- "getSystemTextClassifierPackages not implemented in subclass");
+ "getSystemTextClassifierPackageName not implemented in subclass");
}
/**
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index 140363c..b128ea7 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -676,7 +676,8 @@
}
try {
- mService.registerConnectivityDiagnosticsCallback(binder, request);
+ mService.registerConnectivityDiagnosticsCallback(
+ binder, request, mContext.getOpPackageName());
} catch (RemoteException exception) {
exception.rethrowFromSystemServer();
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 1089a19..0fae607 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -222,7 +222,7 @@
boolean isCallerCurrentAlwaysOnVpnLockdownApp();
void registerConnectivityDiagnosticsCallback(in IConnectivityDiagnosticsCallback callback,
- in NetworkRequest request);
+ in NetworkRequest request, String callingPackageName);
void unregisterConnectivityDiagnosticsCallback(in IConnectivityDiagnosticsCallback callback);
IBinder startOrGetTestNetworkService();
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 4f4e27b..cf5f225 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -858,8 +858,8 @@
*
* <p>In general, user-supplied networks (such as WiFi networks) do not have an administrator.
*
- * <p>An app is granted owner privileges over Networks that it supplies. Owner privileges
- * implicitly include administrator privileges.
+ * <p>An app is granted owner privileges over Networks that it supplies. The owner UID MUST
+ * always be included in administratorUids.
*
* @param administratorUids the UIDs to be set as administrators of this Network.
* @hide
diff --git a/core/java/android/os/TimestampedValue.java b/core/java/android/os/TimestampedValue.java
index f4c87ac..4c4335b 100644
--- a/core/java/android/os/TimestampedValue.java
+++ b/core/java/android/os/TimestampedValue.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import java.util.Objects;
@@ -36,7 +35,6 @@
* @param <T> the type of the value with an associated timestamp
* @hide
*/
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final class TimestampedValue<T> implements Parcelable {
private final long mReferenceTimeMillis;
@Nullable
@@ -96,7 +94,6 @@
}
/** @hide */
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final @NonNull Parcelable.Creator<TimestampedValue<?>> CREATOR =
new Parcelable.ClassLoaderCreator<TimestampedValue<?>>() {
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 368c94c..79852d3 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -31,7 +31,6 @@
import android.content.Intent;
import android.graphics.Rect;
import android.os.Build;
-import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
@@ -93,7 +92,7 @@
// Used for metrics / debug only
private ComponentName mServiceComponentName;
- private final IAugmentedAutofillService mInterface = new IAugmentedAutofillService.Stub() {
+ private final class AugmentedAutofillServiceImpl extends IAugmentedAutofillService.Stub {
@Override
public void onConnected(boolean debug, boolean verbose) {
@@ -137,7 +136,7 @@
public final IBinder onBind(Intent intent) {
mServiceComponentName = intent.getComponent();
if (SERVICE_INTERFACE.equals(intent.getAction())) {
- return mInterface.asBinder();
+ return new AugmentedAutofillServiceImpl();
}
Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
return null;
@@ -352,11 +351,13 @@
static final int REPORT_EVENT_NO_RESPONSE = 1;
static final int REPORT_EVENT_UI_SHOWN = 2;
static final int REPORT_EVENT_UI_DESTROYED = 3;
+ static final int REPORT_EVENT_INLINE_RESPONSE = 4;
@IntDef(prefix = { "REPORT_EVENT_" }, value = {
REPORT_EVENT_NO_RESPONSE,
REPORT_EVENT_UI_SHOWN,
- REPORT_EVENT_UI_DESTROYED
+ REPORT_EVENT_UI_DESTROYED,
+ REPORT_EVENT_INLINE_RESPONSE
})
@Retention(RetentionPolicy.SOURCE)
@interface ReportEvent{}
@@ -365,8 +366,8 @@
private final Object mLock = new Object();
private final IAugmentedAutofillManagerClient mClient;
private final int mSessionId;
- public final int taskId;
- public final ComponentName componentName;
+ public final int mTaskId;
+ public final ComponentName mComponentName;
// Used for metrics / debug only
private String mServicePackageName;
@GuardedBy("mLock")
@@ -406,8 +407,8 @@
mSessionId = sessionId;
mClient = IAugmentedAutofillManagerClient.Stub.asInterface(client);
mCallback = callback;
- this.taskId = taskId;
- this.componentName = componentName;
+ mTaskId = taskId;
+ mComponentName = componentName;
mServicePackageName = serviceComponentName.getPackageName();
mFocusedId = focusedId;
mFocusedValue = focusedValue;
@@ -514,22 +515,24 @@
}
}
- public void onInlineSuggestionsDataReady(@NonNull List<Dataset> inlineSuggestionsData,
- @Nullable Bundle clientState) {
+ void reportResult(@Nullable List<Dataset> inlineSuggestionsData) {
try {
- mCallback.onSuccess(inlineSuggestionsData.toArray(new Dataset[]{}), clientState);
+ final Dataset[] inlineSuggestions = (inlineSuggestionsData != null)
+ ? inlineSuggestionsData.toArray(new Dataset[inlineSuggestionsData.size()])
+ : null;
+ mCallback.onSuccess(inlineSuggestions);
} catch (RemoteException e) {
Log.e(TAG, "Error calling back with the inline suggestions data: " + e);
}
}
- // Used (mostly) for metrics.
- public void report(@ReportEvent int event) {
- if (sVerbose) Log.v(TAG, "report(): " + event);
+ void logEvent(@ReportEvent int event) {
+ if (sVerbose) Log.v(TAG, "returnAndLogResult(): " + event);
long duration = -1;
int type = MetricsEvent.TYPE_UNKNOWN;
+
switch (event) {
- case REPORT_EVENT_NO_RESPONSE:
+ case REPORT_EVENT_NO_RESPONSE: {
type = MetricsEvent.TYPE_SUCCESS;
if (mFirstOnSuccessTime == 0) {
mFirstOnSuccessTime = SystemClock.elapsedRealtime();
@@ -538,40 +541,49 @@
Log.d(TAG, "Service responded nothing in " + formatDuration(duration));
}
}
- try {
- mCallback.onSuccess(/* inlineSuggestionsData= */null, /* clientState=*/
- null);
- } catch (RemoteException e) {
- Log.e(TAG, "Error reporting success: " + e);
+ } break;
+
+ case REPORT_EVENT_INLINE_RESPONSE: {
+ // TODO: Define a constant and log this event
+ // type = MetricsEvent.TYPE_SUCCESS_INLINE;
+ if (mFirstOnSuccessTime == 0) {
+ mFirstOnSuccessTime = SystemClock.elapsedRealtime();
+ duration = mFirstOnSuccessTime - mFirstRequestTime;
+ if (sDebug) {
+ Log.d(TAG, "Service responded nothing in " + formatDuration(duration));
+ }
}
- break;
- case REPORT_EVENT_UI_SHOWN:
+ } break;
+
+ case REPORT_EVENT_UI_SHOWN: {
type = MetricsEvent.TYPE_OPEN;
if (mUiFirstShownTime == 0) {
mUiFirstShownTime = SystemClock.elapsedRealtime();
duration = mUiFirstShownTime - mFirstRequestTime;
if (sDebug) Log.d(TAG, "UI shown in " + formatDuration(duration));
}
- break;
- case REPORT_EVENT_UI_DESTROYED:
+ } break;
+
+ case REPORT_EVENT_UI_DESTROYED: {
type = MetricsEvent.TYPE_CLOSE;
if (mUiFirstDestroyedTime == 0) {
mUiFirstDestroyedTime = SystemClock.elapsedRealtime();
- duration = mUiFirstDestroyedTime - mFirstRequestTime;
+ duration = mUiFirstDestroyedTime - mFirstRequestTime;
if (sDebug) Log.d(TAG, "UI destroyed in " + formatDuration(duration));
}
- break;
+ } break;
+
default:
Log.w(TAG, "invalid event reported: " + event);
}
- logResponse(type, mServicePackageName, componentName, mSessionId, duration);
+ logResponse(type, mServicePackageName, mComponentName, mSessionId, duration);
}
public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
pw.print(prefix); pw.print("sessionId: "); pw.println(mSessionId);
- pw.print(prefix); pw.print("taskId: "); pw.println(taskId);
+ pw.print(prefix); pw.print("taskId: "); pw.println(mTaskId);
pw.print(prefix); pw.print("component: ");
- pw.println(componentName.flattenToShortString());
+ pw.println(mComponentName.flattenToShortString());
pw.print(prefix); pw.print("focusedId: "); pw.println(mFocusedId);
if (mFocusedValue != null) {
pw.print(prefix); pw.print("focusedValue: "); pw.println(mFocusedValue);
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index d0ffd7b..19eff57 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -54,13 +54,15 @@
if (sDebug) Log.d(TAG, "onSuccess(): " + response);
if (response == null) {
- mProxy.report(AutofillProxy.REPORT_EVENT_NO_RESPONSE);
+ mProxy.logEvent(AutofillProxy.REPORT_EVENT_NO_RESPONSE);
+ mProxy.reportResult(null /*inlineSuggestions*/);
return;
}
List<Dataset> inlineSuggestions = response.getInlineSuggestions();
if (inlineSuggestions != null && !inlineSuggestions.isEmpty()) {
- mProxy.onInlineSuggestionsDataReady(inlineSuggestions, response.getClientState());
+ mProxy.logEvent(AutofillProxy.REPORT_EVENT_INLINE_RESPONSE);
+ mProxy.reportResult(inlineSuggestions);
return;
}
diff --git a/core/java/android/service/autofill/augmented/FillController.java b/core/java/android/service/autofill/augmented/FillController.java
index 63ec2d8..7d552d6 100644
--- a/core/java/android/service/autofill/augmented/FillController.java
+++ b/core/java/android/service/autofill/augmented/FillController.java
@@ -62,12 +62,13 @@
try {
mProxy.autofill(values);
- final FillWindow fillWindow = mProxy.getFillWindow();
- if (fillWindow != null) {
- fillWindow.destroy();
- }
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
+
+ final FillWindow fillWindow = mProxy.getFillWindow();
+ if (fillWindow != null) {
+ fillWindow.destroy();
+ }
}
}
diff --git a/core/java/android/service/autofill/augmented/FillRequest.java b/core/java/android/service/autofill/augmented/FillRequest.java
index ca49e7d..6927cf6 100644
--- a/core/java/android/service/autofill/augmented/FillRequest.java
+++ b/core/java/android/service/autofill/augmented/FillRequest.java
@@ -53,7 +53,7 @@
* Gets the task of the activity associated with this request.
*/
public int getTaskId() {
- return mProxy.taskId;
+ return mProxy.mTaskId;
}
/**
@@ -61,7 +61,7 @@
*/
@NonNull
public ComponentName getActivityComponent() {
- return mProxy.componentName;
+ return mProxy.mComponentName;
}
/**
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index 5d00370..077df6c 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -21,6 +21,7 @@
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.graphics.Rect;
@@ -41,6 +42,7 @@
import dalvik.system.CloseGuard;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
/**
* Handle to a window used to display the augmented autofill UI.
@@ -70,23 +72,22 @@
private final CloseGuard mCloseGuard = CloseGuard.get();
private final @NonNull Handler mUiThreadHandler = new Handler(Looper.getMainLooper());
- private final @NonNull FillWindowPresenter mFillWindowPresenter = new FillWindowPresenter();
@GuardedBy("mLock")
- private WindowManager mWm;
+ private @NonNull WindowManager mWm;
@GuardedBy("mLock")
private View mFillView;
@GuardedBy("mLock")
private boolean mShowing;
@GuardedBy("mLock")
- private Rect mBounds;
+ private @Nullable Rect mBounds;
@GuardedBy("mLock")
private boolean mUpdateCalled;
@GuardedBy("mLock")
private boolean mDestroyed;
- private AutofillProxy mProxy;
+ private @NonNull AutofillProxy mProxy;
/**
* Updates the content of the window.
@@ -172,11 +173,11 @@
try {
mProxy.requestShowFillUi(mBounds.right - mBounds.left,
mBounds.bottom - mBounds.top,
- /*anchorBounds=*/ null, mFillWindowPresenter);
+ /*anchorBounds=*/ null, new FillWindowPresenter(this));
} catch (RemoteException e) {
Log.w(TAG, "Error requesting to show fill window", e);
}
- mProxy.report(AutofillProxy.REPORT_EVENT_UI_SHOWN);
+ mProxy.logEvent(AutofillProxy.REPORT_EVENT_UI_SHOWN);
}
}
}
@@ -244,7 +245,7 @@
if (mUpdateCalled) {
mFillView.setOnClickListener(null);
hide();
- mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
+ mProxy.logEvent(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
}
mDestroyed = true;
mCloseGuard.close();
@@ -254,9 +255,7 @@
@Override
protected void finalize() throws Throwable {
try {
- if (mCloseGuard != null) {
- mCloseGuard.warnIfOpen();
- }
+ mCloseGuard.warnIfOpen();
destroy();
} finally {
super.finalize();
@@ -289,22 +288,36 @@
/** @hide */
@Override
- public void close() throws Exception {
+ public void close() {
destroy();
}
- private final class FillWindowPresenter extends IAutofillWindowPresenter.Stub {
+ private static final class FillWindowPresenter extends IAutofillWindowPresenter.Stub {
+ private final @NonNull WeakReference<FillWindow> mFillWindowReference;
+
+ FillWindowPresenter(@NonNull FillWindow fillWindow) {
+ mFillWindowReference = new WeakReference<>(fillWindow);
+ }
+
@Override
public void show(WindowManager.LayoutParams p, Rect transitionEpicenter,
boolean fitsSystemWindows, int layoutDirection) {
if (sDebug) Log.d(TAG, "FillWindowPresenter.show()");
- mUiThreadHandler.sendMessage(obtainMessage(FillWindow::handleShow, FillWindow.this, p));
+ final FillWindow fillWindow = mFillWindowReference.get();
+ if (fillWindow != null) {
+ fillWindow.mUiThreadHandler.sendMessage(
+ obtainMessage(FillWindow::handleShow, fillWindow, p));
+ }
}
@Override
public void hide(Rect transitionEpicenter) {
if (sDebug) Log.d(TAG, "FillWindowPresenter.hide()");
- mUiThreadHandler.sendMessage(obtainMessage(FillWindow::handleHide, FillWindow.this));
+ final FillWindow fillWindow = mFillWindowReference.get();
+ if (fillWindow != null) {
+ fillWindow.mUiThreadHandler.sendMessage(
+ obtainMessage(FillWindow::handleHide, fillWindow));
+ }
}
}
}
diff --git a/core/java/android/service/autofill/augmented/IFillCallback.aidl b/core/java/android/service/autofill/augmented/IFillCallback.aidl
index 31e77f35..d983721 100644
--- a/core/java/android/service/autofill/augmented/IFillCallback.aidl
+++ b/core/java/android/service/autofill/augmented/IFillCallback.aidl
@@ -28,7 +28,7 @@
*/
interface IFillCallback {
void onCancellable(in ICancellationSignal cancellation);
- void onSuccess(in @nullable Dataset[] inlineSuggestionsData, in @nullable Bundle clientState);
+ void onSuccess(in @nullable Dataset[] inlineSuggestionsData);
boolean isCompleted();
void cancel();
}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 3f9462c..af91596 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -16,6 +16,9 @@
package android.service.notification;
+import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
+import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT;
+import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_NONE;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
@@ -99,6 +102,8 @@
private static final boolean DEFAULT_ALLOW_REMINDERS = false;
private static final boolean DEFAULT_ALLOW_EVENTS = false;
private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = true;
+ private static final boolean DEFAULT_ALLOW_CONV = true;
+ private static final int DEFAULT_ALLOW_CONV_FROM = ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
private static final boolean DEFAULT_CHANNELS_BYPASSING_DND = false;
private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS = 0;
@@ -120,6 +125,8 @@
private static final String ALLOW_ATT_EVENTS = "events";
private static final String ALLOW_ATT_SCREEN_OFF = "visualScreenOff";
private static final String ALLOW_ATT_SCREEN_ON = "visualScreenOn";
+ private static final String ALLOW_ATT_CONV = "conv";
+ private static final String ALLOW_ATT_CONV_FROM = "convFrom";
private static final String DISALLOW_TAG = "disallow";
private static final String DISALLOW_ATT_VISUAL_EFFECTS = "visualEffects";
private static final String STATE_TAG = "state";
@@ -170,6 +177,8 @@
public boolean allowEvents = DEFAULT_ALLOW_EVENTS;
public int allowCallsFrom = DEFAULT_CALLS_SOURCE;
public int allowMessagesFrom = DEFAULT_SOURCE;
+ public boolean allowConversations = DEFAULT_ALLOW_CONV;
+ public int allowConversationsFrom = DEFAULT_ALLOW_CONV_FROM;
public int user = UserHandle.USER_SYSTEM;
public int suppressedVisualEffects = DEFAULT_SUPPRESSED_VISUAL_EFFECTS;
public boolean areChannelsBypassingDnd = DEFAULT_CHANNELS_BYPASSING_DND;
@@ -207,6 +216,8 @@
allowSystem = source.readInt() == 1;
suppressedVisualEffects = source.readInt();
areChannelsBypassingDnd = source.readInt() == 1;
+ allowConversations = source.readBoolean();
+ allowConversationsFrom = source.readInt();
}
@Override
@@ -239,6 +250,8 @@
dest.writeInt(allowSystem ? 1 : 0);
dest.writeInt(suppressedVisualEffects);
dest.writeInt(areChannelsBypassingDnd ? 1 : 0);
+ dest.writeBoolean(allowConversations);
+ dest.writeInt(allowConversationsFrom);
}
@Override
@@ -253,8 +266,11 @@
.append(",allowCalls=").append(allowCalls)
.append(",allowRepeatCallers=").append(allowRepeatCallers)
.append(",allowMessages=").append(allowMessages)
+ .append(",allowConversations=").append(allowConversations)
.append(",allowCallsFrom=").append(sourceToString(allowCallsFrom))
.append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom))
+ .append(",allowConvFrom=").append(ZenPolicy.conversationTypeToString
+ (allowConversationsFrom))
.append(",suppressedVisualEffects=").append(suppressedVisualEffects)
.append(",areChannelsBypassingDnd=").append(areChannelsBypassingDnd)
.append(",\nautomaticRules=").append(rulesToString())
@@ -431,7 +447,9 @@
&& Objects.equals(other.automaticRules, automaticRules)
&& Objects.equals(other.manualRule, manualRule)
&& other.suppressedVisualEffects == suppressedVisualEffects
- && other.areChannelsBypassingDnd == areChannelsBypassingDnd;
+ && other.areChannelsBypassingDnd == areChannelsBypassingDnd
+ && other.allowConversations == allowConversations
+ && other.allowConversationsFrom == allowConversationsFrom;
}
@Override
@@ -440,7 +458,8 @@
allowRepeatCallers, allowMessages,
allowCallsFrom, allowMessagesFrom, allowReminders, allowEvents,
user, automaticRules, manualRule,
- suppressedVisualEffects, areChannelsBypassingDnd);
+ suppressedVisualEffects, areChannelsBypassingDnd, allowConversations,
+ allowConversationsFrom);
}
private static String toDayList(int[] days) {
@@ -518,10 +537,13 @@
DEFAULT_ALLOW_MESSAGES);
rt.allowReminders = safeBoolean(parser, ALLOW_ATT_REMINDERS,
DEFAULT_ALLOW_REMINDERS);
+ rt.allowConversations = safeBoolean(parser, ALLOW_ATT_CONV, DEFAULT_ALLOW_CONV);
rt.allowEvents = safeBoolean(parser, ALLOW_ATT_EVENTS, DEFAULT_ALLOW_EVENTS);
final int from = safeInt(parser, ALLOW_ATT_FROM, -1);
final int callsFrom = safeInt(parser, ALLOW_ATT_CALLS_FROM, -1);
final int messagesFrom = safeInt(parser, ALLOW_ATT_MESSAGES_FROM, -1);
+ rt.allowConversationsFrom = safeInt(parser, ALLOW_ATT_CONV_FROM,
+ DEFAULT_ALLOW_CONV_FROM);
if (isValidSource(callsFrom) && isValidSource(messagesFrom)) {
rt.allowCallsFrom = callsFrom;
rt.allowMessagesFrom = messagesFrom;
@@ -602,6 +624,8 @@
out.attribute(null, ALLOW_ATT_ALARMS, Boolean.toString(allowAlarms));
out.attribute(null, ALLOW_ATT_MEDIA, Boolean.toString(allowMedia));
out.attribute(null, ALLOW_ATT_SYSTEM, Boolean.toString(allowSystem));
+ out.attribute(null, ALLOW_ATT_CONV, Boolean.toString(allowConversations));
+ out.attribute(null, ALLOW_ATT_CONV_FROM, Integer.toString(allowConversationsFrom));
out.endTag(null, ALLOW_TAG);
out.startTag(null, DISALLOW_TAG);
@@ -944,6 +968,7 @@
int suppressedVisualEffects = 0;
int callSenders = defaultPolicy.priorityCallSenders;
int messageSenders = defaultPolicy.priorityMessageSenders;
+ int conversationSenders = defaultPolicy.priorityConversationSenders;
if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_REMINDERS,
isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_REMINDERS, defaultPolicy))) {
@@ -962,6 +987,14 @@
messageSenders);
}
+ if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CONVERSATIONS,
+ isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CONVERSATIONS, defaultPolicy))) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
+ conversationSenders = getNotificationPolicySenders(
+ zenPolicy.getPriorityConversationSenders(),
+ conversationSenders);
+ }
+
if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CALLS,
isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CALLS, defaultPolicy))) {
priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
@@ -1047,7 +1080,7 @@
}
return new NotificationManager.Policy(priorityCategories, callSenders,
- messageSenders, suppressedVisualEffects, defaultPolicy.state);
+ messageSenders, suppressedVisualEffects, defaultPolicy.state, conversationSenders);
}
private boolean isPriorityCategoryEnabled(int categoryType, Policy policy) {
@@ -1088,11 +1121,14 @@
}
}
-
public Policy toNotificationPolicy() {
int priorityCategories = 0;
int priorityCallSenders = Policy.PRIORITY_SENDERS_CONTACTS;
int priorityMessageSenders = Policy.PRIORITY_SENDERS_CONTACTS;
+ int priorityConversationSenders = Policy.CONVERSATION_SENDERS_IMPORTANT;
+ if (allowConversations) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
+ }
if (allowCalls) {
priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
}
@@ -1119,10 +1155,12 @@
}
priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders);
priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders);
+ priorityConversationSenders = allowConversationsFrom;
return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders,
suppressedVisualEffects, areChannelsBypassingDnd
- ? Policy.STATE_CHANNELS_BYPASSING_DND : 0);
+ ? Policy.STATE_CHANNELS_BYPASSING_DND : 0,
+ priorityConversationSenders);
}
/**
@@ -1157,6 +1195,27 @@
}
}
+ private static int normalizePrioritySenders(int prioritySenders, int def) {
+ if (!(prioritySenders == Policy.PRIORITY_SENDERS_CONTACTS
+ || prioritySenders == Policy.PRIORITY_SENDERS_STARRED
+ || prioritySenders == Policy.PRIORITY_SENDERS_ANY)) {
+ return def;
+ }
+ return prioritySenders;
+ }
+
+ private static int normalizeConversationSenders(boolean allowed, int senders, int def) {
+ if (!allowed) {
+ return CONVERSATION_SENDERS_NONE;
+ }
+ if (!(senders == CONVERSATION_SENDERS_ANYONE
+ || senders == CONVERSATION_SENDERS_IMPORTANT
+ || senders == CONVERSATION_SENDERS_NONE)) {
+ return def;
+ }
+ return senders;
+ }
+
public void applyNotificationPolicy(Policy policy) {
if (policy == null) return;
allowAlarms = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0;
@@ -1168,12 +1227,17 @@
allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0;
allowRepeatCallers = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS)
!= 0;
- allowCallsFrom = prioritySendersToSource(policy.priorityCallSenders, allowCallsFrom);
- allowMessagesFrom = prioritySendersToSource(policy.priorityMessageSenders,
+ allowCallsFrom = normalizePrioritySenders(policy.priorityCallSenders, allowCallsFrom);
+ allowMessagesFrom = normalizePrioritySenders(policy.priorityMessageSenders,
allowMessagesFrom);
if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) {
suppressedVisualEffects = policy.suppressedVisualEffects;
}
+ allowConversations = (policy.priorityCategories
+ & Policy.PRIORITY_CATEGORY_CONVERSATIONS) != 0;
+ allowConversationsFrom = normalizeConversationSenders(allowConversations,
+ policy.priorityConversationSenders,
+ allowConversationsFrom);
if (policy.state != Policy.STATE_UNSET) {
areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0;
}
@@ -1919,10 +1983,13 @@
& NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS) != 0;
boolean allowRepeatCallers = (policy.priorityCategories
& NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) != 0;
+ boolean allowConversations = (policy.priorityConversationSenders
+ & Policy.PRIORITY_CATEGORY_CONVERSATIONS) != 0;
boolean areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0;
boolean allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0;
return !allowReminders && !allowCalls && !allowMessages && !allowEvents
- && !allowRepeatCallers && !areChannelsBypassingDnd && !allowSystem;
+ && !allowRepeatCallers && !areChannelsBypassingDnd && !allowSystem
+ && !allowConversations;
}
/**
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index 6e2faa9..87295e1 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -41,6 +41,7 @@
private ArrayList<Integer> mVisualEffects;
private @PeopleType int mPriorityMessages = PEOPLE_TYPE_UNSET;
private @PeopleType int mPriorityCalls = PEOPLE_TYPE_UNSET;
+ private @ConversationSenders int mConversationSenders = CONVERSATION_SENDERS_UNSET;
/** @hide */
@IntDef(prefix = { "PRIORITY_CATEGORY_" }, value = {
@@ -52,6 +53,7 @@
PRIORITY_CATEGORY_ALARMS,
PRIORITY_CATEGORY_MEDIA,
PRIORITY_CATEGORY_SYSTEM,
+ PRIORITY_CATEGORY_CONVERSATIONS,
})
@Retention(RetentionPolicy.SOURCE)
public @interface PriorityCategory {}
@@ -72,6 +74,8 @@
public static final int PRIORITY_CATEGORY_MEDIA = 6;
/** @hide */
public static final int PRIORITY_CATEGORY_SYSTEM = 7;
+ /** @hide */
+ public static final int PRIORITY_CATEGORY_CONVERSATIONS = 8;
/** @hide */
@IntDef(prefix = { "VISUAL_EFFECT_" }, value = {
@@ -138,6 +142,37 @@
*/
public static final int PEOPLE_TYPE_NONE = 4;
+
+ /** @hide */
+ @IntDef(prefix = { "CONVERSATION_SENDERS_" }, value = {
+ CONVERSATION_SENDERS_UNSET,
+ CONVERSATION_SENDERS_ANYONE,
+ CONVERSATION_SENDERS_IMPORTANT,
+ CONVERSATION_SENDERS_NONE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ConversationSenders {}
+
+ /**
+ * Used to indicate no preference for the type of conversations that can bypass dnd.
+ */
+ public static final int CONVERSATION_SENDERS_UNSET = 0;
+
+ /**
+ * Used to indicate all conversations can bypass dnd.
+ */
+ public static final int CONVERSATION_SENDERS_ANYONE = 1;
+
+ /**
+ * Used to indicate important conversations can bypass dnd.
+ */
+ public static final int CONVERSATION_SENDERS_IMPORTANT = 2;
+
+ /**
+ * Used to indicate no conversations can bypass dnd.
+ */
+ public static final int CONVERSATION_SENDERS_NONE = 3;
+
/** @hide */
@IntDef(prefix = { "STATE_" }, value = {
STATE_UNSET,
@@ -165,11 +200,20 @@
/** @hide */
public ZenPolicy() {
- mPriorityCategories = new ArrayList<>(Collections.nCopies(8, 0));
+ mPriorityCategories = new ArrayList<>(Collections.nCopies(9, 0));
mVisualEffects = new ArrayList<>(Collections.nCopies(7, 0));
}
/**
+ * Conversation type that can bypass DND.
+ * @return {@link #CONVERSATION_SENDERS_UNSET}, {@link #CONVERSATION_SENDERS_ANYONE},
+ * {@link #CONVERSATION_SENDERS_IMPORTANT}, {@link #CONVERSATION_SENDERS_NONE}.
+ */
+ public @PeopleType int getPriorityConversationSenders() {
+ return mConversationSenders;
+ }
+
+ /**
* Message senders that can bypass DND.
* @return {@link #PEOPLE_TYPE_UNSET}, {@link #PEOPLE_TYPE_ANYONE},
* {@link #PEOPLE_TYPE_CONTACTS}, {@link #PEOPLE_TYPE_STARRED} or {@link #PEOPLE_TYPE_NONE}
@@ -188,6 +232,16 @@
}
/**
+ * Whether this policy wants to allow conversation notifications
+ * (see {@link NotificationChannel#getConversationId()}) to play sounds and visually appear
+ * or to intercept them when DND is active.
+ * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
+ */
+ public @State int getPriorityCategoryConversations() {
+ return mPriorityCategories.get(PRIORITY_CATEGORY_CONVERSATIONS);
+ }
+
+ /**
* Whether this policy wants to allow notifications with category
* {@link Notification#CATEGORY_REMINDER} to play sounds and visually appear
* or to intercept them when DND is active.
@@ -392,6 +446,7 @@
}
mZenPolicy.mPriorityMessages = PEOPLE_TYPE_ANYONE;
mZenPolicy.mPriorityCalls = PEOPLE_TYPE_ANYONE;
+ mZenPolicy.mConversationSenders = CONVERSATION_SENDERS_ANYONE;
return this;
}
@@ -408,6 +463,7 @@
}
mZenPolicy.mPriorityMessages = PEOPLE_TYPE_NONE;
mZenPolicy.mPriorityCalls = PEOPLE_TYPE_NONE;
+ mZenPolicy.mConversationSenders = CONVERSATION_SENDERS_NONE;
return this;
}
@@ -443,6 +499,8 @@
mZenPolicy.mPriorityMessages = STATE_UNSET;
} else if (category == PRIORITY_CATEGORY_CALLS) {
mZenPolicy.mPriorityCalls = STATE_UNSET;
+ } else if (category == PRIORITY_CATEGORY_CONVERSATIONS) {
+ mZenPolicy.mConversationSenders = STATE_UNSET;
}
return this;
@@ -479,6 +537,31 @@
}
/**
+ * Whether to allow conversation notifications
+ * (see {@link NotificationChannel#setConversationId(String, String)})
+ * that match audienceType to play sounds and visually appear or to intercept
+ * them when DND is active.
+ * @param audienceType callers that are allowed to bypass DND
+ */
+ public @NonNull Builder allowConversations(@ConversationSenders int audienceType) {
+ if (audienceType == STATE_UNSET) {
+ return unsetPriorityCategory(PRIORITY_CATEGORY_CONVERSATIONS);
+ }
+
+ if (audienceType == CONVERSATION_SENDERS_NONE) {
+ mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_CONVERSATIONS, STATE_DISALLOW);
+ } else if (audienceType == CONVERSATION_SENDERS_ANYONE
+ || audienceType == CONVERSATION_SENDERS_IMPORTANT) {
+ mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_CONVERSATIONS, STATE_ALLOW);
+ } else {
+ return this;
+ }
+
+ mZenPolicy.mConversationSenders = audienceType;
+ return this;
+ }
+
+ /**
* Whether to allow notifications with category {@link Notification#CATEGORY_MESSAGE}
* that match audienceType to play sounds and visually appear or to intercept
* them when DND is active.
@@ -537,7 +620,6 @@
return this;
}
-
/**
* Whether to allow notifications with category {@link Notification#CATEGORY_ALARM}
* to play sounds and visually appear or to intercept them when DND is active.
@@ -712,6 +794,7 @@
dest.writeList(mVisualEffects);
dest.writeInt(mPriorityCalls);
dest.writeInt(mPriorityMessages);
+ dest.writeInt(mConversationSenders);
}
public static final @android.annotation.NonNull Parcelable.Creator<ZenPolicy> CREATOR =
@@ -723,6 +806,7 @@
policy.mVisualEffects = source.readArrayList(Integer.class.getClassLoader());
policy.mPriorityCalls = source.readInt();
policy.mPriorityMessages = source.readInt();
+ policy.mConversationSenders = source.readInt();
return policy;
}
@@ -738,8 +822,10 @@
.append('{')
.append("priorityCategories=[").append(priorityCategoriesToString())
.append("], visualEffects=[").append(visualEffectsToString())
- .append("], priorityCalls=").append(peopleTypeToString(mPriorityCalls))
- .append(", priorityMessages=").append(peopleTypeToString(mPriorityMessages))
+ .append("], priorityCallsSenders=").append(peopleTypeToString(mPriorityCalls))
+ .append(", priorityMessagesSenders=").append(peopleTypeToString(mPriorityMessages))
+ .append(", priorityConversationSenders=").append(
+ conversationTypeToString(mConversationSenders))
.append('}')
.toString();
}
@@ -811,6 +897,8 @@
return "media";
case PRIORITY_CATEGORY_SYSTEM:
return "system";
+ case PRIORITY_CATEGORY_CONVERSATIONS:
+ return "convs";
}
return null;
}
@@ -843,6 +931,23 @@
return "invalidPeopleType{" + peopleType + "}";
}
+ /**
+ * @hide
+ */
+ public static String conversationTypeToString(@ConversationSenders int conversationType) {
+ switch (conversationType) {
+ case CONVERSATION_SENDERS_ANYONE:
+ return "anyone";
+ case CONVERSATION_SENDERS_IMPORTANT:
+ return "important";
+ case CONVERSATION_SENDERS_NONE:
+ return "none";
+ case CONVERSATION_SENDERS_UNSET:
+ return "unset";
+ }
+ return "invalidConversationType{" + conversationType + "}";
+ }
+
@Override
public boolean equals(Object o) {
if (!(o instanceof ZenPolicy)) return false;
@@ -852,12 +957,14 @@
return Objects.equals(other.mPriorityCategories, mPriorityCategories)
&& Objects.equals(other.mVisualEffects, mVisualEffects)
&& other.mPriorityCalls == mPriorityCalls
- && other.mPriorityMessages == mPriorityMessages;
+ && other.mPriorityMessages == mPriorityMessages
+ && other.mConversationSenders == mConversationSenders;
}
@Override
public int hashCode() {
- return Objects.hash(mPriorityCategories, mVisualEffects, mPriorityCalls, mPriorityMessages);
+ return Objects.hash(mPriorityCategories, mVisualEffects, mPriorityCalls, mPriorityMessages,
+ mConversationSenders);
}
private @ZenPolicy.State int getZenPolicyPriorityCategoryState(@PriorityCategory int
@@ -879,6 +986,8 @@
return getPriorityCategoryMedia();
case PRIORITY_CATEGORY_SYSTEM:
return getPriorityCategorySystem();
+ case PRIORITY_CATEGORY_CONVERSATIONS:
+ return getPriorityCategoryConversations();
}
return -1;
}
@@ -953,6 +1062,9 @@
} else if (category == PRIORITY_CATEGORY_CALLS
&& mPriorityCalls < policyToApply.mPriorityCalls) {
mPriorityCalls = policyToApply.mPriorityCalls;
+ } else if (category == PRIORITY_CATEGORY_CONVERSATIONS
+ && mConversationSenders < policyToApply.mConversationSenders) {
+ mConversationSenders = policyToApply.mConversationSenders;
}
}
}
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 8dca69f..848868a 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -27,7 +27,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Bundle;
@@ -42,7 +41,6 @@
import android.view.textclassifier.ConversationActions;
import android.view.textclassifier.SelectionEvent;
import android.view.textclassifier.TextClassification;
-import android.view.textclassifier.TextClassificationConstants;
import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassificationSessionId;
@@ -394,19 +392,32 @@
*/
@Deprecated
public final TextClassifier getLocalTextClassifier() {
- // Deprecated: In the future, we may not guarantee that this runs in the service's process.
- return getDefaultTextClassifierImplementation(this);
+ return TextClassifier.NO_OP;
}
/**
* Returns the platform's default TextClassifier implementation.
+ *
+ * @throws RuntimeException if the TextClassifier from
+ * PackageManager#getDefaultTextClassifierPackageName() calls
+ * this method.
*/
@NonNull
public static TextClassifier getDefaultTextClassifierImplementation(@NonNull Context context) {
+ final String defaultTextClassifierPackageName =
+ context.getPackageManager().getDefaultTextClassifierPackageName();
+ if (TextUtils.isEmpty(defaultTextClassifierPackageName)) {
+ return TextClassifier.NO_OP;
+ }
+ if (defaultTextClassifierPackageName.equals(context.getPackageName())) {
+ throw new RuntimeException(
+ "The default text classifier itself should not call the"
+ + "getDefaultTextClassifierImplementation() method.");
+ }
final TextClassificationManager tcm =
context.getSystemService(TextClassificationManager.class);
if (tcm != null) {
- return tcm.getTextClassifier(TextClassifier.LOCAL);
+ return tcm.getTextClassifier(TextClassifier.DEFAULT_SERVICE);
}
return TextClassifier.NO_OP;
}
@@ -434,46 +445,20 @@
}
/**
- * Returns the component name of the system default textclassifier service if it can be found
- * on the system. Otherwise, returns null.
+ * Returns the component name of the textclassifier service from the given package.
+ * Otherwise, returns null.
*
- * @param context the text classification context
+ * @param context
+ * @param packageName the package to look for.
+ * @param resolveFlags the flags that are used by PackageManager to resolve the component name.
* @hide
*/
@Nullable
- public static ComponentName getServiceComponentName(@NonNull Context context) {
- final TextClassificationConstants settings = TextClassificationManager.getSettings(context);
- // get override TextClassifierService package name
- String packageName = settings.getTextClassifierServicePackageOverride();
-
- ComponentName serviceComponent = null;
- final boolean isOverrideService = !TextUtils.isEmpty(packageName);
- if (isOverrideService) {
- serviceComponent = getServiceComponentNameByPackage(context, packageName,
- isOverrideService);
- }
- if (serviceComponent != null) {
- return serviceComponent;
- }
- // If no TextClassifierService override or invalid override package name, read the first
- // package defined in the config
- final String[] packages = context.getPackageManager().getSystemTextClassifierPackages();
- if (packages.length == 0 || TextUtils.isEmpty(packages[0])) {
- Slog.d(LOG_TAG, "No configured system TextClassifierService");
- return null;
- }
- packageName = packages[0];
- serviceComponent = getServiceComponentNameByPackage(context, packageName,
- isOverrideService);
- return serviceComponent;
- }
-
- private static ComponentName getServiceComponentNameByPackage(Context context,
- String packageName, boolean isOverrideService) {
+ public static ComponentName getServiceComponentName(
+ Context context, String packageName, int resolveFlags) {
final Intent intent = new Intent(SERVICE_INTERFACE).setPackage(packageName);
- final int flags = isOverrideService ? 0 : PackageManager.MATCH_SYSTEM_ONLY;
- final ResolveInfo ri = context.getPackageManager().resolveService(intent, flags);
+ final ResolveInfo ri = context.getPackageManager().resolveService(intent, resolveFlags);
if ((ri == null) || (ri.serviceInfo == null)) {
Slog.w(LOG_TAG, String.format("Package or service not found in package %s for user %d",
diff --git a/core/java/android/timezone/CountryTimeZones.java b/core/java/android/timezone/CountryTimeZones.java
index 970acd0..ab2c4fc 100644
--- a/core/java/android/timezone/CountryTimeZones.java
+++ b/core/java/android/timezone/CountryTimeZones.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
import android.icu.util.TimeZone;
import java.util.ArrayList;
@@ -32,7 +31,6 @@
*
* @hide
*/
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final class CountryTimeZones {
/**
@@ -40,7 +38,6 @@
*
* @hide
*/
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final class TimeZoneMapping {
@NonNull
@@ -97,7 +94,6 @@
*
* @hide
*/
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final class OffsetResult {
private final TimeZone mTimeZone;
diff --git a/core/java/android/timezone/TelephonyLookup.java b/core/java/android/timezone/TelephonyLookup.java
index 8a5864e..a4c3fbd 100644
--- a/core/java/android/timezone/TelephonyLookup.java
+++ b/core/java/android/timezone/TelephonyLookup.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import com.android.internal.annotations.GuardedBy;
@@ -29,7 +28,6 @@
*
* @hide
*/
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final class TelephonyLookup {
private static final Object sLock = new Object();
diff --git a/core/java/android/timezone/TelephonyNetwork.java b/core/java/android/timezone/TelephonyNetwork.java
index 487b3f2..823cd25 100644
--- a/core/java/android/timezone/TelephonyNetwork.java
+++ b/core/java/android/timezone/TelephonyNetwork.java
@@ -17,7 +17,6 @@
package android.timezone;
import android.annotation.NonNull;
-import android.annotation.SystemApi;
import java.util.Objects;
@@ -26,7 +25,6 @@
*
* @hide
*/
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final class TelephonyNetwork {
@NonNull
diff --git a/core/java/android/timezone/TelephonyNetworkFinder.java b/core/java/android/timezone/TelephonyNetworkFinder.java
index 2ddd3d9..4bfeff8 100644
--- a/core/java/android/timezone/TelephonyNetworkFinder.java
+++ b/core/java/android/timezone/TelephonyNetworkFinder.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import java.util.Objects;
@@ -27,7 +26,6 @@
*
* @hide
*/
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final class TelephonyNetworkFinder {
@NonNull
diff --git a/core/java/android/timezone/TimeZoneFinder.java b/core/java/android/timezone/TimeZoneFinder.java
index c76bb1d..03f5013 100644
--- a/core/java/android/timezone/TimeZoneFinder.java
+++ b/core/java/android/timezone/TimeZoneFinder.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import com.android.internal.annotations.GuardedBy;
@@ -29,7 +28,6 @@
*
* @hide
*/
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final class TimeZoneFinder {
private static final Object sLock = new Object();
diff --git a/core/java/android/timezone/TzDataSetVersion.java b/core/java/android/timezone/TzDataSetVersion.java
index efe50a0..f993012 100644
--- a/core/java/android/timezone/TzDataSetVersion.java
+++ b/core/java/android/timezone/TzDataSetVersion.java
@@ -17,7 +17,6 @@
package android.timezone;
import android.annotation.NonNull;
-import android.annotation.SystemApi;
import com.android.internal.annotations.VisibleForTesting;
@@ -45,7 +44,6 @@
* @hide
*/
@VisibleForTesting
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final class TzDataSetVersion {
/**
@@ -88,7 +86,6 @@
* A checked exception used in connection with time zone data sets.
* @hide
*/
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final class TzDataSetException extends Exception {
/** Creates an instance with a message. */
diff --git a/core/java/android/timezone/ZoneInfoDb.java b/core/java/android/timezone/ZoneInfoDb.java
index 4612a56..9354a69 100644
--- a/core/java/android/timezone/ZoneInfoDb.java
+++ b/core/java/android/timezone/ZoneInfoDb.java
@@ -17,7 +17,6 @@
package android.timezone;
import android.annotation.NonNull;
-import android.annotation.SystemApi;
import com.android.internal.annotations.GuardedBy;
@@ -29,7 +28,6 @@
*
* @hide
*/
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final class ZoneInfoDb {
private static final Object sLock = new Object();
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index d79fc9a..f61217d 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1514,6 +1514,7 @@
public HdrCapabilities(int[] supportedHdrTypes, float maxLuminance,
float maxAverageLuminance, float minLuminance) {
mSupportedHdrTypes = supportedHdrTypes;
+ Arrays.sort(mSupportedHdrTypes);
mMaxLuminance = maxLuminance;
mMaxAverageLuminance = maxAverageLuminance;
mMinLuminance = minLuminance;
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index b9f08ad..b4c8795 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -20,7 +20,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
-import android.graphics.Bitmap;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -829,31 +828,6 @@
}
/**
- * Takes the screenshot of the specified display and returns it by bitmap format.
- *
- * @param connectionId The id of a connection for interacting with the system.
- * @param displayId The logic display id, use {@link Display#DEFAULT_DISPLAY} for
- * default display.
- * @return The screenshot bitmap on success, null otherwise.
- */
- public Bitmap takeScreenshot(int connectionId, int displayId) {
- Bitmap screenShot = null;
- try {
- IAccessibilityServiceConnection connection = getConnection(connectionId);
- if (connection != null) {
- screenShot = connection.takeScreenshot(displayId);
- } else {
- if (DEBUG) {
- Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
- }
- }
- } catch (RemoteException re) {
- Log.w(LOG_TAG, "Error while calling remote takeScreenshot", re);
- }
- return screenShot;
- }
-
- /**
* Clears the result state.
*/
private void clearResultLocked() {
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index 80027b1e..6246b50 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -323,6 +323,7 @@
private int mUserId = UserHandle.USER_NULL;
@NonNull
private Bundle mExtras;
+ private boolean mUseDefaultTextClassifier;
private Request(
@NonNull List<Message> conversation,
@@ -347,6 +348,8 @@
String callingPackageName = in.readString();
int userId = in.readInt();
Bundle extras = in.readBundle();
+ boolean useDefaultTextClassifier = in.readBoolean();
+
Request request = new Request(
conversation,
typeConfig,
@@ -355,6 +358,7 @@
extras);
request.setCallingPackageName(callingPackageName);
request.setUserId(userId);
+ request.setUseDefaultTextClassifier(useDefaultTextClassifier);
return request;
}
@@ -367,6 +371,7 @@
parcel.writeString(mCallingPackageName);
parcel.writeInt(mUserId);
parcel.writeBundle(mExtras);
+ parcel.writeBoolean(mUseDefaultTextClassifier);
}
@Override
@@ -455,6 +460,26 @@
}
/**
+ * Sets whether to use the default text classifier to handle this request.
+ * This will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
+ mUseDefaultTextClassifier = useDefaultTextClassifier;
+ }
+
+ /**
+ * Returns whether to use the default text classifier to handle this request. This
+ * will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ public boolean getUseDefaultTextClassifier() {
+ return mUseDefaultTextClassifier;
+ }
+
+ /**
* Returns the extended data related to this request.
*
* <p><b>NOTE: </b>Do not modify this bundle.
diff --git a/core/java/android/view/textclassifier/SelectionEvent.java b/core/java/android/view/textclassifier/SelectionEvent.java
index 09cb7a0..e0f29a9 100644
--- a/core/java/android/view/textclassifier/SelectionEvent.java
+++ b/core/java/android/view/textclassifier/SelectionEvent.java
@@ -140,6 +140,7 @@
private int mEnd;
private int mSmartStart;
private int mSmartEnd;
+ private boolean mUseDefaultTextClassifier;
SelectionEvent(
int start, int end,
@@ -175,6 +176,7 @@
mSmartStart = in.readInt();
mSmartEnd = in.readInt();
mUserId = in.readInt();
+ mUseDefaultTextClassifier = in.readBoolean();
}
@Override
@@ -204,6 +206,7 @@
dest.writeInt(mSmartStart);
dest.writeInt(mSmartEnd);
dest.writeInt(mUserId);
+ dest.writeBoolean(mUseDefaultTextClassifier);
}
@Override
@@ -428,6 +431,26 @@
}
/**
+ * Sets whether to use the default text classifier to handle this request.
+ * This will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
+ mUseDefaultTextClassifier = useDefaultTextClassifier;
+ }
+
+ /**
+ * Returns whether to use the default text classifier to handle this request. This
+ * will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ public boolean getUseDefaultTextClassifier() {
+ return mUseDefaultTextClassifier;
+ }
+
+ /**
* Returns the type of widget that was involved in triggering this event.
*/
@WidgetType
@@ -642,7 +665,8 @@
return Objects.hash(mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType,
mWidgetVersion, mPackageName, mUserId, mWidgetType, mInvocationMethod, mResultId,
mEventTime, mDurationSinceSessionStart, mDurationSincePreviousEvent,
- mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd);
+ mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd,
+ mUseDefaultTextClassifier);
}
@Override
@@ -673,7 +697,8 @@
&& mStart == other.mStart
&& mEnd == other.mEnd
&& mSmartStart == other.mSmartStart
- && mSmartEnd == other.mSmartEnd;
+ && mSmartEnd == other.mSmartEnd
+ && mUseDefaultTextClassifier == other.mUseDefaultTextClassifier;
}
@Override
@@ -683,12 +708,13 @@
+ "widgetVersion=%s, packageName=%s, widgetType=%s, invocationMethod=%s, "
+ "userId=%d, resultId=%s, eventTime=%d, durationSinceSessionStart=%d, "
+ "durationSincePreviousEvent=%d, eventIndex=%d,"
- + "sessionId=%s, start=%d, end=%d, smartStart=%d, smartEnd=%d}",
+ + "sessionId=%s, start=%d, end=%d, smartStart=%d, smartEnd=%d, "
+ + "mUseDefaultTextClassifier=%b}",
mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType,
mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod,
mUserId, mResultId, mEventTime, mDurationSinceSessionStart,
mDurationSincePreviousEvent, mEventIndex,
- mSessionId, mStart, mEnd, mSmartStart, mSmartEnd);
+ mSessionId, mStart, mEnd, mSmartStart, mSmartEnd, mUseDefaultTextClassifier);
}
public static final @android.annotation.NonNull Creator<SelectionEvent> CREATOR = new Creator<SelectionEvent>() {
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index 138d25d..fe5e8d6 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -55,17 +55,20 @@
// service will throw a remote exception.
@UserIdInt
private final int mUserId;
+ private final boolean mUseDefault;
private TextClassificationSessionId mSessionId;
- public SystemTextClassifier(Context context, TextClassificationConstants settings)
- throws ServiceManager.ServiceNotFoundException {
+ public SystemTextClassifier(
+ Context context,
+ TextClassificationConstants settings,
+ boolean useDefault) throws ServiceManager.ServiceNotFoundException {
mManagerService = ITextClassifierService.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.TEXT_CLASSIFICATION_SERVICE));
mSettings = Objects.requireNonNull(settings);
- mFallback = context.getSystemService(TextClassificationManager.class)
- .getTextClassifier(TextClassifier.LOCAL);
+ mFallback = TextClassifier.NO_OP;
mPackageName = Objects.requireNonNull(context.getOpPackageName());
mUserId = context.getUserId();
+ mUseDefault = useDefault;
}
/**
@@ -79,6 +82,7 @@
try {
request.setCallingPackageName(mPackageName);
request.setUserId(mUserId);
+ request.setUseDefaultTextClassifier(mUseDefault);
final BlockingCallback<TextSelection> callback =
new BlockingCallback<>("textselection");
mManagerService.onSuggestSelection(mSessionId, request, callback);
@@ -103,6 +107,7 @@
try {
request.setCallingPackageName(mPackageName);
request.setUserId(mUserId);
+ request.setUseDefaultTextClassifier(mUseDefault);
final BlockingCallback<TextClassification> callback =
new BlockingCallback<>("textclassification");
mManagerService.onClassifyText(mSessionId, request, callback);
@@ -124,7 +129,9 @@
public TextLinks generateLinks(@NonNull TextLinks.Request request) {
Objects.requireNonNull(request);
Utils.checkMainThread();
-
+ if (!Utils.checkTextLength(request.getText(), getMaxGenerateLinksTextLength())) {
+ return mFallback.generateLinks(request);
+ }
if (!mSettings.isSmartLinkifyEnabled() && request.isLegacyFallback()) {
return Utils.generateLegacyLinks(request);
}
@@ -132,6 +139,7 @@
try {
request.setCallingPackageName(mPackageName);
request.setUserId(mUserId);
+ request.setUseDefaultTextClassifier(mUseDefault);
final BlockingCallback<TextLinks> callback =
new BlockingCallback<>("textlinks");
mManagerService.onGenerateLinks(mSessionId, request, callback);
@@ -152,6 +160,7 @@
try {
event.setUserId(mUserId);
+ event.setUseDefaultTextClassifier(mUseDefault);
mManagerService.onSelectionEvent(mSessionId, event);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Error reporting selection event.", e);
@@ -169,6 +178,7 @@
.build()
: event.getEventContext();
tcContext.setUserId(mUserId);
+ tcContext.setUseDefaultTextClassifier(mUseDefault);
event.setEventContext(tcContext);
mManagerService.onTextClassifierEvent(mSessionId, event);
} catch (RemoteException e) {
@@ -184,6 +194,7 @@
try {
request.setCallingPackageName(mPackageName);
request.setUserId(mUserId);
+ request.setUseDefaultTextClassifier(mUseDefault);
final BlockingCallback<TextLanguage> callback =
new BlockingCallback<>("textlanguage");
mManagerService.onDetectLanguage(mSessionId, request, callback);
@@ -205,6 +216,7 @@
try {
request.setCallingPackageName(mPackageName);
request.setUserId(mUserId);
+ request.setUseDefaultTextClassifier(mUseDefault);
final BlockingCallback<ConversationActions> callback =
new BlockingCallback<>("conversation-actions");
mManagerService.onSuggestConversationActions(mSessionId, request, callback);
@@ -225,7 +237,7 @@
@WorkerThread
public int getMaxGenerateLinksTextLength() {
// TODO: retrieve this from the bound service.
- return mFallback.getMaxGenerateLinksTextLength();
+ return mSettings.getGenerateLinksMaxTextLength();
}
@Override
@@ -247,6 +259,7 @@
printWriter.printPair("mPackageName", mPackageName);
printWriter.printPair("mSessionId", mSessionId);
printWriter.printPair("mUserId", mUserId);
+ printWriter.printPair("mUseDefault", mUseDefault);
printWriter.decreaseIndent();
printWriter.println();
}
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 3628d2d4..00f762b 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -555,6 +555,7 @@
@Nullable private String mCallingPackageName;
@UserIdInt
private int mUserId = UserHandle.USER_NULL;
+ private boolean mUseDefaultTextClassifier;
private Request(
CharSequence text,
@@ -654,6 +655,26 @@
}
/**
+ * Sets whether to use the default text classifier to handle this request.
+ * This will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
+ mUseDefaultTextClassifier = useDefaultTextClassifier;
+ }
+
+ /**
+ * Returns whether to use the default text classifier to handle this request. This
+ * will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ public boolean getUseDefaultTextClassifier() {
+ return mUseDefaultTextClassifier;
+ }
+
+ /**
* Returns the extended data.
*
* <p><b>NOTE: </b>Do not modify this bundle.
@@ -755,6 +776,7 @@
dest.writeString(mCallingPackageName);
dest.writeInt(mUserId);
dest.writeBundle(mExtras);
+ dest.writeBoolean(mUseDefaultTextClassifier);
}
private static Request readFromParcel(Parcel in) {
@@ -768,11 +790,13 @@
final String callingPackageName = in.readString();
final int userId = in.readInt();
final Bundle extras = in.readBundle();
+ final boolean useDefaultTextClassifier = in.readBoolean();
final Request request = new Request(text, startIndex, endIndex,
defaultLocales, referenceTime, extras);
request.setCallingPackageName(callingPackageName);
request.setUserId(userId);
+ request.setUseDefaultTextClassifier(useDefaultTextClassifier);
return request;
}
diff --git a/core/java/android/view/textclassifier/TextClassificationContext.java b/core/java/android/view/textclassifier/TextClassificationContext.java
index 930765b..d58d175 100644
--- a/core/java/android/view/textclassifier/TextClassificationContext.java
+++ b/core/java/android/view/textclassifier/TextClassificationContext.java
@@ -38,6 +38,7 @@
@Nullable private final String mWidgetVersion;
@UserIdInt
private int mUserId = UserHandle.USER_NULL;
+ private boolean mUseDefaultTextClassifier;
private TextClassificationContext(
String packageName,
@@ -76,6 +77,26 @@
}
/**
+ * Sets whether to use the default text classifier to handle this request.
+ * This will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
+ mUseDefaultTextClassifier = useDefaultTextClassifier;
+ }
+
+ /**
+ * Returns whether to use the default text classifier to handle this request. This
+ * will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ public boolean getUseDefaultTextClassifier() {
+ return mUseDefaultTextClassifier;
+ }
+
+ /**
* Returns the widget type for this classification context.
*/
@NonNull
@@ -156,6 +177,7 @@
parcel.writeString(mWidgetType);
parcel.writeString(mWidgetVersion);
parcel.writeInt(mUserId);
+ parcel.writeBoolean(mUseDefaultTextClassifier);
}
private TextClassificationContext(Parcel in) {
@@ -163,6 +185,7 @@
mWidgetType = in.readString();
mWidgetVersion = in.readString();
mUserId = in.readInt();
+ mUseDefaultTextClassifier = in.readBoolean();
}
public static final @android.annotation.NonNull Parcelable.Creator<TextClassificationContext> CREATOR =
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index bb96d55..a6c83a1 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -25,7 +25,7 @@
import android.os.ServiceManager;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
-import android.service.textclassifier.TextClassifierService;
+import android.util.SparseArray;
import android.view.textclassifier.TextClassifier.TextClassifierType;
import com.android.internal.annotations.GuardedBy;
@@ -62,8 +62,7 @@
@Nullable
private TextClassifier mLocalTextClassifier;
@GuardedBy("mLock")
- @Nullable
- private TextClassifier mSystemTextClassifier;
+ private SparseArray<TextClassifier> mSystemTextClassifiers = new SparseArray<>();
@GuardedBy("mLock")
private TextClassificationSessionFactory mSessionFactory;
@GuardedBy("mLock")
@@ -91,8 +90,8 @@
synchronized (mLock) {
if (mCustomTextClassifier != null) {
return mCustomTextClassifier;
- } else if (isSystemTextClassifierEnabled()) {
- return getSystemTextClassifier();
+ } else if (getSettings().isSystemTextClassifierEnabled()) {
+ return getSystemTextClassifier(SystemTextClassifier.SYSTEM);
} else {
return getLocalTextClassifier();
}
@@ -116,6 +115,7 @@
*
* @see TextClassifier#LOCAL
* @see TextClassifier#SYSTEM
+ * @see TextClassifier#DEFAULT_SERVICE
* @hide
*/
@UnsupportedAppUsage
@@ -124,7 +124,7 @@
case TextClassifier.LOCAL:
return getLocalTextClassifier();
default:
- return getSystemTextClassifier();
+ return getSystemTextClassifier(type);
}
}
@@ -204,21 +204,28 @@
}
}
- private TextClassifier getSystemTextClassifier() {
+ /** @hide */
+ private TextClassifier getSystemTextClassifier(@TextClassifierType int type) {
synchronized (mLock) {
- if (mSystemTextClassifier == null && isSystemTextClassifierEnabled()) {
+ if (mSystemTextClassifiers.get(type) == null
+ && getSettings().isSystemTextClassifierEnabled()) {
try {
- mSystemTextClassifier = new SystemTextClassifier(mContext, getSettings());
- Log.d(LOG_TAG, "Initialized SystemTextClassifier");
+ mSystemTextClassifiers.put(
+ type,
+ new SystemTextClassifier(
+ mContext,
+ getSettings(),
+ /* useDefault= */ type == TextClassifier.DEFAULT_SERVICE));
+ Log.d(LOG_TAG, "Initialized SystemTextClassifier, type = " + type);
} catch (ServiceManager.ServiceNotFoundException e) {
Log.e(LOG_TAG, "Could not initialize SystemTextClassifier", e);
}
}
+ if (mSystemTextClassifiers.get(type) != null) {
+ return mSystemTextClassifiers.get(type);
+ }
+ return TextClassifier.NO_OP;
}
- if (mSystemTextClassifier != null) {
- return mSystemTextClassifier;
- }
- return TextClassifier.NO_OP;
}
/**
@@ -240,11 +247,6 @@
}
}
- private boolean isSystemTextClassifierEnabled() {
- return getSettings().isSystemTextClassifierEnabled()
- && TextClassifierService.getServiceComponentName(mContext) != null;
- }
-
/** @hide */
@VisibleForTesting
public void invalidateForTesting() {
@@ -261,7 +263,7 @@
private void invalidateTextClassifiers() {
synchronized (mLock) {
mLocalTextClassifier = null;
- mSystemTextClassifier = null;
+ mSystemTextClassifiers.clear();
}
}
@@ -274,7 +276,8 @@
/** @hide **/
public void dump(IndentingPrintWriter pw) {
getLocalTextClassifier().dump(pw);
- getSystemTextClassifier().dump(pw);
+ getSystemTextClassifier(TextClassifier.DEFAULT_SERVICE).dump(pw);
+ getSystemTextClassifier(TextClassifier.SYSTEM).dump(pw);
getSettings().dump(pw);
}
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 9b33693..2cc226d 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -66,12 +66,14 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {LOCAL, SYSTEM})
+ @IntDef(value = {LOCAL, SYSTEM, DEFAULT_SERVICE})
@interface TextClassifierType {} // TODO: Expose as system APIs.
/** Specifies a TextClassifier that runs locally in the app's process. @hide */
int LOCAL = 0;
/** Specifies a TextClassifier that runs in the system process and serves all apps. @hide */
int SYSTEM = 1;
+ /** Specifies the default TextClassifier that runs in the system process. @hide */
+ int DEFAULT_SERVICE = 2;
/** The TextClassifier failed to run. */
String TYPE_UNKNOWN = "";
@@ -667,8 +669,10 @@
Preconditions.checkArgument(endIndex > startIndex);
}
- static void checkTextLength(CharSequence text, int maxLength) {
- Preconditions.checkArgumentInRange(text.length(), 0, maxLength, "text.length()");
+ /** Returns if the length of the text is within the range. */
+ static boolean checkTextLength(CharSequence text, int maxLength) {
+ int textLength = text.length();
+ return textLength >= 0 && textLength <= maxLength;
}
/**
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 61bd7c7..d7149ee 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -286,8 +286,10 @@
@WorkerThread
public TextLinks generateLinks(@NonNull TextLinks.Request request) {
Objects.requireNonNull(request);
- Utils.checkTextLength(request.getText(), getMaxGenerateLinksTextLength());
Utils.checkMainThread();
+ if (!Utils.checkTextLength(request.getText(), getMaxGenerateLinksTextLength())) {
+ return mFallback.generateLinks(request);
+ }
if (!mSettings.isSmartLinkifyEnabled() && request.isLegacyFallback()) {
return Utils.generateLegacyLinks(request);
diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java
index cc9109e..58024dc 100644
--- a/core/java/android/view/textclassifier/TextLanguage.java
+++ b/core/java/android/view/textclassifier/TextLanguage.java
@@ -230,6 +230,7 @@
@Nullable private String mCallingPackageName;
@UserIdInt
private int mUserId = UserHandle.USER_NULL;
+ private boolean mUseDefaultTextClassifier;
private Request(CharSequence text, Bundle bundle) {
mText = text;
@@ -283,6 +284,26 @@
}
/**
+ * Sets whether to use the default text classifier to handle this request.
+ * This will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
+ mUseDefaultTextClassifier = useDefaultTextClassifier;
+ }
+
+ /**
+ * Returns whether to use the default text classifier to handle this request. This
+ * will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ public boolean getUseDefaultTextClassifier() {
+ return mUseDefaultTextClassifier;
+ }
+
+ /**
* Returns a bundle containing non-structured extra information about this request.
*
* <p><b>NOTE: </b>Do not modify this bundle.
@@ -303,6 +324,7 @@
dest.writeString(mCallingPackageName);
dest.writeInt(mUserId);
dest.writeBundle(mExtra);
+ dest.writeBoolean(mUseDefaultTextClassifier);
}
private static Request readFromParcel(Parcel in) {
@@ -310,10 +332,12 @@
final String callingPackageName = in.readString();
final int userId = in.readInt();
final Bundle extra = in.readBundle();
+ final boolean useDefaultTextClassifier = in.readBoolean();
final Request request = new Request(text, extra);
request.setCallingPackageName(callingPackageName);
request.setUserId(userId);
+ request.setUseDefaultTextClassifier(useDefaultTextClassifier);
return request;
}
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index bda12b0..7430cb3 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -345,6 +345,7 @@
@Nullable private final ZonedDateTime mReferenceTime;
@UserIdInt
private int mUserId = UserHandle.USER_NULL;
+ private boolean mUseDefaultTextClassifier;
private Request(
CharSequence text,
@@ -447,6 +448,26 @@
}
/**
+ * Sets whether to use the default text classifier to handle this request.
+ * This will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
+ mUseDefaultTextClassifier = useDefaultTextClassifier;
+ }
+
+ /**
+ * Returns whether to use the default text classifier to handle this request. This
+ * will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ public boolean getUseDefaultTextClassifier() {
+ return mUseDefaultTextClassifier;
+ }
+
+ /**
* Returns the extended data.
*
* <p><b>NOTE: </b>Do not modify this bundle.
@@ -568,6 +589,7 @@
dest.writeInt(mUserId);
dest.writeBundle(mExtras);
dest.writeString(mReferenceTime == null ? null : mReferenceTime.toString());
+ dest.writeBoolean(mUseDefaultTextClassifier);
}
private static Request readFromParcel(Parcel in) {
@@ -580,11 +602,13 @@
final String referenceTimeString = in.readString();
final ZonedDateTime referenceTime = referenceTimeString == null
? null : ZonedDateTime.parse(referenceTimeString);
+ final boolean useDefaultTextClassifier = in.readBoolean();
final Request request = new Request(text, defaultLocales, entityConfig,
/* legacyFallback= */ true, referenceTime, extras);
request.setCallingPackageName(callingPackageName);
request.setUserId(userId);
+ request.setUseDefaultTextClassifier(useDefaultTextClassifier);
return request;
}
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index 4a36cbf..575a072 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -216,6 +216,7 @@
@Nullable private String mCallingPackageName;
@UserIdInt
private int mUserId = UserHandle.USER_NULL;
+ private boolean mUseDefaultTextClassifier;
private Request(
CharSequence text,
@@ -316,6 +317,26 @@
}
/**
+ * Sets whether to use the default text classifier to handle this request.
+ * This will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
+ mUseDefaultTextClassifier = useDefaultTextClassifier;
+ }
+
+ /**
+ * Returns whether to use the default text classifier to handle this request. This
+ * will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ public boolean getUseDefaultTextClassifier() {
+ return mUseDefaultTextClassifier;
+ }
+
+ /**
* Returns the extended data.
*
* <p><b>NOTE: </b>Do not modify this bundle.
@@ -420,6 +441,7 @@
dest.writeString(mCallingPackageName);
dest.writeInt(mUserId);
dest.writeBundle(mExtras);
+ dest.writeBoolean(mUseDefaultTextClassifier);
}
private static Request readFromParcel(Parcel in) {
@@ -430,11 +452,13 @@
final String callingPackageName = in.readString();
final int userId = in.readInt();
final Bundle extras = in.readBundle();
+ final boolean systemTextClassifierType = in.readBoolean();
final Request request = new Request(text, startIndex, endIndex, defaultLocales,
/* darkLaunchAllowed= */ false, extras);
request.setCallingPackageName(callingPackageName);
request.setUserId(userId);
+ request.setUseDefaultTextClassifier(systemTextClassifierType);
return request;
}
diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
index 6a6a60d..82eb55a 100644
--- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
@@ -267,8 +267,7 @@
targets.addAll(getAccessibilityServiceTargets(context));
targets.addAll(getWhiteListingServiceTargets(context));
- final AccessibilityManager ams = (AccessibilityManager) context.getSystemService(
- Context.ACCESSIBILITY_SERVICE);
+ final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class);
final List<String> requiredTargets = ams.getAccessibilityShortcutTargets(shortcutType);
targets.removeIf(target -> !requiredTargets.contains(target.getId()));
@@ -277,8 +276,7 @@
private static List<AccessibilityButtonTarget> getAccessibilityServiceTargets(
@NonNull Context context) {
- final AccessibilityManager ams = (AccessibilityManager) context.getSystemService(
- Context.ACCESSIBILITY_SERVICE);
+ final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class);
final List<AccessibilityServiceInfo> installedServices =
ams.getInstalledAccessibilityServiceList();
if (installedServices == null) {
@@ -354,10 +352,11 @@
}
private static class ViewHolder {
+ View mItemView;
ImageView mIconView;
TextView mLabelView;
FrameLayout mItemContainer;
- ImageView mViewItem;
+ ImageView mActionViewItem;
Switch mSwitchItem;
}
@@ -407,12 +406,13 @@
R.layout.accessibility_button_chooser_item, parent, /* attachToRoot= */
false);
holder = new ViewHolder();
+ holder.mItemView = convertView;
holder.mIconView = convertView.findViewById(R.id.accessibility_button_target_icon);
holder.mLabelView = convertView.findViewById(
R.id.accessibility_button_target_label);
holder.mItemContainer = convertView.findViewById(
R.id.accessibility_button_target_item_container);
- holder.mViewItem = convertView.findViewById(
+ holder.mActionViewItem = convertView.findViewById(
R.id.accessibility_button_target_view_item);
holder.mSwitchItem = convertView.findViewById(
R.id.accessibility_button_target_switch_item);
@@ -465,11 +465,12 @@
holder.mIconView.setAlpha(enabledState
? ENABLED_ALPHA : DISABLED_ALPHA);
holder.mLabelView.setEnabled(enabledState);
- holder.mViewItem.setEnabled(enabledState);
- holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
- holder.mViewItem.setVisibility(View.VISIBLE);
+ holder.mActionViewItem.setEnabled(enabledState);
+ holder.mActionViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
+ holder.mActionViewItem.setVisibility(View.VISIBLE);
holder.mSwitchItem.setVisibility(View.GONE);
holder.mItemContainer.setVisibility(isLaunchMenuMode ? View.GONE : View.VISIBLE);
+ holder.mItemView.setEnabled(enabledState);
}
private void updateInvisibleActionItemVisibility(@NonNull Context context,
@@ -477,12 +478,13 @@
holder.mIconView.setColorFilter(null);
holder.mIconView.setAlpha(ENABLED_ALPHA);
holder.mLabelView.setEnabled(true);
- holder.mViewItem.setEnabled(true);
- holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
- holder.mViewItem.setVisibility(View.VISIBLE);
+ holder.mActionViewItem.setEnabled(true);
+ holder.mActionViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
+ holder.mActionViewItem.setVisibility(View.VISIBLE);
holder.mSwitchItem.setVisibility(View.GONE);
holder.mItemContainer.setVisibility((mShortcutMenuMode == ShortcutMenuMode.EDIT)
? View.VISIBLE : View.GONE);
+ holder.mItemView.setEnabled(true);
}
private void updateIntuitiveActionItemVisibility(@NonNull Context context,
@@ -495,12 +497,13 @@
holder.mIconView.setColorFilter(null);
holder.mIconView.setAlpha(ENABLED_ALPHA);
holder.mLabelView.setEnabled(true);
- holder.mViewItem.setEnabled(true);
- holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
- holder.mViewItem.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
+ holder.mActionViewItem.setEnabled(true);
+ holder.mActionViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
+ holder.mActionViewItem.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
holder.mSwitchItem.setVisibility(isEditMenuMode ? View.GONE : View.VISIBLE);
holder.mSwitchItem.setChecked(!isEditMenuMode && isServiceEnabled);
holder.mItemContainer.setVisibility(View.VISIBLE);
+ holder.mItemView.setEnabled(true);
}
private void updateBounceActionItemVisibility(@NonNull Context context,
@@ -508,12 +511,13 @@
holder.mIconView.setColorFilter(null);
holder.mIconView.setAlpha(ENABLED_ALPHA);
holder.mLabelView.setEnabled(true);
- holder.mViewItem.setEnabled(true);
- holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
- holder.mViewItem.setVisibility((mShortcutMenuMode == ShortcutMenuMode.EDIT)
+ holder.mActionViewItem.setEnabled(true);
+ holder.mActionViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
+ holder.mActionViewItem.setVisibility((mShortcutMenuMode == ShortcutMenuMode.EDIT)
? View.VISIBLE : View.GONE);
holder.mSwitchItem.setVisibility(View.GONE);
holder.mItemContainer.setVisibility(View.VISIBLE);
+ holder.mItemView.setEnabled(true);
}
}
@@ -559,8 +563,7 @@
private static boolean isAccessibilityServiceEnabled(@NonNull Context context,
AccessibilityButtonTarget target) {
- final AccessibilityManager ams = (AccessibilityManager) context.getSystemService(
- Context.ACCESSIBILITY_SERVICE);
+ final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class);
final List<AccessibilityServiceInfo> enabledServices =
ams.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
@@ -598,8 +601,7 @@
private void onLegacyTargetSelected(AccessibilityButtonTarget target) {
if (mShortcutType == ACCESSIBILITY_BUTTON) {
- final AccessibilityManager ams = (AccessibilityManager) getSystemService(
- Context.ACCESSIBILITY_SERVICE);
+ final AccessibilityManager ams = getSystemService(AccessibilityManager.class);
ams.notifyAccessibilityButtonClicked(getDisplayId(), target.getId());
} else if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
switchServiceState(target);
@@ -607,9 +609,12 @@
}
private void onInvisibleTargetSelected(AccessibilityButtonTarget target) {
- final AccessibilityManager ams = (AccessibilityManager) getSystemService(
- Context.ACCESSIBILITY_SERVICE);
- ams.notifyAccessibilityButtonClicked(getDisplayId(), target.getId());
+ final AccessibilityManager ams = getSystemService(AccessibilityManager.class);
+ if (mShortcutType == ACCESSIBILITY_BUTTON) {
+ ams.notifyAccessibilityButtonClicked(getDisplayId(), target.getId());
+ } else if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
+ ams.performAccessibilityShortcut(target.getId());
+ }
}
private void onIntuitiveTargetSelected(AccessibilityButtonTarget target) {
diff --git a/core/java/com/android/internal/app/BlockedAppActivity.java b/core/java/com/android/internal/app/BlockedAppActivity.java
new file mode 100644
index 0000000..fbdbbfb
--- /dev/null
+++ b/core/java/com/android/internal/app/BlockedAppActivity.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 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.internal.app;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.R;
+
+/**
+ * A dialog shown to the user when they try to launch an app that is not allowed in lock task
+ * mode. The intent to start this activity must be created with the static factory method provided
+ * below.
+ */
+public class BlockedAppActivity extends AlertActivity {
+
+ private static final String TAG = "BlockedAppActivity";
+ private static final String PACKAGE_NAME = "com.android.internal.app";
+ private static final String EXTRA_BLOCKED_PACKAGE = PACKAGE_NAME + ".extra.BLOCKED_PACKAGE";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Intent intent = getIntent();
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, /* defaultValue= */ -1);
+ if (userId < 0) {
+ Slog.wtf(TAG, "Invalid user: " + userId);
+ finish();
+ return;
+ }
+
+ String packageName = intent.getStringExtra(EXTRA_BLOCKED_PACKAGE);
+ if (TextUtils.isEmpty(packageName)) {
+ Slog.wtf(TAG, "Invalid package: " + packageName);
+ finish();
+ return;
+ }
+
+ CharSequence appLabel = getAppLabel(userId, packageName);
+
+ mAlertParams.mTitle = getString(R.string.app_blocked_title);
+ mAlertParams.mMessage = getString(R.string.app_blocked_message, appLabel);
+ mAlertParams.mPositiveButtonText = getString(android.R.string.ok);
+ setupAlert();
+ }
+
+ private CharSequence getAppLabel(int userId, String packageName) {
+ PackageManager pm = getPackageManager();
+ try {
+ ApplicationInfo aInfo =
+ pm.getApplicationInfoAsUser(packageName, /* flags= */ 0, userId);
+ return aInfo.loadLabel(pm);
+ } catch (PackageManager.NameNotFoundException ne) {
+ Slog.e(TAG, "Package " + packageName + " not found", ne);
+ }
+ return packageName;
+ }
+
+
+ /** Creates an intent that launches {@link BlockedAppActivity}. */
+ public static Intent createIntent(int userId, String packageName) {
+ return new Intent()
+ .setClassName("android", BlockedAppActivity.class.getName())
+ .putExtra(Intent.EXTRA_USER_ID, userId)
+ .putExtra(EXTRA_BLOCKED_PACKAGE, packageName);
+ }
+}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index cec68df..9758673 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -35,8 +35,13 @@
"android_animation_PropertyValuesHolder.cpp",
"android_os_SystemClock.cpp",
"android_os_SystemProperties.cpp",
+ "android_os_Trace.cpp",
+ "android_text_AndroidCharacter.cpp",
"android_util_EventLog.cpp",
"android_util_Log.cpp",
+ "android_util_StringBlock.cpp",
+ "android_util_XmlBlock.cpp",
+ "android_view_RenderNodeAnimator.cpp",
"com_android_internal_util_VirtualRefBasePtr.cpp",
"com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp",
],
@@ -114,14 +119,12 @@
"android_view_KeyEvent.cpp",
"android_view_MotionEvent.cpp",
"android_view_PointerIcon.cpp",
- "android_view_RenderNodeAnimator.cpp",
"android_view_Surface.cpp",
"android_view_SurfaceControl.cpp",
"android_graphics_BLASTBufferQueue.cpp",
"android_view_SurfaceSession.cpp",
"android_view_TextureView.cpp",
"android_view_VelocityTracker.cpp",
- "android_text_AndroidCharacter.cpp",
"android_text_Hyphenator.cpp",
"android_os_Debug.cpp",
"android_os_GraphicsEnvironment.cpp",
@@ -138,7 +141,6 @@
"android_os_SELinux.cpp",
"android_os_SharedMemory.cpp",
"android_os_storage_StorageManager.cpp",
- "android_os_Trace.cpp",
"android_os_UEventObserver.cpp",
"android_os_VintfObject.cpp",
"android_os_VintfRuntimeInfo.cpp",
@@ -150,8 +152,6 @@
"android_util_Binder.cpp",
"android_util_MemoryIntArray.cpp",
"android_util_Process.cpp",
- "android_util_StringBlock.cpp",
- "android_util_XmlBlock.cpp",
"android_util_jar_StrictJarFile.cpp",
"android_media_AudioDeviceAddress.cpp",
"android_media_AudioEffectDescriptor.cpp",
@@ -311,11 +311,8 @@
srcs: [
"android_content_res_ApkAssets.cpp",
"android_os_MessageQueue.cpp",
- "android_os_Trace.cpp",
"android_util_AssetManager.cpp",
"android_util_FileObserver.cpp",
- "android_util_StringBlock.cpp",
- "android_util_XmlBlock.cpp",
],
},
},
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 481be24..657336e 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -634,8 +634,6 @@
char cachePruneBuf[sizeof("-Xzygote-max-boot-retry=")-1 + PROPERTY_VALUE_MAX];
char dex2oatXmsImageFlagsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];
char dex2oatXmxImageFlagsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
- char dex2oatXmsFlagsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];
- char dex2oatXmxFlagsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
char dex2oatCompilerFilterBuf[sizeof("--compiler-filter=")-1 + PROPERTY_VALUE_MAX];
char dex2oatImageCompilerFilterBuf[sizeof("--compiler-filter=")-1 + PROPERTY_VALUE_MAX];
char dex2oatThreadsBuf[sizeof("-j")-1 + PROPERTY_VALUE_MAX];
@@ -885,88 +883,45 @@
bool skip_compilation = ((strcmp(voldDecryptBuf, "trigger_restart_min_framework") == 0) ||
(strcmp(voldDecryptBuf, "1") == 0));
- // Extra options for boot.art/boot.oat image generation.
- parseCompilerRuntimeOption("dalvik.vm.image-dex2oat-Xms", dex2oatXmsImageFlagsBuf,
- "-Xms", "-Ximage-compiler-option");
- parseCompilerRuntimeOption("dalvik.vm.image-dex2oat-Xmx", dex2oatXmxImageFlagsBuf,
- "-Xmx", "-Ximage-compiler-option");
- if (skip_compilation) {
- addOption("-Ximage-compiler-option");
- addOption("--compiler-filter=assume-verified");
- } else {
- parseCompilerOption("dalvik.vm.image-dex2oat-filter", dex2oatImageCompilerFilterBuf,
- "--compiler-filter=", "-Ximage-compiler-option");
- }
-
- // If there is a boot profile, it takes precedence over the image and preloaded classes.
- if (hasFile("/system/etc/boot-image.prof")) {
- addOption("-Ximage-compiler-option");
- addOption("--profile-file=/system/etc/boot-image.prof");
- addOption("-Ximage-compiler-option");
- addOption("--compiler-filter=speed-profile");
- } else {
- ALOGE("Missing boot-image.prof file, /system/etc/boot-image.prof not found: %s\n",
- strerror(errno));
- return -1;
- }
-
-
- // If there is a dirty-image-objects file, push it.
- if (hasFile("/system/etc/dirty-image-objects")) {
- addOption("-Ximage-compiler-option");
- addOption("--dirty-image-objects=/system/etc/dirty-image-objects");
- }
-
- property_get("dalvik.vm.image-dex2oat-flags", dex2oatImageFlagsBuf, "");
- parseExtraOpts(dex2oatImageFlagsBuf, "-Ximage-compiler-option");
-
- // Extra options for DexClassLoader.
- parseCompilerRuntimeOption("dalvik.vm.dex2oat-Xms", dex2oatXmsFlagsBuf,
- "-Xms", "-Xcompiler-option");
- parseCompilerRuntimeOption("dalvik.vm.dex2oat-Xmx", dex2oatXmxFlagsBuf,
- "-Xmx", "-Xcompiler-option");
+ // Extra options for JIT.
if (skip_compilation) {
addOption("-Xcompiler-option");
addOption("--compiler-filter=assume-verified");
-
- // We skip compilation when a minimal runtime is brought up for decryption. In that case
- // /data is temporarily backed by a tmpfs, which is usually small.
- // If the system image contains prebuilts, they will be relocated into the tmpfs. In this
- // specific situation it is acceptable to *not* relocate and run out of the prebuilts
- // directly instead.
- addOption("--runtime-arg");
- addOption("-Xnorelocate");
} else {
parseCompilerOption("dalvik.vm.dex2oat-filter", dex2oatCompilerFilterBuf,
"--compiler-filter=", "-Xcompiler-option");
}
parseCompilerOption("dalvik.vm.dex2oat-threads", dex2oatThreadsBuf, "-j", "-Xcompiler-option");
- parseCompilerOption("dalvik.vm.image-dex2oat-threads", dex2oatThreadsImageBuf, "-j",
- "-Ximage-compiler-option");
parseCompilerOption("dalvik.vm.dex2oat-cpu-set", dex2oatCpuSetBuf, "--cpu-set=",
"-Xcompiler-option");
- parseCompilerOption("dalvik.vm.image-dex2oat-cpu-set", dex2oatCpuSetImageBuf, "--cpu-set=",
- "-Ximage-compiler-option");
-
- // The runtime will compile a boot image, when necessary, not using installd. Thus, we need to
- // pass the instruction-set-features/variant as an image-compiler-option.
- // Note: it is OK to reuse the buffer, as the values are exactly the same between
- // * compiler-option, used for runtime compilation (DexClassLoader)
- // * image-compiler-option, used for boot-image compilation on device
// Copy the variant.
sprintf(dex2oat_isa_variant_key, "dalvik.vm.isa.%s.variant", ABI_STRING);
parseCompilerOption(dex2oat_isa_variant_key, dex2oat_isa_variant,
- "--instruction-set-variant=", "-Ximage-compiler-option");
- parseCompilerOption(dex2oat_isa_variant_key, dex2oat_isa_variant,
"--instruction-set-variant=", "-Xcompiler-option");
// Copy the features.
sprintf(dex2oat_isa_features_key, "dalvik.vm.isa.%s.features", ABI_STRING);
parseCompilerOption(dex2oat_isa_features_key, dex2oat_isa_features,
- "--instruction-set-features=", "-Ximage-compiler-option");
- parseCompilerOption(dex2oat_isa_features_key, dex2oat_isa_features,
"--instruction-set-features=", "-Xcompiler-option");
+ /*
+ * When running with debug.generate-debug-info, add --generate-debug-info to
+ * the compiler options so that both JITted code and the boot image extension,
+ * if it is compiled on device, will include native debugging information.
+ */
+ property_get("debug.generate-debug-info", propBuf, "");
+ bool generate_debug_info = (strcmp(propBuf, "true") == 0);
+ if (generate_debug_info) {
+ addOption("-Xcompiler-option");
+ addOption("--generate-debug-info");
+ }
+
+ // The mini-debug-info makes it possible to backtrace through compiled code.
+ bool generate_mini_debug_info = property_get_bool("dalvik.vm.minidebuginfo", 0);
+ if (generate_mini_debug_info) {
+ addOption("-Xcompiler-option");
+ addOption("--generate-mini-debug-info");
+ }
property_get("dalvik.vm.dex2oat-flags", dex2oatFlagsBuf, "");
parseExtraOpts(dex2oatFlagsBuf, "-Xcompiler-option");
@@ -975,6 +930,53 @@
property_get("dalvik.vm.extra-opts", extraOptsBuf, "");
parseExtraOpts(extraOptsBuf, NULL);
+ // Extra options for boot image extension generation.
+ if (skip_compilation) {
+ addOption("-Xnoimage-dex2oat");
+ } else {
+ parseCompilerRuntimeOption("dalvik.vm.image-dex2oat-Xms", dex2oatXmsImageFlagsBuf,
+ "-Xms", "-Ximage-compiler-option");
+ parseCompilerRuntimeOption("dalvik.vm.image-dex2oat-Xmx", dex2oatXmxImageFlagsBuf,
+ "-Xmx", "-Ximage-compiler-option");
+
+ parseCompilerOption("dalvik.vm.image-dex2oat-filter", dex2oatImageCompilerFilterBuf,
+ "--compiler-filter=", "-Ximage-compiler-option");
+
+ // If there is a dirty-image-objects file, push it.
+ if (hasFile("/system/etc/dirty-image-objects")) {
+ addOption("-Ximage-compiler-option");
+ addOption("--dirty-image-objects=/system/etc/dirty-image-objects");
+ }
+
+ parseCompilerOption("dalvik.vm.image-dex2oat-threads", dex2oatThreadsImageBuf, "-j",
+ "-Ximage-compiler-option");
+ parseCompilerOption("dalvik.vm.image-dex2oat-cpu-set", dex2oatCpuSetImageBuf, "--cpu-set=",
+ "-Ximage-compiler-option");
+
+ // The runtime may compile a boot image extension, when necessary, not using installd.
+ // Thus, we need to pass the instruction-set-features/variant as an image-compiler-option.
+ // Note: it is OK to reuse the buffer, as the values are exactly the same between
+ // * compiler-option, used for runtime compilation (DexClassLoader)
+ // * image-compiler-option, used for boot-image compilation on device
+ parseCompilerOption(dex2oat_isa_variant_key, dex2oat_isa_variant,
+ "--instruction-set-variant=", "-Ximage-compiler-option");
+ parseCompilerOption(dex2oat_isa_features_key, dex2oat_isa_features,
+ "--instruction-set-features=", "-Ximage-compiler-option");
+
+ if (generate_debug_info) {
+ addOption("-Ximage-compiler-option");
+ addOption("--generate-debug-info");
+ }
+
+ if (generate_mini_debug_info) {
+ addOption("-Ximage-compiler-option");
+ addOption("--generate-mini-debug-info");
+ }
+
+ property_get("dalvik.vm.image-dex2oat-flags", dex2oatImageFlagsBuf, "");
+ parseExtraOpts(dex2oatImageFlagsBuf, "-Ximage-compiler-option");
+ }
+
/* Set the properties for locale */
{
strcpy(localeOption, "-Duser.locale=");
@@ -1032,25 +1034,6 @@
parseRuntimeOption("dalvik.vm.zygote.max-boot-retry", cachePruneBuf,
"-Xzygote-max-boot-retry=");
- /*
- * When running with debug.generate-debug-info, add --generate-debug-info to
- * the compiler options so that the boot image, if it is compiled on device,
- * will include native debugging information.
- */
- property_get("debug.generate-debug-info", propBuf, "");
- if (strcmp(propBuf, "true") == 0) {
- addOption("-Xcompiler-option");
- addOption("--generate-debug-info");
- addOption("-Ximage-compiler-option");
- addOption("--generate-debug-info");
- }
-
- // The mini-debug-info makes it possible to backtrace through JIT code.
- if (property_get_bool("dalvik.vm.minidebuginfo", 0)) {
- addOption("-Xcompiler-option");
- addOption("--generate-mini-debug-info");
- }
-
// If set, the property below can be used to enable core platform API violation reporting.
property_get("persist.debug.dalvik.vm.core_platform_api_policy", propBuf, "");
if (propBuf[0] != '\0') {
diff --git a/core/jni/LayoutlibLoader.cpp b/core/jni/LayoutlibLoader.cpp
index 6c0680f..571a3387 100644
--- a/core/jni/LayoutlibLoader.cpp
+++ b/core/jni/LayoutlibLoader.cpp
@@ -75,10 +75,12 @@
extern int register_android_os_SystemClock(JNIEnv* env);
extern int register_android_os_SystemProperties(JNIEnv* env);
extern int register_android_os_Trace(JNIEnv* env);
+extern int register_android_text_AndroidCharacter(JNIEnv* env);
extern int register_android_util_EventLog(JNIEnv* env);
extern int register_android_util_Log(JNIEnv* env);
extern int register_android_util_PathParser(JNIEnv* env);
extern int register_android_view_RenderNode(JNIEnv* env);
+extern int register_android_view_RenderNodeAnimator(JNIEnv* env);
extern int register_android_view_DisplayListCanvas(JNIEnv* env);
extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
extern int register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper(JNIEnv *env);
@@ -90,58 +92,66 @@
// Map of all possible class names to register to their corresponding JNI registration function pointer
// The actual list of registered classes will be determined at runtime via the 'native_classes' System property
-static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = {
- {"android.animation.PropertyValuesHolder", REG_JNI(register_android_animation_PropertyValuesHolder)},
+static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = {
+ {"android.animation.PropertyValuesHolder",
+ REG_JNI(register_android_animation_PropertyValuesHolder)},
#ifdef __linux__
- {"android.content.AssetManager", REG_JNI(register_android_content_AssetManager)},
- {"android.content.StringBlock", REG_JNI(register_android_content_StringBlock)},
- {"android.content.XmlBlock", REG_JNI(register_android_content_XmlBlock)},
- {"android.content.res.ApkAssets", REG_JNI(register_android_content_res_ApkAssets)},
+ {"android.content.res.ApkAssets", REG_JNI(register_android_content_res_ApkAssets)},
+ {"android.content.res.AssetManager", REG_JNI(register_android_content_AssetManager)},
#endif
- {"android.graphics.Bitmap", REG_JNI(register_android_graphics_Bitmap)},
- {"android.graphics.BitmapFactory", REG_JNI(register_android_graphics_BitmapFactory)},
- {"android.graphics.ByteBufferStreamAdaptor", REG_JNI(register_android_graphics_ByteBufferStreamAdaptor)},
- {"android.graphics.Canvas", REG_JNI(register_android_graphics_Canvas)},
- {"android.graphics.RenderNode", REG_JNI(register_android_view_RenderNode)},
- {"android.graphics.ColorFilter", REG_JNI(register_android_graphics_ColorFilter)},
- {"android.graphics.ColorSpace", REG_JNI(register_android_graphics_ColorSpace)},
- {"android.graphics.CreateJavaOutputStreamAdaptor", REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor)},
- {"android.graphics.DrawFilter", REG_JNI(register_android_graphics_DrawFilter)},
- {"android.graphics.FontFamily", REG_JNI(register_android_graphics_FontFamily)},
- {"android.graphics.Graphics", REG_JNI(register_android_graphics_Graphics)},
- {"android.graphics.ImageDecoder", REG_JNI(register_android_graphics_ImageDecoder)},
- {"android.graphics.MaskFilter", REG_JNI(register_android_graphics_MaskFilter)},
- {"android.graphics.Matrix", REG_JNI(register_android_graphics_Matrix)},
- {"android.graphics.NinePatch", REG_JNI(register_android_graphics_NinePatch)},
- {"android.graphics.Paint", REG_JNI(register_android_graphics_Paint)},
- {"android.graphics.Path", REG_JNI(register_android_graphics_Path)},
- {"android.graphics.PathEffect", REG_JNI(register_android_graphics_PathEffect)},
- {"android.graphics.PathMeasure", REG_JNI(register_android_graphics_PathMeasure)},
- {"android.graphics.Picture", REG_JNI(register_android_graphics_Picture)},
- {"android.graphics.RecordingCanvas", REG_JNI(register_android_view_DisplayListCanvas)},
- {"android.graphics.Region", REG_JNI(register_android_graphics_Region)},
- {"android.graphics.Shader", REG_JNI(register_android_graphics_Shader)},
- {"android.graphics.Typeface", REG_JNI(register_android_graphics_Typeface)},
- {"android.graphics.drawable.AnimatedVectorDrawable", REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable)},
- {"android.graphics.drawable.VectorDrawable", REG_JNI(register_android_graphics_drawable_VectorDrawable)},
- {"android.graphics.fonts.Font", REG_JNI(register_android_graphics_fonts_Font)},
- {"android.graphics.fonts.FontFamily", REG_JNI(register_android_graphics_fonts_FontFamily)},
- {"android.graphics.text.LineBreaker", REG_JNI(register_android_graphics_text_LineBreaker)},
- {"android.graphics.text.MeasuredText", REG_JNI(register_android_graphics_text_MeasuredText)},
+ {"android.content.res.StringBlock", REG_JNI(register_android_content_StringBlock)},
+ {"android.content.res.XmlBlock", REG_JNI(register_android_content_XmlBlock)},
+ {"android.graphics.Bitmap", REG_JNI(register_android_graphics_Bitmap)},
+ {"android.graphics.BitmapFactory", REG_JNI(register_android_graphics_BitmapFactory)},
+ {"android.graphics.ByteBufferStreamAdaptor",
+ REG_JNI(register_android_graphics_ByteBufferStreamAdaptor)},
+ {"android.graphics.Canvas", REG_JNI(register_android_graphics_Canvas)},
+ {"android.graphics.RenderNode", REG_JNI(register_android_view_RenderNode)},
+ {"android.graphics.ColorFilter", REG_JNI(register_android_graphics_ColorFilter)},
+ {"android.graphics.ColorSpace", REG_JNI(register_android_graphics_ColorSpace)},
+ {"android.graphics.CreateJavaOutputStreamAdaptor",
+ REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor)},
+ {"android.graphics.DrawFilter", REG_JNI(register_android_graphics_DrawFilter)},
+ {"android.graphics.FontFamily", REG_JNI(register_android_graphics_FontFamily)},
+ {"android.graphics.Graphics", REG_JNI(register_android_graphics_Graphics)},
+ {"android.graphics.ImageDecoder", REG_JNI(register_android_graphics_ImageDecoder)},
+ {"android.graphics.MaskFilter", REG_JNI(register_android_graphics_MaskFilter)},
+ {"android.graphics.Matrix", REG_JNI(register_android_graphics_Matrix)},
+ {"android.graphics.NinePatch", REG_JNI(register_android_graphics_NinePatch)},
+ {"android.graphics.Paint", REG_JNI(register_android_graphics_Paint)},
+ {"android.graphics.Path", REG_JNI(register_android_graphics_Path)},
+ {"android.graphics.PathEffect", REG_JNI(register_android_graphics_PathEffect)},
+ {"android.graphics.PathMeasure", REG_JNI(register_android_graphics_PathMeasure)},
+ {"android.graphics.Picture", REG_JNI(register_android_graphics_Picture)},
+ {"android.graphics.RecordingCanvas", REG_JNI(register_android_view_DisplayListCanvas)},
+ {"android.graphics.Region", REG_JNI(register_android_graphics_Region)},
+ {"android.graphics.Shader", REG_JNI(register_android_graphics_Shader)},
+ {"android.graphics.Typeface", REG_JNI(register_android_graphics_Typeface)},
+ {"android.graphics.drawable.AnimatedVectorDrawable",
+ REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable)},
+ {"android.graphics.drawable.VectorDrawable",
+ REG_JNI(register_android_graphics_drawable_VectorDrawable)},
+ {"android.graphics.fonts.Font", REG_JNI(register_android_graphics_fonts_Font)},
+ {"android.graphics.fonts.FontFamily", REG_JNI(register_android_graphics_fonts_FontFamily)},
+ {"android.graphics.text.LineBreaker", REG_JNI(register_android_graphics_text_LineBreaker)},
+ {"android.graphics.text.MeasuredText",
+ REG_JNI(register_android_graphics_text_MeasuredText)},
#ifdef __linux__
- {"android.os.FileObserver", REG_JNI(register_android_os_FileObserver)},
- {"android.os.MessageQueue", REG_JNI(register_android_os_MessageQueue)},
+ {"android.os.FileObserver", REG_JNI(register_android_os_FileObserver)},
+ {"android.os.MessageQueue", REG_JNI(register_android_os_MessageQueue)},
#endif
- {"android.os.SystemClock", REG_JNI(register_android_os_SystemClock)},
- {"android.os.SystemProperties", REG_JNI(register_android_os_SystemProperties)},
-#ifdef __linux__
- {"android.os.Trace", REG_JNI(register_android_os_Trace)},
-#endif
- {"android.util.EventLog", REG_JNI(register_android_util_EventLog)},
- {"android.util.Log", REG_JNI(register_android_util_Log)},
- {"android.util.PathParser", REG_JNI(register_android_util_PathParser)},
- {"com.android.internal.util.VirtualRefBasePtr", REG_JNI(register_com_android_internal_util_VirtualRefBasePtr)},
- {"com.android.internal.view.animation.NativeInterpolatorFactoryHelper", REG_JNI(register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper)},
+ {"android.os.SystemClock", REG_JNI(register_android_os_SystemClock)},
+ {"android.os.SystemProperties", REG_JNI(register_android_os_SystemProperties)},
+ {"android.os.Trace", REG_JNI(register_android_os_Trace)},
+ {"android.text.AndroidCharacter", REG_JNI(register_android_text_AndroidCharacter)},
+ {"android.util.EventLog", REG_JNI(register_android_util_EventLog)},
+ {"android.util.Log", REG_JNI(register_android_util_Log)},
+ {"android.util.PathParser", REG_JNI(register_android_util_PathParser)},
+ {"android.view.RenderNodeAnimator", REG_JNI(register_android_view_RenderNodeAnimator)},
+ {"com.android.internal.util.VirtualRefBasePtr",
+ REG_JNI(register_com_android_internal_util_VirtualRefBasePtr)},
+ {"com.android.internal.view.animation.NativeInterpolatorFactoryHelper",
+ REG_JNI(register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper)},
};
// Vector to store the names of classes that need delegates of their native methods
static vector<string> classesToDelegate;
@@ -159,7 +169,6 @@
int AndroidRuntime::registerNativeMethods(JNIEnv* env,
const char* className, const JNINativeMethod* gMethods, int numMethods) {
-
string classNameString = string(className);
if (find(classesToDelegate.begin(), classesToDelegate.end(), classNameString)
!= classesToDelegate.end()) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 753d64a..7a30256 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2603,7 +2603,7 @@
<!-- Allows telephony to suggest the time / time zone.
<p>Not for use by third-party applications.
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @hide
+ @hide
-->
<permission android:name="android.permission.SUGGEST_PHONE_TIME_AND_ZONE"
android:protectionLevel="signature|telephony" />
@@ -5075,6 +5075,12 @@
android:process=":ui">
</activity>
+ <activity android:name="com.android.internal.app.BlockedAppActivity"
+ android:theme="@style/Theme.Dialog.Confirmation"
+ android:excludeFromRecents="true"
+ android:process=":ui">
+ </activity>
+
<activity android:name="com.android.settings.notification.NotificationAccessConfirmationActivity"
android:theme="@style/Theme.Dialog.Confirmation"
android:excludeFromRecents="true">
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3d7b1e1..b2d08a9 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3560,25 +3560,16 @@
-->
<string name="config_defaultAutofillService" translatable="false"></string>
- <!-- The package name for the default system textclassifier service.
+ <!-- The package name for the OEM custom system textclassifier service.
This service must be trusted, as it can be activated without explicit consent of the user.
Example: "com.android.textclassifier"
- If no textclassifier service with the specified name exists on the device (or if this is
- set to empty string), a default textclassifier will be loaded in the calling app's process.
+ If this is empty, the default textclassifier service (i.e. config_servicesExtensionPackage)
+ will be used.
+
See android.view.textclassifier.TextClassificationManager.
-->
- <!-- TODO(b/144896755) remove the config -->
<string name="config_defaultTextClassifierPackage" translatable="false"></string>
- <!-- A list of supported system textClassifier service package names. Only one of the packages
- will be activated. The first package in the list is the default system textClassifier
- service. OS only tries to bind and grant permissions to the first trusted service and the
- others can be selected via device config. These services must be trusted, as they can be
- activated without explicit consent of the user. Example: "com.android.textclassifier"
- -->
- <string-array name="config_defaultTextClassifierPackages" translatable="false">
- <item>android.ext.services</item>
- </string-array>
<!-- The package name for the system companion device manager service.
This service must be trusted, as it can be activated without explicit consent of the user.
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 11cc365..a54566c 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4936,6 +4936,13 @@
<!-- Title for button to turn on work profile. [CHAR LIMIT=NONE] -->
<string name="work_mode_turn_on">Turn on</string>
+ <!-- Title of the dialog that is shown when the user tries to launch a blocked application [CHAR LIMIT=50] -->
+ <string name="app_blocked_title">App is not available</string>
+ <!-- Default message shown in the dialog that is shown when the user tries to launch a blocked application [CHAR LIMIT=NONE] -->
+ <string name="app_blocked_message">
+ <xliff:g id="app_name" example="Gmail">%1$s</xliff:g> is not available right now.
+ </string>
+
<!-- Message displayed in dialog when app is too old to run on this verison of android. [CHAR LIMIT=NONE] -->
<string name="deprecated_target_sdk_message">This app was built for an older version of Android and may not work properly. Try checking for updates, or contact the developer.</string>
<!-- Title for button to see application detail in app store which it came from - it may allow user to update to newer version. [CHAR LIMIT=50] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 36296a8..c59d25f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3048,6 +3048,9 @@
<java-symbol type="string" name="app_suspended_unsuspend_message" />
<java-symbol type="string" name="app_suspended_default_message" />
+ <java-symbol type="string" name="app_blocked_title" />
+ <java-symbol type="string" name="app_blocked_message" />
+
<!-- Used internally for assistant to launch activity transitions -->
<java-symbol type="id" name="cross_task_transition" />
@@ -3390,7 +3393,6 @@
<java-symbol type="string" name="notification_channel_do_not_disturb" />
<java-symbol type="string" name="config_defaultAutofillService" />
<java-symbol type="string" name="config_defaultTextClassifierPackage" />
- <java-symbol type="array" name="config_defaultTextClassifierPackages" />
<java-symbol type="string" name="config_defaultWellbeingPackage" />
<java-symbol type="string" name="config_telephonyPackages" />
<java-symbol type="string" name="config_defaultContentCaptureService" />
diff --git a/core/res/res/xml/default_zen_mode_config.xml b/core/res/res/xml/default_zen_mode_config.xml
index 6cf6a82..9110661 100644
--- a/core/res/res/xml/default_zen_mode_config.xml
+++ b/core/res/res/xml/default_zen_mode_config.xml
@@ -18,9 +18,10 @@
-->
<!-- Default configuration for zen mode. See android.service.notification.ZenModeConfig. -->
-<zen version="8">
+<zen version="9">
<allow alarms="true" media="true" system="false" calls="true" callsFrom="2" messages="false"
- reminders="false" events="false" repeatCallers="true" />
+ reminders="false" events="false" repeatCallers="true" conversations="true"
+ conversationsFrom="2"/>
<automatic ruleId="EVENTS_DEFAULT_RULE" enabled="false" snoozing="false" name="Event" zen="1"
component="android/com.android.server.notification.EventConditionProvider"
conditionId="condition://android/event?userId=-10000&calendar=&reply=1"/>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 2d1c61c..0d7ac08 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -39,6 +39,9 @@
<!-- Albania: 5 digits, known short codes listed -->
<shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
+ <!-- Argentia: 5 digits, known short codes listed -->
+ <shortcode country="ar" pattern="\\d{5}" free="11711|28291" />
+
<!-- Armenia: 3-4 digits, emergency numbers 10[123] -->
<shortcode country="am" pattern="\\d{3,4}" premium="11[2456]1|3024" free="10[123]" />
@@ -80,7 +83,7 @@
<shortcode country="cn" premium="1066.*" free="1065.*" />
<!-- Colombia: 1-6 digits (not confirmed) -->
- <shortcode country="co" pattern="\\d{1,6}" free="890350|908160" />
+ <shortcode country="co" pattern="\\d{1,6}" free="890350|908160|892255|898002|898880|899960" />
<!-- Cyprus: 4-6 digits (not confirmed), known premium codes listed, plus EU -->
<shortcode country="cy" pattern="\\d{4,6}" premium="7510" free="116\\d{3}" />
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 8faf790..1ca4649 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -25,7 +25,6 @@
import android.content.Context;
import android.content.Intent;
import android.os.LocaleList;
-import android.service.textclassifier.TextClassifierService;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -64,9 +63,7 @@
@Test
public void testGetSystemTextClassifier() {
- assertTrue(
- TextClassifierService.getServiceComponentName(mContext) == null
- || mTcm.getTextClassifier(TextClassifier.SYSTEM) instanceof SystemTextClassifier);
+ assertTrue(mTcm.getTextClassifier(TextClassifier.SYSTEM) instanceof SystemTextClassifier);
}
@Test
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index 2304ba6..372a478 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -27,6 +27,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.LocaleList;
+import android.service.textclassifier.TextClassifierService;
import android.text.Spannable;
import android.text.SpannableString;
@@ -58,11 +59,12 @@
public class TextClassifierTest {
private static final String LOCAL = "local";
private static final String SESSION = "session";
+ private static final String DEFAULT = "default";
// TODO: Add SYSTEM, which tests TextClassifier.SYSTEM.
@Parameterized.Parameters(name = "{0}")
public static Iterable<Object> textClassifierTypes() {
- return Arrays.asList(LOCAL, SESSION);
+ return Arrays.asList(LOCAL, SESSION, DEFAULT);
}
@Parameterized.Parameter
@@ -84,13 +86,15 @@
if (mTextClassifierType.equals(LOCAL)) {
mClassifier = mTcm.getTextClassifier(TextClassifier.LOCAL);
- } else {
+ } else if (mTextClassifierType.equals(SESSION)) {
mClassifier = mTcm.createTextClassificationSession(
new TextClassificationContext.Builder(
"android",
TextClassifier.WIDGET_TYPE_NOTIFICATION)
.build(),
mTcm.getTextClassifier(TextClassifier.LOCAL));
+ } else {
+ mClassifier = TextClassifierService.getDefaultTextClassifierImplementation(mContext);
}
}
@@ -369,16 +373,14 @@
mClassifier.generateLinks(request).apply(url, 0, null));
}
-
- @Test(expected = IllegalArgumentException.class)
+ @Test
public void testGenerateLinks_tooLong() {
- if (isTextClassifierDisabled()) {
- throw new IllegalArgumentException("pass if disabled");
- }
+ if (isTextClassifierDisabled()) return;
char[] manySpaces = new char[mClassifier.getMaxGenerateLinksTextLength() + 1];
Arrays.fill(manySpaces, ' ');
TextLinks.Request request = new TextLinks.Request.Builder(new String(manySpaces)).build();
- mClassifier.generateLinks(request);
+ TextLinks links = mClassifier.generateLinks(request);
+ assertTrue(links.getLinks().isEmpty());
}
@Test
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
index dcc6b95..1290633 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
@@ -38,6 +38,10 @@
ICredentialStoreFactory storeFactory =
ICredentialStoreFactory.Stub.asInterface(
ServiceManager.getService("android.security.identity"));
+ if (storeFactory == null) {
+ // This can happen if credstore is not running or not installed.
+ return null;
+ }
ICredentialStore credStore = null;
try {
diff --git a/media/java/android/media/AudioRecordingConfiguration.java b/media/java/android/media/AudioRecordingConfiguration.java
index f3613d3..42841d1 100644
--- a/media/java/android/media/AudioRecordingConfiguration.java
+++ b/media/java/android/media/AudioRecordingConfiguration.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -229,13 +230,19 @@
* <p>This information is only available if the caller has the
* {@link android.Manifest.permission.MODIFY_AUDIO_ROUTING}
* permission.
- * <br>The result is -1 without the permission.
* @return the user id
+ * @throws SecurityException Thrown if the caller is missing the MODIFY_AUDIO_ROUTING permission
*
* @hide
*/
@SystemApi
- public int getClientUid() { return mClientUid; }
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public int getClientUid() {
+ if (mClientUid == -1) {
+ throw new SecurityException("MODIFY_AUDIO_ROUTING permission is missing");
+ }
+ return mClientUid;
+ }
/**
* Returns information about the audio input device used for this recording.
diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl
index a25aff6..194669c 100644
--- a/media/java/android/media/IMediaRoute2Provider.aidl
+++ b/media/java/android/media/IMediaRoute2Provider.aidl
@@ -18,6 +18,7 @@
import android.content.Intent;
import android.media.IMediaRoute2ProviderClient;
+import android.media.RouteDiscoveryPreference;
import android.os.Bundle;
/**
@@ -28,6 +29,7 @@
void requestCreateSession(String packageName, String routeId, long requestId,
in @nullable Bundle sessionHints);
void releaseSession(String sessionId);
+ void updateDiscoveryPreference(in RouteDiscoveryPreference discoveryPreference);
void selectRoute(String sessionId, String routeId);
void deselectRoute(String sessionId, String routeId);
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index e39b7bc..d0b5d48 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -370,7 +370,6 @@
*
* @param preference the new discovery preference
*/
- // TODO: This method needs tests.
public void onDiscoveryPreferenceChanged(@NonNull RouteDiscoveryPreference preference) {}
/**
@@ -456,6 +455,16 @@
}
@Override
+ public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) {
+ if (!checkCallerisSystem()) {
+ return;
+ }
+ mHandler.sendMessage(obtainMessage(
+ MediaRoute2ProviderService::onDiscoveryPreferenceChanged,
+ MediaRoute2ProviderService.this, discoveryPreference));
+ }
+
+ @Override
public void selectRoute(@NonNull String sessionId, String routeId) {
if (!checkCallerisSystem()) {
return;
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index f751a22..515cabe 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -182,11 +182,16 @@
Client2 client = new Client2();
try {
mMediaRouterService.registerClient2(client, mPackageName);
- updateDiscoveryRequestLocked();
- mMediaRouterService.setDiscoveryRequest2(client, mDiscoveryPreference);
mClient = client;
} catch (RemoteException ex) {
- Log.e(TAG, "Unable to register media router.", ex);
+ Log.e(TAG, "registerRouteCallback: Unable to register client.", ex);
+ }
+ }
+ if (mClient != null && updateDiscoveryPreferenceIfNeededLocked()) {
+ try {
+ mMediaRouterService.setDiscoveryRequest2(mClient, mDiscoveryPreference);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "registerRouteCallback: Unable to set discovery request.");
}
}
}
@@ -209,22 +214,37 @@
}
synchronized (sRouterLock) {
- if (mRouteCallbackRecords.size() == 0 && mClient != null) {
- try {
- mMediaRouterService.unregisterClient2(mClient);
- } catch (RemoteException ex) {
- Log.e(TAG, "Unable to unregister media router.", ex);
+ if (mClient != null) {
+ if (updateDiscoveryPreferenceIfNeededLocked()) {
+ try {
+ mMediaRouterService.setDiscoveryRequest2(mClient, mDiscoveryPreference);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unregisterRouteCallback: Unable to set discovery request.");
+ }
}
- //TODO: Clean up mRoutes. (onHandler?)
+ if (mRouteCallbackRecords.size() == 0) {
+ try {
+ mMediaRouterService.unregisterClient2(mClient);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to unregister media router.", ex);
+ }
+ }
+ mShouldUpdateRoutes = true;
mClient = null;
}
}
}
- private void updateDiscoveryRequestLocked() {
- mDiscoveryPreference = new RouteDiscoveryPreference.Builder(
+ private boolean updateDiscoveryPreferenceIfNeededLocked() {
+ RouteDiscoveryPreference newDiscoveryPreference = new RouteDiscoveryPreference.Builder(
mRouteCallbackRecords.stream().map(record -> record.mPreference).collect(
Collectors.toList())).build();
+ if (Objects.equals(mDiscoveryPreference, newDiscoveryPreference)) {
+ return false;
+ }
+ mDiscoveryPreference = newDiscoveryPreference;
+ mShouldUpdateRoutes = true;
+ return true;
}
/**
diff --git a/media/java/android/media/RouteDiscoveryPreference.java b/media/java/android/media/RouteDiscoveryPreference.java
index 7ec1123..ebcb9ed 100644
--- a/media/java/android/media/RouteDiscoveryPreference.java
+++ b/media/java/android/media/RouteDiscoveryPreference.java
@@ -31,7 +31,7 @@
import java.util.Set;
/**
- * A media route discovery preference describing the kinds of routes that media router
+ * A media route discovery preference describing the kinds of routes that media router
* would like to discover and whether to perform active scanning.
*
* @see MediaRouter2#registerRouteCallback
@@ -58,6 +58,7 @@
private final Bundle mExtras;
/**
+ * An empty discovery preference.
* @hide
*/
public static final RouteDiscoveryPreference EMPTY =
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
index e29b323..f1a08f2 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
@@ -155,7 +155,6 @@
@Override
public void onUpdateVolume(String routeId, int delta) {
- android.util.Log.d(TAG, "onUpdateVolume routeId= " + routeId + "delta=" + delta);
MediaRoute2Info route = mRoutes.get(routeId);
if (route == null) {
return;
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 82cd5f7..07a926f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1812,16 +1812,16 @@
<string name="demote">Mark this notification as not a conversation</string>
<!-- [CHAR LIMIT=100] Mark this conversation as a favorite -->
- <string name="notification_conversation_favorite">Favorite</string>
+ <string name="notification_conversation_favorite">Mark as important</string>
<!-- [CHAR LIMIT=100] Unmark this conversation as a favorite -->
- <string name="notification_conversation_unfavorite">Unfavorite</string>
+ <string name="notification_conversation_unfavorite">Mark as unimportant</string>
<!-- [CHAR LIMIT=100] Mute this conversation -->
- <string name="notification_conversation_mute">Mute</string>
+ <string name="notification_conversation_mute">Silence</string>
<!-- [CHAR LIMIT=100] Umute this conversation -->
- <string name="notification_conversation_unmute">Unmute</string>
+ <string name="notification_conversation_unmute">Alerting</string>
<!-- [CHAR LIMIT=100] Show notification as bubble -->
<string name="notification_conversation_bubble">Show as bubble</string>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 6045524..bb0681c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -280,7 +280,7 @@
Button favorite = findViewById(R.id.fave);
favorite.setOnClickListener(mOnFavoriteClick);
- if (mNotificationChannel.canBypassDnd()) {
+ if (mNotificationChannel.isImportantConversation()) {
favorite.setText(R.string.notification_conversation_unfavorite);
favorite.setCompoundDrawablesRelative(
mContext.getDrawable(R.drawable.ic_star), null, null, null);
@@ -621,8 +621,8 @@
}
break;
case ACTION_FAVORITE:
- // TODO: extend beyond DND
- mChannelToUpdate.setBypassDnd(!mChannelToUpdate.canBypassDnd());
+ mChannelToUpdate.setImportantConversation(
+ !mChannelToUpdate.isImportantConversation());
break;
case ACTION_MUTE:
if (mChannelToUpdate.getImportance() == IMPORTANCE_UNSPECIFIED
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 20a089f..f080d67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -655,13 +655,13 @@
ArgumentCaptor.forClass(NotificationChannel.class);
verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
anyString(), anyInt(), captor.capture());
- assertTrue(captor.getValue().canBypassDnd());
+ assertTrue(captor.getValue().isImportantConversation());
}
@Test
public void testFavorite_unfavorite() throws Exception {
- mNotificationChannel.setBypassDnd(true);
- mConversationChannel.setBypassDnd(true);
+ mNotificationChannel.setImportantConversation(true);
+ mConversationChannel.setImportantConversation(true);
mNotificationInfo.bindNotification(
mShortcutManager,
@@ -688,7 +688,7 @@
ArgumentCaptor.forClass(NotificationChannel.class);
verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
anyString(), anyInt(), captor.capture());
- assertFalse(captor.getValue().canBypassDnd());
+ assertFalse(captor.getValue().isImportantConversation());
}
@Test
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 1a4fc32..1cb9313 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -818,27 +818,26 @@
}
}
- void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId,
- @Nullable Bundle clientState) {
+ void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId) {
synchronized (mLock) {
if (mAugmentedAutofillEventHistory == null
|| mAugmentedAutofillEventHistory.getSessionId() != sessionId) {
return;
}
mAugmentedAutofillEventHistory.addEvent(
- new Event(Event.TYPE_DATASET_SELECTED, suggestionId, clientState, null, null,
+ new Event(Event.TYPE_DATASET_SELECTED, suggestionId, null, null, null,
null, null, null, null, null, null));
}
}
- void logAugmentedAutofillShown(int sessionId, @Nullable Bundle clientState) {
+ void logAugmentedAutofillShown(int sessionId) {
synchronized (mLock) {
if (mAugmentedAutofillEventHistory == null
|| mAugmentedAutofillEventHistory.getSessionId() != sessionId) {
return;
}
mAugmentedAutofillEventHistory.addEvent(
- new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null,
+ new Event(Event.TYPE_DATASETS_SHOWN, null, null, null, null, null,
null, null, null, null, null));
}
@@ -1227,16 +1226,15 @@
}
@Override
- public void logAugmentedAutofillShown(int sessionId, Bundle clientState) {
- AutofillManagerServiceImpl.this.logAugmentedAutofillShown(sessionId,
- clientState);
+ public void logAugmentedAutofillShown(int sessionId) {
+ AutofillManagerServiceImpl.this.logAugmentedAutofillShown(sessionId);
}
@Override
- public void logAugmentedAutofillSelected(int sessionId, String suggestionId,
- Bundle clientState) {
+ public void logAugmentedAutofillSelected(int sessionId,
+ String suggestionId) {
AutofillManagerServiceImpl.this.logAugmentedAutofillSelected(sessionId,
- suggestionId, clientState);
+ suggestionId);
}
@Override
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 5e6f6fea..880c401 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -55,6 +55,7 @@
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.ArrayUtils;
import com.android.internal.view.IInlineSuggestionsResponseCallback;
+import com.android.server.autofill.ui.InlineSuggestionFactory;
import java.util.concurrent.CancellationException;
import java.util.concurrent.TimeUnit;
@@ -144,7 +145,8 @@
int taskId, @NonNull ComponentName activityComponent, @NonNull AutofillId focusedId,
@Nullable AutofillValue focusedValue,
@Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
- @Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback) {
+ @Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback,
+ @NonNull Runnable onErrorCallback) {
long requestTime = SystemClock.elapsedRealtime();
AtomicReference<ICancellationSignal> cancellationRef = new AtomicReference<>();
@@ -161,12 +163,12 @@
focusedId, focusedValue, requestTime, inlineSuggestionsRequest,
new IFillCallback.Stub() {
@Override
- public void onSuccess(@Nullable Dataset[] inlineSuggestionsData,
- @Nullable Bundle clientState) {
+ public void onSuccess(@Nullable Dataset[] inlineSuggestionsData) {
mCallbacks.resetLastResponse();
maybeRequestShowInlineSuggestions(sessionId,
inlineSuggestionsData, focusedId,
- inlineSuggestionsCallback, client, clientState);
+ inlineSuggestionsCallback, client,
+ onErrorCallback);
requestAutofill.complete(null);
}
@@ -231,29 +233,31 @@
private void maybeRequestShowInlineSuggestions(int sessionId,
@Nullable Dataset[] inlineSuggestionsData, @NonNull AutofillId focusedId,
@Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback,
- @NonNull IAutoFillManagerClient client, @Nullable Bundle clientState) {
+ @NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback) {
if (ArrayUtils.isEmpty(inlineSuggestionsData) || inlineSuggestionsCallback == null) {
return;
}
mCallbacks.setLastResponse(sessionId);
+
try {
inlineSuggestionsCallback.onInlineSuggestionsResponse(
InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse(
inlineSuggestionsData, focusedId, mContext,
dataset -> {
mCallbacks.logAugmentedAutofillSelected(sessionId,
- dataset.getId(), clientState);
+ dataset.getId());
try {
client.autofill(sessionId, dataset.getFieldIds(),
dataset.getFieldValues());
} catch (RemoteException e) {
Slog.w(TAG, "Encounter exception autofilling the values");
}
- }));
+ }, onErrorCallback));
} catch (RemoteException e) {
Slog.w(TAG, "Exception sending inline suggestions response back to IME.");
}
- mCallbacks.logAugmentedAutofillShown(sessionId, clientState);
+
+ mCallbacks.logAugmentedAutofillShown(sessionId);
}
@Override
@@ -275,9 +279,8 @@
void setLastResponse(int sessionId);
- void logAugmentedAutofillShown(int sessionId, @Nullable Bundle clientState);
+ void logAugmentedAutofillShown(int sessionId);
- void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId,
- @Nullable Bundle clientState);
+ void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId);
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 415ecd8..7e5123c 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -103,6 +103,7 @@
import com.android.internal.view.IInlineSuggestionsRequestCallback;
import com.android.internal.view.IInlineSuggestionsResponseCallback;
import com.android.server.autofill.ui.AutoFillUI;
+import com.android.server.autofill.ui.InlineSuggestionFactory;
import com.android.server.autofill.ui.PendingUi;
import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -2681,7 +2682,6 @@
}
}
-
getUiForShowing().showFillUi(filledId, response, filterText,
mService.getServicePackageName(), mComponentName,
serviceLabel, serviceIcon, this, id, mCompatMode);
@@ -2733,7 +2733,11 @@
InlineSuggestionsResponse inlineSuggestionsResponse =
InlineSuggestionFactory.createInlineSuggestionsResponse(response.getRequestId(),
- datasets.toArray(new Dataset[]{}), mCurrentViewId, mContext, this);
+ datasets.toArray(new Dataset[]{}), mCurrentViewId, mContext, this, () -> {
+ synchronized (mLock) {
+ requestHideFillUi(mCurrentViewId);
+ }
+ });
try {
inlineContentCallback.onInlineSuggestionsResponse(inlineSuggestionsResponse);
} catch (RemoteException e) {
@@ -3024,7 +3028,11 @@
mInlineSuggestionsRequestCallback != null
? mInlineSuggestionsRequestCallback.getResponseCallback() : null;
remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, focusedId,
- currentValue, inlineSuggestionsRequest, inlineSuggestionsResponseCallback);
+ currentValue, inlineSuggestionsRequest, inlineSuggestionsResponseCallback, () -> {
+ synchronized (mLock) {
+ cancelAugmentedAutofillLocked();
+ }
+ });
if (mAugmentedAutofillDestroyer == null) {
mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest();
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
similarity index 80%
rename from services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java
rename to services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index cb6c8f5..38a5b5b 100644
--- a/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.autofill;
+package com.android.server.autofill.ui;
import static com.android.server.autofill.Helper.sDebug;
@@ -32,18 +32,13 @@
import android.view.inputmethod.InlineSuggestionInfo;
import android.view.inputmethod.InlineSuggestionsResponse;
+import com.android.internal.util.function.QuadFunction;
import com.android.internal.view.inline.IInlineContentCallback;
import com.android.internal.view.inline.IInlineContentProvider;
import com.android.server.UiThread;
-import com.android.server.autofill.ui.AutoFillUI;
-import com.android.server.autofill.ui.InlineSuggestionUi;
import java.util.ArrayList;
-
-/**
- * @hide
- */
public final class InlineSuggestionFactory {
private static final String TAG = "InlineSuggestionFactory";
@@ -65,28 +60,12 @@
@NonNull Dataset[] datasets,
@NonNull AutofillId autofillId,
@NonNull Context context,
- @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback) {
- if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
-
- final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>();
- final InlineSuggestionUi inlineSuggestionUi = new InlineSuggestionUi(context);
- for (Dataset dataset : datasets) {
- final int fieldIndex = dataset.getFieldIds().indexOf(autofillId);
- if (fieldIndex < 0) {
- Slog.w(TAG, "AutofillId=" + autofillId + " not found in dataset");
- return null;
- }
- final InlinePresentation inlinePresentation = dataset.getFieldInlinePresentation(
- fieldIndex);
- if (inlinePresentation == null) {
- Slog.w(TAG, "InlinePresentation not found in dataset");
- return null;
- }
- InlineSuggestion inlineSuggestion = createAugmentedInlineSuggestion(dataset,
- inlinePresentation, inlineSuggestionUi, inlineSuggestionUiCallback);
- inlineSuggestions.add(inlineSuggestion);
- }
- return new InlineSuggestionsResponse(inlineSuggestions);
+ @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback,
+ @NonNull Runnable onErrorCallback) {
+ return createInlineSuggestionsResponseInternal(datasets, autofillId,
+ context, onErrorCallback, (dataset, inlinePresentation, inlineSuggestionUi,
+ filedIndex) -> createAugmentedInlineSuggestion(dataset,
+ inlinePresentation, inlineSuggestionUi, inlineSuggestionUiCallback));
}
/**
@@ -97,11 +76,26 @@
@NonNull Dataset[] datasets,
@NonNull AutofillId autofillId,
@NonNull Context context,
- @NonNull AutoFillUI.AutoFillUiCallback client) {
- if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called");
+ @NonNull AutoFillUI.AutoFillUiCallback client,
+ @NonNull Runnable onErrorCallback) {
+ return createInlineSuggestionsResponseInternal(datasets, autofillId,
+ context, onErrorCallback, (dataset, inlinePresentation, inlineSuggestionUi,
+ filedIndex) -> createInlineSuggestion(requestId, dataset, filedIndex,
+ inlinePresentation, inlineSuggestionUi, client));
+ }
+
+ private static InlineSuggestionsResponse createInlineSuggestionsResponseInternal(
+ @NonNull Dataset[] datasets,
+ @NonNull AutofillId autofillId,
+ @NonNull Context context,
+ @NonNull Runnable onErrorCallback,
+ @NonNull QuadFunction<Dataset, InlinePresentation, InlineSuggestionUi,
+ Integer, InlineSuggestion> suggestionFactory) {
+ if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>();
- final InlineSuggestionUi inlineSuggestionUi = new InlineSuggestionUi(context);
+ final InlineSuggestionUi inlineSuggestionUi = new InlineSuggestionUi(context,
+ onErrorCallback);
for (Dataset dataset : datasets) {
final int fieldIndex = dataset.getFieldIds().indexOf(autofillId);
if (fieldIndex < 0) {
@@ -114,9 +108,8 @@
Slog.w(TAG, "InlinePresentation not found in dataset");
return null;
}
- InlineSuggestion inlineSuggestion = createInlineSuggestion(requestId, dataset,
- fieldIndex,
- inlinePresentation, inlineSuggestionUi, client);
+ InlineSuggestion inlineSuggestion = suggestionFactory.apply(dataset,
+ inlinePresentation, inlineSuggestionUi, fieldIndex);
inlineSuggestions.add(inlineSuggestion);
}
return new InlineSuggestionsResponse(inlineSuggestions);
@@ -131,9 +124,8 @@
inlinePresentation.getInlinePresentationSpec(),
InlineSuggestionInfo.SOURCE_PLATFORM, new String[]{""},
InlineSuggestionInfo.TYPE_SUGGESTION);
- final View.OnClickListener onClickListener = v -> {
+ final View.OnClickListener onClickListener = v ->
inlineSuggestionUiCallback.autofill(dataset);
- };
final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
createInlineContentProvider(inlinePresentation, inlineSuggestionUi,
onClickListener));
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java
new file mode 100644
index 0000000..8d476d7
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2020 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.autofill.ui;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.util.Log;
+import android.util.MathUtils;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+import android.widget.FrameLayout;
+
+import com.android.server.LocalServices;
+import com.android.server.wm.WindowManagerInternal;
+
+/**
+ * This class is the root view for an inline suggestion. It is responsible for
+ * detecting the click on the item and to also transfer input focus to the IME
+ * window if we detect the user is scrolling.
+ */
+ // TODO(b/146453086) Move to ExtServices and add @SystemApi to transfer touch focus
+@SuppressLint("ViewConstructor")
+class InlineSuggestionRoot extends FrameLayout {
+ private static final String LOG_TAG = InlineSuggestionRoot.class.getSimpleName();
+
+ private final @NonNull Runnable mOnErrorCallback;
+ private final int mTouchSlop;
+
+ private float mDownX;
+ private float mDownY;
+
+ InlineSuggestionRoot(@NonNull Context context, @NonNull Runnable onErrorCallback) {
+ super(context);
+ mOnErrorCallback = onErrorCallback;
+ mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ }
+
+ @Override
+ @SuppressLint("ClickableViewAccessibility")
+ public boolean onTouchEvent(@NonNull MotionEvent event) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN: {
+ mDownX = event.getX();
+ mDownY = event.getY();
+ } break;
+
+ case MotionEvent.ACTION_MOVE: {
+ final float distance = MathUtils.dist(mDownX, mDownY,
+ event.getX(), event.getY());
+ if (distance > mTouchSlop) {
+ transferTouchFocusToImeWindow();
+ }
+ } break;
+ }
+ return super.onTouchEvent(event);
+ }
+
+ private void transferTouchFocusToImeWindow() {
+ final WindowManagerInternal windowManagerInternal = LocalServices.getService(
+ WindowManagerInternal.class);
+ if (!windowManagerInternal.transferTouchFocusToImeWindow(getViewRootImpl().getInputToken(),
+ getContext().getDisplayId())) {
+ Log.e(LOG_TAG, "Cannot transfer touch focus from suggestion to IME");
+ mOnErrorCallback.run();
+ }
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java
index 2adefea..bf148a6 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java
@@ -67,10 +67,12 @@
// (int)}. This name is a single string of the form "package:type/entry".
private static final Pattern RESOURCE_NAME_PATTERN = Pattern.compile("([^:]+):([^/]+)/(\\S+)");
- private final Context mContext;
+ private final @NonNull Context mContext;
+ private final @NonNull Runnable mOnErrorCallback;
- public InlineSuggestionUi(Context context) {
+ InlineSuggestionUi(@NonNull Context context, @NonNull Runnable onErrorCallback) {
this.mContext = context;
+ mOnErrorCallback = onErrorCallback;
}
/**
@@ -94,15 +96,17 @@
}
final View suggestionView = renderSlice(inlinePresentation.getSlice(),
contextThemeWrapper);
- if (onClickListener != null) {
- suggestionView.setOnClickListener(onClickListener);
- }
+
+ final InlineSuggestionRoot suggestionRoot = new InlineSuggestionRoot(
+ mContext, mOnErrorCallback);
+ suggestionRoot.addView(suggestionView);
+ suggestionRoot.setOnClickListener(onClickListener);
WindowManager.LayoutParams lp =
new WindowManager.LayoutParams(width, height,
WindowManager.LayoutParams.TYPE_APPLICATION, 0,
PixelFormat.TRANSPARENT);
- wvr.addView(suggestionView, lp);
+ wvr.addView(suggestionRoot, lp);
return sc;
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index ce5e241..ec3dbe9 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -48,8 +48,11 @@
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
+import static java.util.Map.Entry;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -62,6 +65,8 @@
import android.database.ContentObserver;
import android.net.CaptivePortal;
import android.net.ConnectionInfo;
+import android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
+import android.net.ConnectivityDiagnosticsManager.DataStallReport;
import android.net.ConnectivityManager;
import android.net.ICaptivePortal;
import android.net.IConnectivityDiagnosticsCallback;
@@ -130,6 +135,7 @@
import android.os.Messenger;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
@@ -170,6 +176,7 @@
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.LocationPermissionChecker;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
@@ -492,9 +499,9 @@
/**
* Event for NetworkMonitor/NetworkAgentInfo to inform ConnectivityService that the network has
* been tested.
- * obj = String representing URL that Internet probe was redirect to, if it was redirected.
- * arg1 = One of the NETWORK_TESTED_RESULT_* constants.
- * arg2 = NetID.
+ * obj = {@link NetworkTestedResults} representing information sent from NetworkMonitor.
+ * data = PersistableBundle of extras passed from NetworkMonitor. If {@link
+ * NetworkMonitorCallbacks#notifyNetworkTested} is called, this will be null.
*/
private static final int EVENT_NETWORK_TESTED = 41;
@@ -596,6 +603,9 @@
private Set<String> mWolSupportedInterfaces;
private TelephonyManager mTelephonyManager;
+ private final AppOpsManager mAppOpsManager;
+
+ private final LocationPermissionChecker mLocationPermissionChecker;
private KeepaliveTracker mKeepaliveTracker;
private NetworkNotificationManager mNotifier;
@@ -992,6 +1002,8 @@
mNetd = netd;
mKeyStore = KeyStore.getInstance();
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ mLocationPermissionChecker = new LocationPermissionChecker(mContext);
// To ensure uid rules are synchronized with Network Policy, register for
// NetworkPolicyManagerService events must happen prior to NetworkPolicyManagerService
@@ -2101,6 +2113,12 @@
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
+ private boolean checkNetworkStackPermission(int pid, int uid) {
+ return checkAnyPermissionOf(pid, uid,
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ }
+
private boolean checkNetworkSignalStrengthWakeupPermission(int pid, int uid) {
return checkAnyPermissionOf(pid, uid,
android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP,
@@ -2747,88 +2765,21 @@
break;
}
case EVENT_NETWORK_TESTED: {
- final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
+ final NetworkTestedResults results = (NetworkTestedResults) msg.obj;
+
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(results.mNetId);
if (nai == null) break;
- final boolean wasPartial = nai.partialConnectivity;
- nai.partialConnectivity = ((msg.arg1 & NETWORK_VALIDATION_RESULT_PARTIAL) != 0);
- final boolean partialConnectivityChanged =
- (wasPartial != nai.partialConnectivity);
+ handleNetworkTested(nai, results.mTestResult,
+ (results.mRedirectUrl == null) ? "" : results.mRedirectUrl);
- final boolean valid = ((msg.arg1 & NETWORK_VALIDATION_RESULT_VALID) != 0);
- final boolean wasValidated = nai.lastValidated;
- final boolean wasDefault = isDefaultNetwork(nai);
- // Only show a connected notification if the network is pending validation
- // after the captive portal app was open, and it has now validated.
- if (nai.captivePortalValidationPending && valid) {
- // User is now logged in, network validated.
- nai.captivePortalValidationPending = false;
- showNetworkNotification(nai, NotificationType.LOGGED_IN);
- }
-
- final String redirectUrl = (msg.obj instanceof String) ? (String) msg.obj : "";
-
- if (DBG) {
- final String logMsg = !TextUtils.isEmpty(redirectUrl)
- ? " with redirect to " + redirectUrl
- : "";
- log(nai.name() + " validation " + (valid ? "passed" : "failed") + logMsg);
- }
- if (valid != nai.lastValidated) {
- if (wasDefault) {
- mDeps.getMetricsLogger()
- .defaultNetworkMetrics().logDefaultNetworkValidity(
- SystemClock.elapsedRealtime(), valid);
- }
- final int oldScore = nai.getCurrentScore();
- nai.lastValidated = valid;
- nai.everValidated |= valid;
- updateCapabilities(oldScore, nai, nai.networkCapabilities);
- // If score has changed, rebroadcast to NetworkProviders. b/17726566
- if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
- if (valid) {
- handleFreshlyValidatedNetwork(nai);
- // Clear NO_INTERNET, PRIVATE_DNS_BROKEN, PARTIAL_CONNECTIVITY and
- // LOST_INTERNET notifications if network becomes valid.
- mNotifier.clearNotification(nai.network.netId,
- NotificationType.NO_INTERNET);
- mNotifier.clearNotification(nai.network.netId,
- NotificationType.LOST_INTERNET);
- mNotifier.clearNotification(nai.network.netId,
- NotificationType.PARTIAL_CONNECTIVITY);
- mNotifier.clearNotification(nai.network.netId,
- NotificationType.PRIVATE_DNS_BROKEN);
- // If network becomes valid, the hasShownBroken should be reset for
- // that network so that the notification will be fired when the private
- // DNS is broken again.
- nai.networkAgentConfig.hasShownBroken = false;
- }
- } else if (partialConnectivityChanged) {
- updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
- }
- updateInetCondition(nai);
- // Let the NetworkAgent know the state of its network
- Bundle redirectUrlBundle = new Bundle();
- redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, redirectUrl);
- // TODO: Evaluate to update partial connectivity to status to NetworkAgent.
- nai.asyncChannel.sendMessage(
- NetworkAgent.CMD_REPORT_NETWORK_STATUS,
- (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
- 0, redirectUrlBundle);
-
- // If NetworkMonitor detects partial connectivity before
- // EVENT_PROMPT_UNVALIDATED arrives, show the partial connectivity notification
- // immediately. Re-notify partial connectivity silently if no internet
- // notification already there.
- if (!wasPartial && nai.partialConnectivity) {
- // Remove delayed message if there is a pending message.
- mHandler.removeMessages(EVENT_PROMPT_UNVALIDATED, nai.network);
- handlePromptUnvalidated(nai.network);
- }
-
- if (wasValidated && !nai.lastValidated) {
- handleNetworkUnvalidated(nai);
- }
+ // Invoke ConnectivityReport generation for this Network test event.
+ final Message m =
+ mConnectivityDiagnosticsHandler.obtainMessage(
+ ConnectivityDiagnosticsHandler.EVENT_NETWORK_TESTED,
+ new ConnectivityReportEvent(results.mTimestampMillis, nai));
+ m.setData(msg.getData());
+ mConnectivityDiagnosticsHandler.sendMessage(m);
break;
}
case EVENT_PROVISIONING_NOTIFICATION: {
@@ -2879,6 +2830,87 @@
return true;
}
+ private void handleNetworkTested(
+ @NonNull NetworkAgentInfo nai, int testResult, @NonNull String redirectUrl) {
+ final boolean wasPartial = nai.partialConnectivity;
+ nai.partialConnectivity = ((testResult & NETWORK_VALIDATION_RESULT_PARTIAL) != 0);
+ final boolean partialConnectivityChanged =
+ (wasPartial != nai.partialConnectivity);
+
+ final boolean valid = ((testResult & NETWORK_VALIDATION_RESULT_VALID) != 0);
+ final boolean wasValidated = nai.lastValidated;
+ final boolean wasDefault = isDefaultNetwork(nai);
+ // Only show a connected notification if the network is pending validation
+ // after the captive portal app was open, and it has now validated.
+ if (nai.captivePortalValidationPending && valid) {
+ // User is now logged in, network validated.
+ nai.captivePortalValidationPending = false;
+ showNetworkNotification(nai, NotificationType.LOGGED_IN);
+ }
+
+ if (DBG) {
+ final String logMsg = !TextUtils.isEmpty(redirectUrl)
+ ? " with redirect to " + redirectUrl
+ : "";
+ log(nai.name() + " validation " + (valid ? "passed" : "failed") + logMsg);
+ }
+ if (valid != nai.lastValidated) {
+ if (wasDefault) {
+ mDeps.getMetricsLogger()
+ .defaultNetworkMetrics().logDefaultNetworkValidity(
+ SystemClock.elapsedRealtime(), valid);
+ }
+ final int oldScore = nai.getCurrentScore();
+ nai.lastValidated = valid;
+ nai.everValidated |= valid;
+ updateCapabilities(oldScore, nai, nai.networkCapabilities);
+ // If score has changed, rebroadcast to NetworkProviders. b/17726566
+ if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
+ if (valid) {
+ handleFreshlyValidatedNetwork(nai);
+ // Clear NO_INTERNET, PRIVATE_DNS_BROKEN, PARTIAL_CONNECTIVITY and
+ // LOST_INTERNET notifications if network becomes valid.
+ mNotifier.clearNotification(nai.network.netId,
+ NotificationType.NO_INTERNET);
+ mNotifier.clearNotification(nai.network.netId,
+ NotificationType.LOST_INTERNET);
+ mNotifier.clearNotification(nai.network.netId,
+ NotificationType.PARTIAL_CONNECTIVITY);
+ mNotifier.clearNotification(nai.network.netId,
+ NotificationType.PRIVATE_DNS_BROKEN);
+ // If network becomes valid, the hasShownBroken should be reset for
+ // that network so that the notification will be fired when the private
+ // DNS is broken again.
+ nai.networkAgentConfig.hasShownBroken = false;
+ }
+ } else if (partialConnectivityChanged) {
+ updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
+ }
+ updateInetCondition(nai);
+ // Let the NetworkAgent know the state of its network
+ Bundle redirectUrlBundle = new Bundle();
+ redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, redirectUrl);
+ // TODO: Evaluate to update partial connectivity to status to NetworkAgent.
+ nai.asyncChannel.sendMessage(
+ NetworkAgent.CMD_REPORT_NETWORK_STATUS,
+ (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
+ 0, redirectUrlBundle);
+
+ // If NetworkMonitor detects partial connectivity before
+ // EVENT_PROMPT_UNVALIDATED arrives, show the partial connectivity notification
+ // immediately. Re-notify partial connectivity silently if no internet
+ // notification already there.
+ if (!wasPartial && nai.partialConnectivity) {
+ // Remove delayed message if there is a pending message.
+ mHandler.removeMessages(EVENT_PROMPT_UNVALIDATED, nai.network);
+ handlePromptUnvalidated(nai.network);
+ }
+
+ if (wasValidated && !nai.lastValidated) {
+ handleNetworkUnvalidated(nai);
+ }
+ }
+
private int getCaptivePortalMode() {
return Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.CAPTIVE_PORTAL_MODE,
@@ -2927,8 +2959,23 @@
@Override
public void notifyNetworkTested(int testResult, @Nullable String redirectUrl) {
- mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(EVENT_NETWORK_TESTED,
- testResult, mNetId, redirectUrl));
+ notifyNetworkTestedWithExtras(testResult, redirectUrl, SystemClock.elapsedRealtime(),
+ PersistableBundle.EMPTY);
+ }
+
+ @Override
+ public void notifyNetworkTestedWithExtras(
+ int testResult,
+ @Nullable String redirectUrl,
+ long timestampMillis,
+ @NonNull PersistableBundle extras) {
+ final Message msg =
+ mTrackerHandler.obtainMessage(
+ EVENT_NETWORK_TESTED,
+ new NetworkTestedResults(
+ mNetId, testResult, timestampMillis, redirectUrl));
+ msg.setData(new Bundle(extras));
+ mTrackerHandler.sendMessage(msg);
}
@Override
@@ -2970,6 +3017,21 @@
}
@Override
+ public void notifyDataStallSuspected(
+ long timestampMillis, int detectionMethod, PersistableBundle extras) {
+ final Message msg =
+ mConnectivityDiagnosticsHandler.obtainMessage(
+ ConnectivityDiagnosticsHandler.EVENT_DATA_STALL_SUSPECTED,
+ detectionMethod, mNetId, timestampMillis);
+ msg.setData(new Bundle(extras));
+
+ // NetworkStateTrackerHandler currently doesn't take any actions based on data
+ // stalls so send the message directly to ConnectivityDiagnosticsHandler and avoid
+ // the cost of going through two handlers.
+ mConnectivityDiagnosticsHandler.sendMessage(msg);
+ }
+
+ @Override
public int getInterfaceVersion() {
return this.VERSION;
}
@@ -4143,6 +4205,19 @@
final int connectivityInfo = encodeBool(hasConnectivity);
mHandler.sendMessage(
mHandler.obtainMessage(EVENT_REVALIDATE_NETWORK, uid, connectivityInfo, network));
+
+ final NetworkAgentInfo nai;
+ if (network == null) {
+ nai = getDefaultNetwork();
+ } else {
+ nai = getNetworkAgentInfoForNetwork(network);
+ }
+ if (nai != null) {
+ mConnectivityDiagnosticsHandler.sendMessage(
+ mConnectivityDiagnosticsHandler.obtainMessage(
+ ConnectivityDiagnosticsHandler.EVENT_NETWORK_CONNECTIVITY_REPORTED,
+ connectivityInfo, 0, nai));
+ }
}
private void handleReportNetworkConnectivity(
@@ -7373,7 +7448,11 @@
@GuardedBy("mVpns")
private Vpn getVpnIfOwner() {
- final int uid = Binder.getCallingUid();
+ return getVpnIfOwner(Binder.getCallingUid());
+ }
+
+ @GuardedBy("mVpns")
+ private Vpn getVpnIfOwner(int uid) {
final int user = UserHandle.getUserId(uid);
final Vpn vpn = mVpns.get(user);
@@ -7469,6 +7548,8 @@
*/
@VisibleForTesting
class ConnectivityDiagnosticsHandler extends Handler {
+ private final String mTag = ConnectivityDiagnosticsHandler.class.getSimpleName();
+
/**
* Used to handle ConnectivityDiagnosticsCallback registration events from {@link
* android.net.ConnectivityDiagnosticsManager}.
@@ -7485,6 +7566,37 @@
*/
private static final int EVENT_UNREGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK = 2;
+ /**
+ * Event for {@link NetworkStateTrackerHandler} to trigger ConnectivityReport callbacks
+ * after processing {@link #EVENT_NETWORK_TESTED} events.
+ * obj = {@link ConnectivityReportEvent} representing ConnectivityReport info reported from
+ * NetworkMonitor.
+ * data = PersistableBundle of extras passed from NetworkMonitor.
+ *
+ * <p>See {@link ConnectivityService#EVENT_NETWORK_TESTED}.
+ */
+ private static final int EVENT_NETWORK_TESTED = ConnectivityService.EVENT_NETWORK_TESTED;
+
+ /**
+ * Event for NetworkMonitor to inform ConnectivityService that a potential data stall has
+ * been detected on the network.
+ * obj = Long the timestamp (in millis) for when the suspected data stall was detected.
+ * arg1 = {@link DataStallReport#DetectionMethod} indicating the detection method.
+ * arg2 = NetID.
+ * data = PersistableBundle of extras passed from NetworkMonitor.
+ */
+ private static final int EVENT_DATA_STALL_SUSPECTED = 4;
+
+ /**
+ * Event for ConnectivityDiagnosticsHandler to handle network connectivity being reported to
+ * the platform. This event will invoke {@link
+ * IConnectivityDiagnosticsCallback#onNetworkConnectivityReported} for permissioned
+ * callbacks.
+ * obj = Network that was reported on
+ * arg1 = boolint for the quality reported
+ */
+ private static final int EVENT_NETWORK_CONNECTIVITY_REPORTED = 5;
+
private ConnectivityDiagnosticsHandler(Looper looper) {
super(looper);
}
@@ -7502,6 +7614,37 @@
(IConnectivityDiagnosticsCallback) msg.obj, msg.arg1);
break;
}
+ case EVENT_NETWORK_TESTED: {
+ final ConnectivityReportEvent reportEvent =
+ (ConnectivityReportEvent) msg.obj;
+
+ // This is safe because {@link
+ // NetworkMonitorCallbacks#notifyNetworkTestedWithExtras} receives a
+ // PersistableBundle and converts it to the Bundle in the incoming Message. If
+ // {@link NetworkMonitorCallbacks#notifyNetworkTested} is called, msg.data will
+ // not be set. This is also safe, as msg.getData() will return an empty Bundle.
+ final PersistableBundle extras = new PersistableBundle(msg.getData());
+ handleNetworkTestedWithExtras(reportEvent, extras);
+ break;
+ }
+ case EVENT_DATA_STALL_SUSPECTED: {
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
+ if (nai == null) break;
+
+ // This is safe because NetworkMonitorCallbacks#notifyDataStallSuspected
+ // receives a PersistableBundle and converts it to the Bundle in the incoming
+ // Message.
+ final PersistableBundle extras = new PersistableBundle(msg.getData());
+ handleDataStallSuspected(nai, (long) msg.obj, msg.arg1, extras);
+ break;
+ }
+ case EVENT_NETWORK_CONNECTIVITY_REPORTED: {
+ handleNetworkConnectivityReported((NetworkAgentInfo) msg.obj, toBool(msg.arg1));
+ break;
+ }
+ default: {
+ Log.e(mTag, "Unrecognized event in ConnectivityDiagnostics: " + msg.what);
+ }
}
}
}
@@ -7511,12 +7654,16 @@
class ConnectivityDiagnosticsCallbackInfo implements Binder.DeathRecipient {
@NonNull private final IConnectivityDiagnosticsCallback mCb;
@NonNull private final NetworkRequestInfo mRequestInfo;
+ @NonNull private final String mCallingPackageName;
@VisibleForTesting
ConnectivityDiagnosticsCallbackInfo(
- @NonNull IConnectivityDiagnosticsCallback cb, @NonNull NetworkRequestInfo nri) {
+ @NonNull IConnectivityDiagnosticsCallback cb,
+ @NonNull NetworkRequestInfo nri,
+ @NonNull String callingPackageName) {
mCb = cb;
mRequestInfo = nri;
+ mCallingPackageName = callingPackageName;
}
@Override
@@ -7526,6 +7673,39 @@
}
}
+ /**
+ * Class used for sending information from {@link
+ * NetworkMonitorCallbacks#notifyNetworkTestedWithExtras} to the handler for processing it.
+ */
+ private static class NetworkTestedResults {
+ private final int mNetId;
+ private final int mTestResult;
+ private final long mTimestampMillis;
+ @Nullable private final String mRedirectUrl;
+
+ private NetworkTestedResults(
+ int netId, int testResult, long timestampMillis, @Nullable String redirectUrl) {
+ mNetId = netId;
+ mTestResult = testResult;
+ mTimestampMillis = timestampMillis;
+ mRedirectUrl = redirectUrl;
+ }
+ }
+
+ /**
+ * Class used for sending information from {@link NetworkStateTrackerHandler} to {@link
+ * ConnectivityDiagnosticsHandler}.
+ */
+ private static class ConnectivityReportEvent {
+ private final long mTimestampMillis;
+ @NonNull private final NetworkAgentInfo mNai;
+
+ private ConnectivityReportEvent(long timestampMillis, @NonNull NetworkAgentInfo nai) {
+ mTimestampMillis = timestampMillis;
+ mNai = nai;
+ }
+ }
+
private void handleRegisterConnectivityDiagnosticsCallback(
@NonNull ConnectivityDiagnosticsCallbackInfo cbInfo) {
ensureRunningOnConnectivityServiceThread();
@@ -7573,13 +7753,109 @@
cb.asBinder().unlinkToDeath(mConnectivityDiagnosticsCallbacks.remove(cb), 0);
}
+ private void handleNetworkTestedWithExtras(
+ @NonNull ConnectivityReportEvent reportEvent, @NonNull PersistableBundle extras) {
+ final NetworkAgentInfo nai = reportEvent.mNai;
+ final ConnectivityReport report =
+ new ConnectivityReport(
+ reportEvent.mNai.network,
+ reportEvent.mTimestampMillis,
+ nai.linkProperties,
+ nai.networkCapabilities,
+ extras);
+ final List<IConnectivityDiagnosticsCallback> results =
+ getMatchingPermissionedCallbacks(nai);
+ for (final IConnectivityDiagnosticsCallback cb : results) {
+ try {
+ cb.onConnectivityReport(report);
+ } catch (RemoteException ex) {
+ loge("Error invoking onConnectivityReport", ex);
+ }
+ }
+ }
+
+ private void handleDataStallSuspected(
+ @NonNull NetworkAgentInfo nai, long timestampMillis, int detectionMethod,
+ @NonNull PersistableBundle extras) {
+ final DataStallReport report =
+ new DataStallReport(nai.network, timestampMillis, detectionMethod, extras);
+ final List<IConnectivityDiagnosticsCallback> results =
+ getMatchingPermissionedCallbacks(nai);
+ for (final IConnectivityDiagnosticsCallback cb : results) {
+ try {
+ cb.onDataStallSuspected(report);
+ } catch (RemoteException ex) {
+ loge("Error invoking onDataStallSuspected", ex);
+ }
+ }
+ }
+
+ private void handleNetworkConnectivityReported(
+ @NonNull NetworkAgentInfo nai, boolean connectivity) {
+ final List<IConnectivityDiagnosticsCallback> results =
+ getMatchingPermissionedCallbacks(nai);
+ for (final IConnectivityDiagnosticsCallback cb : results) {
+ try {
+ cb.onNetworkConnectivityReported(nai.network, connectivity);
+ } catch (RemoteException ex) {
+ loge("Error invoking onNetworkConnectivityReported", ex);
+ }
+ }
+ }
+
+ private List<IConnectivityDiagnosticsCallback> getMatchingPermissionedCallbacks(
+ @NonNull NetworkAgentInfo nai) {
+ final List<IConnectivityDiagnosticsCallback> results = new ArrayList<>();
+ for (Entry<IConnectivityDiagnosticsCallback, ConnectivityDiagnosticsCallbackInfo> entry :
+ mConnectivityDiagnosticsCallbacks.entrySet()) {
+ final ConnectivityDiagnosticsCallbackInfo cbInfo = entry.getValue();
+ final NetworkRequestInfo nri = cbInfo.mRequestInfo;
+ if (nai.satisfies(nri.request)) {
+ if (checkConnectivityDiagnosticsPermissions(
+ nri.mPid, nri.mUid, nai, cbInfo.mCallingPackageName)) {
+ results.add(entry.getKey());
+ }
+ }
+ }
+ return results;
+ }
+
+ @VisibleForTesting
+ boolean checkConnectivityDiagnosticsPermissions(
+ int callbackPid, int callbackUid, NetworkAgentInfo nai, String callbackPackageName) {
+ if (checkNetworkStackPermission(callbackPid, callbackUid)) {
+ return true;
+ }
+
+ if (!mLocationPermissionChecker.checkLocationPermission(
+ callbackPackageName, null /* featureId */, callbackUid, null /* message */)) {
+ return false;
+ }
+
+ synchronized (mVpns) {
+ if (getVpnIfOwner(callbackUid) != null) {
+ return true;
+ }
+ }
+
+ // Administrator UIDs also contains the Owner UID
+ if (nai.networkCapabilities.getAdministratorUids().contains(callbackUid)) {
+ return true;
+ }
+
+ return false;
+ }
+
@Override
public void registerConnectivityDiagnosticsCallback(
- @NonNull IConnectivityDiagnosticsCallback callback, @NonNull NetworkRequest request) {
+ @NonNull IConnectivityDiagnosticsCallback callback,
+ @NonNull NetworkRequest request,
+ @NonNull String callingPackageName) {
if (request.legacyType != TYPE_NONE) {
throw new IllegalArgumentException("ConnectivityManager.TYPE_* are deprecated."
+ " Please use NetworkCapabilities instead.");
}
+ mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackageName);
// This NetworkCapabilities is only used for matching to Networks. Clear out its owner uid
// and administrator uids to be safe.
@@ -7597,7 +7873,7 @@
// callback's binder death.
final NetworkRequestInfo nri = new NetworkRequestInfo(requestWithId);
final ConnectivityDiagnosticsCallbackInfo cbInfo =
- new ConnectivityDiagnosticsCallbackInfo(callback, nri);
+ new ConnectivityDiagnosticsCallbackInfo(callback, nri, callingPackageName);
mConnectivityDiagnosticsHandler.sendMessage(
mConnectivityDiagnosticsHandler.obtainMessage(
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 4a65a96..fabe92db 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -287,7 +287,7 @@
// When the restriction is enabled, foreground service started from background will not have
// while-in-use permissions like location, camera and microphone. (The foreground service can be
// started, the restriction is on while-in-use permissions.)
- volatile boolean mFlagBackgroundFgsStartRestrictionEnabled;
+ volatile boolean mFlagBackgroundFgsStartRestrictionEnabled = true;
private final ActivityManagerService mService;
private ContentResolver mResolver;
@@ -304,18 +304,19 @@
// we have no limit on the number of service, visible, foreground, or other such
// processes and the number of those processes does not count against the cached
// process limit.
- public int CUR_MAX_CACHED_PROCESSES;
+ public int CUR_MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
// The maximum number of empty app processes we will let sit around.
- public int CUR_MAX_EMPTY_PROCESSES;
+ public int CUR_MAX_EMPTY_PROCESSES = computeEmptyProcessLimit(CUR_MAX_CACHED_PROCESSES);
// The number of empty apps at which we don't consider it necessary to do
// memory trimming.
- public int CUR_TRIM_EMPTY_PROCESSES;
+ public int CUR_TRIM_EMPTY_PROCESSES = computeEmptyProcessLimit(MAX_CACHED_PROCESSES) / 2;
// The number of cached at which we don't consider it necessary to do
// memory trimming.
- public int CUR_TRIM_CACHED_PROCESSES;
+ public int CUR_TRIM_CACHED_PROCESSES =
+ (MAX_CACHED_PROCESSES - computeEmptyProcessLimit(MAX_CACHED_PROCESSES)) / 3;
/**
* Packages that can't be killed even if it's requested to be killed on imperceptible.
@@ -419,6 +420,8 @@
context.getResources().getIntArray(
com.android.internal.R.array.config_defaultImperceptibleKillingExemptionProcStates))
.boxed().collect(Collectors.toList());
+ IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.addAll(mDefaultImperceptibleKillExemptPackages);
+ IMPERCEPTIBLE_KILL_EXEMPT_PROC_STATES.addAll(mDefaultImperceptibleKillExemptProcStates);
}
public void start(ContentResolver resolver) {
@@ -550,8 +553,6 @@
// For new flags that are intended for server-side experiments, please use the new
// DeviceConfig package.
-
- updateMaxCachedProcesses();
}
}
@@ -580,6 +581,9 @@
}
private void updateOomAdjUpdatePolicy() {
+
+
+
OOMADJ_UPDATE_QUICK = DeviceConfig.getInt(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_OOMADJ_UPDATE_POLICY,
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 784ce4a..a0589c5 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2126,24 +2126,27 @@
UserHandle user = UserHandle.getUserHandleForUid(uid);
boolean isRevokedCompat;
if (permissionInfo.backgroundPermission != null) {
- boolean isBackgroundRevokedCompat = mode != AppOpsManager.MODE_ALLOWED;
+ if (packageManager.checkPermission(permissionInfo.backgroundPermission, packageName)
+ == PackageManager.PERMISSION_GRANTED) {
+ boolean isBackgroundRevokedCompat = mode != AppOpsManager.MODE_ALLOWED;
- if (isBackgroundRevokedCompat && supportsRuntimePermissions) {
- Slog.w(TAG, "setUidMode() called with a mode inconsistent with runtime"
- + " permission state, this is discouraged and you should revoke the"
- + " runtime permission instead: uid=" + uid + ", switchCode="
- + switchCode + ", mode=" + mode + ", permission="
- + permissionInfo.backgroundPermission);
- }
+ if (isBackgroundRevokedCompat && supportsRuntimePermissions) {
+ Slog.w(TAG, "setUidMode() called with a mode inconsistent with runtime"
+ + " permission state, this is discouraged and you should revoke the"
+ + " runtime permission instead: uid=" + uid + ", switchCode="
+ + switchCode + ", mode=" + mode + ", permission="
+ + permissionInfo.backgroundPermission);
+ }
- long identity = Binder.clearCallingIdentity();
- try {
- packageManager.updatePermissionFlags(permissionInfo.backgroundPermission,
- packageName, PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
- isBackgroundRevokedCompat
- ? PackageManager.FLAG_PERMISSION_REVOKED_COMPAT : 0, user);
- } finally {
- Binder.restoreCallingIdentity(identity);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ packageManager.updatePermissionFlags(permissionInfo.backgroundPermission,
+ packageName, PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
+ isBackgroundRevokedCompat
+ ? PackageManager.FLAG_PERMISSION_REVOKED_COMPAT : 0, user);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
isRevokedCompat = mode != AppOpsManager.MODE_ALLOWED
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index b2548af..82a2f01 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3406,13 +3406,11 @@
}
public void binderDied() {
- int oldModeOwnerPid = 0;
+ int oldModeOwnerPid;
int newModeOwnerPid = 0;
synchronized (mDeviceBroker.mSetModeLock) {
Log.w(TAG, "setMode() client died");
- if (!mSetModeDeathHandlers.isEmpty()) {
- oldModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
- }
+ oldModeOwnerPid = getModeOwnerPid();
int index = mSetModeDeathHandlers.indexOf(this);
if (index < 0) {
Log.w(TAG, "unregistered setMode() client died");
@@ -3446,17 +3444,19 @@
/** @see AudioManager#setMode(int) */
public void setMode(int mode, IBinder cb, String callingPackage) {
- if (DEBUG_MODE) { Log.v(TAG, "setMode(mode=" + mode + ", callingPackage=" + callingPackage + ")"); }
+ if (DEBUG_MODE) {
+ Log.v(TAG, "setMode(mode=" + mode + ", callingPackage=" + callingPackage + ")");
+ }
if (!checkAudioSettingsPermission("setMode()")) {
return;
}
-
- if ( (mode == AudioSystem.MODE_IN_CALL) &&
- (mContext.checkCallingOrSelfPermission(
+ final boolean hasModifyPhoneStatePermission = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE)
- != PackageManager.PERMISSION_GRANTED)) {
+ == PackageManager.PERMISSION_GRANTED;
+ final int callingPid = Binder.getCallingPid();
+ if ((mode == AudioSystem.MODE_IN_CALL) && !hasModifyPhoneStatePermission) {
Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(MODE_IN_CALL) from pid="
- + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+ + callingPid + ", uid=" + Binder.getCallingUid());
return;
}
@@ -3470,16 +3470,25 @@
return;
}
- int oldModeOwnerPid = 0;
- int newModeOwnerPid = 0;
+ int oldModeOwnerPid;
+ int newModeOwnerPid;
synchronized (mDeviceBroker.mSetModeLock) {
- if (!mSetModeDeathHandlers.isEmpty()) {
- oldModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
- }
if (mode == AudioSystem.MODE_CURRENT) {
mode = mMode;
}
- newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid(), callingPackage);
+ oldModeOwnerPid = getModeOwnerPid();
+ // Do not allow changing mode if a call is active and the requester
+ // does not have permission to modify phone state or is not the mode owner.
+ if (((mMode == AudioSystem.MODE_IN_CALL)
+ || (mMode == AudioSystem.MODE_IN_COMMUNICATION))
+ && !(hasModifyPhoneStatePermission || (oldModeOwnerPid == callingPid))) {
+ Log.w(TAG, "setMode(" + mode + ") from pid=" + callingPid
+ + ", uid=" + Binder.getCallingUid()
+ + ", cannot change mode from " + mMode
+ + " without permission or being mode owner");
+ return;
+ }
+ newModeOwnerPid = setModeInt(mode, cb, callingPid, callingPackage);
}
// when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
// SCO connections not started by the application changing the mode when pid changes
@@ -3569,10 +3578,9 @@
if (status == AudioSystem.AUDIO_STATUS_OK) {
if (actualMode != AudioSystem.MODE_NORMAL) {
- if (mSetModeDeathHandlers.isEmpty()) {
+ newModeOwnerPid = getModeOwnerPid();
+ if (newModeOwnerPid == 0) {
Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack");
- } else {
- newModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
}
}
// Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 87a5730..704cbff4 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -49,6 +49,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/**
* A display adapter for the local displays managed by Surface Flinger.
@@ -118,22 +119,28 @@
physicalDisplayId);
activeColorMode = Display.COLOR_MODE_INVALID;
}
- int[] colorModes = SurfaceControl.getDisplayColorModes(displayToken);
SurfaceControl.DesiredDisplayConfigSpecs configSpecs =
SurfaceControl.getDesiredDisplayConfigSpecs(displayToken);
+ int[] colorModes = SurfaceControl.getDisplayColorModes(displayToken);
+ Display.HdrCapabilities hdrCapabilities =
+ SurfaceControl.getHdrCapabilities(displayToken);
LocalDisplayDevice device = mDevices.get(physicalDisplayId);
if (device == null) {
// Display was added.
final boolean isInternal = mDevices.size() == 0;
device = new LocalDisplayDevice(displayToken, physicalDisplayId, info,
configs, activeConfig, configSpecs, colorModes, activeColorMode,
- isInternal);
+ hdrCapabilities, isInternal);
mDevices.put(physicalDisplayId, device);
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
- } else if (device.updateDisplayConfigsLocked(configs, activeConfig, configSpecs,
- colorModes, activeColorMode)) {
- // Display properties changed.
- sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
+ } else {
+ boolean changed = device.updateDisplayConfigsLocked(configs, activeConfig,
+ configSpecs);
+ changed |= device.updateColorModesLocked(colorModes, activeColorMode);
+ changed |= device.updateHdrCapabilitiesLocked(hdrCapabilities);
+ if (changed) {
+ sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
+ }
}
} else {
// The display is no longer available. Ignore the attempt to add it.
@@ -202,16 +209,16 @@
LocalDisplayDevice(IBinder displayToken, long physicalDisplayId,
SurfaceControl.DisplayInfo info, SurfaceControl.DisplayConfig[] configs,
int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs,
- int[] colorModes, int activeColorMode, boolean isInternal) {
+ int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities,
+ boolean isInternal) {
super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId);
mPhysicalDisplayId = physicalDisplayId;
mIsInternal = isInternal;
mDisplayInfo = info;
- updateDisplayConfigsLocked(configs, activeConfigId, configSpecs, colorModes,
- activeColorMode);
+ updateDisplayConfigsLocked(configs, activeConfigId, configSpecs);
updateColorModesLocked(colorModes, activeColorMode);
-
+ updateHdrCapabilitiesLocked(hdrCapabilities);
mSidekickInternal = LocalServices.getService(SidekickInternal.class);
if (mIsInternal) {
LightsManager lights = LocalServices.getService(LightsManager.class);
@@ -219,7 +226,6 @@
} else {
mBacklight = null;
}
- mHdrCapabilities = SurfaceControl.getHdrCapabilities(displayToken);
mAllmSupported = SurfaceControl.getAutoLowLatencyModeSupport(displayToken);
mGameContentTypeSupported = SurfaceControl.getGameContentTypeSupport(displayToken);
mHalBrightnessSupport = SurfaceControl.getDisplayBrightnessSupport(displayToken);
@@ -236,11 +242,9 @@
public boolean updateDisplayConfigsLocked(
SurfaceControl.DisplayConfig[] configs, int activeConfigId,
- SurfaceControl.DesiredDisplayConfigSpecs configSpecs, int[] colorModes,
- int activeColorMode) {
+ SurfaceControl.DesiredDisplayConfigSpecs configSpecs) {
mDisplayConfigs = Arrays.copyOf(configs, configs.length);
mActiveConfigId = activeConfigId;
-
// Build an updated list of all existing modes.
ArrayList<DisplayModeRecord> records = new ArrayList<DisplayModeRecord>();
boolean modesAdded = false;
@@ -389,14 +393,15 @@
mSystemBrightnessToNits = sysToNits;
}
- private boolean updateColorModesLocked(int[] colorModes,
- int activeColorMode) {
- List<Integer> pendingColorModes = new ArrayList<>();
+ private boolean updateColorModesLocked(int[] colorModes, int activeColorMode) {
+ if (colorModes == null) {
+ return false;
+ }
- if (colorModes == null) return false;
+ List<Integer> pendingColorModes = new ArrayList<>();
// Build an updated list of all existing color modes.
boolean colorModesAdded = false;
- for (int colorMode: colorModes) {
+ for (int colorMode : colorModes) {
if (!mSupportedColorModes.contains(colorMode)) {
colorModesAdded = true;
}
@@ -441,6 +446,15 @@
return true;
}
+ private boolean updateHdrCapabilitiesLocked(Display.HdrCapabilities newHdrCapabilities) {
+ // If the HDR capabilities haven't changed, then we're done here.
+ if (Objects.equals(mHdrCapabilities, newHdrCapabilities)) {
+ return false;
+ }
+ mHdrCapabilities = newHdrCapabilities;
+ return true;
+ }
+
private DisplayModeRecord findDisplayModeRecord(SurfaceControl.DisplayConfig config) {
for (int i = 0; i < mSupportedModes.size(); i++) {
DisplayModeRecord record = mSupportedModes.valueAt(i);
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 90358ca..a8f706c 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -223,7 +223,7 @@
int displayId, InputApplicationHandle application);
private static native void nativeSetFocusedDisplay(long ptr, int displayId);
private static native boolean nativeTransferTouchFocus(long ptr,
- InputChannel fromChannel, InputChannel toChannel);
+ IBinder fromChannelToken, IBinder toChannelToken);
private static native void nativeSetPointerSpeed(long ptr, int speed);
private static native void nativeSetShowTouches(long ptr, boolean enabled);
private static native void nativeSetInteractive(long ptr, boolean interactive);
@@ -1554,14 +1554,29 @@
* @return True if the transfer was successful. False if the window with the
* specified channel did not actually have touch focus at the time of the request.
*/
- public boolean transferTouchFocus(InputChannel fromChannel, InputChannel toChannel) {
- if (fromChannel == null) {
- throw new IllegalArgumentException("fromChannel must not be null.");
- }
- if (toChannel == null) {
- throw new IllegalArgumentException("toChannel must not be null.");
- }
- return nativeTransferTouchFocus(mPtr, fromChannel, toChannel);
+ public boolean transferTouchFocus(@NonNull InputChannel fromChannel,
+ @NonNull InputChannel toChannel) {
+ return nativeTransferTouchFocus(mPtr, fromChannel.getToken(), toChannel.getToken());
+ }
+
+ /**
+ * Atomically transfers touch focus from one window to another as identified by
+ * their input channels. It is possible for multiple windows to have
+ * touch focus if they support split touch dispatch
+ * {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH} but this
+ * method only transfers touch focus of the specified window without affecting
+ * other windows that may also have touch focus at the same time.
+ * @param fromChannelToken The channel token of a window that currently has touch focus.
+ * @param toChannelToken The channel token of the window that should receive touch focus in
+ * place of the first.
+ * @return True if the transfer was successful. False if the window with the
+ * specified channel did not actually have touch focus at the time of the request.
+ */
+ public boolean transferTouchFocus(@NonNull IBinder fromChannelToken,
+ @NonNull IBinder toChannelToken) {
+ Objects.nonNull(fromChannelToken);
+ Objects.nonNull(toChannelToken);
+ return nativeTransferTouchFocus(mPtr, fromChannelToken, toChannelToken);
}
@Override // Binder call
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 5123362..7b17b4e 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -21,6 +21,7 @@
import android.content.ComponentName;
import android.content.Intent;
import android.media.MediaRoute2ProviderInfo;
+import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
import android.os.Bundle;
@@ -54,6 +55,7 @@
public abstract void requestCreateSession(String packageName, String routeId, long requestId,
@Nullable Bundle sessionHints);
public abstract void releaseSession(String sessionId);
+ public abstract void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference);
public abstract void selectRoute(String sessionId, String routeId);
public abstract void deselectRoute(String sessionId, String routeId);
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index e23542e..e22ea46 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -25,6 +25,7 @@
import android.media.IMediaRoute2ProviderClient;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2ProviderService;
+import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
import android.os.Bundle;
import android.os.Handler;
@@ -93,6 +94,14 @@
}
@Override
+ public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) {
+ if (mConnectionReady) {
+ mActiveConnection.updateDiscoveryPreference(discoveryPreference);
+ updateBinding();
+ }
+ }
+
+ @Override
public void selectRoute(String sessionId, String routeId) {
if (mConnectionReady) {
mActiveConnection.selectRoute(sessionId, routeId);
@@ -461,6 +470,14 @@
}
}
+ public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) {
+ try {
+ mProvider.updateDiscoveryPreference(discoveryPreference);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "updateDiscoveryPreference(): Failed to deliver request.");
+ }
+ }
+
public void selectRoute(String sessionId, String routeId) {
try {
mProvider.selectRoute(sessionId, routeId);
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 9594659..358cc29 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -598,6 +598,9 @@
clientRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::updateClientUsage,
clientRecord.mUserRecord.mHandler, clientRecord));
+ clientRecord.mUserRecord.mHandler.sendMessage(
+ obtainMessage(UserHandler::updateDiscoveryPreference,
+ clientRecord.mUserRecord.mHandler));
}
}
@@ -856,6 +859,7 @@
//TODO: make records private for thread-safety
final ArrayList<Client2Record> mClientRecords = new ArrayList<>();
final ArrayList<ManagerRecord> mManagerRecords = new ArrayList<>();
+ RouteDiscoveryPreference mCompositeDiscoveryPreference = RouteDiscoveryPreference.EMPTY;
final UserHandler mHandler;
UserRecord(int userId) {
@@ -885,8 +889,6 @@
public final int mClientId;
public RouteDiscoveryPreference mDiscoveryPreference;
- public boolean mIsManagerSelecting;
- public MediaRoute2Info mSelectingRoute;
public MediaRoute2Info mSelectedRoute;
Client2Record(UserRecord userRecord, IMediaRouter2Client client,
@@ -1003,6 +1005,7 @@
public void onAddProvider(MediaRoute2ProviderProxy provider) {
provider.setCallback(this);
mMediaProviders.add(provider);
+ provider.updateDiscoveryPreference(mUserRecord.mCompositeDiscoveryPreference);
}
@Override
@@ -1642,6 +1645,25 @@
}
}
+ private void updateDiscoveryPreference() {
+ MediaRouter2ServiceImpl service = mServiceRef.get();
+ if (service == null) {
+ return;
+ }
+ List<RouteDiscoveryPreference> discoveryPreferences = new ArrayList<>();
+ synchronized (service.mLock) {
+ for (Client2Record clientRecord : mUserRecord.mClientRecords) {
+ discoveryPreferences.add(clientRecord.mDiscoveryPreference);
+ }
+ mUserRecord.mCompositeDiscoveryPreference =
+ new RouteDiscoveryPreference.Builder(discoveryPreferences)
+ .build();
+ }
+ for (MediaRoute2Provider provider : mMediaProviders) {
+ provider.updateDiscoveryPreference(mUserRecord.mCompositeDiscoveryPreference);
+ }
+ }
+
private MediaRoute2Provider findProvider(String providerId) {
for (MediaRoute2Provider provider : mMediaProviders) {
if (TextUtils.equals(provider.getUniqueId(), providerId)) {
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
index 820731d..11b7a8a 100644
--- a/services/core/java/com/android/server/media/MediaSession2Record.java
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -52,6 +52,8 @@
private boolean mIsConnected;
@GuardedBy("mLock")
private int mPolicies;
+ @GuardedBy("mLock")
+ private boolean mIsClosed;
public MediaSession2Record(Session2Token sessionToken, MediaSessionService service,
Looper handlerLooper, int policies) {
@@ -119,6 +121,7 @@
@Override
public void close() {
synchronized (mLock) {
+ mIsClosed = true;
// Call close regardless of the mIsAvailable. This may be called when it's not yet
// connected.
mController.close();
@@ -126,6 +129,13 @@
}
@Override
+ public boolean isClosed() {
+ synchronized (mLock) {
+ return mIsClosed;
+ }
+ }
+
+ @Override
public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService,
KeyEvent ke, int sequenceId, ResultReceiver cb) {
// TODO(jaewan): Implement.
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 05f7e1d..7bcbcd4 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -420,6 +420,13 @@
}
}
+ @Override
+ public boolean isClosed() {
+ synchronized (mLock) {
+ return mDestroyed;
+ }
+ }
+
/**
* Sends media button.
*
diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
index 6e10880..74e6ded 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
@@ -154,4 +154,9 @@
*/
@Override
void close();
+
+ /**
+ * Returns whether {@link #close()} is called before.
+ */
+ boolean isClosed();
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index d0efef0..88b884e 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -434,9 +434,14 @@
if (DEBUG) {
Log.d(TAG, "Destroying " + session);
}
+ if (session.isClosed()) {
+ Log.w(TAG, "Destroying already destroyed session. Ignoring.");
+ return;
+ }
+
FullUserRecord user = getFullUserRecordLocked(session.getUserId());
- if (user != null) {
+ if (user != null && session instanceof MediaSessionRecord) {
final int uid = session.getUid();
final int sessionCount = user.mUidToSessionCount.get(uid, 0);
if (sessionCount <= 0) {
@@ -570,6 +575,13 @@
throw new RuntimeException("Session request from invalid user.");
}
+ final int sessionCount = user.mUidToSessionCount.get(callerUid, 0);
+ if (sessionCount >= SESSION_CREATION_LIMIT_PER_UID
+ && !hasMediaControlPermission(callerPid, callerUid)) {
+ throw new RuntimeException("Created too many sessions. count="
+ + sessionCount + ")");
+ }
+
final MediaSessionRecord session;
try {
session = new MediaSessionRecord(callerPid, callerUid, userId,
@@ -579,12 +591,6 @@
throw new RuntimeException("Media Session owner died prematurely.", e);
}
- final int sessionCount = user.mUidToSessionCount.get(callerUid, 0);
- if (sessionCount >= SESSION_CREATION_LIMIT_PER_UID
- && !hasMediaControlPermission(callerPid, callerUid)) {
- throw new RuntimeException("Created too many sessions. count="
- + sessionCount + ")");
- }
user.mUidToSessionCount.put(callerUid, sessionCount + 1);
user.mPriorityStack.addSession(session);
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 924a9b7..798a781 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -29,6 +29,7 @@
import android.media.IAudioService;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
+import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
import android.os.Bundle;
import android.os.Handler;
@@ -118,6 +119,10 @@
public void releaseSession(String sessionId) {
// Do nothing
}
+ @Override
+ public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) {
+ // Do nothing
+ }
@Override
public void selectRoute(String sessionId, String routeId) {
diff --git a/services/core/java/com/android/server/notification/BadgeExtractor.java b/services/core/java/com/android/server/notification/BadgeExtractor.java
index d91d541..af8baa5 100644
--- a/services/core/java/com/android/server/notification/BadgeExtractor.java
+++ b/services/core/java/com/android/server/notification/BadgeExtractor.java
@@ -43,9 +43,9 @@
if (DBG) Slog.d(TAG, "missing config");
return null;
}
- boolean userWantsBadges = mConfig.badgingEnabled(record.sbn.getUser());
+ boolean userWantsBadges = mConfig.badgingEnabled(record.getSbn().getUser());
boolean appCanShowBadge =
- mConfig.canShowBadge(record.sbn.getPackageName(), record.sbn.getUid());
+ mConfig.canShowBadge(record.getSbn().getPackageName(), record.getSbn().getUid());
if (!userWantsBadges || !appCanShowBadge) {
record.setShowBadge(false);
} else {
diff --git a/services/core/java/com/android/server/notification/BubbleExtractor.java b/services/core/java/com/android/server/notification/BubbleExtractor.java
index e59bf16..c9c8042 100644
--- a/services/core/java/com/android/server/notification/BubbleExtractor.java
+++ b/services/core/java/com/android/server/notification/BubbleExtractor.java
@@ -42,7 +42,7 @@
return null;
}
boolean appCanShowBubble =
- mConfig.areBubblesAllowed(record.sbn.getPackageName(), record.sbn.getUid());
+ mConfig.areBubblesAllowed(record.getSbn().getPackageName(), record.getSbn().getUid());
if (!mConfig.bubblesEnabled() || !appCanShowBubble) {
record.setAllowBubble(false);
} else {
diff --git a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
index 0e14364..83ca699 100644
--- a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
@@ -15,10 +15,8 @@
*/
package com.android.server.notification;
-import android.app.Notification;
import android.app.NotificationChannel;
import android.content.Context;
-import android.util.FeatureFlagUtils;
import android.util.Slog;
/**
@@ -47,9 +45,9 @@
return null;
}
NotificationChannel updatedChannel = mConfig.getConversationNotificationChannel(
- record.sbn.getPackageName(),
- record.sbn.getUid(), record.getChannel().getId(),
- record.sbn.getShortcutId(mContext), true, false);
+ record.getSbn().getPackageName(),
+ record.getSbn().getUid(), record.getChannel().getId(),
+ record.getSbn().getShortcutId(mContext), true, false);
record.updateNotificationChannel(updatedChannel);
return null;
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index a7e40cb..29b5e81 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -122,8 +122,8 @@
return -1 * Integer.compare(leftPackagePriority, rightPackagePriority);
}
- final int leftPriority = left.sbn.getNotification().priority;
- final int rightPriority = right.sbn.getNotification().priority;
+ final int leftPriority = left.getSbn().getNotification().priority;
+ final int rightPriority = right.getSbn().getNotification().priority;
if (leftPriority != rightPriority) {
// by priority, high to low
return -1 * Integer.compare(leftPriority, rightPriority);
@@ -169,7 +169,7 @@
}
protected boolean isImportantMessaging(NotificationRecord record) {
- return mMessagingUtil.isImportantMessaging(record.sbn, record.getImportance());
+ return mMessagingUtil.isImportantMessaging(record.getSbn(), record.getImportance());
}
private boolean isOngoing(NotificationRecord record) {
@@ -183,7 +183,7 @@
private boolean isCall(NotificationRecord record) {
return record.isCategory(Notification.CATEGORY_CALL)
- && isDefaultPhoneApp(record.sbn.getPackageName());
+ && isDefaultPhoneApp(record.getSbn().getPackageName());
}
private boolean isDefaultPhoneApp(String pkg) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 68cc014..b52289e 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -684,14 +684,14 @@
if (summary == null) {
return;
}
- int oldFlags = summary.sbn.getNotification().flags;
+ int oldFlags = summary.getSbn().getNotification().flags;
if (needsOngoingFlag) {
- summary.sbn.getNotification().flags |= FLAG_ONGOING_EVENT;
+ summary.getSbn().getNotification().flags |= FLAG_ONGOING_EVENT;
} else {
- summary.sbn.getNotification().flags &= ~FLAG_ONGOING_EVENT;
+ summary.getSbn().getNotification().flags &= ~FLAG_ONGOING_EVENT;
}
- if (summary.sbn.getNotification().flags != oldFlags) {
+ if (summary.getSbn().getNotification().flags != oldFlags) {
mHandler.post(new EnqueueNotificationRunnable(userId, summary, isAppForeground));
}
}
@@ -917,7 +917,7 @@
r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now),
nv.rank, nv.count);
- StatusBarNotification sbn = r.sbn;
+ StatusBarNotification sbn = r.getSbn();
cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
sbn.getId(), Notification.FLAG_AUTO_CANCEL,
FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
@@ -959,7 +959,7 @@
nv.recycle();
reportUserInteraction(r);
mAssistants.notifyAssistantActionClicked(
- r.sbn, actionIndex, action, generatedByAssistant);
+ r.getSbn(), actionIndex, action, generatedByAssistant);
}
}
@@ -1044,7 +1044,7 @@
reportSeen(r);
}
r.setVisibility(true, nv.rank, nv.count);
- mAssistants.notifyAssistantVisibilityChangedLocked(r.sbn, true);
+ mAssistants.notifyAssistantVisibilityChangedLocked(r.getSbn(), true);
boolean isHun = (nv.location
== NotificationVisibility.NotificationLocation.LOCATION_FIRST_HEADS_UP);
// hasBeenVisiblyExpanded must be called after updating the expansion state of
@@ -1063,7 +1063,7 @@
NotificationRecord r = mNotificationsByKey.get(nv.key);
if (r == null) continue;
r.setVisibility(false, nv.rank, nv.count);
- mAssistants.notifyAssistantVisibilityChangedLocked(r.sbn, false);
+ mAssistants.notifyAssistantVisibilityChangedLocked(r.getSbn(), false);
nv.recycle();
}
}
@@ -1090,7 +1090,8 @@
r.recordExpanded();
reportUserInteraction(r);
}
- mAssistants.notifyAssistantExpansionChangedLocked(r.sbn, userAction, expanded);
+ mAssistants.notifyAssistantExpansionChangedLocked(
+ r.getSbn(), userAction, expanded);
}
}
}
@@ -1106,7 +1107,7 @@
.setCategory(MetricsEvent.NOTIFICATION_DIRECT_REPLY_ACTION)
.setType(MetricsEvent.TYPE_ACTION));
reportUserInteraction(r);
- mAssistants.notifyAssistantNotificationDirectReplyLocked(r.sbn);
+ mAssistants.notifyAssistantNotificationDirectReplyLocked(r.getSbn());
}
}
}
@@ -1150,7 +1151,7 @@
// Treat clicking on a smart reply as a user interaction.
reportUserInteraction(r);
mAssistants.notifyAssistantSuggestedReplySent(
- r.sbn, reply, r.getSuggestionsGeneratedByAssistant());
+ r.getSbn(), reply, r.getSuggestionsGeneratedByAssistant());
}
}
}
@@ -1170,7 +1171,7 @@
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
- final StatusBarNotification n = r.sbn;
+ final StatusBarNotification n = r.getSbn();
final int callingUid = n.getUid();
final String pkg = n.getPackageName();
applyFlagBubble(r, pkg, callingUid, null /* oldEntry */, isBubble);
@@ -1372,9 +1373,9 @@
record = findNotificationByKeyLocked(intent.getStringExtra(EXTRA_KEY));
}
if (record != null) {
- cancelNotification(record.sbn.getUid(), record.sbn.getInitialPid(),
- record.sbn.getPackageName(), record.sbn.getTag(),
- record.sbn.getId(), 0,
+ cancelNotification(record.getSbn().getUid(), record.getSbn().getInitialPid(),
+ record.getSbn().getPackageName(), record.getSbn().getTag(),
+ record.getSbn().getId(), 0,
FLAG_FOREGROUND_SERVICE, true, record.getUserId(),
REASON_TIMEOUT, null);
}
@@ -1637,7 +1638,7 @@
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(bubbleKey);
if (r != null) {
- final StatusBarNotification n = r.sbn;
+ final StatusBarNotification n = r.getSbn();
final int callingUid = n.getUid();
final String pkg = n.getPackageName();
applyFlagBubble(r, pkg, callingUid, null /* oldEntry */, isAppForeground);
@@ -1780,7 +1781,7 @@
if (mNotificationsByKey.containsKey(posted.getKey())) {
count--;
}
- if (posted.sbn.isGroup() && posted.getNotification().isGroupSummary()) {
+ if (posted.getSbn().isGroup() && posted.getNotification().isGroupSummary()) {
count--;
}
}
@@ -1802,8 +1803,8 @@
@VisibleForTesting
void addNotification(NotificationRecord r) {
mNotificationList.add(r);
- mNotificationsByKey.put(r.sbn.getKey(), r);
- if (r.sbn.isGroup()) {
+ mNotificationsByKey.put(r.getSbn().getKey(), r);
+ if (r.getSbn().isGroup()) {
mSummaryByGroupKey.put(r.getGroupKey(), r);
}
}
@@ -2059,9 +2060,9 @@
if (DBG) {
Slog.d(TAG, "Reposting " + r.getKey());
}
- enqueueNotificationInternal(r.sbn.getPackageName(), r.sbn.getOpPkg(),
- r.sbn.getUid(), r.sbn.getInitialPid(), r.sbn.getTag(), r.sbn.getId(),
- r.sbn.getNotification(), userId);
+ enqueueNotificationInternal(r.getSbn().getPackageName(), r.getSbn().getOpPkg(),
+ r.getSbn().getUid(), r.getSbn().getInitialPid(), r.getSbn().getTag(),
+ r.getSbn().getId(), r.getSbn().getNotification(), userId);
} catch (Exception e) {
Slog.e(TAG, "Cannot un-snooze notification", e);
}
@@ -2201,7 +2202,7 @@
String pkg;
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
- pkg = r != null && r.sbn != null ? r.sbn.getPackageName() : null;
+ pkg = r != null && r.getSbn() != null ? r.getSbn().getPackageName() : null;
}
boolean isAppForeground = pkg != null
&& mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
@@ -2209,7 +2210,7 @@
NotificationRecord r = mNotificationsByKey.get(key);
if (r == null) return;
updateAutobundledSummaryFlags(r.getUser().getIdentifier(),
- r.sbn.getPackageName(), needsOngoingFlag, isAppForeground);
+ r.getSbn().getPackageName(), needsOngoingFlag, isAppForeground);
}
}
});
@@ -2280,7 +2281,8 @@
final long updatedSuppressedEffects = calculateSuppressedEffects();
if (updatedSuppressedEffects == mZenModeHelper.getSuppressedEffects()) return;
final List<ComponentName> suppressors = getSuppressors();
- ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressors, suppressors, updatedSuppressedEffects);
+ ZenLog.traceEffectsSuppressorChanged(
+ mEffectsSuppressors, suppressors, updatedSuppressedEffects);
mEffectsSuppressors = suppressors;
mZenModeHelper.setSuppressedEffects(updatedSuppressedEffects);
sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
@@ -2486,6 +2488,18 @@
scheduleInterruptionFilterChanged(interruptionFilter);
}
+ int correctCategory(int requestedCategoryList, int categoryType,
+ int currentCategoryList) {
+ if ((requestedCategoryList & categoryType) != 0
+ && (currentCategoryList & categoryType) == 0) {
+ requestedCategoryList &= ~categoryType;
+ } else if ((requestedCategoryList & categoryType) == 0
+ && (currentCategoryList & categoryType) != 0){
+ requestedCategoryList |= categoryType;
+ }
+ return requestedCategoryList;
+ }
+
@VisibleForTesting
INotificationManager getBinderService() {
return INotificationManager.Stub.asInterface(mService);
@@ -2498,8 +2512,8 @@
@GuardedBy("mNotificationLock")
protected void reportSeen(NotificationRecord r) {
if (!r.isProxied()) {
- mAppUsageStats.reportEvent(r.sbn.getPackageName(),
- getRealUserId(r.sbn.getUserId()),
+ mAppUsageStats.reportEvent(r.getSbn().getPackageName(),
+ getRealUserId(r.getSbn().getUserId()),
UsageEvents.Event.NOTIFICATION_SEEN);
}
}
@@ -2574,18 +2588,18 @@
@GuardedBy("mNotificationLock")
protected void maybeRecordInterruptionLocked(NotificationRecord r) {
if (r.isInterruptive() && !r.hasRecordedInterruption()) {
- mAppUsageStats.reportInterruptiveNotification(r.sbn.getPackageName(),
+ mAppUsageStats.reportInterruptiveNotification(r.getSbn().getPackageName(),
r.getChannel().getId(),
- getRealUserId(r.sbn.getUserId()));
+ getRealUserId(r.getSbn().getUserId()));
mHistoryManager.addNotification(new HistoricalNotification.Builder()
- .setPackage(r.sbn.getPackageName())
- .setUid(r.sbn.getUid())
+ .setPackage(r.getSbn().getPackageName())
+ .setUid(r.getSbn().getUid())
.setChannelId(r.getChannel().getId())
.setChannelName(r.getChannel().getName().toString())
.setPostedTimeMs(System.currentTimeMillis())
.setTitle(getHistoryTitle(r.getNotification()))
.setText(getHistoryText(
- r.sbn.getPackageContext(getContext()), r.getNotification()))
+ r.getSbn().getPackageContext(getContext()), r.getNotification()))
.setIcon(r.getNotification().getSmallIcon())
.build());
r.setRecordedInterruption(true);
@@ -2632,8 +2646,8 @@
* @param r notification record
*/
protected void reportUserInteraction(NotificationRecord r) {
- mAppUsageStats.reportEvent(r.sbn.getPackageName(),
- getRealUserId(r.sbn.getUserId()),
+ mAppUsageStats.reportEvent(r.getSbn().getPackageName(),
+ getRealUserId(r.getSbn().getUserId()),
UsageEvents.Event.USER_INTERACTION);
}
@@ -3509,7 +3523,7 @@
tmp = new StatusBarNotification[mNotificationList.size()];
final int N = mNotificationList.size();
for (int i=0; i<N; i++) {
- tmp[i] = mNotificationList.get(i).sbn;
+ tmp[i] = mNotificationList.get(i).getSbn();
}
}
}
@@ -3541,13 +3555,13 @@
final int N = mNotificationList.size();
for (int i = 0; i < N; i++) {
StatusBarNotification sbn = sanitizeSbn(pkg, userId,
- mNotificationList.get(i).sbn);
+ mNotificationList.get(i).getSbn());
if (sbn != null) {
map.put(sbn.getKey(), sbn);
}
}
for(NotificationRecord snoozed: mSnoozeHelper.getSnoozed(userId, pkg)) {
- StatusBarNotification sbn = sanitizeSbn(pkg, userId, snoozed.sbn);
+ StatusBarNotification sbn = sanitizeSbn(pkg, userId, snoozed.getSbn());
if (sbn != null) {
map.put(sbn.getKey(), sbn);
}
@@ -3555,7 +3569,7 @@
final int M = mEnqueuedNotifications.size();
for (int i = 0; i < M; i++) {
StatusBarNotification sbn = sanitizeSbn(pkg, userId,
- mEnqueuedNotifications.get(i).sbn);
+ mEnqueuedNotifications.get(i).getSbn());
if (sbn != null) {
map.put(sbn.getKey(), sbn); // pending update overwrites existing post here
}
@@ -3673,15 +3687,15 @@
for (int i = 0; i < N; i++) {
NotificationRecord r = mNotificationsByKey.get(keys[i]);
if (r == null) continue;
- final int userId = r.sbn.getUserId();
+ final int userId = r.getSbn().getUserId();
if (userId != info.userid && userId != UserHandle.USER_ALL &&
!mUserProfiles.isCurrentProfile(userId)) {
throw new SecurityException("Disallowed call from listener: "
+ info.service);
}
cancelNotificationFromListenerLocked(info, callingUid, callingPid,
- r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
- userId);
+ r.getSbn().getPackageName(), r.getSbn().getTag(),
+ r.getSbn().getId(), userId);
}
} else {
cancelAllLocked(callingUid, callingPid, info.userid,
@@ -3741,7 +3755,7 @@
for (int i = 0; i < n; i++) {
NotificationRecord r = mNotificationsByKey.get(keys[i]);
if (r == null) continue;
- final int userId = r.sbn.getUserId();
+ final int userId = r.getSbn().getUserId();
if (userId != info.userid && userId != UserHandle.USER_ALL
&& !mUserProfiles.isCurrentProfile(userId)) {
throw new SecurityException("Disallowed call from listener: "
@@ -3891,7 +3905,7 @@
? mNotificationsByKey.get(keys[i])
: mNotificationList.get(i);
if (r == null) continue;
- StatusBarNotification sbn = r.sbn;
+ StatusBarNotification sbn = r.getSbn();
if (!isVisibleToListener(sbn, info)) continue;
StatusBarNotification sbnToSend =
(trim == TRIM_FULL) ? sbn : sbn.cloneLight();
@@ -3921,7 +3935,7 @@
for (int i=0; i < N; i++) {
final NotificationRecord r = snoozedRecords.get(i);
if (r == null) continue;
- StatusBarNotification sbn = r.sbn;
+ StatusBarNotification sbn = r.getSbn();
if (!isVisibleToListener(sbn, info)) continue;
StatusBarNotification sbnToSend =
(trim == TRIM_FULL) ? sbn : sbn.cloneLight();
@@ -4096,7 +4110,8 @@
Objects.requireNonNull(packageName, "Package name is null");
enforceSystemOrSystemUI("removeAutomaticZenRules");
- return mZenModeHelper.removeAutomaticZenRules(packageName, "removeAutomaticZenRules");
+ return mZenModeHelper.removeAutomaticZenRules(packageName,
+ packageName + "|removeAutomaticZenRules");
}
@Override
@@ -4411,11 +4426,20 @@
policy.priorityCallSenders, policy.priorityMessageSenders,
policy.suppressedVisualEffects);
}
+ if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.R) {
+ int priorityCategories = correctCategory(policy.priorityCategories,
+ Policy.PRIORITY_CATEGORY_CONVERSATIONS,
+ currPolicy.priorityCategories);
+
+ policy = new Policy(priorityCategories,
+ policy.priorityCallSenders, policy.priorityMessageSenders,
+ policy.suppressedVisualEffects, currPolicy.priorityConversationSenders);
+ }
int newVisualEffects = calculateSuppressedVisualEffects(
policy, currPolicy, applicationInfo.targetSdkVersion);
policy = new Policy(policy.priorityCategories,
policy.priorityCallSenders, policy.priorityMessageSenders,
- newVisualEffects);
+ newVisualEffects, policy.priorityConversationSenders);
ZenLog.traceSetNotificationPolicy(pkg, applicationInfo.targetSdkVersion, policy);
mZenModeHelper.setNotificationPolicy(policy);
} catch (RemoteException e) {
@@ -4424,6 +4448,8 @@
}
}
+
+
@Override
public List<String> getEnabledNotificationListenerPackages() {
checkCallerIsSystem();
@@ -4637,8 +4663,8 @@
Objects.requireNonNull(user);
verifyPrivilegedListener(token, user, true);
- return mPreferencesHelper.getNotificationChannels(pkg, getUidForPackageAndUser(pkg, user),
- false /* includeDeleted */);
+ return mPreferencesHelper.getNotificationChannels(pkg,
+ getUidForPackageAndUser(pkg, user), false /* includeDeleted */);
}
@Override
@@ -4830,7 +4856,7 @@
if (r == null) {
return;
}
- if (r.sbn.getOverrideGroupKey() == null) {
+ if (r.getSbn().getOverrideGroupKey() == null) {
addAutoGroupAdjustment(r, GroupHelper.AUTOGROUP_KEY);
EventLogTags.writeNotificationAutogrouped(key);
mRankingHandler.requestSort();
@@ -4843,7 +4869,7 @@
if (r == null) {
return;
}
- if (r.sbn.getOverrideGroupKey() != null) {
+ if (r.getSbn().getOverrideGroupKey() != null) {
addAutoGroupAdjustment(r, null);
EventLogTags.writeNotificationUnautogrouped(key);
mRankingHandler.requestSort();
@@ -4853,8 +4879,8 @@
private void addAutoGroupAdjustment(NotificationRecord r, String overrideGroupKey) {
Bundle signals = new Bundle();
signals.putString(Adjustment.KEY_GROUP_KEY, overrideGroupKey);
- Adjustment adjustment =
- new Adjustment(r.sbn.getPackageName(), r.getKey(), signals, "", r.sbn.getUserId());
+ Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals, "",
+ r.getSbn().getUserId());
r.addAdjustment(adjustment);
}
@@ -4890,7 +4916,7 @@
// adjustment will post a summary if needed.
return;
}
- final StatusBarNotification adjustedSbn = notificationRecord.sbn;
+ final StatusBarNotification adjustedSbn = notificationRecord.getSbn();
userId = adjustedSbn.getUser().getIdentifier();
ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
if (summaries == null) {
@@ -4938,7 +4964,8 @@
}
}
if (summaryRecord != null && checkDisqualifyingFeatures(userId, MY_UID,
- summaryRecord.sbn.getId(), summaryRecord.sbn.getTag(), summaryRecord, true)) {
+ summaryRecord.getSbn().getId(), summaryRecord.getSbn().getTag(), summaryRecord,
+ true)) {
mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord, isAppForeground));
}
}
@@ -4999,14 +5026,14 @@
int N = mNotificationList.size();
for (int i = 0; i < N; i++) {
final NotificationRecord nr = mNotificationList.get(i);
- if (filter.filtered && !filter.matches(nr.sbn)) continue;
+ if (filter.filtered && !filter.matches(nr.getSbn())) continue;
nr.dump(proto, NotificationServiceDumpProto.RECORDS, filter.redact,
NotificationRecordProto.POSTED);
}
N = mEnqueuedNotifications.size();
for (int i = 0; i < N; i++) {
final NotificationRecord nr = mEnqueuedNotifications.get(i);
- if (filter.filtered && !filter.matches(nr.sbn)) continue;
+ if (filter.filtered && !filter.matches(nr.getSbn())) continue;
nr.dump(proto, NotificationServiceDumpProto.RECORDS, filter.redact,
NotificationRecordProto.ENQUEUED);
}
@@ -5014,7 +5041,7 @@
N = snoozed.size();
for (int i = 0; i < N; i++) {
final NotificationRecord nr = snoozed.get(i);
- if (filter.filtered && !filter.matches(nr.sbn)) continue;
+ if (filter.filtered && !filter.matches(nr.getSbn())) continue;
nr.dump(proto, NotificationServiceDumpProto.RECORDS, filter.redact,
NotificationRecordProto.SNOOZED);
}
@@ -5075,7 +5102,7 @@
pw.println(" Notification List:");
for (int i = 0; i < N; i++) {
final NotificationRecord nr = mNotificationList.get(i);
- if (filter.filtered && !filter.matches(nr.sbn)) continue;
+ if (filter.filtered && !filter.matches(nr.getSbn())) continue;
nr.dump(pw, " ", getContext(), filter.redact);
}
pw.println(" ");
@@ -5155,7 +5182,7 @@
pw.println(" Enqueued Notification List:");
for (int i = 0; i < N; i++) {
final NotificationRecord nr = mEnqueuedNotifications.get(i);
- if (filter.filtered && !filter.matches(nr.sbn)) continue;
+ if (filter.filtered && !filter.matches(nr.getSbn())) continue;
nr.dump(pw, " ", getContext(), filter.redact);
}
pw.println(" ");
@@ -5281,7 +5308,7 @@
if (r == null) {
return;
}
- StatusBarNotification sbn = r.sbn;
+ StatusBarNotification sbn = r.getSbn();
// NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
// FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove
// FLAG_FOREGROUND_SERVICE, we have to revert to the flags we received
@@ -5312,7 +5339,7 @@
// Look for the notification, searching both the posted and enqueued lists.
NotificationRecord r = findNotificationLocked(pkg, tag, id, userId);
if (r != null) {
- if (!Objects.equals(opPkg, r.sbn.getOpPkg())) {
+ if (!Objects.equals(opPkg, r.getSbn().getOpPkg())) {
throw new SecurityException(opPkg + " does not have permission to "
+ "cancel a notification they did not post " + tag + " " + id);
}
@@ -5360,8 +5387,8 @@
try {
fixNotification(notification, pkg, tag, id, userId);
- } catch (NameNotFoundException e) {
- Slog.e(TAG, "Cannot create a context for sending app", e);
+ } catch (Exception e) {
+ Slog.e(TAG, "Cannot fix notification", e);
return;
}
@@ -5442,7 +5469,7 @@
}
if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
- r.sbn.getOverrideGroupKey() != null)) {
+ r.getSbn().getOverrideGroupKey() != null)) {
return;
}
@@ -5586,12 +5613,12 @@
if (shortcutId != null) {
// Must track shortcut based bubbles in case the shortcut is removed
HashMap<String, String> packageBubbles = mActiveShortcutBubbles.get(
- r.sbn.getPackageName());
+ r.getSbn().getPackageName());
if (packageBubbles == null) {
packageBubbles = new HashMap<>();
}
packageBubbles.put(shortcutId, r.getKey());
- mActiveShortcutBubbles.put(r.sbn.getPackageName(), packageBubbles);
+ mActiveShortcutBubbles.put(r.getSbn().getPackageName(), packageBubbles);
if (!mLauncherAppsCallbackRegistered) {
mLauncherAppsService.registerCallback(mLauncherAppsCallback, mHandler);
mLauncherAppsCallbackRegistered = true;
@@ -5602,12 +5629,12 @@
if (shortcutId != null) {
// No longer track shortcut
HashMap<String, String> packageBubbles = mActiveShortcutBubbles.get(
- r.sbn.getPackageName());
+ r.getSbn().getPackageName());
if (packageBubbles != null) {
packageBubbles.remove(shortcutId);
}
if (packageBubbles != null && packageBubbles.isEmpty()) {
- mActiveShortcutBubbles.remove(r.sbn.getPackageName());
+ mActiveShortcutBubbles.remove(r.getSbn().getPackageName());
}
if (mLauncherAppsCallbackRegistered && mActiveShortcutBubbles.isEmpty()) {
mLauncherAppsService.unregisterCallback(mLauncherAppsCallback);
@@ -5853,7 +5880,7 @@
*/
private boolean checkDisqualifyingFeatures(int userId, int uid, int id, String tag,
NotificationRecord r, boolean isAutogroup) {
- final String pkg = r.sbn.getPackageName();
+ final String pkg = r.getSbn().getPackageName();
final boolean isSystemNotification =
isUidSystemOrPhone(uid) || ("android".equals(pkg));
final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
@@ -5863,7 +5890,7 @@
if (!isSystemNotification && !isNotificationFromListener) {
synchronized (mNotificationLock) {
final int callingUid = Binder.getCallingUid();
- if (mNotificationsByKey.get(r.sbn.getKey()) == null
+ if (mNotificationsByKey.get(r.getSbn().getKey()) == null
&& isCallerInstantApp(callingUid, userId)) {
// Ephemeral apps have some special constraints for notifications.
// They are not allowed to create new notifications however they are allowed to
@@ -5874,7 +5901,7 @@
}
// rate limit updates that aren't completed progress notifications
- if (mNotificationsByKey.get(r.sbn.getKey()) != null
+ if (mNotificationsByKey.get(r.getSbn().getKey()) != null
&& !r.getNotification().hasCompletedProgress()
&& !isAutogroup) {
@@ -5884,7 +5911,7 @@
final long now = SystemClock.elapsedRealtime();
if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
- + ". Shedding " + r.sbn.getKey() + ". package=" + pkg);
+ + ". Shedding " + r.getSbn().getKey() + ". package=" + pkg);
mLastOverRateLogTime = now;
}
return false;
@@ -5933,10 +5960,10 @@
final int N = mNotificationList.size();
for (int i = 0; i < N; i++) {
final NotificationRecord existing = mNotificationList.get(i);
- if (existing.sbn.getPackageName().equals(pkg)
- && existing.sbn.getUserId() == userId) {
- if (existing.sbn.getId() == excludedId
- && TextUtils.equals(existing.sbn.getTag(), excludedTag)) {
+ if (existing.getSbn().getPackageName().equals(pkg)
+ && existing.getSbn().getUserId() == userId) {
+ if (existing.getSbn().getId() == excludedId
+ && TextUtils.equals(existing.getSbn().getTag(), excludedTag)) {
continue;
}
count++;
@@ -5945,8 +5972,8 @@
final int M = mEnqueuedNotifications.size();
for (int i = 0; i < M; i++) {
final NotificationRecord existing = mEnqueuedNotifications.get(i);
- if (existing.sbn.getPackageName().equals(pkg)
- && existing.sbn.getUserId() == userId) {
+ if (existing.getSbn().getPackageName().equals(pkg)
+ && existing.getSbn().getUserId() == userId) {
count++;
}
}
@@ -5965,8 +5992,8 @@
}
private boolean isBlocked(NotificationRecord r) {
- final String pkg = r.sbn.getPackageName();
- final int callingUid = r.sbn.getUid();
+ final String pkg = r.getSbn().getPackageName();
+ final int callingUid = r.getSbn().getUid();
return mPreferencesHelper.isGroupBlocked(pkg, callingUid, r.getChannel().getGroup())
|| mPreferencesHelper.getImportance(pkg, callingUid)
== NotificationManager.IMPORTANCE_NONE
@@ -5996,10 +6023,11 @@
@GuardedBy("mNotificationLock")
void snoozeLocked(NotificationRecord r) {
- if (r.sbn.isGroup()) {
+ if (r.getSbn().isGroup()) {
final List<NotificationRecord> groupNotifications =
findCurrentAndSnoozedGroupNotificationsLocked(
- r.sbn.getPackageName(), r.sbn.getGroupKey(), r.sbn.getUserId());
+ r.getSbn().getPackageName(),
+ r.getSbn().getGroupKey(), r.getSbn().getUserId());
if (r.getNotification().isGroupSummary()) {
// snooze all children
for (int i = 0; i < groupNotifications.size(); i++) {
@@ -6010,7 +6038,7 @@
} else {
// if there is a valid summary for this group, and we are snoozing the only
// child, also snooze the summary
- if (mSummaryByGroupKey.containsKey(r.sbn.getGroupKey())) {
+ if (mSummaryByGroupKey.containsKey(r.getSbn().getGroupKey())) {
if (groupNotifications.size() == 2) {
// snooze summary and the one child
for (int i = 0; i < groupNotifications.size(); i++) {
@@ -6041,7 +6069,7 @@
cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted, null);
updateLightsLocked();
if (mSnoozeCriterionId != null) {
- mAssistants.notifyAssistantSnoozedLocked(r.sbn, mSnoozeCriterionId);
+ mAssistants.notifyAssistantSnoozedLocked(r.getSbn(), mSnoozeCriterionId);
mSnoozeHelper.snooze(r, mSnoozeCriterionId);
} else {
mSnoozeHelper.snooze(r, mDuration);
@@ -6172,10 +6200,10 @@
final Long snoozeAt =
mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
r.getUser().getIdentifier(),
- r.sbn.getPackageName(), r.sbn.getKey());
+ r.getSbn().getPackageName(), r.getSbn().getKey());
final long currentTime = System.currentTimeMillis();
if (snoozeAt.longValue() > currentTime) {
- (new SnoozeNotificationRunnable(r.sbn.getKey(),
+ (new SnoozeNotificationRunnable(r.getSbn().getKey(),
snoozeAt.longValue() - currentTime, null)).snoozeLocked(r);
return;
}
@@ -6183,9 +6211,9 @@
final String contextId =
mSnoozeHelper.getSnoozeContextForUnpostedNotification(
r.getUser().getIdentifier(),
- r.sbn.getPackageName(), r.sbn.getKey());
+ r.getSbn().getPackageName(), r.getSbn().getKey());
if (contextId != null) {
- (new SnoozeNotificationRunnable(r.sbn.getKey(),
+ (new SnoozeNotificationRunnable(r.getSbn().getKey(),
0, contextId)).snoozeLocked(r);
return;
}
@@ -6193,7 +6221,7 @@
mEnqueuedNotifications.add(r);
scheduleTimeoutLocked(r);
- final StatusBarNotification n = r.sbn;
+ final StatusBarNotification n = r.getSbn();
if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
NotificationRecord old = mNotificationsByKey.get(n.getKey());
if (old != null) {
@@ -6291,20 +6319,20 @@
}
final boolean isPackageSuspended =
- isPackagePausedOrSuspended(r.sbn.getPackageName(), r.getUid());
+ isPackagePausedOrSuspended(r.getSbn().getPackageName(), r.getUid());
r.setHidden(isPackageSuspended);
if (isPackageSuspended) {
mUsageStats.registerSuspendedByAdmin(r);
}
NotificationRecord old = mNotificationsByKey.get(key);
- final StatusBarNotification n = r.sbn;
+ final StatusBarNotification n = r.getSbn();
final Notification notification = n.getNotification();
// Make sure the SBN has an instance ID for statsd logging.
- if (old == null || old.sbn.getInstanceId() == null) {
+ if (old == null || old.getSbn().getInstanceId() == null) {
n.setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
} else {
- n.setInstanceId(old.sbn.getInstanceId());
+ n.setInstanceId(old.getSbn().getInstanceId());
}
int index = indexOfNotificationLocked(n.getKey());
@@ -6344,7 +6372,7 @@
}
if (notification.getSmallIcon() != null) {
- StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
+ StatusBarNotification oldSbn = (old != null) ? old.getSbn() : null;
mListeners.notifyPostedLocked(r, old);
if ((oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup()))
&& !isCritical(r)) {
@@ -6358,7 +6386,7 @@
} else if (oldSbn != null) {
final NotificationRecord finalRecord = r;
mHandler.post(() -> mGroupHelper.onNotificationUpdated(
- finalRecord.sbn, hasAutoGroupSummaryLocked(n)));
+ finalRecord.getSbn(), hasAutoGroupSummaryLocked(n)));
}
} else {
Slog.e(TAG, "Not posting notification without small icon: " + notification);
@@ -6405,7 +6433,7 @@
@VisibleForTesting
protected boolean isVisuallyInterruptive(NotificationRecord old, NotificationRecord r) {
// Ignore summary updates because we don't display most of the information.
- if (r.sbn.isGroup() && r.sbn.getNotification().isGroupSummary()) {
+ if (r.getSbn().isGroup() && r.getSbn().getNotification().isGroupSummary()) {
if (DEBUG_INTERRUPTIVENESS) {
Slog.v(TAG, "INTERRUPTIVENESS: "
+ r.getKey() + " is not interruptive: summary");
@@ -6429,8 +6457,8 @@
return false;
}
- Notification oldN = old.sbn.getNotification();
- Notification newN = r.sbn.getNotification();
+ Notification oldN = old.getSbn().getNotification();
+ Notification newN = r.getSbn().getNotification();
if (oldN.extras == null || newN.extras == null) {
if (DEBUG_INTERRUPTIVENESS) {
Slog.v(TAG, "INTERRUPTIVENESS: "
@@ -6441,7 +6469,7 @@
// Ignore visual interruptions from foreground services because users
// consider them one 'session'. Count them for everything else.
- if ((r.sbn.getNotification().flags & FLAG_FOREGROUND_SERVICE) != 0) {
+ if ((r.getSbn().getNotification().flags & FLAG_FOREGROUND_SERVICE) != 0) {
if (DEBUG_INTERRUPTIVENESS) {
Slog.v(TAG, "INTERRUPTIVENESS: "
+ r.getKey() + " is not interruptive: foreground service");
@@ -6555,7 +6583,7 @@
@GuardedBy("mNotificationLock")
private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
int callingUid, int callingPid) {
- StatusBarNotification sbn = r.sbn;
+ StatusBarNotification sbn = r.getSbn();
Notification n = sbn.getNotification();
if (n.isGroupSummary() && !sbn.isAppGroup()) {
// notifications without a group shouldn't be a summary, otherwise autobundling can
@@ -6566,8 +6594,8 @@
String group = sbn.getGroupKey();
boolean isSummary = n.isGroupSummary();
- Notification oldN = old != null ? old.sbn.getNotification() : null;
- String oldGroup = old != null ? old.sbn.getGroupKey() : null;
+ Notification oldN = old != null ? old.getSbn().getNotification() : null;
+ String oldGroup = old != null ? old.getSbn().getGroupKey() : null;
boolean oldIsSummary = old != null && oldN.isGroupSummary();
if (oldIsSummary) {
@@ -6624,7 +6652,7 @@
boolean beep = false;
boolean blink = false;
- final Notification notification = record.sbn.getNotification();
+ final Notification notification = record.getSbn().getNotification();
final String key = record.getKey();
// Should this notification make noise, vibe, or use the LED?
@@ -6642,7 +6670,7 @@
// If the notification will appear in the status bar, it should send an accessibility
// event
if (!record.isUpdate && record.getImportance() > IMPORTANCE_MIN) {
- sendAccessibilityEvent(notification, record.sbn.getPackageName());
+ sendAccessibilityEvent(notification, record.getSbn().getPackageName());
sentAccessibilityEvent = true;
}
@@ -6664,7 +6692,7 @@
boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
if (!sentAccessibilityEvent) {
- sendAccessibilityEvent(notification, record.sbn.getPackageName());
+ sendAccessibilityEvent(notification, record.getSbn().getPackageName());
sentAccessibilityEvent = true;
}
if (DBG) Slog.v(TAG, "Interrupting!");
@@ -6719,7 +6747,7 @@
final int buzzBeepBlink = (buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0);
if (buzzBeepBlink > 0) {
// Ignore summary updates because we don't display most of the information.
- if (record.sbn.isGroup() && record.sbn.getNotification().isGroupSummary()) {
+ if (record.getSbn().isGroup() && record.getSbn().getNotification().isGroupSummary()) {
if (DEBUG_INTERRUPTIVENESS) {
Slog.v(TAG, "INTERRUPTIVENESS: "
+ record.getKey() + " is not interruptive: summary");
@@ -6774,7 +6802,7 @@
return false;
}
// Suppressed because another notification in its group handles alerting
- if (record.sbn.isGroup() && record.getNotification().suppressAlertingDueToGrouping()) {
+ if (record.getSbn().isGroup() && record.getNotification().suppressAlertingDueToGrouping()) {
return false;
}
// not if in call or the screen's on
@@ -6806,14 +6834,14 @@
}
// Suppressed because another notification in its group handles alerting
- if (record.sbn.isGroup()) {
+ if (record.getSbn().isGroup()) {
if (notification.suppressAlertingDueToGrouping()) {
return true;
}
}
// Suppressed for being too recently noisy
- final String pkg = record.sbn.getPackageName();
+ final String pkg = record.getSbn().getPackageName();
if (mUsageStats.isAlertRateLimited(pkg)) {
Slog.e(TAG, "Muting recently noisy " + record.getKey());
return true;
@@ -6854,7 +6882,7 @@
if (player != null) {
if (DBG) Slog.v(TAG, "Playing sound " + soundUri
+ " with attributes " + record.getAudioAttributes());
- player.playAsync(soundUri, record.sbn.getUser(), looping,
+ player.playAsync(soundUri, record.getSbn().getUser(), looping,
record.getAudioAttributes());
return true;
}
@@ -6897,15 +6925,16 @@
// so need to check the notification still valide for vibrate.
synchronized (mNotificationLock) {
if (mNotificationsByKey.get(record.getKey()) != null) {
- mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
+ mVibrator.vibrate(record.getSbn().getUid(), record.getSbn().getOpPkg(),
effect, "Notification (delayed)", record.getAudioAttributes());
} else {
- Slog.e(TAG, "No vibration for canceled notification : " + record.getKey());
+ Slog.e(TAG, "No vibration for canceled notification : "
+ + record.getKey());
}
}
}).start();
} else {
- mVibrator.vibrate(record.sbn.getUid(), record.sbn.getPackageName(),
+ mVibrator.vibrate(record.getSbn().getUid(), record.getSbn().getPackageName(),
effect, "Notification", record.getAudioAttributes());
}
return true;
@@ -7388,7 +7417,7 @@
if ((recordInList = findNotificationByListLocked(mNotificationList, r.getKey()))
!= null) {
mNotificationList.remove(recordInList);
- mNotificationsByKey.remove(recordInList.sbn.getKey());
+ mNotificationsByKey.remove(recordInList.getSbn().getKey());
wasPosted = true;
}
while ((recordInList = findNotificationByListLocked(mEnqueuedNotifications, r.getKey()))
@@ -7429,7 +7458,7 @@
} catch (PendingIntent.CanceledException ex) {
// do nothing - there's no relevant way to recover, and
// no reason to let this propagate
- Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
+ Slog.w(TAG, "canceled PendingIntent for " + r.getSbn().getPackageName(), ex);
}
}
}
@@ -7445,7 +7474,7 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- mGroupHelper.onNotificationRemoved(r.sbn);
+ mGroupHelper.onNotificationRemoved(r.getSbn());
}
});
}
@@ -7501,13 +7530,15 @@
if (groupSummary != null && groupSummary.getKey().equals(canceledKey)) {
mSummaryByGroupKey.remove(groupKey);
}
- final ArrayMap<String, String> summaries = mAutobundledSummaries.get(r.sbn.getUserId());
- if (summaries != null && r.sbn.getKey().equals(summaries.get(r.sbn.getPackageName()))) {
- summaries.remove(r.sbn.getPackageName());
+ final ArrayMap<String, String> summaries =
+ mAutobundledSummaries.get(r.getSbn().getUserId());
+ if (summaries != null && r.getSbn().getKey().equals(
+ summaries.get(r.getSbn().getPackageName()))) {
+ summaries.remove(r.getSbn().getPackageName());
}
// Save it for users of getHistoricalNotifications()
- mArchive.record(r.sbn);
+ mArchive.record(r.getSbn());
final long now = System.currentTimeMillis();
final LogMaker logMaker = r.getItemLogMaker()
@@ -7563,7 +7594,7 @@
for (int i = 0; i < newUris.size(); i++) {
final Uri uri = newUris.valueAt(i);
if (oldUris == null || !oldUris.contains(uri)) {
- if (DBG) Slog.d(TAG, key + ": granting " + uri);
+ Slog.d(TAG, key + ": granting " + uri);
grantUriPermission(permissionOwner, uri, newRecord.getUid(), targetPkg,
targetUserId);
}
@@ -7600,6 +7631,8 @@
targetUserId);
} catch (RemoteException ignored) {
// Ignored because we're in same process
+ } catch (SecurityException e) {
+ Slog.e(TAG, "Cannot grant uri access; " + sourceUid + " does not own " + uri);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -7756,7 +7789,7 @@
if (!flagChecker.apply(r.getFlags())) {
continue;
}
- if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
+ if (pkg != null && !r.getSbn().getPackageName().equals(pkg)) {
continue;
}
if (channelId != null && !channelId.equals(r.getChannel().getId())) {
@@ -7852,7 +7885,7 @@
return;
}
- String pkg = r.sbn.getPackageName();
+ String pkg = r.getSbn().getPackageName();
if (pkg == null) {
if (DBG) Slog.e(TAG, "No package for group summary: " + r.getKey());
@@ -7869,12 +7902,12 @@
private void cancelGroupChildrenByListLocked(ArrayList<NotificationRecord> notificationList,
NotificationRecord parentNotification, int callingUid, int callingPid,
String listenerName, boolean sendDelete, boolean wasPosted, FlagChecker flagChecker) {
- final String pkg = parentNotification.sbn.getPackageName();
+ final String pkg = parentNotification.getSbn().getPackageName();
final int userId = parentNotification.getUserId();
final int reason = REASON_GROUP_SUMMARY_CANCELED;
for (int i = notificationList.size() - 1; i >= 0; i--) {
final NotificationRecord childR = notificationList.get(i);
- final StatusBarNotification childSbn = childR.sbn;
+ final StatusBarNotification childSbn = childR.getSbn();
if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
childR.getGroupKey().equals(parentNotification.getGroupKey())
&& (childR.getFlags() & FLAG_FOREGROUND_SERVICE) == 0
@@ -7952,7 +7985,7 @@
for (int i = 0; i < len; i++) {
NotificationRecord r = list.get(i);
if (notificationMatchesUserId(r, userId) && r.getGroupKey().equals(groupKey)
- && r.sbn.getPackageName().equals(pkg)) {
+ && r.getSbn().getPackageName().equals(pkg)) {
records.add(r);
}
}
@@ -7993,8 +8026,9 @@
final int len = list.size();
for (int i = 0; i < len; i++) {
NotificationRecord r = list.get(i);
- if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
- TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
+ if (notificationMatchesUserId(r, userId) && r.getSbn().getId() == id &&
+ TextUtils.equals(r.getSbn().getTag(), tag)
+ && r.getSbn().getPackageName().equals(pkg)) {
return r;
}
}
@@ -8008,8 +8042,9 @@
final int len = list.size();
for (int i = 0; i < len; i++) {
NotificationRecord r = list.get(i);
- if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
- TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
+ if (notificationMatchesUserId(r, userId) && r.getSbn().getId() == id &&
+ TextUtils.equals(r.getSbn().getTag(), tag)
+ && r.getSbn().getPackageName().equals(pkg)) {
matching.add(r);
}
}
@@ -8047,7 +8082,7 @@
int numNotifications = mNotificationList.size();
for (int i = 0; i < numNotifications; i++) {
NotificationRecord rec = mNotificationList.get(i);
- if (pkgList.contains(rec.sbn.getPackageName())) {
+ if (pkgList.contains(rec.getSbn().getPackageName())) {
rec.setHidden(true);
changedNotifications.add(rec);
}
@@ -8065,7 +8100,7 @@
int numNotifications = mNotificationList.size();
for (int i = 0; i < numNotifications; i++) {
NotificationRecord rec = mNotificationList.get(i);
- if (pkgList.contains(rec.sbn.getPackageName())) {
+ if (pkgList.contains(rec.getSbn().getPackageName())) {
rec.setHidden(false);
changedNotifications.add(rec);
}
@@ -8264,10 +8299,10 @@
for (int i = 0; i < N; i++) {
NotificationRecord record = mNotificationList.get(i);
- if (!isVisibleToListener(record.sbn, info)) {
+ if (!isVisibleToListener(record.getSbn(), info)) {
continue;
}
- final String key = record.sbn.getKey();
+ final String key = record.getSbn().getKey();
final NotificationListenerService.Ranking ranking =
new NotificationListenerService.Ranking();
ranking.populate(
@@ -8278,7 +8313,7 @@
record.getSuppressedVisualEffects(),
record.getImportance(),
record.getImportanceExplanation(),
- record.sbn.getOverrideGroupKey(),
+ record.getSbn().getOverrideGroupKey(),
record.getChannel(),
record.getPeopleOverride(),
record.getSnoozeCriteria(),
@@ -8550,7 +8585,7 @@
for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
ArrayList<String> keys = new ArrayList<>(records.size());
for (NotificationRecord r : records) {
- boolean sbnVisible = isVisibleToListener(r.sbn, info)
+ boolean sbnVisible = isVisibleToListener(r.getSbn(), info)
&& info.isSameUser(r.getUserId());
if (sbnVisible) {
keys.add(r.getKey());
@@ -8638,7 +8673,7 @@
if (debug) {
Slog.v(TAG, "onNotificationEnqueuedLocked() called with: r = [" + r + "]");
}
- final StatusBarNotification sbn = r.sbn;
+ final StatusBarNotification sbn = r.getSbn();
notifyAssistantLocked(
sbn,
true /* sameUserOnly */,
@@ -8977,59 +9012,64 @@
@GuardedBy("mNotificationLock")
private void notifyPostedLocked(NotificationRecord r, NotificationRecord old,
boolean notifyAllListeners) {
- // Lazily initialized snapshots of the notification.
- StatusBarNotification sbn = r.sbn;
- StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
- TrimCache trimCache = new TrimCache(sbn);
+ try {
+ // Lazily initialized snapshots of the notification.
+ StatusBarNotification sbn = r.getSbn();
+ StatusBarNotification oldSbn = (old != null) ? old.getSbn() : null;
+ TrimCache trimCache = new TrimCache(sbn);
- for (final ManagedServiceInfo info : getServices()) {
- boolean sbnVisible = isVisibleToListener(sbn, info);
- boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
- // This notification hasn't been and still isn't visible -> ignore.
- 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.
- if (r.isHidden() && info.targetSdkVersion < Build.VERSION_CODES.P) {
- continue;
- }
+ for (final ManagedServiceInfo info : getServices()) {
+ boolean sbnVisible = isVisibleToListener(sbn, info);
+ boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info)
+ : false;
+ // This notification hasn't been and still isn't visible -> ignore.
+ 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.
+ if (r.isHidden() && info.targetSdkVersion < Build.VERSION_CODES.P) {
+ continue;
+ }
- // If we shouldn't notify all listeners, this means the hidden state of
- // a notification was changed. Don't notifyPosted listeners targeting >= P.
- // Instead, those listeners will receive notifyRankingUpdate.
- if (!notifyAllListeners && info.targetSdkVersion >= Build.VERSION_CODES.P) {
- continue;
- }
+ // If we shouldn't notify all listeners, this means the hidden state of
+ // a notification was changed. Don't notifyPosted listeners targeting >= P.
+ // Instead, those listeners will receive notifyRankingUpdate.
+ if (!notifyAllListeners && info.targetSdkVersion >= Build.VERSION_CODES.P) {
+ continue;
+ }
- final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
+ final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
- // This notification became invisible -> remove the old one.
- if (oldSbnVisible && !sbnVisible) {
- final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
+ // This notification became invisible -> remove the old one.
+ if (oldSbnVisible && !sbnVisible) {
+ final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ notifyRemoved(
+ info, oldSbnLightClone, update, null, REASON_USER_STOPPED);
+ }
+ });
+ continue;
+ }
+
+ // Grant access before listener is notified
+ final int targetUserId = (info.userid == UserHandle.USER_ALL)
+ ? UserHandle.USER_SYSTEM : info.userid;
+ updateUriPermissions(r, old, info.component.getPackageName(), targetUserId);
+
+ final StatusBarNotification sbnToPost = trimCache.ForListener(info);
mHandler.post(new Runnable() {
@Override
public void run() {
- notifyRemoved(
- info, oldSbnLightClone, update, null, REASON_USER_STOPPED);
+ notifyPosted(info, sbnToPost, update);
}
});
- continue;
}
-
- // Grant access before listener is notified
- final int targetUserId = (info.userid == UserHandle.USER_ALL)
- ? UserHandle.USER_SYSTEM : info.userid;
- updateUriPermissions(r, old, info.component.getPackageName(), targetUserId);
-
- final StatusBarNotification sbnToPost = trimCache.ForListener(info);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- notifyPosted(info, sbnToPost, update);
- }
- });
+ } catch (Exception e) {
+ Slog.e(TAG, "Could not notify listeners for " + r.getKey(), e);
}
}
@@ -9039,7 +9079,7 @@
@GuardedBy("mNotificationLock")
public void notifyRemovedLocked(NotificationRecord r, int reason,
NotificationStats notificationStats) {
- final StatusBarNotification sbn = r.sbn;
+ final StatusBarNotification sbn = r.getSbn();
// make a copy in case changes are made to the underlying Notification object
// NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
@@ -9103,7 +9143,7 @@
if (isHiddenRankingUpdate && serviceInfo.targetSdkVersion >=
Build.VERSION_CODES.P) {
for (NotificationRecord rec : changedHiddenNotifications) {
- if (isVisibleToListener(rec.sbn, serviceInfo)) {
+ if (isVisibleToListener(rec.getSbn(), serviceInfo)) {
notifyThisListener = true;
break;
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 660d574..4785da9 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -91,7 +91,7 @@
static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
// the period after which a notification is updated where it can make sound
private static final int MAX_SOUND_DELAY_MS = 2000;
- final StatusBarNotification sbn;
+ private final StatusBarNotification sbn;
IActivityManager mAm;
UriGrantsManagerInternal mUgmInternal;
final int mTargetSdkVersion;
@@ -229,7 +229,7 @@
}
private Uri calculateSound() {
- final Notification n = sbn.getNotification();
+ final Notification n = getSbn().getNotification();
// No notification sounds on tv
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
@@ -265,7 +265,7 @@
if (mPreChannelsNotification
&& (getChannel().getUserLockedFields()
& NotificationChannel.USER_LOCKED_LIGHTS) == 0) {
- final Notification notification = sbn.getNotification();
+ final Notification notification = getSbn().getNotification();
if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
light = new Light(notification.ledARGB, notification.ledOnMS,
notification.ledOffMS);
@@ -296,7 +296,7 @@
if (mPreChannelsNotification
&& (getChannel().getUserLockedFields()
& NotificationChannel.USER_LOCKED_VIBRATION) == 0) {
- final Notification notification = sbn.getNotification();
+ final Notification notification = getSbn().getNotification();
final boolean useDefaultVibrate =
(notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
if (useDefaultVibrate) {
@@ -309,7 +309,7 @@
}
private AudioAttributes calculateAttributes() {
- final Notification n = sbn.getNotification();
+ final Notification n = getSbn().getNotification();
AudioAttributes attributes = getChannel().getAudioAttributes();
if (attributes == null) {
attributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
@@ -335,7 +335,7 @@
}
private int calculateInitialImportance() {
- final Notification n = sbn.getNotification();
+ final Notification n = getSbn().getNotification();
int importance = getChannel().getImportance(); // Post-channels notifications use this
mInitialImportanceExplanationCode = getChannel().hasUserSetImportance()
? MetricsEvent.IMPORTANCE_EXPLANATION_USER
@@ -406,31 +406,31 @@
mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
mCreationTimeMs = previous.mCreationTimeMs;
mVisibleSinceMs = previous.mVisibleSinceMs;
- if (previous.sbn.getOverrideGroupKey() != null && !sbn.isAppGroup()) {
- sbn.setOverrideGroupKey(previous.sbn.getOverrideGroupKey());
+ if (previous.getSbn().getOverrideGroupKey() != null && !getSbn().isAppGroup()) {
+ getSbn().setOverrideGroupKey(previous.getSbn().getOverrideGroupKey());
}
// Don't copy importance information or mGlobalSortKey, recompute them.
}
- public Notification getNotification() { return sbn.getNotification(); }
- public int getFlags() { return sbn.getNotification().flags; }
- public UserHandle getUser() { return sbn.getUser(); }
- public String getKey() { return sbn.getKey(); }
+ public Notification getNotification() { return getSbn().getNotification(); }
+ public int getFlags() { return getSbn().getNotification().flags; }
+ public UserHandle getUser() { return getSbn().getUser(); }
+ public String getKey() { return getSbn().getKey(); }
/** @deprecated Use {@link #getUser()} instead. */
- public int getUserId() { return sbn.getUserId(); }
- public int getUid() { return sbn.getUid(); }
+ public int getUserId() { return getSbn().getUserId(); }
+ public int getUid() { return getSbn().getUid(); }
void dump(ProtoOutputStream proto, long fieldId, boolean redact, int state) {
final long token = proto.start(fieldId);
- proto.write(NotificationRecordProto.KEY, sbn.getKey());
+ proto.write(NotificationRecordProto.KEY, getSbn().getKey());
proto.write(NotificationRecordProto.STATE, state);
if (getChannel() != null) {
proto.write(NotificationRecordProto.CHANNEL_ID, getChannel().getId());
}
proto.write(NotificationRecordProto.CAN_SHOW_LIGHT, getLight() != null);
proto.write(NotificationRecordProto.CAN_VIBRATE, getVibration() != null);
- proto.write(NotificationRecordProto.FLAGS, sbn.getNotification().flags);
+ proto.write(NotificationRecordProto.FLAGS, getSbn().getNotification().flags);
proto.write(NotificationRecordProto.GROUP_KEY, getGroupKey());
proto.write(NotificationRecordProto.IMPORTANCE, getImportance());
if (getSound() != null) {
@@ -439,8 +439,8 @@
if (getAudioAttributes() != null) {
getAudioAttributes().dumpDebug(proto, NotificationRecordProto.AUDIO_ATTRIBUTES);
}
- proto.write(NotificationRecordProto.PACKAGE, sbn.getPackageName());
- proto.write(NotificationRecordProto.DELEGATE_PACKAGE, sbn.getOpPkg());
+ proto.write(NotificationRecordProto.PACKAGE, getSbn().getPackageName());
+ proto.write(NotificationRecordProto.DELEGATE_PACKAGE, getSbn().getOpPkg());
proto.end(token);
}
@@ -452,15 +452,15 @@
}
void dump(PrintWriter pw, String prefix, Context baseContext, boolean redact) {
- final Notification notification = sbn.getNotification();
+ final Notification notification = getSbn().getNotification();
pw.println(prefix + this);
prefix = prefix + " ";
- pw.println(prefix + "uid=" + sbn.getUid() + " userId=" + sbn.getUserId());
- pw.println(prefix + "opPkg=" + sbn.getOpPkg());
+ pw.println(prefix + "uid=" + getSbn().getUid() + " userId=" + getSbn().getUserId());
+ pw.println(prefix + "opPkg=" + getSbn().getOpPkg());
pw.println(prefix + "icon=" + notification.getSmallIcon());
pw.println(prefix + "flags=0x" + Integer.toHexString(notification.flags));
pw.println(prefix + "pri=" + notification.priority);
- pw.println(prefix + "key=" + sbn.getKey());
+ pw.println(prefix + "key=" + getSbn().getKey());
pw.println(prefix + "seen=" + mStats.hasSeen());
pw.println(prefix + "groupKey=" + getGroupKey());
pw.println(prefix + "fullscreenIntent=" + notification.fullScreenIntent);
@@ -594,9 +594,9 @@
"NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s importance=%d key=%s" +
": %s)",
System.identityHashCode(this),
- this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(),
- this.sbn.getTag(), this.mImportance, this.sbn.getKey(),
- this.sbn.getNotification());
+ this.getSbn().getPackageName(), this.getSbn().getUser(), this.getSbn().getId(),
+ this.getSbn().getTag(), this.mImportance, this.getSbn().getKey(),
+ this.getSbn().getNotification());
}
public boolean hasAdjustment(String key) {
@@ -936,7 +936,7 @@
private long calculateRankingTimeMs(long previousRankingTimeMs) {
Notification n = getNotification();
// Take developer provided 'when', unless it's in the future.
- if (n.when != 0 && n.when <= sbn.getPostTime()) {
+ if (n.when != 0 && n.when <= getSbn().getPostTime()) {
return n.when;
}
// If we've ranked a previous instance with a timestamp, inherit it. This case is
@@ -944,7 +944,7 @@
if (previousRankingTimeMs > 0) {
return previousRankingTimeMs;
}
- return sbn.getPostTime();
+ return getSbn().getPostTime();
}
public void setGlobalSortKey(String globalSortKey) {
@@ -977,11 +977,11 @@
}
public String getGroupKey() {
- return sbn.getGroupKey();
+ return getSbn().getGroupKey();
}
public void setOverrideGroupKey(String overrideGroupKey) {
- sbn.setOverrideGroupKey(overrideGroupKey);
+ getSbn().setOverrideGroupKey(overrideGroupKey);
}
public NotificationChannel getChannel() {
@@ -1202,7 +1202,7 @@
* Returns whether this notification was posted by a secondary app
*/
public boolean isProxied() {
- return !Objects.equals(sbn.getPackageName(), sbn.getOpPkg());
+ return !Objects.equals(getSbn().getPackageName(), getSbn().getOpPkg());
}
/**
@@ -1245,7 +1245,7 @@
if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
// We can't grant Uri permissions from system
- final int sourceUid = sbn.getUid();
+ final int sourceUid = getSbn().getUid();
if (sourceUid == android.os.Process.SYSTEM_UID) return;
final long ident = Binder.clearCallingIdentity();
@@ -1274,7 +1274,7 @@
}
public LogMaker getLogMaker(long now) {
- LogMaker lm = sbn.getLogMaker()
+ LogMaker lm = getSbn().getLogMaker()
.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_IMPORTANCE, mImportance)
.addTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS, getLifespanMs(now))
.addTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS, getFreshnessMs(now))
@@ -1351,6 +1351,10 @@
return true;
}
+ StatusBarNotification getSbn() {
+ return sbn;
+ }
+
@VisibleForTesting
static final class Light {
public final int color;
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index 9bbc3924..8d8511f 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -88,12 +88,12 @@
return true;
}
- return !(Objects.equals(r.sbn.getChannelIdLogTag(), old.sbn.getChannelIdLogTag())
- && Objects.equals(r.sbn.getGroupLogTag(), old.sbn.getGroupLogTag())
- && (r.sbn.getNotification().isGroupSummary()
- == old.sbn.getNotification().isGroupSummary())
- && Objects.equals(r.sbn.getNotification().category,
- old.sbn.getNotification().category)
+ return !(Objects.equals(r.getSbn().getChannelIdLogTag(), old.getSbn().getChannelIdLogTag())
+ && Objects.equals(r.getSbn().getGroupLogTag(), old.getSbn().getGroupLogTag())
+ && (r.getSbn().getNotification().isGroupSummary()
+ == old.getSbn().getNotification().isGroupSummary())
+ && Objects.equals(r.getSbn().getNotification().category,
+ old.getSbn().getNotification().category)
&& (r.getImportance() == old.getImportance()));
}
@@ -106,7 +106,7 @@
* @return hash code for the notification style class, or 0 if none exists.
*/
public int getStyle() {
- return getStyle(r.sbn.getNotification().extras);
+ return getStyle(r.getSbn().getNotification().extras);
}
private int getStyle(@Nullable Bundle extras) {
@@ -120,7 +120,7 @@
}
int getNumPeople() {
- return getNumPeople(r.sbn.getNotification().extras);
+ return getNumPeople(r.getSbn().getNotification().extras);
}
private int getNumPeople(@Nullable Bundle extras) {
@@ -140,7 +140,7 @@
}
int getInstanceId() {
- return (r.sbn.getInstanceId() == null ? 0 : r.sbn.getInstanceId().getId());
+ return (r.getSbn().getInstanceId() == null ? 0 : r.getSbn().getInstanceId().getId());
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index d613799..4974c30 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -34,15 +34,15 @@
FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_REPORTED,
/* int32 event_id = 1 */ p.getUiEvent().getId(),
/* int32 uid = 2 */ r.getUid(),
- /* string package_name = 3 */ r.sbn.getPackageName(),
+ /* string package_name = 3 */ r.getSbn().getPackageName(),
/* int32 instance_id = 4 */ p.getInstanceId(),
- /* int32 notification_id = 5 */ r.sbn.getId(),
- /* string notification_tag = 6 */ r.sbn.getTag(),
- /* string channel_id = 7 */ r.sbn.getChannelIdLogTag(),
- /* string group_id = 8 */ r.sbn.getGroupLogTag(),
+ /* int32 notification_id = 5 */ r.getSbn().getId(),
+ /* string notification_tag = 6 */ r.getSbn().getTag(),
+ /* string channel_id = 7 */ r.getSbn().getChannelIdLogTag(),
+ /* string group_id = 8 */ r.getSbn().getGroupLogTag(),
/* int32 group_instance_id = 9 */ 0, // TODO generate and fill instance ids
- /* bool is_group_summary = 10 */ r.sbn.getNotification().isGroupSummary(),
- /* string category = 11 */ r.sbn.getNotification().category,
+ /* bool is_group_summary = 10 */ r.getSbn().getNotification().isGroupSummary(),
+ /* string category = 11 */ r.getSbn().getNotification().category,
/* int32 style = 12 */ p.getStyle(),
/* int32 num_people = 13 */ p.getNumPeople(),
/* int32 position = 14 */ position,
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index d1fe0d9..b42fe92 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -276,7 +276,7 @@
// Locked by this.
private AggregatedStats[] getAggregatedStatsLocked(NotificationRecord record) {
- return getAggregatedStatsLocked(record.sbn.getPackageName());
+ return getAggregatedStatsLocked(record.getSbn().getPackageName());
}
// Locked by this.
@@ -1142,7 +1142,7 @@
long nowMs = System.currentTimeMillis();
switch (msg.what) {
case MSG_POST:
- writeEvent(r.sbn.getPostTime(), EVENT_TYPE_POST, r);
+ writeEvent(r.getSbn().getPostTime(), EVENT_TYPE_POST, r);
break;
case MSG_CLICK:
writeEvent(nowMs, EVENT_TYPE_CLICK, r);
@@ -1287,7 +1287,7 @@
private void writeEvent(long eventTimeMs, int eventType, NotificationRecord r) {
ContentValues cv = new ContentValues();
- cv.put(COL_EVENT_USER_ID, r.sbn.getUser().getIdentifier());
+ cv.put(COL_EVENT_USER_ID, r.getSbn().getUser().getIdentifier());
cv.put(COL_EVENT_TIME, eventTimeMs);
cv.put(COL_EVENT_TYPE, eventType);
putNotificationIdentifiers(r, cv);
@@ -1324,16 +1324,16 @@
}
private static void putNotificationIdentifiers(NotificationRecord r, ContentValues outCv) {
- outCv.put(COL_KEY, r.sbn.getKey());
- outCv.put(COL_PKG, r.sbn.getPackageName());
+ outCv.put(COL_KEY, r.getSbn().getKey());
+ outCv.put(COL_PKG, r.getSbn().getPackageName());
}
private static void putNotificationDetails(NotificationRecord r, ContentValues outCv) {
- outCv.put(COL_NOTIFICATION_ID, r.sbn.getId());
- if (r.sbn.getTag() != null) {
- outCv.put(COL_TAG, r.sbn.getTag());
+ outCv.put(COL_NOTIFICATION_ID, r.getSbn().getId());
+ if (r.getSbn().getTag() != null) {
+ outCv.put(COL_TAG, r.getSbn().getTag());
}
- outCv.put(COL_WHEN_MS, r.sbn.getPostTime());
+ outCv.put(COL_WHEN_MS, r.getSbn().getPostTime());
outCv.put(COL_FLAGS, r.getNotification().flags);
final int before = r.stats.requestedImportance;
final int after = r.getImportance();
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index b0c1863..fe39322 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1380,7 +1380,8 @@
policy.priorityCategories, policy.priorityCallSenders,
policy.priorityMessageSenders, policy.suppressedVisualEffects,
(areChannelsBypassingDnd ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND
- : 0)));
+ : 0),
+ policy.priorityConversationSenders));
}
public boolean areChannelsBypassingDnd() {
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 9e32d0e..661297a 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -166,7 +166,7 @@
ArrayMap<String, NotificationRecord> packages =
mSnoozedNotifications.get(userId).get(pkg);
for (int i = 0; i < packages.size(); i++) {
- String currentGroupKey = packages.valueAt(i).sbn.getGroup();
+ String currentGroupKey = packages.valueAt(i).getSbn().getGroup();
if (currentGroupKey.equals(groupKey)) {
records.add(packages.valueAt(i));
}
@@ -223,7 +223,7 @@
* Snoozes a notification and schedules an alarm to repost at that time.
*/
protected void snooze(NotificationRecord record, long duration) {
- String pkg = record.sbn.getPackageName();
+ String pkg = record.getSbn().getPackageName();
String key = record.getKey();
int userId = record.getUser().getIdentifier();
@@ -242,7 +242,7 @@
int userId = record.getUser().getIdentifier();
if (contextId != null) {
synchronized (mPersistedSnoozedNotificationsWithContext) {
- storeRecord(record.sbn.getPackageName(), record.getKey(),
+ storeRecord(record.getSbn().getPackageName(), record.getKey(),
userId, mPersistedSnoozedNotificationsWithContext, contextId);
}
}
@@ -254,9 +254,9 @@
if (DEBUG) {
Slog.d(TAG, "Snoozing " + record.getKey());
}
- storeRecord(record.sbn.getPackageName(), record.getKey(),
+ storeRecord(record.getSbn().getPackageName(), record.getKey(),
userId, mSnoozedNotifications, record);
- mPackages.put(record.getKey(), record.sbn.getPackageName());
+ mPackages.put(record.getKey(), record.getSbn().getPackageName());
mUsers.put(record.getKey(), userId);
}
@@ -308,7 +308,7 @@
if (recordsForPkg != null) {
final Set<Map.Entry<String, NotificationRecord>> records = recordsForPkg.entrySet();
for (Map.Entry<String, NotificationRecord> record : records) {
- final StatusBarNotification sbn = record.getValue().sbn;
+ final StatusBarNotification sbn = record.getValue().getSbn();
if (Objects.equals(sbn.getTag(), tag) && sbn.getId() == id) {
record.getValue().isCanceled = true;
return true;
@@ -369,7 +369,7 @@
if (records == null) {
return;
}
- ArrayMap<String, NotificationRecord> pkgRecords = records.get(record.sbn.getPackageName());
+ ArrayMap<String, NotificationRecord> pkgRecords = records.get(record.getSbn().getPackageName());
if (pkgRecords == null) {
return;
}
@@ -420,7 +420,7 @@
int N = recordsByKey.size();
for (int i = 0; i < N; i++) {
final NotificationRecord potentialGroupSummary = recordsByKey.valueAt(i);
- if (potentialGroupSummary.sbn.isGroup()
+ if (potentialGroupSummary.getSbn().isGroup()
&& potentialGroupSummary.getNotification().isGroupSummary()
&& groupKey.equals(potentialGroupSummary.getGroupKey())) {
groupSummaryKey = potentialGroupSummary.getKey();
diff --git a/services/core/java/com/android/server/notification/ZenModeFiltering.java b/services/core/java/com/android/server/notification/ZenModeFiltering.java
index 6045f6c..4d19855 100644
--- a/services/core/java/com/android/server/notification/ZenModeFiltering.java
+++ b/services/core/java/com/android/server/notification/ZenModeFiltering.java
@@ -17,6 +17,7 @@
package com.android.server.notification;
import static android.provider.Settings.Global.ZEN_MODE_OFF;
+import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE;
import android.app.Notification;
import android.app.NotificationManager;
@@ -106,8 +107,8 @@
}
private static Bundle extras(NotificationRecord record) {
- return record != null && record.sbn != null && record.sbn.getNotification() != null
- ? record.sbn.getNotification().extras : null;
+ return record != null && record.getSbn() != null && record.getSbn().getNotification() != null
+ ? record.getSbn().getNotification().extras : null;
}
protected void recordCall(NotificationRecord record) {
@@ -125,8 +126,8 @@
}
// Make an exception to policy for the notification saying that policy has changed
if (NotificationManager.Policy.areAllVisualEffectsSuppressed(policy.suppressedVisualEffects)
- && "android".equals(record.sbn.getPackageName())
- && SystemMessageProto.SystemMessage.NOTE_ZEN_UPGRADE == record.sbn.getId()) {
+ && "android".equals(record.getSbn().getPackageName())
+ && SystemMessageProto.SystemMessage.NOTE_ZEN_UPGRADE == record.getSbn().getId()) {
ZenLog.traceNotIntercepted(record, "systemDndChangedNotification");
return false;
}
@@ -156,25 +157,6 @@
}
return false;
}
- if (isCall(record)) {
- if (policy.allowRepeatCallers()
- && REPEAT_CALLERS.isRepeat(mContext, extras(record))) {
- ZenLog.traceNotIntercepted(record, "repeatCaller");
- return false;
- }
- if (!policy.allowCalls()) {
- ZenLog.traceIntercepted(record, "!allowCalls");
- return true;
- }
- return shouldInterceptAudience(policy.allowCallsFrom(), record);
- }
- if (isMessage(record)) {
- if (!policy.allowMessages()) {
- ZenLog.traceIntercepted(record, "!allowMessages");
- return true;
- }
- return shouldInterceptAudience(policy.allowMessagesFrom(), record);
- }
if (isEvent(record)) {
if (!policy.allowEvents()) {
ZenLog.traceIntercepted(record, "!allowEvents");
@@ -203,6 +185,41 @@
}
return false;
}
+ if (isConversation(record)) {
+ if (policy.allowConversations()) {
+ if (policy.priorityConversationSenders == CONVERSATION_SENDERS_ANYONE) {
+ ZenLog.traceNotIntercepted(record, "conversationAnyone");
+ return false;
+ } else if (policy.priorityConversationSenders
+ == NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT
+ && record.getChannel().isImportantConversation()) {
+ ZenLog.traceNotIntercepted(record, "conversationMatches");
+ return false;
+ }
+ }
+ // if conversations aren't allowed record might still be allowed thanks
+ // to call or message metadata, so don't return yet
+ }
+ if (isCall(record)) {
+ if (policy.allowRepeatCallers()
+ && REPEAT_CALLERS.isRepeat(mContext, extras(record))) {
+ ZenLog.traceNotIntercepted(record, "repeatCaller");
+ return false;
+ }
+ if (!policy.allowCalls()) {
+ ZenLog.traceIntercepted(record, "!allowCalls");
+ return true;
+ }
+ return shouldInterceptAudience(policy.allowCallsFrom(), record);
+ }
+ if (isMessage(record)) {
+ if (!policy.allowMessages()) {
+ ZenLog.traceIntercepted(record, "!allowMessages");
+ return true;
+ }
+ return shouldInterceptAudience(policy.allowMessagesFrom(), record);
+ }
+
ZenLog.traceIntercepted(record, "!priority");
return true;
default:
@@ -245,7 +262,7 @@
}
public boolean isCall(NotificationRecord record) {
- return record != null && (isDefaultPhoneApp(record.sbn.getPackageName())
+ return record != null && (isDefaultPhoneApp(record.getSbn().getPackageName())
|| record.isCategory(Notification.CATEGORY_CALL));
}
@@ -273,7 +290,11 @@
}
protected boolean isMessage(NotificationRecord record) {
- return mMessagingUtil.isMessaging(record.sbn);
+ return mMessagingUtil.isMessaging(record.getSbn());
+ }
+
+ protected boolean isConversation(NotificationRecord record) {
+ return record.isConversation();
}
private static boolean audienceMatches(int source, float contactAffinity) {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 696d2ea..3b564c3 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -641,9 +641,11 @@
}
public void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("mZenMode=");
+ pw.print(prefix);
+ pw.print("mZenMode=");
pw.println(Global.zenModeToString(mZenMode));
- pw.print("mConsolidatedPolicy=" + mConsolidatedPolicy.toString());
+ pw.print(prefix);
+ pw.println("mConsolidatedPolicy=" + mConsolidatedPolicy.toString());
final int N = mConfigs.size();
for (int i = 0; i < N; i++) {
dump(pw, prefix, "mConfigs[u=" + mConfigs.keyAt(i) + "]", mConfigs.valueAt(i));
@@ -665,13 +667,17 @@
return;
}
pw.printf("allow(alarms=%b,media=%b,system=%b,calls=%b,callsFrom=%s,repeatCallers=%b,"
- + "messages=%b,messagesFrom=%s,events=%b,reminders=%b)\n",
+ + "messages=%b,messagesFrom=%s,conversations=%b,conversationsFrom=%s,"
+ + "events=%b,reminders=%b)\n",
config.allowAlarms, config.allowMedia, config.allowSystem,
config.allowCalls, ZenModeConfig.sourceToString(config.allowCallsFrom),
config.allowRepeatCallers, config.allowMessages,
ZenModeConfig.sourceToString(config.allowMessagesFrom),
+ config.allowConversations,
+ ZenPolicy.conversationTypeToString(config.allowConversationsFrom),
config.allowEvents, config.allowReminders);
- pw.printf(" disallow(visualEffects=%s)\n", config.suppressedVisualEffects);
+ pw.print(prefix);
+ pw.printf(" disallow(visualEffects=%s)\n", config.suppressedVisualEffects);
pw.print(prefix); pw.print(" manualRule="); pw.println(config.manualRule);
if (config.automaticRules.isEmpty()) return;
final int N = config.automaticRules.size();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 79cf23f..8e5ff66 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1537,15 +1537,16 @@
final @NonNull String mRequiredPermissionControllerPackage;
final @Nullable String mSetupWizardPackage;
final @Nullable String mStorageManagerPackage;
- final @Nullable String mSystemTextClassifierPackage;
+ final @Nullable String mDefaultTextClassifierPackage;
+ final @Nullable String mSystemTextClassifierPackageName;
final @Nullable String mWellbeingPackage;
final @Nullable String mDocumenterPackage;
final @Nullable String mConfiguratorPackage;
final @Nullable String mAppPredictionServicePackage;
final @Nullable String mIncidentReportApproverPackage;
final @Nullable String[] mTelephonyPackages;
- final @NonNull String mServicesExtensionPackageName;
- final @NonNull String mSharedSystemSharedLibraryPackageName;
+ final @Nullable String mServicesExtensionPackageName;
+ final @Nullable String mSharedSystemSharedLibraryPackageName;
final @Nullable String mRetailDemoPackage;
private final PackageUsage mPackageUsage = new PackageUsage();
@@ -3155,8 +3156,8 @@
mSetupWizardPackage = getSetupWizardPackageNameImpl();
mComponentResolver.fixProtectedFilterPriorities();
- mSystemTextClassifierPackage = getSystemTextClassifierPackageName();
-
+ mDefaultTextClassifierPackage = getDefaultTextClassifierPackageName();
+ mSystemTextClassifierPackageName = getSystemTextClassifierPackageName();
mWellbeingPackage = getWellbeingPackageName();
mDocumenterPackage = getDocumenterPackageName();
mConfiguratorPackage = getDeviceConfiguratorPackageName();
@@ -3351,6 +3352,7 @@
mServicesExtensionPackageName = null;
mSharedSystemSharedLibraryPackageName = null;
}
+
// PermissionController hosts default permission granting and role management, so it's a
// critical part of the core system.
mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr();
@@ -19626,15 +19628,15 @@
}
@Override
- public String getSystemTextClassifierPackageName() {
- return ensureSystemPackageName(mContext.getString(
- R.string.config_defaultTextClassifierPackage));
+ public String getDefaultTextClassifierPackageName() {
+ return ensureSystemPackageName(
+ mContext.getString(R.string.config_servicesExtensionPackage));
}
@Override
- public String[] getSystemTextClassifierPackages() {
- return ensureSystemPackageNames(mContext.getResources().getStringArray(
- R.array.config_defaultTextClassifierPackages));
+ public String getSystemTextClassifierPackageName() {
+ return ensureSystemPackageName(
+ mContext.getString(R.string.config_defaultTextClassifierPackage));
}
@Override
@@ -23063,7 +23065,7 @@
}
private String[] getKnownPackageNamesInternal(int knownPackage, int userId) {
- switch(knownPackage) {
+ switch (knownPackage) {
case PackageManagerInternal.PACKAGE_BROWSER:
return new String[]{mPermissionManager.getDefaultBrowser(userId)};
case PackageManagerInternal.PACKAGE_INSTALLER:
@@ -23075,7 +23077,8 @@
case PackageManagerInternal.PACKAGE_VERIFIER:
return filterOnlySystemPackages(mRequiredVerifierPackage);
case PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER:
- return filterOnlySystemPackages(mSystemTextClassifierPackage);
+ return filterOnlySystemPackages(
+ mDefaultTextClassifierPackage, mSystemTextClassifierPackageName);
case PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER:
return filterOnlySystemPackages(mRequiredPermissionControllerPackage);
case PackageManagerInternal.PACKAGE_WELLBEING:
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 46893b2..e6eaf21 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -720,12 +720,9 @@
userId, STORAGE_PERMISSIONS);
// TextClassifier Service
- final String[] packages = mContext.getPackageManager().getSystemTextClassifierPackages();
- if (packages.length > 0) {
- // We have a list of supported system TextClassifier package names, the first one
- // package is the default system TextClassifier service. Grant permissions to default
- // TextClassifier Service.
- grantPermissionsToSystemPackage(packages[0], userId,
+ for (String textClassifierPackage :
+ getKnownPackages(PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER, userId)) {
+ grantPermissionsToSystemPackage(textClassifierPackage, userId,
COARSE_BACKGROUND_LOCATION_PERMISSIONS, CONTACTS_PERMISSIONS);
}
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index bf7413b..4d7af9c 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -93,19 +93,6 @@
*/
static final int ROLLBACK_STATE_DELETED = 4;
- @IntDef(flag = true, prefix = { "MATCH_" }, value = {
- MATCH_APK_IN_APEX,
- })
- @Retention(RetentionPolicy.SOURCE)
- @interface RollbackInfoFlags {}
-
- /**
- * {@link RollbackInfo} flag: include {@code RollbackInfo} packages that are apk-in-apex.
- * These packages do not have their own sessions. They are embedded in an apex which has a
- * session id.
- */
- static final int MATCH_APK_IN_APEX = 1;
-
/**
* The session ID for the staged session if this rollback data represents a staged session,
* {@code -1} otherwise.
@@ -783,33 +770,6 @@
}
/**
- * Returns the number of {@link PackageRollbackInfo} we are storing in this {@link Rollback}
- * instance. By default, this method does not include apk-in-apex package in the count.
- *
- * @param flags Apk-in-apex packages can be included in the count by passing
- * {@link Rollback#MATCH_APK_IN_APEX}
- *
- * @return Counts number of {@link PackageRollbackInfo} stored in the {@link Rollback}
- * according to {@code flags} passed
- */
- int getPackageCount(@RollbackInfoFlags int flags) {
- synchronized (mLock) {
- List<PackageRollbackInfo> packages = info.getPackages();
- if ((flags & MATCH_APK_IN_APEX) != 0) {
- return packages.size();
- }
-
- int packagesWithoutApkInApex = 0;
- for (PackageRollbackInfo rollbackInfo : packages) {
- if (!rollbackInfo.isApkInApex()) {
- packagesWithoutApkInApex++;
- }
- }
- return packagesWithoutApkInApex;
- }
- }
-
- /**
* Adds a rollback token to be associated with this rollback. This may be used to
* identify which rollback should be removed in case {@link PackageManager} sends an
* {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} intent.
@@ -842,13 +802,6 @@
}
/**
- * Returns the number of package session ids in this rollback.
- */
- int getPackageSessionIdCount() {
- return mPackageSessionIds.length;
- }
-
- /**
* Called when a child session finished with success.
* Returns true when all child sessions are notified with success. This rollback will be
* enabled only after all child sessions finished with success.
@@ -859,6 +812,23 @@
}
}
+ /**
+ * Returns true if all packages in this rollback are enabled. We won't enable this rollback
+ * until all packages are enabled. Note we don't count apk-in-apex here since they are enabled
+ * automatically when the embedding apex is enabled.
+ */
+ boolean allPackagesEnabled() {
+ synchronized (mLock) {
+ int packagesWithoutApkInApex = 0;
+ for (PackageRollbackInfo rollbackInfo : info.getPackages()) {
+ if (!rollbackInfo.isApkInApex()) {
+ packagesWithoutApkInApex++;
+ }
+ }
+ return packagesWithoutApkInApex == mPackageSessionIds.length;
+ }
+ }
+
static String rollbackStateToString(@RollbackState int state) {
switch (state) {
case Rollback.ROLLBACK_STATE_ENABLING: return "enabling";
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 3fa114e..8bd9533 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -1237,8 +1237,7 @@
// equal to the number of sessions we are installing, to ensure we didn't skip enabling
// of any sessions. If we successfully enable an apex, then we can assume we enabled
// rollback for the embedded apk-in-apex, if any.
- // TODO: add a helper instead of exposing 2 methods from Rollback
- if (rollback.getPackageCount(0 /*flags*/) != rollback.getPackageSessionIdCount()) {
+ if (!rollback.allPackagesEnabled()) {
Slog.e(TAG, "Failed to enable rollback for all packages in session.");
rollback.delete(mAppDataRollbackHelper);
return null;
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index 4f89482..7b046c1 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -199,11 +199,6 @@
* Creates a new Rollback instance for a non-staged rollback with
* backupDir assigned.
*/
- Rollback createNonStagedRollback(int rollbackId, int userId, String installerPackageName) {
- File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
- return new Rollback(rollbackId, backupDir, -1, userId, installerPackageName);
- }
-
Rollback createNonStagedRollback(int rollbackId, int userId, String installerPackageName,
int[] packageSessionIds) {
File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
@@ -216,16 +211,6 @@
* backupDir assigned.
*/
Rollback createStagedRollback(int rollbackId, int stagedSessionId, int userId,
- String installerPackageName) {
- File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
- return new Rollback(rollbackId, backupDir, stagedSessionId, userId, installerPackageName);
- }
-
- /**
- * TODO: Now we have 4 factory methods for creating Rollback objects which is verbose and
- * cumbersome. Need to merge them for simplicity.
- */
- Rollback createStagedRollback(int rollbackId, int stagedSessionId, int userId,
String installerPackageName, int[] packageSessionIds) {
File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
return new Rollback(rollbackId, backupDir, stagedSessionId, userId, installerPackageName,
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 3dee853..34d2c16 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -36,7 +36,7 @@
import android.service.textclassifier.TextClassifierService;
import android.service.textclassifier.TextClassifierService.ConnectionState;
import android.text.TextUtils;
-import android.util.ArrayMap;
+import android.util.LruCache;
import android.util.Slog;
import android.util.SparseArray;
import android.view.textclassifier.ConversationActions;
@@ -63,7 +63,8 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayDeque;
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
import java.util.Queue;
@@ -132,9 +133,7 @@
synchronized (mManagerService.mLock) {
UserState userState = mManagerService.peekUserStateLocked(userId);
if (userState != null) {
- if (userState.mConnection != null) {
- userState.mConnection.cleanupService();
- }
+ userState.cleanupServiceLocked();
mManagerService.mUserStates.remove(userId);
}
}
@@ -147,22 +146,31 @@
private final Object mLock;
@GuardedBy("mLock")
final SparseArray<UserState> mUserStates = new SparseArray<>();
+ // SystemTextClassifier.onDestroy() is not guaranteed to be called, use LruCache here
+ // to avoid leak.
@GuardedBy("mLock")
- private final Map<TextClassificationSessionId, Integer> mSessionUserIds = new ArrayMap<>();
- @GuardedBy("mLock")
- private TextClassificationConstants mSettings;
+ private final LruCache<TextClassificationSessionId, TextClassificationContext>
+ mSessionContextCache = new LruCache<>(40);
+ private final TextClassificationConstants mSettings;
+ @Nullable
+ private final String mDefaultTextClassifierPackage;
+ @Nullable
+ private final String mSystemTextClassifierPackage;
private TextClassificationManagerService(Context context) {
mContext = Objects.requireNonNull(context);
mLock = new Object();
+ mSettings = new TextClassificationConstants();
mSettingsListener = new TextClassifierSettingsListener(mContext);
+ PackageManager packageManager = mContext.getPackageManager();
+ mDefaultTextClassifierPackage = packageManager.getDefaultTextClassifierPackageName();
+ mSystemTextClassifierPackage = packageManager.getSystemTextClassifierPackageName();
}
private void startListenSettings() {
mSettingsListener.registerObserver();
}
-
@Override
public void onConnectedStateChanged(@ConnectionState int connected) {
}
@@ -178,6 +186,7 @@
request.getUserId(),
request.getCallingPackageName(),
/* attemptToBind= */ true,
+ request.getUseDefaultTextClassifier(),
service -> service.onSuggestSelection(sessionId, request, callback),
"onSuggestSelection",
callback);
@@ -194,6 +203,7 @@
request.getUserId(),
request.getCallingPackageName(),
/* attemptToBind= */ true,
+ request.getUseDefaultTextClassifier(),
service -> service.onClassifyText(sessionId, request, callback),
"onClassifyText",
callback);
@@ -210,6 +220,7 @@
request.getUserId(),
request.getCallingPackageName(),
/* attemptToBind= */ true,
+ request.getUseDefaultTextClassifier(),
service -> service.onGenerateLinks(sessionId, request, callback),
"onGenerateLinks",
callback);
@@ -223,28 +234,32 @@
handleRequest(
event.getUserId(),
- event.getPackageName(),
+ /* callingPackageName= */ null,
/* attemptToBind= */ false,
+ event.getUseDefaultTextClassifier(),
service -> service.onSelectionEvent(sessionId, event),
"onSelectionEvent",
NO_OP_CALLBACK);
}
+
@Override
public void onTextClassifierEvent(
@Nullable TextClassificationSessionId sessionId,
TextClassifierEvent event) throws RemoteException {
Objects.requireNonNull(event);
- final String packageName = event.getEventContext() == null
- ? null
- : event.getEventContext().getPackageName();
final int userId = event.getEventContext() == null
? UserHandle.getCallingUserId()
: event.getEventContext().getUserId();
+ final boolean useDefaultTextClassifier =
+ event.getEventContext() != null
+ ? event.getEventContext().getUseDefaultTextClassifier()
+ : true;
handleRequest(
userId,
- packageName,
+ /* callingPackageName= */ null,
/* attemptToBind= */ false,
+ useDefaultTextClassifier,
service -> service.onTextClassifierEvent(sessionId, event),
"onTextClassifierEvent",
NO_OP_CALLBACK);
@@ -261,6 +276,7 @@
request.getUserId(),
request.getCallingPackageName(),
/* attemptToBind= */ true,
+ request.getUseDefaultTextClassifier(),
service -> service.onDetectLanguage(sessionId, request, callback),
"onDetectLanguage",
callback);
@@ -277,6 +293,7 @@
request.getUserId(),
request.getCallingPackageName(),
/* attemptToBind= */ true,
+ request.getUseDefaultTextClassifier(),
service -> service.onSuggestConversationActions(sessionId, request, callback),
"onSuggestConversationActions",
callback);
@@ -294,9 +311,10 @@
userId,
classificationContext.getPackageName(),
/* attemptToBind= */ false,
+ classificationContext.getUseDefaultTextClassifier(),
service -> {
service.onCreateTextClassificationSession(classificationContext, sessionId);
- mSessionUserIds.put(sessionId, userId);
+ mSessionContextCache.put(sessionId, classificationContext);
},
"onCreateTextClassificationSession",
NO_OP_CALLBACK);
@@ -308,16 +326,23 @@
Objects.requireNonNull(sessionId);
synchronized (mLock) {
- final int userId = mSessionUserIds.containsKey(sessionId)
- ? mSessionUserIds.get(sessionId)
+ TextClassificationContext textClassificationContext =
+ mSessionContextCache.get(sessionId);
+ final int userId = textClassificationContext != null
+ ? textClassificationContext.getUserId()
: UserHandle.getCallingUserId();
+ final boolean useDefaultTextClassifier =
+ textClassificationContext != null
+ ? textClassificationContext.getUseDefaultTextClassifier()
+ : true;
handleRequest(
userId,
/* callingPackageName= */ null,
/* attemptToBind= */ false,
+ useDefaultTextClassifier,
service -> {
service.onDestroyTextClassificationSession(sessionId);
- mSessionUserIds.remove(sessionId);
+ mSessionContextCache.remove(sessionId);
},
"onDestroyTextClassificationSession",
NO_OP_CALLBACK);
@@ -328,7 +353,7 @@
private UserState getUserStateLocked(int userId) {
UserState result = mUserStates.get(userId);
if (result == null) {
- result = new UserState(userId, mContext, mLock);
+ result = new UserState(userId);
mUserStates.put(userId, result);
}
return result;
@@ -339,6 +364,19 @@
return mUserStates.get(userId);
}
+ private int resolvePackageToUid(@Nullable String packageName, @UserIdInt int userId) {
+ if (packageName == null) {
+ return Process.INVALID_UID;
+ }
+ final PackageManager pm = mContext.getPackageManager();
+ try {
+ return pm.getPackageUidAsUser(packageName, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(LOG_TAG, "Could not get the UID for " + packageName);
+ }
+ return Process.INVALID_UID;
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, fout)) return;
@@ -353,18 +391,25 @@
pw.printPair("context", mContext);
pw.println();
+ pw.printPair("defaultTextClassifierPackage", mDefaultTextClassifierPackage);
+ pw.println();
+ pw.printPair("systemTextClassifierPackage", mSystemTextClassifierPackage);
+ pw.println();
synchronized (mLock) {
int size = mUserStates.size();
- pw.print("Number user states: "); pw.println(size);
+ pw.print("Number user states: ");
+ pw.println(size);
if (size > 0) {
for (int i = 0; i < size; i++) {
pw.increaseIndent();
UserState userState = mUserStates.valueAt(i);
- pw.print(i); pw.print(":"); userState.dump(pw); pw.println();
+ pw.printPair("User", mUserStates.keyAt(i));
+ pw.println();
+ userState.dump(pw);
pw.decreaseIndent();
}
}
- pw.println("Number of active sessions: " + mSessionUserIds.size());
+ pw.println("Number of active sessions: " + mSessionContextCache.size());
}
}
@@ -372,57 +417,55 @@
@UserIdInt int userId,
@Nullable String callingPackageName,
boolean attemptToBind,
+ boolean useDefaultTextClassifier,
@NonNull ThrowingConsumer<ITextClassifierService> textClassifierServiceConsumer,
@NonNull String methodName,
- @NonNull ITextClassifierCallback callback)
- throws RemoteException {
+ @NonNull ITextClassifierCallback callback) throws RemoteException {
Objects.requireNonNull(textClassifierServiceConsumer);
Objects.requireNonNull(methodName);
Objects.requireNonNull(callback);
- validateInput(mContext, callingPackageName, userId);
+ try {
+ validateCallingPackage(callingPackageName);
+ validateUser(userId);
+ } catch (Exception e) {
+ throw new RemoteException("Invalid request: " + e.getMessage(), e,
+ /* enableSuppression */ true, /* writableStackTrace */ true);
+ }
synchronized (mLock) {
UserState userState = getUserStateLocked(userId);
- if (attemptToBind && !userState.bindLocked()) {
+ ServiceState serviceState =
+ userState.getServiceStateLocked(useDefaultTextClassifier);
+ if (serviceState == null) {
+ Slog.d(LOG_TAG, "No configured system TextClassifierService");
+ callback.onFailure();
+ } else if (attemptToBind && !serviceState.bindLocked()) {
Slog.d(LOG_TAG, "Unable to bind TextClassifierService at " + methodName);
callback.onFailure();
- } else if (userState.isBoundLocked()) {
- if (!userState.checkRequestAcceptedLocked(Binder.getCallingUid(), methodName)) {
+ } else if (serviceState.isBoundLocked()) {
+ if (!serviceState.checkRequestAcceptedLocked(Binder.getCallingUid(), methodName)) {
return;
}
- textClassifierServiceConsumer.accept(userState.mService);
+ textClassifierServiceConsumer.accept(serviceState.mService);
} else {
- userState.mPendingRequests.add(
+ serviceState.mPendingRequests.add(
new PendingRequest(
methodName,
- () -> textClassifierServiceConsumer.accept(userState.mService),
+ () -> textClassifierServiceConsumer.accept(serviceState.mService),
callback::onFailure, callback.asBinder(),
this,
- userState,
+ serviceState,
Binder.getCallingUid()));
}
}
}
- private void unbindServiceIfNecessary() {
- final ComponentName serviceComponentName =
- TextClassifierService.getServiceComponentName(mContext);
- if (serviceComponentName == null) {
- // It should not occur if we had defined default service names in config.xml
- Slog.w(LOG_TAG, "No default configured system TextClassifierService.");
- return;
- }
+ private void onTextClassifierServicePackageOverrideChanged(String overriddenPackage) {
synchronized (mLock) {
final int size = mUserStates.size();
for (int i = 0; i < size; i++) {
UserState userState = mUserStates.valueAt(i);
- // Only unbind for a new service
- if (userState.isCurrentlyBoundToComponentLocked(serviceComponentName)) {
- return;
- }
- if (userState.isBoundLocked()) {
- userState.unbindLocked();
- }
+ userState.onTextClassifierServicePackageOverrideChangedLocked(overriddenPackage);
}
}
}
@@ -430,27 +473,35 @@
private static final class PendingRequest implements IBinder.DeathRecipient {
private final int mUid;
- @Nullable private final String mName;
- @Nullable private final IBinder mBinder;
- @NonNull private final Runnable mRequest;
- @Nullable private final Runnable mOnServiceFailure;
+ @Nullable
+ private final String mName;
+ @Nullable
+ private final IBinder mBinder;
+ @NonNull
+ private final Runnable mRequest;
+ @Nullable
+ private final Runnable mOnServiceFailure;
@GuardedBy("mLock")
- @NonNull private final UserState mOwningUser;
- @NonNull private final TextClassificationManagerService mService;
+ @NonNull
+ private final ServiceState mServiceState;
+ @NonNull
+ private final TextClassificationManagerService mService;
/**
* Initializes a new pending request.
- * @param request action to perform when the service is bound
+ *
+ * @param request action to perform when the service is bound
* @param onServiceFailure action to perform when the service dies or disconnects
- * @param binder binder to the process that made this pending request
- * @param service
- * @param owningUser
+ * @param binder binder to the process that made this pending request
+ * @parm service the TCMS instance.
+ * @param serviceState the service state of the service that will execute the request.
+ * @param uid the calling uid of the request.
*/
PendingRequest(@Nullable String name,
@NonNull ThrowingRunnable request, @Nullable ThrowingRunnable onServiceFailure,
@Nullable IBinder binder,
- TextClassificationManagerService service,
- UserState owningUser, int uid) {
+ @NonNull TextClassificationManagerService service,
+ @NonNull ServiceState serviceState, int uid) {
mName = name;
mRequest =
logOnFailure(Objects.requireNonNull(request), "handling pending request");
@@ -458,7 +509,7 @@
logOnFailure(onServiceFailure, "notifying callback of service failure");
mBinder = binder;
mService = service;
- mOwningUser = owningUser;
+ mServiceState = Objects.requireNonNull(serviceState);
if (mBinder != null) {
try {
mBinder.linkToDeath(this, 0);
@@ -479,7 +530,7 @@
@GuardedBy("mLock")
private void removeLocked() {
- mOwningUser.mPendingRequests.remove(this);
+ mServiceState.mPendingRequests.remove(this);
if (mBinder != null) {
mBinder.unlinkToDeath(this, 0);
}
@@ -492,59 +543,172 @@
e -> Slog.d(LOG_TAG, "Error " + opDesc + ": " + e.getMessage()));
}
- private static void validateInput(
- Context context, @Nullable String packageName, @UserIdInt int userId)
- throws RemoteException {
+ private void validateCallingPackage(@Nullable String callingPackage)
+ throws PackageManager.NameNotFoundException {
+ if (callingPackage != null) {
+ final int packageUid = mContext.getPackageManager()
+ .getPackageUidAsUser(callingPackage, UserHandle.getCallingUserId());
+ final int callingUid = Binder.getCallingUid();
+ Preconditions.checkArgument(
+ callingUid == packageUid
+ // Trust the system process:
+ || callingUid == android.os.Process.SYSTEM_UID,
+ "Invalid package name. callingPackage=" + callingPackage
+ + ", callingUid=" + callingUid);
+ }
+ }
- try {
- if (packageName != null) {
- final int packageUid = context.getPackageManager()
- .getPackageUidAsUser(packageName, UserHandle.getCallingUserId());
- final int callingUid = Binder.getCallingUid();
- Preconditions.checkArgument(callingUid == packageUid
- // Trust the system process:
- || callingUid == android.os.Process.SYSTEM_UID,
- "Invalid package name. Package=" + packageName
- + ", CallingUid=" + callingUid);
- }
-
- Preconditions.checkArgument(userId != UserHandle.USER_NULL, "Null userId");
- final int callingUserId = UserHandle.getCallingUserId();
- if (callingUserId != userId) {
- context.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- "Invalid userId. UserId=" + userId + ", CallingUserId=" + callingUserId);
- }
- } catch (Exception e) {
- throw new RemoteException("Invalid request: " + e.getMessage(), e,
- /* enableSuppression */ true, /* writableStackTrace */ true);
+ private void validateUser(@UserIdInt int userId) {
+ Preconditions.checkArgument(userId != UserHandle.USER_NULL, "Null userId");
+ final int callingUserId = UserHandle.getCallingUserId();
+ if (callingUserId != userId) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "Invalid userId. UserId=" + userId + ", CallingUserId=" + callingUserId);
}
}
private final class UserState {
- @UserIdInt final int mUserId;
+ @UserIdInt
+ final int mUserId;
+ @Nullable
+ private final ServiceState mDefaultServiceState;
+ @Nullable
+ private final ServiceState mSystemServiceState;
@GuardedBy("mLock")
- TextClassifierServiceConnection mConnection = null;
+ @Nullable
+ private ServiceState mUntrustedServiceState;
+
+ private UserState(int userId) {
+ mUserId = userId;
+ mDefaultServiceState = TextUtils.isEmpty(mDefaultTextClassifierPackage)
+ ? null
+ : new ServiceState(userId, mDefaultTextClassifierPackage, /* isTrusted= */true);
+ mSystemServiceState = TextUtils.isEmpty(mSystemTextClassifierPackage)
+ ? null
+ : new ServiceState(userId, mSystemTextClassifierPackage, /* isTrusted= */ true);
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ ServiceState getServiceStateLocked(boolean useDefaultTextClassifier) {
+ if (useDefaultTextClassifier) {
+ return mDefaultServiceState;
+ }
+ String textClassifierServicePackageOverride =
+ mSettings.getTextClassifierServicePackageOverride();
+ if (!TextUtils.isEmpty(textClassifierServicePackageOverride)) {
+ if (textClassifierServicePackageOverride.equals(mDefaultTextClassifierPackage)) {
+ return mDefaultServiceState;
+ }
+ if (textClassifierServicePackageOverride.equals(mSystemTextClassifierPackage)
+ && mSystemServiceState != null) {
+ return mSystemServiceState;
+ }
+ if (mUntrustedServiceState == null) {
+ mUntrustedServiceState =
+ new ServiceState(
+ mUserId,
+ textClassifierServicePackageOverride,
+ /* isTrusted= */false);
+ }
+ return mUntrustedServiceState;
+ }
+ return mSystemServiceState != null ? mSystemServiceState : mDefaultServiceState;
+ }
+
+ @GuardedBy("mLock")
+ void onTextClassifierServicePackageOverrideChangedLocked(String overriddenPackageName) {
+ // The override config is just used for testing, and the flag value is not expected
+ // to change often. So, let's keep it simple and just unbind all the services here. The
+ // right service will be bound when the next request comes.
+ for (ServiceState serviceState : getAllServiceStatesLocked()) {
+ serviceState.unbindIfBoundLocked();
+ }
+ mUntrustedServiceState = null;
+ }
+
+ @GuardedBy("mLock")
+ void bindIfHasPendingRequestsLocked() {
+ for (ServiceState serviceState : getAllServiceStatesLocked()) {
+ serviceState.bindIfHasPendingRequestsLocked();
+ }
+ }
+
+ @GuardedBy("mLock")
+ void cleanupServiceLocked() {
+ for (ServiceState serviceState : getAllServiceStatesLocked()) {
+ if (serviceState.mConnection != null) {
+ serviceState.mConnection.cleanupService();
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ @NonNull
+ private List<ServiceState> getAllServiceStatesLocked() {
+ List<ServiceState> serviceStates = new ArrayList<>();
+ if (mDefaultServiceState != null) {
+ serviceStates.add(mDefaultServiceState);
+ }
+ if (mSystemServiceState != null) {
+ serviceStates.add(mSystemServiceState);
+ }
+ if (mUntrustedServiceState != null) {
+ serviceStates.add(mUntrustedServiceState);
+ }
+ return serviceStates;
+ }
+
+ void dump(IndentingPrintWriter pw) {
+ synchronized (mLock) {
+ pw.increaseIndent();
+ dump(pw, mDefaultServiceState, "Default");
+ dump(pw, mSystemServiceState, "System");
+ dump(pw, mUntrustedServiceState, "Untrusted");
+ pw.decreaseIndent();
+ }
+ }
+
+ private void dump(
+ IndentingPrintWriter pw, @Nullable ServiceState serviceState, String name) {
+ synchronized (mLock) {
+ if (serviceState != null) {
+ pw.print(name + ": ");
+ serviceState.dump(pw);
+ pw.println();
+ }
+ }
+ }
+ }
+
+ private final class ServiceState {
+ @UserIdInt
+ final int mUserId;
+ @NonNull
+ final String mPackageName;
+ @NonNull
+ final TextClassifierServiceConnection mConnection;
+ final boolean mIsTrusted;
+ @NonNull
@GuardedBy("mLock")
final Queue<PendingRequest> mPendingRequests = new ArrayDeque<>();
+ @Nullable
@GuardedBy("mLock")
ITextClassifierService mService;
@GuardedBy("mLock")
boolean mBinding;
+ @Nullable
@GuardedBy("mLock")
ComponentName mBoundComponentName = null;
@GuardedBy("mLock")
- boolean mBoundToDefaultTrustService;
- @GuardedBy("mLock")
- int mBoundServiceUid;
+ int mBoundServiceUid = Process.INVALID_UID;
- private final Context mContext;
- private final Object mLock;
-
- private UserState(int userId, Context context, Object lock) {
+ private ServiceState(@UserIdInt int userId, String packageName, boolean isTrusted) {
mUserId = userId;
- mContext = Objects.requireNonNull(context);
- mLock = Objects.requireNonNull(lock);
+ mPackageName = packageName;
+ mConnection = new TextClassifierServiceConnection(mUserId);
+ mIsTrusted = isTrusted;
}
@GuardedBy("mLock")
@@ -581,18 +745,12 @@
}
@GuardedBy("mLock")
- private boolean isCurrentlyBoundToComponentLocked(@NonNull ComponentName componentName) {
- return (mBoundComponentName != null
- && mBoundComponentName.getPackageName().equals(
- componentName.getPackageName()));
- }
-
- @GuardedBy("mLock")
- private void unbindLocked() {
- Slog.v(LOG_TAG, "unbinding from " + mBoundComponentName + " for " + mUserId);
- mContext.unbindService(mConnection);
- mConnection.cleanupService();
- mConnection = null;
+ void unbindIfBoundLocked() {
+ if (isBoundLocked()) {
+ Slog.v(LOG_TAG, "Unbinding " + mBoundComponentName + " for " + mUserId);
+ mContext.unbindService(mConnection);
+ mConnection.cleanupService();
+ }
}
/**
@@ -609,8 +767,7 @@
final boolean willBind;
final long identity = Binder.clearCallingIdentity();
try {
- final ComponentName componentName =
- TextClassifierService.getServiceComponentName(mContext);
+ final ComponentName componentName = getTextClassifierServiceComponent();
if (componentName == null) {
// Might happen if the storage is encrypted and the user is not unlocked
return false;
@@ -618,7 +775,6 @@
Intent serviceIntent = new Intent(TextClassifierService.SERVICE_INTERFACE)
.setComponent(componentName);
Slog.d(LOG_TAG, "Binding to " + serviceIntent.getComponent());
- mConnection = new TextClassifierServiceConnection(mUserId);
willBind = mContext.bindServiceAsUser(
serviceIntent, mConnection,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
@@ -631,12 +787,21 @@
return willBind;
}
+ @Nullable
+ private ComponentName getTextClassifierServiceComponent() {
+ return TextClassifierService.getServiceComponentName(
+ mContext,
+ mPackageName,
+ mIsTrusted ? PackageManager.MATCH_SYSTEM_ONLY : 0);
+ }
+
private void dump(IndentingPrintWriter pw) {
pw.printPair("context", mContext);
pw.printPair("userId", mUserId);
synchronized (mLock) {
+ pw.printPair("packageName", mPackageName);
pw.printPair("boundComponentName", mBoundComponentName);
- pw.printPair("boundToDefaultTrustService", mBoundToDefaultTrustService);
+ pw.printPair("isTrusted", mIsTrusted);
pw.printPair("boundServiceUid", mBoundServiceUid);
pw.printPair("binding", mBinding);
pw.printPair("numberRequests", mPendingRequests.size());
@@ -645,7 +810,7 @@
@GuardedBy("mLock")
private boolean checkRequestAcceptedLocked(int requestUid, @NonNull String methodName) {
- if (mBoundToDefaultTrustService || (requestUid == mBoundServiceUid)) {
+ if (mIsTrusted || (requestUid == mBoundServiceUid)) {
return true;
}
Slog.w(LOG_TAG, String.format(
@@ -654,47 +819,19 @@
return false;
}
- private boolean isDefaultTrustService(@NonNull ComponentName currentService) {
- final String[] defaultServiceNames =
- mContext.getPackageManager().getSystemTextClassifierPackages();
- final String servicePackageName = currentService.getPackageName();
-
- for (int i = 0; i < defaultServiceNames.length; i++) {
- if (defaultServiceNames[i].equals(servicePackageName)) {
- return true;
- }
- }
- return false;
- }
-
- private int getServiceUid(@Nullable ComponentName service, int userId) {
- if (service == null) {
- return Process.INVALID_UID;
- }
- final String servicePackageName = service.getPackageName();
- final PackageManager pm = mContext.getPackageManager();
- final int serviceUid;
-
- try {
- serviceUid = pm.getPackageUidAsUser(servicePackageName, userId);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.e(LOG_TAG, "Could not verify UID for " + service);
- return Process.INVALID_UID;
- }
- return serviceUid;
- }
-
@GuardedBy("mLock")
private void updateServiceInfoLocked(int userId, @Nullable ComponentName componentName) {
mBoundComponentName = componentName;
- mBoundToDefaultTrustService = (mBoundComponentName != null && isDefaultTrustService(
- mBoundComponentName));
- mBoundServiceUid = getServiceUid(mBoundComponentName, userId);
+ mBoundServiceUid =
+ mBoundComponentName == null
+ ? Process.INVALID_UID
+ : resolvePackageToUid(mBoundComponentName.getPackageName(), userId);
}
private final class TextClassifierServiceConnection implements ServiceConnection {
- @UserIdInt private final int mUserId;
+ @UserIdInt
+ private final int mUserId;
TextClassifierServiceConnection(int userId) {
mUserId = userId;
@@ -745,18 +882,18 @@
private final class TextClassifierSettingsListener implements
DeviceConfig.OnPropertiesChangedListener {
+ @NonNull
+ private final Context mContext;
+ @Nullable
+ private String mServicePackageOverride;
- @NonNull private final Context mContext;
- @NonNull private final TextClassificationConstants mSettings;
- @Nullable private String mServicePackageName;
TextClassifierSettingsListener(Context context) {
mContext = context;
- mSettings = TextClassificationManager.getSettings(mContext);
- mServicePackageName = mSettings.getTextClassifierServicePackageOverride();
+ mServicePackageOverride = mSettings.getTextClassifierServicePackageOverride();
}
- public void registerObserver() {
+ void registerObserver() {
DeviceConfig.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
mContext.getMainExecutor(),
@@ -765,13 +902,13 @@
@Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
- final String overrideServiceName = mSettings.getTextClassifierServicePackageOverride();
-
- if (TextUtils.equals(overrideServiceName, mServicePackageName)) {
+ final String currentServicePackageOverride =
+ mSettings.getTextClassifierServicePackageOverride();
+ if (TextUtils.equals(currentServicePackageOverride, mServicePackageOverride)) {
return;
}
- mServicePackageName = overrideServiceName;
- unbindServiceIfNecessary();
+ mServicePackageOverride = currentServicePackageOverride;
+ onTextClassifierServicePackageOverrideChanged(currentServicePackageOverride);
}
}
}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index b7d6360..0bb0f94 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -37,6 +37,9 @@
import java.io.PrintWriter;
import java.util.Objects;
+/**
+ * The implementation of ITimeDetectorService.aidl.
+ */
public final class TimeDetectorService extends ITimeDetectorService.Stub {
private static final String TAG = "TimeDetectorService";
@@ -75,7 +78,7 @@
Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
new ContentObserver(handler) {
public void onChange(boolean selfChange) {
- timeDetectorService.handleAutoTimeDetectionToggle();
+ timeDetectorService.handleAutoTimeDetectionChanged();
}
});
@@ -114,8 +117,9 @@
mHandler.post(() -> mTimeDetectorStrategy.suggestNetworkTime(timeSignal));
}
+ /** Internal method for handling the auto time setting being changed. */
@VisibleForTesting
- public void handleAutoTimeDetectionToggle() {
+ public void handleAutoTimeDetectionChanged() {
mHandler.post(mTimeDetectorStrategy::handleAutoTimeDetectionChanged);
}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index 468b806..a7c3b4d 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -26,8 +26,8 @@
import java.io.PrintWriter;
/**
- * The interface for classes that implement the time detection algorithm used by the
- * TimeDetectorService.
+ * The interface for the class that implements the time detection algorithm used by the
+ * {@link TimeDetectorService}.
*
* <p>Most calls will be handled by a single thread but that is not true for all calls. For example
* {@link #dump(PrintWriter, String[])}) may be called on a different thread so implementations must
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index a1e643f..19435ee 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -38,7 +38,7 @@
import java.lang.annotation.RetentionPolicy;
/**
- * An implementation of TimeDetectorStrategy that passes phone and manual suggestions to
+ * An implementation of {@link TimeDetectorStrategy} that passes phone and manual suggestions to
* {@link AlarmManager}. When there are multiple phone sources, the one with the lowest ID is used
* unless the data becomes too stale.
*
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
index adf6d7e..2520316 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
@@ -24,9 +24,9 @@
import android.provider.Settings;
/**
- * The real implementation of {@link TimeZoneDetectorStrategy.Callback}.
+ * The real implementation of {@link TimeZoneDetectorStrategyImpl.Callback}.
*/
-public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrategy.Callback {
+public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrategyImpl.Callback {
private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index 9a1fe65..381ee10 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -67,19 +67,21 @@
private static TimeZoneDetectorService create(@NonNull Context context) {
final TimeZoneDetectorStrategy timeZoneDetectorStrategy =
- TimeZoneDetectorStrategy.create(context);
+ TimeZoneDetectorStrategyImpl.create(context);
Handler handler = FgThread.getHandler();
+ TimeZoneDetectorService service =
+ new TimeZoneDetectorService(context, handler, timeZoneDetectorStrategy);
+
ContentResolver contentResolver = context.getContentResolver();
contentResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
new ContentObserver(handler) {
public void onChange(boolean selfChange) {
- timeZoneDetectorStrategy.handleAutoTimeZoneDetectionChange();
+ service.handleAutoTimeZoneDetectionChanged();
}
});
-
- return new TimeZoneDetectorService(context, handler, timeZoneDetectorStrategy);
+ return service;
}
@VisibleForTesting
@@ -111,17 +113,25 @@
@Nullable String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- mTimeZoneDetectorStrategy.dumpState(pw, args);
+ mTimeZoneDetectorStrategy.dump(pw, args);
+ }
+
+ /** Internal method for handling the auto time zone setting being changed. */
+ @VisibleForTesting
+ public void handleAutoTimeZoneDetectionChanged() {
+ mHandler.post(mTimeZoneDetectorStrategy::handleAutoTimeZoneDetectionChanged);
}
private void enforceSuggestPhoneTimeZonePermission() {
mContext.enforceCallingPermission(
- android.Manifest.permission.SET_TIME_ZONE, "set time zone");
+ android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE,
+ "suggest phone time and time zone");
}
private void enforceSuggestManualTimeZonePermission() {
mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.SET_TIME_ZONE, "set time zone");
+ android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE,
+ "suggest manual time and time zone");
}
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index b0e0069..1d439e9 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -15,192 +15,26 @@
*/
package com.android.server.timezonedetector;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
-
-import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.timezonedetector.ManualTimeZoneSuggestion;
import android.app.timezonedetector.PhoneTimeZoneSuggestion;
-import android.content.Context;
-import android.util.LocalLog;
-import android.util.Slog;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
/**
- * A singleton, stateful time zone detection strategy that is aware of user (manual) suggestions and
- * suggestions from multiple phone devices. Suggestions are acted on or ignored as needed, dependent
- * on the current "auto time zone detection" setting.
+ * The interface for the class that implement the time detection algorithm used by the
+ * {@link TimeZoneDetectorService}.
*
- * <p>For automatic detection it keeps track of the most recent suggestion from each phone it uses
- * the best suggestion based on a scoring algorithm. If several phones provide the same score then
- * the phone with the lowest numeric ID "wins". If the situation changes and it is no longer
- * possible to be confident about the time zone, phones must submit an empty suggestion in order to
- * "withdraw" their previous suggestion.
+ * <p>Most calls will be handled by a single thread but that is not true for all calls. For example
+ * {@link #dump(PrintWriter, String[])}) may be called on a different thread so implementations must
+ * handle thread safety.
+ *
+ * @hide
*/
-public class TimeZoneDetectorStrategy {
+public interface TimeZoneDetectorStrategy {
- /**
- * Used by {@link TimeZoneDetectorStrategy} to interact with the surrounding service. It can be
- * faked for tests.
- *
- * <p>Note: Because the system properties-derived values like
- * {@link #isAutoTimeZoneDetectionEnabled()}, {@link #isAutoTimeZoneDetectionEnabled()},
- * {@link #getDeviceTimeZone()} can be modified independently and from different threads (and
- * processes!), their use are prone to race conditions. That will be true until the
- * responsibility for setting their values is moved to {@link TimeZoneDetectorStrategy}.
- */
- @VisibleForTesting
- public interface Callback {
-
- /**
- * Returns true if automatic time zone detection is enabled in settings.
- */
- boolean isAutoTimeZoneDetectionEnabled();
-
- /**
- * Returns true if the device has had an explicit time zone set.
- */
- boolean isDeviceTimeZoneInitialized();
-
- /**
- * Returns the device's currently configured time zone.
- */
- String getDeviceTimeZone();
-
- /**
- * Sets the device's time zone.
- */
- void setDeviceTimeZone(@NonNull String zoneId);
- }
-
- private static final String LOG_TAG = "TimeZoneDetectorStrategy";
- private static final boolean DBG = false;
-
- @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL })
- @Retention(RetentionPolicy.SOURCE)
- public @interface Origin {}
-
- /** Used when a time value originated from a telephony signal. */
- @Origin
- private static final int ORIGIN_PHONE = 1;
-
- /** Used when a time value originated from a user / manual settings. */
- @Origin
- private static final int ORIGIN_MANUAL = 2;
-
- /**
- * The abstract score for an empty or invalid phone suggestion.
- *
- * Used to score phone suggestions where there is no zone.
- */
- @VisibleForTesting
- public static final int PHONE_SCORE_NONE = 0;
-
- /**
- * The abstract score for a low quality phone suggestion.
- *
- * Used to score suggestions where:
- * The suggested zone ID is one of several possibilities, and the possibilities have different
- * offsets.
- *
- * You would have to be quite desperate to want to use this choice.
- */
- @VisibleForTesting
- public static final int PHONE_SCORE_LOW = 1;
-
- /**
- * The abstract score for a medium quality phone suggestion.
- *
- * Used for:
- * The suggested zone ID is one of several possibilities but at least the possibilities have the
- * same offset. Users would get the correct time but for the wrong reason. i.e. their device may
- * switch to DST at the wrong time and (for example) their calendar events.
- */
- @VisibleForTesting
- public static final int PHONE_SCORE_MEDIUM = 2;
-
- /**
- * The abstract score for a high quality phone suggestion.
- *
- * Used for:
- * The suggestion was for one zone ID and the answer was unambiguous and likely correct given
- * the info available.
- */
- @VisibleForTesting
- public static final int PHONE_SCORE_HIGH = 3;
-
- /**
- * The abstract score for a highest quality phone suggestion.
- *
- * Used for:
- * Suggestions that must "win" because they constitute test or emulator zone ID.
- */
- @VisibleForTesting
- public static final int PHONE_SCORE_HIGHEST = 4;
-
- /**
- * The threshold at which phone suggestions are good enough to use to set the device's time
- * zone.
- */
- @VisibleForTesting
- public static final int PHONE_SCORE_USAGE_THRESHOLD = PHONE_SCORE_MEDIUM;
-
- /** The number of previous phone suggestions to keep for each ID (for use during debugging). */
- private static final int KEEP_PHONE_SUGGESTION_HISTORY_SIZE = 30;
-
- @NonNull
- private final Callback mCallback;
-
- /**
- * A log that records the decisions / decision metadata that affected the device's time zone
- * (for use during debugging).
- */
- @NonNull
- private final LocalLog mTimeZoneChangesLog = new LocalLog(30, false /* useLocalTimestamps */);
-
- /**
- * A mapping from slotIndex to a phone time zone suggestion. We typically expect one or two
- * mappings: devices will have a small number of telephony devices and slotIndexs are assumed to
- * be stable.
- */
- @GuardedBy("this")
- private ArrayMapWithHistory<Integer, QualifiedPhoneTimeZoneSuggestion> mSuggestionBySlotIndex =
- new ArrayMapWithHistory<>(KEEP_PHONE_SUGGESTION_HISTORY_SIZE);
-
- /**
- * Creates a new instance of {@link TimeZoneDetectorStrategy}.
- */
- public static TimeZoneDetectorStrategy create(Context context) {
- Callback timeZoneDetectionServiceHelper = new TimeZoneDetectorCallbackImpl(context);
- return new TimeZoneDetectorStrategy(timeZoneDetectionServiceHelper);
- }
-
- @VisibleForTesting
- public TimeZoneDetectorStrategy(Callback callback) {
- mCallback = Objects.requireNonNull(callback);
- }
-
- /** Process the suggested manually- / user-entered time zone. */
- public synchronized void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion suggestion) {
- Objects.requireNonNull(suggestion);
-
- String timeZoneId = suggestion.getZoneId();
- String cause = "Manual time suggestion received: suggestion=" + suggestion;
- setDeviceTimeZoneIfRequired(ORIGIN_MANUAL, timeZoneId, cause);
- }
+ /** Process the suggested manually-entered (i.e. user sourced) time zone. */
+ void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion suggestion);
/**
* Suggests a time zone for the device, or withdraws a previous suggestion if
@@ -210,312 +44,15 @@
* suggestion. The strategy uses suggestions to decide whether to modify the device's time zone
* setting and what to set it to.
*/
- public synchronized void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion suggestion) {
- if (DBG) {
- Slog.d(LOG_TAG, "Phone suggestion received. newSuggestion=" + suggestion);
- }
- Objects.requireNonNull(suggestion);
-
- // Score the suggestion.
- int score = scorePhoneSuggestion(suggestion);
- QualifiedPhoneTimeZoneSuggestion scoredSuggestion =
- new QualifiedPhoneTimeZoneSuggestion(suggestion, score);
-
- // Store the suggestion against the correct slotIndex.
- mSuggestionBySlotIndex.put(suggestion.getSlotIndex(), scoredSuggestion);
-
- // Now perform auto time zone detection. The new suggestion may be used to modify the time
- // zone setting.
- String reason = "New phone time suggested. suggestion=" + suggestion;
- doAutoTimeZoneDetection(reason);
- }
-
- private static int scorePhoneSuggestion(@NonNull PhoneTimeZoneSuggestion suggestion) {
- int score;
- if (suggestion.getZoneId() == null) {
- score = PHONE_SCORE_NONE;
- } else if (suggestion.getMatchType() == MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY
- || suggestion.getMatchType() == MATCH_TYPE_EMULATOR_ZONE_ID) {
- // Handle emulator / test cases : These suggestions should always just be used.
- score = PHONE_SCORE_HIGHEST;
- } else if (suggestion.getQuality() == QUALITY_SINGLE_ZONE) {
- score = PHONE_SCORE_HIGH;
- } else if (suggestion.getQuality() == QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET) {
- // The suggestion may be wrong, but at least the offset should be correct.
- score = PHONE_SCORE_MEDIUM;
- } else if (suggestion.getQuality() == QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS) {
- // The suggestion has a good chance of being wrong.
- score = PHONE_SCORE_LOW;
- } else {
- throw new AssertionError();
- }
- return score;
- }
-
- /**
- * Finds the best available time zone suggestion from all phones. If it is high-enough quality
- * and automatic time zone detection is enabled then it will be set on the device. The outcome
- * can be that this strategy becomes / remains un-opinionated and nothing is set.
- */
- @GuardedBy("this")
- private void doAutoTimeZoneDetection(@NonNull String detectionReason) {
- if (!mCallback.isAutoTimeZoneDetectionEnabled()) {
- // Avoid doing unnecessary work with this (race-prone) check.
- return;
- }
-
- QualifiedPhoneTimeZoneSuggestion bestPhoneSuggestion = findBestPhoneSuggestion();
-
- // Work out what to do with the best suggestion.
- if (bestPhoneSuggestion == null) {
- // There is no phone suggestion available at all. Become un-opinionated.
- if (DBG) {
- Slog.d(LOG_TAG, "Could not determine time zone: No best phone suggestion."
- + " detectionReason=" + detectionReason);
- }
- return;
- }
-
- // Special case handling for uninitialized devices. This should only happen once.
- String newZoneId = bestPhoneSuggestion.suggestion.getZoneId();
- if (newZoneId != null && !mCallback.isDeviceTimeZoneInitialized()) {
- String cause = "Device has no time zone set. Attempting to set the device to the best"
- + " available suggestion."
- + " bestPhoneSuggestion=" + bestPhoneSuggestion
- + ", detectionReason=" + detectionReason;
- Slog.i(LOG_TAG, cause);
- setDeviceTimeZoneIfRequired(ORIGIN_PHONE, newZoneId, cause);
- return;
- }
-
- boolean suggestionGoodEnough = bestPhoneSuggestion.score >= PHONE_SCORE_USAGE_THRESHOLD;
- if (!suggestionGoodEnough) {
- if (DBG) {
- Slog.d(LOG_TAG, "Best suggestion not good enough."
- + " bestPhoneSuggestion=" + bestPhoneSuggestion
- + ", detectionReason=" + detectionReason);
- }
- return;
- }
-
- // Paranoia: Every suggestion above the SCORE_USAGE_THRESHOLD should have a non-null time
- // zone ID.
- if (newZoneId == null) {
- Slog.w(LOG_TAG, "Empty zone suggestion scored higher than expected. This is an error:"
- + " bestPhoneSuggestion=" + bestPhoneSuggestion
- + " detectionReason=" + detectionReason);
- return;
- }
-
- String zoneId = bestPhoneSuggestion.suggestion.getZoneId();
- String cause = "Found good suggestion."
- + ", bestPhoneSuggestion=" + bestPhoneSuggestion
- + ", detectionReason=" + detectionReason;
- setDeviceTimeZoneIfRequired(ORIGIN_PHONE, zoneId, cause);
- }
-
- @GuardedBy("this")
- private void setDeviceTimeZoneIfRequired(
- @Origin int origin, @NonNull String newZoneId, @NonNull String cause) {
- Objects.requireNonNull(newZoneId);
- Objects.requireNonNull(cause);
-
- boolean isOriginAutomatic = isOriginAutomatic(origin);
- if (isOriginAutomatic) {
- if (!mCallback.isAutoTimeZoneDetectionEnabled()) {
- if (DBG) {
- Slog.d(LOG_TAG, "Auto time zone detection is not enabled."
- + " origin=" + origin
- + ", newZoneId=" + newZoneId
- + ", cause=" + cause);
- }
- return;
- }
- } else {
- if (mCallback.isAutoTimeZoneDetectionEnabled()) {
- if (DBG) {
- Slog.d(LOG_TAG, "Auto time zone detection is enabled."
- + " origin=" + origin
- + ", newZoneId=" + newZoneId
- + ", cause=" + cause);
- }
- return;
- }
- }
-
- String currentZoneId = mCallback.getDeviceTimeZone();
-
- // Avoid unnecessary changes / intents.
- if (newZoneId.equals(currentZoneId)) {
- // No need to set the device time zone - the setting is already what we would be
- // suggesting.
- if (DBG) {
- Slog.d(LOG_TAG, "No need to change the time zone;"
- + " device is already set to the suggested zone."
- + " origin=" + origin
- + ", newZoneId=" + newZoneId
- + ", cause=" + cause);
- }
- return;
- }
-
- mCallback.setDeviceTimeZone(newZoneId);
- String msg = "Set device time zone."
- + " origin=" + origin
- + ", currentZoneId=" + currentZoneId
- + ", newZoneId=" + newZoneId
- + ", cause=" + cause;
- if (DBG) {
- Slog.d(LOG_TAG, msg);
- }
- mTimeZoneChangesLog.log(msg);
- }
-
- private static boolean isOriginAutomatic(@Origin int origin) {
- return origin != ORIGIN_MANUAL;
- }
-
- @GuardedBy("this")
- @Nullable
- private QualifiedPhoneTimeZoneSuggestion findBestPhoneSuggestion() {
- QualifiedPhoneTimeZoneSuggestion bestSuggestion = null;
-
- // Iterate over the latest QualifiedPhoneTimeZoneSuggestion objects received for each phone
- // and find the best. Note that we deliberately do not look at age: the caller can
- // rate-limit so age is not a strong indicator of confidence. Instead, the callers are
- // expected to withdraw suggestions they no longer have confidence in.
- for (int i = 0; i < mSuggestionBySlotIndex.size(); i++) {
- QualifiedPhoneTimeZoneSuggestion candidateSuggestion =
- mSuggestionBySlotIndex.valueAt(i);
- if (candidateSuggestion == null) {
- // Unexpected
- continue;
- }
-
- if (bestSuggestion == null) {
- bestSuggestion = candidateSuggestion;
- } else if (candidateSuggestion.score > bestSuggestion.score) {
- bestSuggestion = candidateSuggestion;
- } else if (candidateSuggestion.score == bestSuggestion.score) {
- // Tie! Use the suggestion with the lowest slotIndex.
- int candidateSlotIndex = candidateSuggestion.suggestion.getSlotIndex();
- int bestSlotIndex = bestSuggestion.suggestion.getSlotIndex();
- if (candidateSlotIndex < bestSlotIndex) {
- bestSuggestion = candidateSuggestion;
- }
- }
- }
- return bestSuggestion;
- }
-
- /**
- * Returns the current best phone suggestion. Not intended for general use: it is used during
- * tests to check strategy behavior.
- */
- @VisibleForTesting
- @Nullable
- public synchronized QualifiedPhoneTimeZoneSuggestion findBestPhoneSuggestionForTests() {
- return findBestPhoneSuggestion();
- }
+ void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion suggestion);
/**
* Called when there has been a change to the automatic time zone detection setting.
*/
- @VisibleForTesting
- public synchronized void handleAutoTimeZoneDetectionChange() {
- if (DBG) {
- Slog.d(LOG_TAG, "handleTimeZoneDetectionChange() called");
- }
- if (mCallback.isAutoTimeZoneDetectionEnabled()) {
- // When the user enabled time zone detection, run the time zone detection and change the
- // device time zone if possible.
- String reason = "Auto time zone detection setting enabled.";
- doAutoTimeZoneDetection(reason);
- }
- }
+ void handleAutoTimeZoneDetectionChanged();
/**
* Dumps internal state such as field values.
*/
- public synchronized void dumpState(PrintWriter pw, String[] args) {
- IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
- ipw.println("TimeZoneDetectorStrategy:");
-
- ipw.increaseIndent(); // level 1
- ipw.println("mCallback.isTimeZoneDetectionEnabled()="
- + mCallback.isAutoTimeZoneDetectionEnabled());
- ipw.println("mCallback.isDeviceTimeZoneInitialized()="
- + mCallback.isDeviceTimeZoneInitialized());
- ipw.println("mCallback.getDeviceTimeZone()="
- + mCallback.getDeviceTimeZone());
-
- ipw.println("Time zone change log:");
- ipw.increaseIndent(); // level 2
- mTimeZoneChangesLog.dump(ipw);
- ipw.decreaseIndent(); // level 2
-
- ipw.println("Phone suggestion history:");
- ipw.increaseIndent(); // level 2
- mSuggestionBySlotIndex.dump(ipw);
- ipw.decreaseIndent(); // level 2
- ipw.decreaseIndent(); // level 1
- ipw.flush();
- }
-
- /**
- * A method used to inspect strategy state during tests. Not intended for general use.
- */
- @VisibleForTesting
- public synchronized QualifiedPhoneTimeZoneSuggestion getLatestPhoneSuggestion(int slotIndex) {
- return mSuggestionBySlotIndex.get(slotIndex);
- }
-
- /**
- * A {@link PhoneTimeZoneSuggestion} with additional qualifying metadata.
- */
- @VisibleForTesting
- public static class QualifiedPhoneTimeZoneSuggestion {
-
- @VisibleForTesting
- public final PhoneTimeZoneSuggestion suggestion;
-
- /**
- * The score the suggestion has been given. This can be used to rank against other
- * suggestions of the same type.
- */
- @VisibleForTesting
- public final int score;
-
- @VisibleForTesting
- public QualifiedPhoneTimeZoneSuggestion(PhoneTimeZoneSuggestion suggestion, int score) {
- this.suggestion = suggestion;
- this.score = score;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- QualifiedPhoneTimeZoneSuggestion that = (QualifiedPhoneTimeZoneSuggestion) o;
- return score == that.score
- && suggestion.equals(that.suggestion);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(score, suggestion);
- }
-
- @Override
- public String toString() {
- return "QualifiedPhoneTimeZoneSuggestion{"
- + "suggestion=" + suggestion
- + ", score=" + score
- + '}';
- }
- }
+ void dump(PrintWriter pw, String[] args);
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
new file mode 100644
index 0000000..f85f9fe
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -0,0 +1,514 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.timezonedetector;
+
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID;
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY;
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
+import android.app.timezonedetector.PhoneTimeZoneSuggestion;
+import android.content.Context;
+import android.util.LocalLog;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * An implementation of {@link TimeZoneDetectorStrategy} that handle telephony and manual
+ * suggestions. Suggestions are acted on or ignored as needed, dependent on the current "auto time
+ * zone detection" setting.
+ *
+ * <p>For automatic detection it keeps track of the most recent suggestion from each phone it uses
+ * the best suggestion based on a scoring algorithm. If several phones provide the same score then
+ * the phone with the lowest numeric ID "wins". If the situation changes and it is no longer
+ * possible to be confident about the time zone, phones must submit an empty suggestion in order to
+ * "withdraw" their previous suggestion.
+ *
+ * <p>Most public methods are marked synchronized to ensure thread safety around internal state.
+ */
+public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrategy {
+
+ /**
+ * Used by {@link TimeZoneDetectorStrategyImpl} to interact with the surrounding service. It can
+ * be faked for tests.
+ *
+ * <p>Note: Because the system properties-derived values like
+ * {@link #isAutoTimeZoneDetectionEnabled()}, {@link #isAutoTimeZoneDetectionEnabled()},
+ * {@link #getDeviceTimeZone()} can be modified independently and from different threads (and
+ * processes!), their use are prone to race conditions. That will be true until the
+ * responsibility for setting their values is moved to {@link TimeZoneDetectorStrategyImpl}.
+ */
+ @VisibleForTesting
+ public interface Callback {
+
+ /**
+ * Returns true if automatic time zone detection is enabled in settings.
+ */
+ boolean isAutoTimeZoneDetectionEnabled();
+
+ /**
+ * Returns true if the device has had an explicit time zone set.
+ */
+ boolean isDeviceTimeZoneInitialized();
+
+ /**
+ * Returns the device's currently configured time zone.
+ */
+ String getDeviceTimeZone();
+
+ /**
+ * Sets the device's time zone.
+ */
+ void setDeviceTimeZone(@NonNull String zoneId);
+ }
+
+ private static final String LOG_TAG = "TimeZoneDetectorStrategy";
+ private static final boolean DBG = false;
+
+ @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Origin {}
+
+ /** Used when a time value originated from a telephony signal. */
+ @Origin
+ private static final int ORIGIN_PHONE = 1;
+
+ /** Used when a time value originated from a user / manual settings. */
+ @Origin
+ private static final int ORIGIN_MANUAL = 2;
+
+ /**
+ * The abstract score for an empty or invalid phone suggestion.
+ *
+ * Used to score phone suggestions where there is no zone.
+ */
+ @VisibleForTesting
+ public static final int PHONE_SCORE_NONE = 0;
+
+ /**
+ * The abstract score for a low quality phone suggestion.
+ *
+ * Used to score suggestions where:
+ * The suggested zone ID is one of several possibilities, and the possibilities have different
+ * offsets.
+ *
+ * You would have to be quite desperate to want to use this choice.
+ */
+ @VisibleForTesting
+ public static final int PHONE_SCORE_LOW = 1;
+
+ /**
+ * The abstract score for a medium quality phone suggestion.
+ *
+ * Used for:
+ * The suggested zone ID is one of several possibilities but at least the possibilities have the
+ * same offset. Users would get the correct time but for the wrong reason. i.e. their device may
+ * switch to DST at the wrong time and (for example) their calendar events.
+ */
+ @VisibleForTesting
+ public static final int PHONE_SCORE_MEDIUM = 2;
+
+ /**
+ * The abstract score for a high quality phone suggestion.
+ *
+ * Used for:
+ * The suggestion was for one zone ID and the answer was unambiguous and likely correct given
+ * the info available.
+ */
+ @VisibleForTesting
+ public static final int PHONE_SCORE_HIGH = 3;
+
+ /**
+ * The abstract score for a highest quality phone suggestion.
+ *
+ * Used for:
+ * Suggestions that must "win" because they constitute test or emulator zone ID.
+ */
+ @VisibleForTesting
+ public static final int PHONE_SCORE_HIGHEST = 4;
+
+ /**
+ * The threshold at which phone suggestions are good enough to use to set the device's time
+ * zone.
+ */
+ @VisibleForTesting
+ public static final int PHONE_SCORE_USAGE_THRESHOLD = PHONE_SCORE_MEDIUM;
+
+ /** The number of previous phone suggestions to keep for each ID (for use during debugging). */
+ private static final int KEEP_PHONE_SUGGESTION_HISTORY_SIZE = 30;
+
+ @NonNull
+ private final Callback mCallback;
+
+ /**
+ * A log that records the decisions / decision metadata that affected the device's time zone
+ * (for use during debugging).
+ */
+ @NonNull
+ private final LocalLog mTimeZoneChangesLog = new LocalLog(30, false /* useLocalTimestamps */);
+
+ /**
+ * A mapping from slotIndex to a phone time zone suggestion. We typically expect one or two
+ * mappings: devices will have a small number of telephony devices and slotIndexs are assumed to
+ * be stable.
+ */
+ @GuardedBy("this")
+ private ArrayMapWithHistory<Integer, QualifiedPhoneTimeZoneSuggestion> mSuggestionBySlotIndex =
+ new ArrayMapWithHistory<>(KEEP_PHONE_SUGGESTION_HISTORY_SIZE);
+
+ /**
+ * Creates a new instance of {@link TimeZoneDetectorStrategyImpl}.
+ */
+ public static TimeZoneDetectorStrategyImpl create(Context context) {
+ Callback timeZoneDetectionServiceHelper = new TimeZoneDetectorCallbackImpl(context);
+ return new TimeZoneDetectorStrategyImpl(timeZoneDetectionServiceHelper);
+ }
+
+ @VisibleForTesting
+ public TimeZoneDetectorStrategyImpl(Callback callback) {
+ mCallback = Objects.requireNonNull(callback);
+ }
+
+ @Override
+ public synchronized void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion suggestion) {
+ Objects.requireNonNull(suggestion);
+
+ String timeZoneId = suggestion.getZoneId();
+ String cause = "Manual time suggestion received: suggestion=" + suggestion;
+ setDeviceTimeZoneIfRequired(ORIGIN_MANUAL, timeZoneId, cause);
+ }
+
+ @Override
+ public synchronized void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion suggestion) {
+ if (DBG) {
+ Slog.d(LOG_TAG, "Phone suggestion received. newSuggestion=" + suggestion);
+ }
+ Objects.requireNonNull(suggestion);
+
+ // Score the suggestion.
+ int score = scorePhoneSuggestion(suggestion);
+ QualifiedPhoneTimeZoneSuggestion scoredSuggestion =
+ new QualifiedPhoneTimeZoneSuggestion(suggestion, score);
+
+ // Store the suggestion against the correct slotIndex.
+ mSuggestionBySlotIndex.put(suggestion.getSlotIndex(), scoredSuggestion);
+
+ // Now perform auto time zone detection. The new suggestion may be used to modify the time
+ // zone setting.
+ String reason = "New phone time suggested. suggestion=" + suggestion;
+ doAutoTimeZoneDetection(reason);
+ }
+
+ private static int scorePhoneSuggestion(@NonNull PhoneTimeZoneSuggestion suggestion) {
+ int score;
+ if (suggestion.getZoneId() == null) {
+ score = PHONE_SCORE_NONE;
+ } else if (suggestion.getMatchType() == MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY
+ || suggestion.getMatchType() == MATCH_TYPE_EMULATOR_ZONE_ID) {
+ // Handle emulator / test cases : These suggestions should always just be used.
+ score = PHONE_SCORE_HIGHEST;
+ } else if (suggestion.getQuality() == QUALITY_SINGLE_ZONE) {
+ score = PHONE_SCORE_HIGH;
+ } else if (suggestion.getQuality() == QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET) {
+ // The suggestion may be wrong, but at least the offset should be correct.
+ score = PHONE_SCORE_MEDIUM;
+ } else if (suggestion.getQuality() == QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS) {
+ // The suggestion has a good chance of being wrong.
+ score = PHONE_SCORE_LOW;
+ } else {
+ throw new AssertionError();
+ }
+ return score;
+ }
+
+ /**
+ * Finds the best available time zone suggestion from all phones. If it is high-enough quality
+ * and automatic time zone detection is enabled then it will be set on the device. The outcome
+ * can be that this strategy becomes / remains un-opinionated and nothing is set.
+ */
+ @GuardedBy("this")
+ private void doAutoTimeZoneDetection(@NonNull String detectionReason) {
+ if (!mCallback.isAutoTimeZoneDetectionEnabled()) {
+ // Avoid doing unnecessary work with this (race-prone) check.
+ return;
+ }
+
+ QualifiedPhoneTimeZoneSuggestion bestPhoneSuggestion = findBestPhoneSuggestion();
+
+ // Work out what to do with the best suggestion.
+ if (bestPhoneSuggestion == null) {
+ // There is no phone suggestion available at all. Become un-opinionated.
+ if (DBG) {
+ Slog.d(LOG_TAG, "Could not determine time zone: No best phone suggestion."
+ + " detectionReason=" + detectionReason);
+ }
+ return;
+ }
+
+ // Special case handling for uninitialized devices. This should only happen once.
+ String newZoneId = bestPhoneSuggestion.suggestion.getZoneId();
+ if (newZoneId != null && !mCallback.isDeviceTimeZoneInitialized()) {
+ String cause = "Device has no time zone set. Attempting to set the device to the best"
+ + " available suggestion."
+ + " bestPhoneSuggestion=" + bestPhoneSuggestion
+ + ", detectionReason=" + detectionReason;
+ Slog.i(LOG_TAG, cause);
+ setDeviceTimeZoneIfRequired(ORIGIN_PHONE, newZoneId, cause);
+ return;
+ }
+
+ boolean suggestionGoodEnough = bestPhoneSuggestion.score >= PHONE_SCORE_USAGE_THRESHOLD;
+ if (!suggestionGoodEnough) {
+ if (DBG) {
+ Slog.d(LOG_TAG, "Best suggestion not good enough."
+ + " bestPhoneSuggestion=" + bestPhoneSuggestion
+ + ", detectionReason=" + detectionReason);
+ }
+ return;
+ }
+
+ // Paranoia: Every suggestion above the SCORE_USAGE_THRESHOLD should have a non-null time
+ // zone ID.
+ if (newZoneId == null) {
+ Slog.w(LOG_TAG, "Empty zone suggestion scored higher than expected. This is an error:"
+ + " bestPhoneSuggestion=" + bestPhoneSuggestion
+ + " detectionReason=" + detectionReason);
+ return;
+ }
+
+ String zoneId = bestPhoneSuggestion.suggestion.getZoneId();
+ String cause = "Found good suggestion."
+ + ", bestPhoneSuggestion=" + bestPhoneSuggestion
+ + ", detectionReason=" + detectionReason;
+ setDeviceTimeZoneIfRequired(ORIGIN_PHONE, zoneId, cause);
+ }
+
+ @GuardedBy("this")
+ private void setDeviceTimeZoneIfRequired(
+ @Origin int origin, @NonNull String newZoneId, @NonNull String cause) {
+ Objects.requireNonNull(newZoneId);
+ Objects.requireNonNull(cause);
+
+ boolean isOriginAutomatic = isOriginAutomatic(origin);
+ if (isOriginAutomatic) {
+ if (!mCallback.isAutoTimeZoneDetectionEnabled()) {
+ if (DBG) {
+ Slog.d(LOG_TAG, "Auto time zone detection is not enabled."
+ + " origin=" + origin
+ + ", newZoneId=" + newZoneId
+ + ", cause=" + cause);
+ }
+ return;
+ }
+ } else {
+ if (mCallback.isAutoTimeZoneDetectionEnabled()) {
+ if (DBG) {
+ Slog.d(LOG_TAG, "Auto time zone detection is enabled."
+ + " origin=" + origin
+ + ", newZoneId=" + newZoneId
+ + ", cause=" + cause);
+ }
+ return;
+ }
+ }
+
+ String currentZoneId = mCallback.getDeviceTimeZone();
+
+ // Avoid unnecessary changes / intents.
+ if (newZoneId.equals(currentZoneId)) {
+ // No need to set the device time zone - the setting is already what we would be
+ // suggesting.
+ if (DBG) {
+ Slog.d(LOG_TAG, "No need to change the time zone;"
+ + " device is already set to the suggested zone."
+ + " origin=" + origin
+ + ", newZoneId=" + newZoneId
+ + ", cause=" + cause);
+ }
+ return;
+ }
+
+ mCallback.setDeviceTimeZone(newZoneId);
+ String msg = "Set device time zone."
+ + " origin=" + origin
+ + ", currentZoneId=" + currentZoneId
+ + ", newZoneId=" + newZoneId
+ + ", cause=" + cause;
+ if (DBG) {
+ Slog.d(LOG_TAG, msg);
+ }
+ mTimeZoneChangesLog.log(msg);
+ }
+
+ private static boolean isOriginAutomatic(@Origin int origin) {
+ return origin != ORIGIN_MANUAL;
+ }
+
+ @GuardedBy("this")
+ @Nullable
+ private QualifiedPhoneTimeZoneSuggestion findBestPhoneSuggestion() {
+ QualifiedPhoneTimeZoneSuggestion bestSuggestion = null;
+
+ // Iterate over the latest QualifiedPhoneTimeZoneSuggestion objects received for each phone
+ // and find the best. Note that we deliberately do not look at age: the caller can
+ // rate-limit so age is not a strong indicator of confidence. Instead, the callers are
+ // expected to withdraw suggestions they no longer have confidence in.
+ for (int i = 0; i < mSuggestionBySlotIndex.size(); i++) {
+ QualifiedPhoneTimeZoneSuggestion candidateSuggestion =
+ mSuggestionBySlotIndex.valueAt(i);
+ if (candidateSuggestion == null) {
+ // Unexpected
+ continue;
+ }
+
+ if (bestSuggestion == null) {
+ bestSuggestion = candidateSuggestion;
+ } else if (candidateSuggestion.score > bestSuggestion.score) {
+ bestSuggestion = candidateSuggestion;
+ } else if (candidateSuggestion.score == bestSuggestion.score) {
+ // Tie! Use the suggestion with the lowest slotIndex.
+ int candidateSlotIndex = candidateSuggestion.suggestion.getSlotIndex();
+ int bestSlotIndex = bestSuggestion.suggestion.getSlotIndex();
+ if (candidateSlotIndex < bestSlotIndex) {
+ bestSuggestion = candidateSuggestion;
+ }
+ }
+ }
+ return bestSuggestion;
+ }
+
+ /**
+ * Returns the current best phone suggestion. Not intended for general use: it is used during
+ * tests to check strategy behavior.
+ */
+ @VisibleForTesting
+ @Nullable
+ public synchronized QualifiedPhoneTimeZoneSuggestion findBestPhoneSuggestionForTests() {
+ return findBestPhoneSuggestion();
+ }
+
+ @Override
+ public synchronized void handleAutoTimeZoneDetectionChanged() {
+ if (DBG) {
+ Slog.d(LOG_TAG, "handleTimeZoneDetectionChange() called");
+ }
+ if (mCallback.isAutoTimeZoneDetectionEnabled()) {
+ // When the user enabled time zone detection, run the time zone detection and change the
+ // device time zone if possible.
+ String reason = "Auto time zone detection setting enabled.";
+ doAutoTimeZoneDetection(reason);
+ }
+ }
+
+ /**
+ * Dumps internal state such as field values.
+ */
+ @Override
+ public synchronized void dump(PrintWriter pw, String[] args) {
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ ipw.println("TimeZoneDetectorStrategy:");
+
+ ipw.increaseIndent(); // level 1
+ ipw.println("mCallback.isTimeZoneDetectionEnabled()="
+ + mCallback.isAutoTimeZoneDetectionEnabled());
+ ipw.println("mCallback.isDeviceTimeZoneInitialized()="
+ + mCallback.isDeviceTimeZoneInitialized());
+ ipw.println("mCallback.getDeviceTimeZone()="
+ + mCallback.getDeviceTimeZone());
+
+ ipw.println("Time zone change log:");
+ ipw.increaseIndent(); // level 2
+ mTimeZoneChangesLog.dump(ipw);
+ ipw.decreaseIndent(); // level 2
+
+ ipw.println("Phone suggestion history:");
+ ipw.increaseIndent(); // level 2
+ mSuggestionBySlotIndex.dump(ipw);
+ ipw.decreaseIndent(); // level 2
+ ipw.decreaseIndent(); // level 1
+ ipw.flush();
+ }
+
+ /**
+ * A method used to inspect strategy state during tests. Not intended for general use.
+ */
+ @VisibleForTesting
+ public synchronized QualifiedPhoneTimeZoneSuggestion getLatestPhoneSuggestion(int slotIndex) {
+ return mSuggestionBySlotIndex.get(slotIndex);
+ }
+
+ /**
+ * A {@link PhoneTimeZoneSuggestion} with additional qualifying metadata.
+ */
+ @VisibleForTesting
+ public static class QualifiedPhoneTimeZoneSuggestion {
+
+ @VisibleForTesting
+ public final PhoneTimeZoneSuggestion suggestion;
+
+ /**
+ * The score the suggestion has been given. This can be used to rank against other
+ * suggestions of the same type.
+ */
+ @VisibleForTesting
+ public final int score;
+
+ @VisibleForTesting
+ public QualifiedPhoneTimeZoneSuggestion(PhoneTimeZoneSuggestion suggestion, int score) {
+ this.suggestion = suggestion;
+ this.score = score;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ QualifiedPhoneTimeZoneSuggestion that = (QualifiedPhoneTimeZoneSuggestion) o;
+ return score == that.score
+ && suggestion.equals(that.suggestion);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(score, suggestion);
+ }
+
+ @Override
+ public String toString() {
+ return "QualifiedPhoneTimeZoneSuggestion{"
+ + "suggestion=" + suggestion
+ + ", score=" + score
+ + '}';
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index ed38e9a..a54f5d4 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1629,12 +1629,7 @@
requestedVrComponent = (aInfo.requestedVrComponent == null) ?
null : ComponentName.unflattenFromString(aInfo.requestedVrComponent);
- lockTaskLaunchMode = aInfo.lockTaskLaunchMode;
- if (info.applicationInfo.isPrivilegedApp()
- && (lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_ALWAYS
- || lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_NEVER)) {
- lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
- }
+ lockTaskLaunchMode = getLockTaskLaunchMode(aInfo, options);
if (options != null) {
pendingOptions = options;
@@ -1642,15 +1637,27 @@
if (usageReport != null) {
appTimeTracker = new AppTimeTracker(usageReport);
}
- final boolean useLockTask = pendingOptions.getLockTaskMode();
- if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) {
- lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
- }
// Gets launch display id from options. It returns INVALID_DISPLAY if not set.
mHandoverLaunchDisplayId = options.getLaunchDisplayId();
}
}
+ static int getLockTaskLaunchMode(ActivityInfo aInfo, @Nullable ActivityOptions options) {
+ int lockTaskLaunchMode = aInfo.lockTaskLaunchMode;
+ if (aInfo.applicationInfo.isPrivilegedApp()
+ && (lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_ALWAYS
+ || lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_NEVER)) {
+ lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
+ }
+ if (options != null) {
+ final boolean useLockTask = options.getLockTaskMode();
+ if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) {
+ lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
+ }
+ }
+ return lockTaskLaunchMode;
+ }
+
@Override
ActivityRecord asActivityRecord() {
// I am an activity record!
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 2fb0ac5..76aa1d1 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -52,6 +52,7 @@
import android.os.UserManager;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.BlockedAppActivity;
import com.android.internal.app.HarmfulAppWarningActivity;
import com.android.internal.app.SuspendedAppActivity;
import com.android.internal.app.UnlaunchableAppActivity;
@@ -166,6 +167,9 @@
// no user action can undo this.
return true;
}
+ if (interceptLockTaskModeViolationPackageIfNeeded()) {
+ return true;
+ }
if (interceptHarmfulAppIfNeeded()) {
// If the app has a "harmful app" warning associated with it, we should ask to uninstall
// before issuing the work challenge.
@@ -270,6 +274,25 @@
return true;
}
+ private boolean interceptLockTaskModeViolationPackageIfNeeded() {
+ if (mAInfo == null || mAInfo.applicationInfo == null) {
+ return false;
+ }
+ LockTaskController controller = mService.getLockTaskController();
+ String packageName = mAInfo.applicationInfo.packageName;
+ int lockTaskLaunchMode = ActivityRecord.getLockTaskLaunchMode(mAInfo, mActivityOptions);
+ if (controller.isActivityAllowed(mUserId, packageName, lockTaskLaunchMode)) {
+ return false;
+ }
+ mIntent = BlockedAppActivity.createIntent(mUserId, mAInfo.applicationInfo.packageName);
+ mCallingPid = mRealCallingPid;
+ mCallingUid = mRealCallingUid;
+ mResolvedType = null;
+ mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid);
+ mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
+ return true;
+ }
+
private boolean interceptWorkProfileChallengeIfNeeded() {
final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(mAInfo, mUserId);
if (interceptingIntent == null) {
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 02413bb..3b25b74 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -23,6 +23,8 @@
import static android.content.Context.DEVICE_POLICY_SERVICE;
import static android.content.Context.STATUS_BAR_SERVICE;
import static android.content.Intent.ACTION_CALL_EMERGENCY;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_CURRENT;
import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
@@ -339,6 +341,25 @@
& DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD) != 0;
}
+ private boolean isBlockingInTaskEnabled(int userId) {
+ return (getLockTaskFeaturesForUser(userId)
+ & DevicePolicyManager.LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK) != 0;
+ }
+
+ boolean isActivityAllowed(int userId, String packageName, int lockTaskLaunchMode) {
+ if (mLockTaskModeState != LOCK_TASK_MODE_LOCKED || !isBlockingInTaskEnabled(userId)) {
+ return true;
+ }
+ switch (lockTaskLaunchMode) {
+ case LOCK_TASK_LAUNCH_MODE_ALWAYS:
+ return true;
+ case LOCK_TASK_LAUNCH_MODE_NEVER:
+ return false;
+ default:
+ }
+ return isPackageWhitelisted(userId, packageName);
+ }
+
private boolean isEmergencyCallTask(Task task) {
final Intent intent = task.intent;
if (intent == null) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 6e243f0..59eee9c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -562,4 +562,14 @@
*/
public abstract void setAccessibilityIdToSurfaceMetadata(
IBinder windowToken, int accessibilityWindowId);
+
+ /**
+ * Transfers input focus from a given input token to that of the IME window.
+ *
+ * @param sourceInputToken The source token.
+ * @param displayId The display hosting the IME window.
+ * @return Whether transfer was successful.
+ */
+ public abstract boolean transferTouchFocusToImeWindow(@NonNull IBinder sourceInputToken,
+ int displayId);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a5b99b0..6e1f46bb 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7558,6 +7558,29 @@
}
}
}
+
+ @Override
+ public boolean transferTouchFocusToImeWindow(@NonNull IBinder sourceInputToken,
+ int displayId) {
+ final IBinder destinationInputToken;
+
+ synchronized (mGlobalLock) {
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent == null) {
+ return false;
+ }
+ final WindowState imeWindow = displayContent.mInputMethodWindow;
+ if (imeWindow == null) {
+ return false;
+ }
+ if (imeWindow.mInputChannel == null) {
+ return false;
+ }
+ destinationInputToken = imeWindow.mInputChannel.getToken();
+ }
+
+ return mInputManager.transferTouchFocus(sourceInputToken, destinationInputToken);
+ }
}
void registerAppFreezeListener(AppFreezeListener listener) {
diff --git a/services/core/jni/com_android_server_GraphicsStatsService.cpp b/services/core/jni/com_android_server_GraphicsStatsService.cpp
index 7644ade..aa7067e 100644
--- a/services/core/jni/com_android_server_GraphicsStatsService.cpp
+++ b/services/core/jni/com_android_server_GraphicsStatsService.cpp
@@ -137,7 +137,7 @@
#define TIME_MILLIS_BUCKETS_FIELD_NUMBER 1
#define FRAME_COUNTS_FIELD_NUMBER 2
-static void writeCpuHistogram(stats_event* event,
+static void writeCpuHistogram(AStatsEvent* event,
const uirenderer::protos::GraphicsStatsProto& stat) {
util::ProtoOutputStream proto;
for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
@@ -154,10 +154,10 @@
}
std::vector<uint8_t> outVector;
proto.serializeToVector(&outVector);
- stats_event_write_byte_array(event, outVector.data(), outVector.size());
+ AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
}
-static void writeGpuHistogram(stats_event* event,
+static void writeGpuHistogram(AStatsEvent* event,
const uirenderer::protos::GraphicsStatsProto& stat) {
util::ProtoOutputStream proto;
for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
@@ -174,20 +174,20 @@
}
std::vector<uint8_t> outVector;
proto.serializeToVector(&outVector);
- stats_event_write_byte_array(event, outVector.data(), outVector.size());
+ AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
}
// graphicsStatsPullCallback is invoked by statsd service to pull GRAPHICS_STATS atom.
-static status_pull_atom_return_t graphicsStatsPullCallback(int32_t atom_tag,
- pulled_stats_event_list* data,
- void* cookie) {
+static AStatsManager_PullAtomCallbackReturn graphicsStatsPullCallback(int32_t atom_tag,
+ AStatsEventList* data,
+ void* cookie) {
JNIEnv* env = getJNIEnv();
if (!env) {
return false;
}
if (gGraphicsStatsServiceObject == nullptr) {
ALOGE("Failed to get graphicsstats service");
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
for (bool lastFullDay : {true, false}) {
@@ -199,7 +199,7 @@
env->ExceptionDescribe();
env->ExceptionClear();
ALOGE("Failed to invoke graphicsstats service");
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
if (!jdata) {
// null means data is not available for that day.
@@ -218,49 +218,51 @@
if (!success) {
ALOGW("Parse failed on GraphicsStatsPuller error='%s' dataSize='%d'",
serviceDump.InitializationErrorString().c_str(), dataSize);
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
for (int stat_index = 0; stat_index < serviceDump.stats_size(); stat_index++) {
auto& stat = serviceDump.stats(stat_index);
- stats_event* event = add_stats_event_to_pull_data(data);
- stats_event_set_atom_id(event, android::util::GRAPHICS_STATS);
- stats_event_write_string8(event, stat.package_name().c_str());
- stats_event_write_int64(event, (int64_t)stat.version_code());
- stats_event_write_int64(event, (int64_t)stat.stats_start());
- stats_event_write_int64(event, (int64_t)stat.stats_end());
- stats_event_write_int32(event, (int32_t)stat.pipeline());
- stats_event_write_int32(event, (int32_t)stat.summary().total_frames());
- stats_event_write_int32(event, (int32_t)stat.summary().missed_vsync_count());
- stats_event_write_int32(event, (int32_t)stat.summary().high_input_latency_count());
- stats_event_write_int32(event, (int32_t)stat.summary().slow_ui_thread_count());
- stats_event_write_int32(event, (int32_t)stat.summary().slow_bitmap_upload_count());
- stats_event_write_int32(event, (int32_t)stat.summary().slow_draw_count());
- stats_event_write_int32(event, (int32_t)stat.summary().missed_deadline_count());
+ AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+ AStatsEvent_setAtomId(event, android::util::GRAPHICS_STATS);
+ AStatsEvent_writeString(event, stat.package_name().c_str());
+ AStatsEvent_writeInt64(event, (int64_t)stat.version_code());
+ AStatsEvent_writeInt64(event, (int64_t)stat.stats_start());
+ AStatsEvent_writeInt64(event, (int64_t)stat.stats_end());
+ AStatsEvent_writeInt32(event, (int32_t)stat.pipeline());
+ AStatsEvent_writeInt32(event, (int32_t)stat.summary().total_frames());
+ AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_vsync_count());
+ AStatsEvent_writeInt32(event, (int32_t)stat.summary().high_input_latency_count());
+ AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_ui_thread_count());
+ AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_bitmap_upload_count());
+ AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_draw_count());
+ AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_deadline_count());
writeCpuHistogram(event, stat);
writeGpuHistogram(event, stat);
// TODO: fill in UI mainline module version, when the feature is available.
- stats_event_write_int64(event, (int64_t)0);
- stats_event_write_bool(event, !lastFullDay);
- stats_event_build(event);
+ AStatsEvent_writeInt64(event, (int64_t)0);
+ AStatsEvent_writeBool(event, !lastFullDay);
+ AStatsEvent_build(event);
}
}
- return STATS_PULL_SUCCESS;
+ return AStatsManager_PULL_SUCCESS;
}
// Register a puller for GRAPHICS_STATS atom with the statsd service.
static void nativeInit(JNIEnv* env, jobject javaObject) {
gGraphicsStatsServiceObject = env->NewGlobalRef(javaObject);
- pull_atom_metadata metadata = {.cool_down_ns = 10 * 1000000, // 10 milliseconds
- .timeout_ns = 2 * NS_PER_SEC, // 2 seconds
- .additive_fields = nullptr,
- .additive_fields_size = 0};
- register_stats_pull_atom_callback(android::util::GRAPHICS_STATS, &graphicsStatsPullCallback,
- &metadata, nullptr);
+ AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
+ AStatsManager_PullAtomMetadata_setCoolDownNs(metadata, 10 * 1000000); // 10 milliseconds
+ AStatsManager_PullAtomMetadata_setTimeoutNs(metadata, 2 * NS_PER_SEC); // 2 seconds
+
+ AStatsManager_registerPullAtomCallback(android::util::GRAPHICS_STATS,
+ &graphicsStatsPullCallback, metadata, nullptr);
+
+ AStatsManager_PullAtomMetadata_release(metadata);
}
static void nativeDestructor(JNIEnv* env, jobject javaObject) {
- //TODO: Unregister the puller callback when a new API is available.
+ AStatsManager_unregisterPullAtomCallback(android::util::GRAPHICS_STATS);
env->DeleteGlobalRef(gGraphicsStatsServiceObject);
gGraphicsStatsServiceObject = nullptr;
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 212a3a6..49db3d5 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1563,20 +1563,17 @@
}
static jboolean nativeTransferTouchFocus(JNIEnv* env,
- jclass /* clazz */, jlong ptr, jobject fromChannelObj, jobject toChannelObj) {
- NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
-
- sp<InputChannel> fromChannel =
- android_view_InputChannel_getInputChannel(env, fromChannelObj);
- sp<InputChannel> toChannel =
- android_view_InputChannel_getInputChannel(env, toChannelObj);
-
- if (fromChannel == nullptr || toChannel == nullptr) {
+ jclass /* clazz */, jlong ptr, jobject fromChannelTokenObj, jobject toChannelTokenObj) {
+ if (fromChannelTokenObj == nullptr || toChannelTokenObj == nullptr) {
return JNI_FALSE;
}
+ sp<IBinder> fromChannelToken = ibinderForJavaObject(env, fromChannelTokenObj);
+ sp<IBinder> toChannelToken = ibinderForJavaObject(env, toChannelTokenObj);
+
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
if (im->getInputManager()->getDispatcher()->transferTouchFocus(
- fromChannel->getConnectionToken(), toChannel->getConnectionToken())) {
+ fromChannelToken, toChannelToken)) {
return JNI_TRUE;
} else {
return JNI_FALSE;
@@ -1784,7 +1781,7 @@
(void*) nativeSetInputDispatchMode },
{ "nativeSetSystemUiVisibility", "(JI)V",
(void*) nativeSetSystemUiVisibility },
- { "nativeTransferTouchFocus", "(JLandroid/view/InputChannel;Landroid/view/InputChannel;)Z",
+ { "nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;)Z",
(void*) nativeTransferTouchFocus },
{ "nativeSetPointerSpeed", "(JI)V",
(void*) nativeSetPointerSpeed },
diff --git a/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp b/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp
index f5b778e..43cd0a2 100644
--- a/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp
+++ b/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp
@@ -31,32 +31,32 @@
static server::stats::PowerStatsPuller gPowerStatsPuller;
static server::stats::SubsystemSleepStatePuller gSubsystemSleepStatePuller;
-static status_pull_atom_return_t onDevicePowerMeasurementCallback(int32_t atom_tag,
- pulled_stats_event_list* data,
- void* cookie) {
+static AStatsManager_PullAtomCallbackReturn onDevicePowerMeasurementCallback(int32_t atom_tag,
+ AStatsEventList* data,
+ void* cookie) {
return gPowerStatsPuller.Pull(atom_tag, data);
}
-static status_pull_atom_return_t subsystemSleepStateCallback(int32_t atom_tag,
- pulled_stats_event_list* data,
- void* cookie) {
+static AStatsManager_PullAtomCallbackReturn subsystemSleepStateCallback(int32_t atom_tag,
+ AStatsEventList* data,
+ void* cookie) {
return gSubsystemSleepStatePuller.Pull(atom_tag, data);
}
static void nativeInit(JNIEnv* env, jobject javaObject) {
// on device power measurement
gPowerStatsPuller = server::stats::PowerStatsPuller();
- register_stats_pull_atom_callback(android::util::ON_DEVICE_POWER_MEASUREMENT,
- onDevicePowerMeasurementCallback,
- /* metadata= */ nullptr,
- /* cookie= */ nullptr);
+ AStatsManager_registerPullAtomCallback(android::util::ON_DEVICE_POWER_MEASUREMENT,
+ onDevicePowerMeasurementCallback,
+ /* metadata= */ nullptr,
+ /* cookie= */ nullptr);
// subsystem sleep state
gSubsystemSleepStatePuller = server::stats::SubsystemSleepStatePuller();
- register_stats_pull_atom_callback(android::util::SUBSYSTEM_SLEEP_STATE,
- subsystemSleepStateCallback,
- /* metadata= */ nullptr,
- /* cookie= */ nullptr);
+ AStatsManager_registerPullAtomCallback(android::util::SUBSYSTEM_SLEEP_STATE,
+ subsystemSleepStateCallback,
+ /* metadata= */ nullptr,
+ /* cookie= */ nullptr);
}
static const JNINativeMethod sMethods[] = {{"nativeInit", "()V", (void*)nativeInit}};
diff --git a/services/core/jni/stats/PowerStatsPuller.cpp b/services/core/jni/stats/PowerStatsPuller.cpp
index e80b5cf..d8f6faa 100644
--- a/services/core/jni/stats/PowerStatsPuller.cpp
+++ b/services/core/jni/stats/PowerStatsPuller.cpp
@@ -78,11 +78,12 @@
PowerStatsPuller::PowerStatsPuller() {}
-status_pull_atom_return_t PowerStatsPuller::Pull(int32_t atomTag, pulled_stats_event_list* data) {
+AStatsManager_PullAtomCallbackReturn PowerStatsPuller::Pull(int32_t atomTag,
+ AStatsEventList* data) {
std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
if (!getPowerStatsHalLocked()) {
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
// Pull getRailInfo if necessary
@@ -100,14 +101,14 @@
if (!resultSuccess || !ret.isOk()) {
ALOGE("power.stats getRailInfo() failed. Description: %s", ret.description().c_str());
gPowerStatsHal = nullptr;
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
// If SUCCESS but empty, or if NOT_SUPPORTED, then never try again.
if (gRailInfo.empty()) {
ALOGE("power.stats has no rail information");
gPowerStatsExist = false; // No rail info, so never try again.
gPowerStatsHal = nullptr;
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
}
@@ -134,15 +135,16 @@
}
const RailInfo& rail = gRailInfo[energyData.index];
- stats_event* event = add_stats_event_to_pull_data(data);
- stats_event_set_atom_id(event,
- android::util::ON_DEVICE_POWER_MEASUREMENT);
- stats_event_write_string8(event,
- rail.subsysName.c_str());
- stats_event_write_string8(event, rail.railName.c_str());
- stats_event_write_int64(event, energyData.timestamp);
- stats_event_write_int64(event, energyData.energy);
- stats_event_build(event);
+ AStatsEvent* event =
+ AStatsEventList_addStatsEvent(data);
+ AStatsEvent_setAtomId(
+ event,
+ android::util::ON_DEVICE_POWER_MEASUREMENT);
+ AStatsEvent_writeString(event, rail.subsysName.c_str());
+ AStatsEvent_writeString(event, rail.railName.c_str());
+ AStatsEvent_writeInt64(event, energyData.timestamp);
+ AStatsEvent_writeInt64(event, energyData.energy);
+ AStatsEvent_build(event);
ALOGV("power.stat: %s.%s: %llu, %llu",
rail.subsysName.c_str(), rail.railName.c_str(),
@@ -153,9 +155,9 @@
if (!resultSuccess || !ret.isOk()) {
ALOGE("power.stats getEnergyData() failed. Description: %s", ret.description().c_str());
gPowerStatsHal = nullptr;
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
- return STATS_PULL_SUCCESS;
+ return AStatsManager_PULL_SUCCESS;
}
} // namespace stats
diff --git a/services/core/jni/stats/PowerStatsPuller.h b/services/core/jni/stats/PowerStatsPuller.h
index 048dbb9..db07d60 100644
--- a/services/core/jni/stats/PowerStatsPuller.h
+++ b/services/core/jni/stats/PowerStatsPuller.h
@@ -29,7 +29,7 @@
class PowerStatsPuller {
public:
PowerStatsPuller();
- status_pull_atom_return_t Pull(int32_t atomTag, pulled_stats_event_list* data);
+ AStatsManager_PullAtomCallbackReturn Pull(int32_t atomTag, AStatsEventList* data);
};
} // namespace stats
diff --git a/services/core/jni/stats/SubsystemSleepStatePuller.cpp b/services/core/jni/stats/SubsystemSleepStatePuller.cpp
index c6a836c..45afb5e 100644
--- a/services/core/jni/stats/SubsystemSleepStatePuller.cpp
+++ b/services/core/jni/stats/SubsystemSleepStatePuller.cpp
@@ -55,7 +55,7 @@
namespace server {
namespace stats {
-static std::function<status_pull_atom_return_t(int32_t atomTag, pulled_stats_event_list* data)>
+static std::function<AStatsManager_PullAtomCallbackReturn(int32_t atomTag, AStatsEventList* data)>
gPuller = {};
static sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0 = nullptr;
@@ -176,12 +176,12 @@
}
// The caller must be holding gPowerHalMutex.
-static status_pull_atom_return_t getIPowerStatsDataLocked(int32_t atomTag,
- pulled_stats_event_list* data) {
+static AStatsManager_PullAtomCallbackReturn getIPowerStatsDataLocked(int32_t atomTag,
+ AStatsEventList* data) {
using android::hardware::power::stats::V1_0::Status;
if(!getPowerStatsHalLocked()) {
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
// Get power entity state residency data
bool success = false;
@@ -194,17 +194,17 @@
}
for (auto result : results) {
for (auto stateResidency : result.stateResidencyData) {
- stats_event* event = add_stats_event_to_pull_data(data);
- stats_event_set_atom_id(event, android::util::SUBSYSTEM_SLEEP_STATE);
- stats_event_write_string8(event,
- gEntityNames.at(result.powerEntityId).c_str());
- stats_event_write_string8(event,
- gStateNames.at(result.powerEntityId)
- .at(stateResidency.powerEntityStateId)
- .c_str());
- stats_event_write_int64(event, stateResidency.totalStateEntryCount);
- stats_event_write_int64(event, stateResidency.totalTimeInStateMs);
- stats_event_build(event);
+ AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+ AStatsEvent_setAtomId(event, android::util::SUBSYSTEM_SLEEP_STATE);
+ AStatsEvent_writeString(event,
+ gEntityNames.at(result.powerEntityId).c_str());
+ AStatsEvent_writeString(event,
+ gStateNames.at(result.powerEntityId)
+ .at(stateResidency.powerEntityStateId)
+ .c_str());
+ AStatsEvent_writeInt64(event, stateResidency.totalStateEntryCount);
+ AStatsEvent_writeInt64(event, stateResidency.totalTimeInStateMs);
+ AStatsEvent_build(event);
}
}
success = true;
@@ -213,9 +213,9 @@
// bool success determines if this succeeded or not.
checkResultLocked(ret, __func__);
if (!success) {
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
- return STATS_PULL_SUCCESS;
+ return AStatsManager_PULL_SUCCESS;
}
// The caller must be holding gPowerHalMutex.
@@ -244,12 +244,12 @@
}
// The caller must be holding gPowerHalMutex.
-static status_pull_atom_return_t getIPowerDataLocked(int32_t atomTag,
- pulled_stats_event_list* data) {
+static AStatsManager_PullAtomCallbackReturn getIPowerDataLocked(int32_t atomTag,
+ AStatsEventList* data) {
using android::hardware::power::V1_0::Status;
if(!getPowerHalLocked()) {
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
Return<void> ret;
@@ -259,26 +259,26 @@
for (size_t i = 0; i < states.size(); i++) {
const PowerStatePlatformSleepState& state = states[i];
- stats_event* event = add_stats_event_to_pull_data(data);
- stats_event_set_atom_id(event, android::util::SUBSYSTEM_SLEEP_STATE);
- stats_event_write_string8(event, state.name.c_str());
- stats_event_write_string8(event, "");
- stats_event_write_int64(event, state.totalTransitions);
- stats_event_write_int64(event, state.residencyInMsecSinceBoot);
- stats_event_build(event);
+ AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+ AStatsEvent_setAtomId(event, android::util::SUBSYSTEM_SLEEP_STATE);
+ AStatsEvent_writeString(event, state.name.c_str());
+ AStatsEvent_writeString(event, "");
+ AStatsEvent_writeInt64(event, state.totalTransitions);
+ AStatsEvent_writeInt64(event, state.residencyInMsecSinceBoot);
+ AStatsEvent_build(event);
ALOGV("powerstate: %s, %lld, %lld, %d", state.name.c_str(),
(long long)state.residencyInMsecSinceBoot,
(long long)state.totalTransitions,
state.supportedOnlyInSuspend ? 1 : 0);
for (const auto& voter : state.voters) {
- stats_event* event = add_stats_event_to_pull_data(data);
- stats_event_set_atom_id(event, android::util::SUBSYSTEM_SLEEP_STATE);
- stats_event_write_string8(event, state.name.c_str());
- stats_event_write_string8(event, voter.name.c_str());
- stats_event_write_int64(event, voter.totalNumberOfTimesVotedSinceBoot);
- stats_event_write_int64(event, voter.totalTimeInMsecVotedForSinceBoot);
- stats_event_build(event);
+ AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+ AStatsEvent_setAtomId(event, android::util::SUBSYSTEM_SLEEP_STATE);
+ AStatsEvent_writeString(event, state.name.c_str());
+ AStatsEvent_writeString(event, voter.name.c_str());
+ AStatsEvent_writeInt64(event, voter.totalNumberOfTimesVotedSinceBoot);
+ AStatsEvent_writeInt64(event, voter.totalTimeInMsecVotedForSinceBoot);
+ AStatsEvent_build(event);
ALOGV("powerstatevoter: %s, %s, %lld, %lld", state.name.c_str(),
voter.name.c_str(),
@@ -288,7 +288,7 @@
}
});
if (!checkResultLocked(ret, __func__)) {
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
// Trying to cast to IPower 1.1, this will succeed only for devices supporting 1.1
@@ -305,14 +305,14 @@
for (size_t j = 0; j < subsystem.states.size(); j++) {
const PowerStateSubsystemSleepState& state =
subsystem.states[j];
- stats_event* event = add_stats_event_to_pull_data(data);
- stats_event_set_atom_id(event,
- android::util::SUBSYSTEM_SLEEP_STATE);
- stats_event_write_string8(event, subsystem.name.c_str());
- stats_event_write_string8(event, state.name.c_str());
- stats_event_write_int64(event, state.totalTransitions);
- stats_event_write_int64(event, state.residencyInMsecSinceBoot);
- stats_event_build(event);
+ AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+ AStatsEvent_setAtomId(event,
+ android::util::SUBSYSTEM_SLEEP_STATE);
+ AStatsEvent_writeString(event, subsystem.name.c_str());
+ AStatsEvent_writeString(event, state.name.c_str());
+ AStatsEvent_writeInt64(event, state.totalTransitions);
+ AStatsEvent_writeInt64(event, state.residencyInMsecSinceBoot);
+ AStatsEvent_build(event);
ALOGV("subsystemstate: %s, %s, %lld, %lld, %lld",
subsystem.name.c_str(), state.name.c_str(),
@@ -324,14 +324,14 @@
}
});
}
- return STATS_PULL_SUCCESS;
+ return AStatsManager_PULL_SUCCESS;
}
// The caller must be holding gPowerHalMutex.
-std::function<status_pull_atom_return_t(int32_t atomTag, pulled_stats_event_list* data)>
+std::function<AStatsManager_PullAtomCallbackReturn(int32_t atomTag, AStatsEventList* data)>
getPullerLocked() {
- std::function<status_pull_atom_return_t(int32_t atomTag, pulled_stats_event_list * data)> ret =
- {};
+ std::function<AStatsManager_PullAtomCallbackReturn(int32_t atomTag, AStatsEventList * data)>
+ ret = {};
// First see if power.stats HAL is available. Fall back to power HAL if
// power.stats HAL is unavailable.
@@ -346,8 +346,8 @@
return ret;
}
-status_pull_atom_return_t SubsystemSleepStatePuller::Pull(int32_t atomTag,
- pulled_stats_event_list* data) {
+AStatsManager_PullAtomCallbackReturn SubsystemSleepStatePuller::Pull(int32_t atomTag,
+ AStatsEventList* data) {
std::lock_guard<std::mutex> lock(gPowerHalMutex);
if(!gPuller) {
@@ -359,7 +359,7 @@
}
ALOGE("Unable to load Power Hal or power.stats HAL");
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
} // namespace stats
diff --git a/services/core/jni/stats/SubsystemSleepStatePuller.h b/services/core/jni/stats/SubsystemSleepStatePuller.h
index 59dbbd2..da9679c 100644
--- a/services/core/jni/stats/SubsystemSleepStatePuller.h
+++ b/services/core/jni/stats/SubsystemSleepStatePuller.h
@@ -29,7 +29,7 @@
class SubsystemSleepStatePuller {
public:
SubsystemSleepStatePuller();
- status_pull_atom_return_t Pull(int32_t atomTag, pulled_stats_event_list* data);
+ AStatsManager_PullAtomCallbackReturn Pull(int32_t atomTag, AStatsEventList* data);
};
} // namespace stats
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 65cabad..28e44f1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4680,12 +4680,15 @@
private void ensureMinimumQuality(
int userId, ActiveAdmin admin, int minimumQuality, String operation) {
- if (admin.mPasswordPolicy.quality < minimumQuality
- && passwordQualityInvocationOrderCheckEnabled(admin.info.getPackageName(),
- userId)) {
- throw new IllegalStateException(String.format(
- "password quality should be at least %d for %s", minimumQuality, operation));
- }
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ if (admin.mPasswordPolicy.quality < minimumQuality
+ && passwordQualityInvocationOrderCheckEnabled(admin.info.getPackageName(),
+ userId)) {
+ throw new IllegalStateException(String.format(
+ "password quality should be at least %d for %s",
+ minimumQuality, operation));
+ }
+ });
}
@Override
@@ -5343,7 +5346,7 @@
synchronized (getLockObject()) {
ActiveAdmin admin = getAdminWithMinimumFailedPasswordsForWipeLocked(
userHandle, parent);
- return admin != null ? admin.getUserHandle().getIdentifier() : UserHandle.USER_NULL;
+ return admin != null ? getUserIdToWipeForFailedPasswords(admin) : UserHandle.USER_NULL;
}
}
@@ -5354,7 +5357,8 @@
* <li>this user and all profiles that don't have their own challenge otherwise.
* </ul>
* <p>If the policy for the primary and any other profile are equal, it returns the admin for
- * the primary profile.
+ * the primary profile. Policy of a PO on an organization-owned device applies to the primary
+ * profile.
* Returns {@code null} if no participating admin has that policy set.
*/
private ActiveAdmin getAdminWithMinimumFailedPasswordsForWipeLocked(
@@ -5373,7 +5377,7 @@
}
// We always favor the primary profile if several profiles have the same value set.
- int userId = admin.getUserHandle().getIdentifier();
+ final int userId = getUserIdToWipeForFailedPasswords(admin);
if (count == 0 ||
count > admin.maximumFailedPasswordsForWipe ||
(count == admin.maximumFailedPasswordsForWipe &&
@@ -7170,7 +7174,7 @@
}
if (wipeData && strictestAdmin != null) {
- final int userId = strictestAdmin.getUserHandle().getIdentifier();
+ final int userId = getUserIdToWipeForFailedPasswords(strictestAdmin);
Slog.i(LOG_TAG, "Max failed password attempts policy reached for admin: "
+ strictestAdmin.info.getComponent().flattenToShortString()
+ ". Calling wipeData for user " + userId);
@@ -7201,6 +7205,17 @@
}
}
+ /**
+ * Returns which user should be wiped if this admin's maximum filed password attempts policy is
+ * violated.
+ */
+ private int getUserIdToWipeForFailedPasswords(ActiveAdmin admin) {
+ final int userId = admin.getUserHandle().getIdentifier();
+ final ComponentName component = admin.info.getComponent();
+ return isProfileOwnerOfOrganizationOwnedDevice(component, userId)
+ ? getProfileParentId(userId) : userId;
+ }
+
@Override
public void reportSuccessfulPasswordAttempt(int userHandle) {
enforceFullCrossUsersPermission(userHandle);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index b193a34..7d5cb13 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -96,6 +96,7 @@
import android.util.ArraySet;
import android.util.Pair;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
@@ -4535,6 +4536,86 @@
.removeUserEvenWhenDisallowed(anyInt());
}
+ public void testMaximumFailedDevicePasswordAttemptsReachedOrgOwnedManagedProfile()
+ throws Exception {
+ final int MANAGED_PROFILE_USER_ID = 15;
+ final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 19436);
+ addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
+
+ // Even if the caller is the managed profile, the current user is the user 0
+ when(getServices().iactivityManager.getCurrentUser())
+ .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
+
+ configureProfileOwnerOfOrgOwnedDevice(admin1, MANAGED_PROFILE_USER_ID);
+
+ mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+ dpm.setMaximumFailedPasswordsForWipe(admin1, 3);
+
+ assertEquals(3, dpm.getMaximumFailedPasswordsForWipe(admin1));
+ assertEquals(3, dpm.getMaximumFailedPasswordsForWipe(null));
+
+ mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+ mContext.callerPermissions.add(permission.BIND_DEVICE_ADMIN);
+
+ assertEquals(3, dpm.getMaximumFailedPasswordsForWipe(null, UserHandle.USER_SYSTEM));
+ // Check that primary will be wiped as a result of failed primary user unlock attempts.
+ assertEquals(UserHandle.USER_SYSTEM,
+ dpm.getProfileWithMinimumFailedPasswordsForWipe(UserHandle.USER_SYSTEM));
+
+ // Failed password attempts on the parent user are taken into account, as there isn't a
+ // separate work challenge.
+ dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM);
+ dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM);
+ dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM);
+
+ // For managed profile on an organization owned device, the whole device should be wiped.
+ verify(getServices().recoverySystem).rebootWipeUserData(
+ /*shutdown=*/ eq(false), anyString(), /*force=*/ eq(true),
+ /*wipeEuicc=*/ eq(false));
+ }
+
+ public void testMaximumFailedProfilePasswordAttemptsReachedOrgOwnedManagedProfile()
+ throws Exception {
+ final int MANAGED_PROFILE_USER_ID = 15;
+ final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 19436);
+ addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
+
+ // Even if the caller is the managed profile, the current user is the user 0
+ when(getServices().iactivityManager.getCurrentUser())
+ .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
+
+ doReturn(true).when(getServices().lockPatternUtils)
+ .isSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID);
+
+ // Configure separate challenge.
+ configureProfileOwnerOfOrgOwnedDevice(admin1, MANAGED_PROFILE_USER_ID);
+
+ mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+ dpm.setMaximumFailedPasswordsForWipe(admin1, 3);
+
+ mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+ mContext.callerPermissions.add(permission.BIND_DEVICE_ADMIN);
+
+ assertEquals(0, dpm.getMaximumFailedPasswordsForWipe(null, UserHandle.USER_SYSTEM));
+ assertEquals(3, dpm.getMaximumFailedPasswordsForWipe(null, MANAGED_PROFILE_USER_ID));
+ // Check that the policy is not affecting primary profile challenge.
+ assertEquals(UserHandle.USER_NULL,
+ dpm.getProfileWithMinimumFailedPasswordsForWipe(UserHandle.USER_SYSTEM));
+ // Check that primary will be wiped as a result of failed profile unlock attempts.
+ assertEquals(UserHandle.USER_SYSTEM,
+ dpm.getProfileWithMinimumFailedPasswordsForWipe(MANAGED_PROFILE_USER_ID));
+
+ // Simulate three failed attempts at solving the separate challenge.
+ dpm.reportFailedPasswordAttempt(MANAGED_PROFILE_USER_ID);
+ dpm.reportFailedPasswordAttempt(MANAGED_PROFILE_USER_ID);
+ dpm.reportFailedPasswordAttempt(MANAGED_PROFILE_USER_ID);
+
+ // For managed profile on an organization owned device, the whole device should be wiped.
+ verify(getServices().recoverySystem).rebootWipeUserData(
+ /*shutdown=*/ eq(false), anyString(), /*force=*/ eq(true),
+ /*wipeEuicc=*/ eq(false));
+ }
+
public void testGetPermissionGrantState() throws Exception {
final String permission = "some.permission";
final String app1 = "com.example.app1";
@@ -5366,6 +5447,7 @@
assertTrue(dpms.isAdminActive(admin1, UserHandle.USER_SYSTEM));
}
+ @FlakyTest(bugId = 148934649)
public void testRevertDeviceOwnership_adminAndDeviceMigrated() throws Exception {
DpmTestUtils.writeInputStreamToFile(
getRawStream(com.android.frameworks.servicestests.R.raw.active_admin_migrated),
@@ -5376,6 +5458,7 @@
assertDeviceOwnershipRevertedWithFakeTransferMetadata();
}
+ @FlakyTest(bugId = 148934649)
public void testRevertDeviceOwnership_deviceNotMigrated()
throws Exception {
DpmTestUtils.writeInputStreamToFile(
@@ -5407,6 +5490,7 @@
UserHandle userHandle = UserHandle.of(DpmMockContext.CALLER_USER_HANDLE);
}
+ @FlakyTest(bugId = 148934649)
public void testRevertProfileOwnership_adminAndProfileMigrated() throws Exception {
getServices().addUser(DpmMockContext.CALLER_USER_HANDLE, 0,
UserManager.USER_TYPE_PROFILE_MANAGED, UserHandle.USER_SYSTEM);
@@ -5419,6 +5503,7 @@
assertProfileOwnershipRevertedWithFakeTransferMetadata();
}
+ @FlakyTest(bugId = 148934649)
public void testRevertProfileOwnership_profileNotMigrated() throws Exception {
getServices().addUser(DpmMockContext.CALLER_USER_HANDLE, 0,
UserManager.USER_TYPE_PROFILE_MANAGED, UserHandle.USER_SYSTEM);
@@ -5770,8 +5855,7 @@
when(getServices().userManager.getProfileParent(eq(UserHandle.of(userId))))
.thenReturn(UserHandle.SYSTEM);
final long ident = mServiceContext.binder.clearCallingIdentity();
- mServiceContext.binder.callingUid =
- UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE, DpmMockContext.SYSTEM_UID);
+ mServiceContext.binder.callingUid = UserHandle.getUid(userId, DpmMockContext.SYSTEM_UID);
configureContextForAccess(mServiceContext, true);
runAsCaller(mServiceContext, dpms, dpm -> {
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
index d0d2edc..64d05f0 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
@@ -119,7 +119,7 @@
@Test
public void createNonStaged() {
- Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER);
+ Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null);
assertThat(rollback.getBackupDir().getAbsolutePath())
.isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
@@ -132,7 +132,7 @@
@Test
public void createStaged() {
- Rollback rollback = mRollbackStore.createStagedRollback(ID, 897, USER, INSTALLER);
+ Rollback rollback = mRollbackStore.createStagedRollback(ID, 897, USER, INSTALLER, null);
assertThat(rollback.getBackupDir().getAbsolutePath())
.isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
@@ -147,7 +147,7 @@
@Test
public void saveAndLoadRollback() {
- Rollback origRb = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER);
+ Rollback origRb = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null);
origRb.setRestoreUserDataInProgress(true);
origRb.info.getCausePackages().add(new VersionedPackage("com.made.up", 2));
@@ -197,7 +197,7 @@
@Test
public void loadFromJson() throws Exception {
- Rollback expectedRb = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER);
+ Rollback expectedRb = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null);
expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z"));
expectedRb.setRestoreUserDataInProgress(true);
@@ -246,7 +246,7 @@
@Test
public void saveAndDelete() {
- Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER);
+ Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null);
RollbackStore.saveRollback(rollback);
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
index 9719509..804c1b9 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
@@ -302,6 +302,22 @@
assertThat(rollback.notifySessionWithSuccess()).isTrue();
}
+ @Test
+ public void allPackagesEnabled() {
+ int[] sessionIds = new int[]{ 7777, 8888 };
+ Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER,
+ sessionIds);
+ // #allPackagesEnabled returns false when 1 out of 2 packages is enabled.
+ rollback.info.getPackages().add(newPkgInfoFor(PKG_1, 12, 10, false));
+ assertThat(rollback.allPackagesEnabled()).isFalse();
+ // #allPackagesEnabled returns false for ApkInApex doesn't count.
+ rollback.info.getPackages().add(newPkgInfoForApkInApex(PKG_3, 157, 156));
+ assertThat(rollback.allPackagesEnabled()).isFalse();
+ // #allPackagesEnabled returns true when 2 out of 2 packages are enabled.
+ rollback.info.getPackages().add(newPkgInfoFor(PKG_2, 18, 12, true));
+ assertThat(rollback.allPackagesEnabled()).isTrue();
+ }
+
private static PackageRollbackInfo newPkgInfoFor(
String packageName, long fromVersion, long toVersion, boolean isApex) {
return new PackageRollbackInfo(new VersionedPackage(packageName, fromVersion),
@@ -310,6 +326,20 @@
new SparseLongArray());
}
+ /**
+ * TODO: merge newPkgInfoFor and newPkgInfoForApkInApex by using enums to specify
+ * 1. IS_APK
+ * 2. IS_APEX
+ * 3. IS_APK_IN_APEX
+ */
+ private static PackageRollbackInfo newPkgInfoForApkInApex(
+ String packageName, long fromVersion, long toVersion) {
+ return new PackageRollbackInfo(new VersionedPackage(packageName, fromVersion),
+ new VersionedPackage(packageName, toVersion),
+ new IntArray(), new ArrayList<>(), false, true, new IntArray(),
+ new SparseLongArray());
+ }
+
private static class PackageRollbackInfoForPackage implements
ArgumentMatcher<PackageRollbackInfo> {
private final String mPkg;
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index ae53692..218f43c 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -33,14 +33,13 @@
import android.app.timedetector.PhoneTimeSuggestion;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.os.Handler;
import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
import android.os.TimestampedValue;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.timezonedetector.TestHandler;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -108,7 +107,7 @@
eq(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE),
anyString());
- mTestHandler.waitForEmptyQueue();
+ mTestHandler.waitForMessagesToBeProcessed();
mStubbedTimeDetectorStrategy.verifySuggestPhoneTimeCalled(phoneTimeSuggestion);
}
@@ -140,7 +139,7 @@
eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE),
anyString());
- mTestHandler.waitForEmptyQueue();
+ mTestHandler.waitForMessagesToBeProcessed();
mStubbedTimeDetectorStrategy.verifySuggestManualTimeCalled(manualTimeSuggestion);
}
@@ -170,7 +169,7 @@
verify(mMockContext).enforceCallingOrSelfPermission(
eq(android.Manifest.permission.SET_TIME), anyString());
- mTestHandler.waitForEmptyQueue();
+ mTestHandler.waitForMessagesToBeProcessed();
mStubbedTimeDetectorStrategy.verifySuggestNetworkTimeCalled(NetworkTimeSuggestion);
}
@@ -187,21 +186,23 @@
@Test
public void testAutoTimeDetectionToggle() throws Exception {
- mTimeDetectorService.handleAutoTimeDetectionToggle();
+ mTimeDetectorService.handleAutoTimeDetectionChanged();
mTestHandler.assertTotalMessagesEnqueued(1);
- mTestHandler.waitForEmptyQueue();
- mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled();
+ mTestHandler.waitForMessagesToBeProcessed();
+ mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionChangedCalled();
- mTimeDetectorService.handleAutoTimeDetectionToggle();
+ mStubbedTimeDetectorStrategy.resetCallTracking();
+
+ mTimeDetectorService.handleAutoTimeDetectionChanged();
mTestHandler.assertTotalMessagesEnqueued(2);
- mTestHandler.waitForEmptyQueue();
- mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled();
+ mTestHandler.waitForMessagesToBeProcessed();
+ mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionChangedCalled();
}
private static PhoneTimeSuggestion createPhoneTimeSuggestion() {
- int phoneId = 1234;
+ int slotIndex = 1234;
TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L);
- return new PhoneTimeSuggestion.Builder(phoneId)
+ return new PhoneTimeSuggestion.Builder(slotIndex)
.setUtcTime(timeValue)
.build();
}
@@ -222,7 +223,7 @@
private PhoneTimeSuggestion mLastPhoneSuggestion;
private ManualTimeSuggestion mLastManualSuggestion;
private NetworkTimeSuggestion mLastNetworkSuggestion;
- private boolean mLastAutoTimeDetectionToggleCalled;
+ private boolean mHandleAutoTimeDetectionChangedCalled;
private boolean mDumpCalled;
@Override
@@ -231,31 +232,26 @@
@Override
public void suggestPhoneTime(PhoneTimeSuggestion timeSuggestion) {
- resetCallTracking();
mLastPhoneSuggestion = timeSuggestion;
}
@Override
public void suggestManualTime(ManualTimeSuggestion timeSuggestion) {
- resetCallTracking();
mLastManualSuggestion = timeSuggestion;
}
@Override
public void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion) {
- resetCallTracking();
mLastNetworkSuggestion = timeSuggestion;
}
@Override
public void handleAutoTimeDetectionChanged() {
- resetCallTracking();
- mLastAutoTimeDetectionToggleCalled = true;
+ mHandleAutoTimeDetectionChangedCalled = true;
}
@Override
public void dump(PrintWriter pw, String[] args) {
- resetCallTracking();
mDumpCalled = true;
}
@@ -263,7 +259,7 @@
mLastPhoneSuggestion = null;
mLastManualSuggestion = null;
mLastNetworkSuggestion = null;
- mLastAutoTimeDetectionToggleCalled = false;
+ mHandleAutoTimeDetectionChangedCalled = false;
mDumpCalled = false;
}
@@ -279,45 +275,12 @@
assertEquals(expectedSuggestion, mLastNetworkSuggestion);
}
- void verifyHandleAutoTimeDetectionToggleCalled() {
- assertTrue(mLastAutoTimeDetectionToggleCalled);
+ void verifyHandleAutoTimeDetectionChangedCalled() {
+ assertTrue(mHandleAutoTimeDetectionChangedCalled);
}
void verifyDumpCalled() {
assertTrue(mDumpCalled);
}
}
-
- /**
- * A Handler that can track posts/sends and wait for work to be completed.
- */
- private static class TestHandler extends Handler {
-
- private int mMessagesSent;
-
- TestHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
- mMessagesSent++;
- return super.sendMessageAtTime(msg, uptimeMillis);
- }
-
- /** Asserts the number of messages posted or sent is as expected. */
- void assertTotalMessagesEnqueued(int expected) {
- assertEquals(expected, mMessagesSent);
- }
-
- /**
- * Waits for all currently enqueued work due to be processed to be completed before
- * returning.
- */
- void waitForEmptyQueue() throws InterruptedException {
- while (!getLooper().getQueue().isIdle()) {
- Thread.sleep(100);
- }
- }
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestHandler.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TestHandler.java
new file mode 100644
index 0000000..21c9685
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TestHandler.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 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.timezonedetector;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+/**
+ * A Handler that can track posts/sends and wait for them to be completed.
+ */
+public class TestHandler extends Handler {
+
+ private final Object mMonitor = new Object();
+ private int mMessagesProcessed = 0;
+ private int mMessagesSent = 0;
+
+ public TestHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+ synchronized (mMonitor) {
+ mMessagesSent++;
+ }
+
+ Runnable callback = msg.getCallback();
+ // Have the callback increment the mMessagesProcessed when it is done. It will notify
+ // any threads waiting for all messages to be processed if appropriate.
+ Runnable newCallback = () -> {
+ callback.run();
+ synchronized (mMonitor) {
+ mMessagesProcessed++;
+ if (mMessagesSent == mMessagesProcessed) {
+ mMonitor.notifyAll();
+ }
+ }
+ };
+ msg.setCallback(newCallback);
+ return super.sendMessageAtTime(msg, uptimeMillis);
+ }
+
+ /** Asserts the number of messages posted or sent is as expected. */
+ public void assertTotalMessagesEnqueued(int expected) {
+ synchronized (mMonitor) {
+ assertEquals(expected, mMessagesSent);
+ }
+ }
+
+ /**
+ * Waits for all enqueued work to be completed before returning.
+ */
+ public void waitForMessagesToBeProcessed() throws InterruptedException {
+ synchronized (mMonitor) {
+ if (mMessagesSent != mMessagesProcessed) {
+ mMonitor.wait();
+ }
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
new file mode 100644
index 0000000..3e7d40a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2020 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.timezonedetector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
+import android.app.timezonedetector.PhoneTimeZoneSuggestion;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.HandlerThread;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.PrintWriter;
+
+@RunWith(AndroidJUnit4.class)
+public class TimeZoneDetectorServiceTest {
+
+ private Context mMockContext;
+ private StubbedTimeZoneDetectorStrategy mStubbedTimeZoneDetectorStrategy;
+
+ private TimeZoneDetectorService mTimeZoneDetectorService;
+ private HandlerThread mHandlerThread;
+ private TestHandler mTestHandler;
+
+
+ @Before
+ public void setUp() {
+ mMockContext = mock(Context.class);
+
+ // Create a thread + handler for processing the work that the service posts.
+ mHandlerThread = new HandlerThread("TimeZoneDetectorServiceTest");
+ mHandlerThread.start();
+ mTestHandler = new TestHandler(mHandlerThread.getLooper());
+
+ mStubbedTimeZoneDetectorStrategy = new StubbedTimeZoneDetectorStrategy();
+
+ mTimeZoneDetectorService = new TimeZoneDetectorService(
+ mMockContext, mTestHandler, mStubbedTimeZoneDetectorStrategy);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mHandlerThread.quit();
+ mHandlerThread.join();
+ }
+
+ @Test(expected = SecurityException.class)
+ public void testSuggestPhoneTime_withoutPermission() {
+ doThrow(new SecurityException("Mock"))
+ .when(mMockContext).enforceCallingPermission(anyString(), any());
+ PhoneTimeZoneSuggestion timeZoneSuggestion = createPhoneTimeZoneSuggestion();
+
+ try {
+ mTimeZoneDetectorService.suggestPhoneTimeZone(timeZoneSuggestion);
+ fail();
+ } finally {
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE),
+ anyString());
+ }
+ }
+
+ @Test
+ public void testSuggestPhoneTimeZone() throws Exception {
+ doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
+
+ PhoneTimeZoneSuggestion timeZoneSuggestion = createPhoneTimeZoneSuggestion();
+ mTimeZoneDetectorService.suggestPhoneTimeZone(timeZoneSuggestion);
+ mTestHandler.assertTotalMessagesEnqueued(1);
+
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE),
+ anyString());
+
+ mTestHandler.waitForMessagesToBeProcessed();
+ mStubbedTimeZoneDetectorStrategy.verifySuggestPhoneTimeZoneCalled(timeZoneSuggestion);
+ }
+
+ @Test(expected = SecurityException.class)
+ public void testSuggestManualTime_withoutPermission() {
+ doThrow(new SecurityException("Mock"))
+ .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+ ManualTimeZoneSuggestion timeZoneSuggestion = createManualTimeZoneSuggestion();
+
+ try {
+ mTimeZoneDetectorService.suggestManualTimeZone(timeZoneSuggestion);
+ fail();
+ } finally {
+ verify(mMockContext).enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE),
+ anyString());
+ }
+ }
+
+ @Test
+ public void testSuggestManualTimeZone() throws Exception {
+ doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+
+ ManualTimeZoneSuggestion timeZoneSuggestion = createManualTimeZoneSuggestion();
+ mTimeZoneDetectorService.suggestManualTimeZone(timeZoneSuggestion);
+ mTestHandler.assertTotalMessagesEnqueued(1);
+
+ verify(mMockContext).enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE),
+ anyString());
+
+ mTestHandler.waitForMessagesToBeProcessed();
+ mStubbedTimeZoneDetectorStrategy.verifySuggestManualTimeZoneCalled(timeZoneSuggestion);
+ }
+
+ @Test
+ public void testDump() {
+ when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+
+ mTimeZoneDetectorService.dump(null, null, null);
+
+ verify(mMockContext).checkCallingOrSelfPermission(eq(android.Manifest.permission.DUMP));
+ mStubbedTimeZoneDetectorStrategy.verifyDumpCalled();
+ }
+
+ @Test
+ public void testAutoTimeZoneDetectionChanged() throws Exception {
+ mTimeZoneDetectorService.handleAutoTimeZoneDetectionChanged();
+ mTestHandler.assertTotalMessagesEnqueued(1);
+ mTestHandler.waitForMessagesToBeProcessed();
+ mStubbedTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneDetectionChangedCalled();
+
+ mStubbedTimeZoneDetectorStrategy.resetCallTracking();
+
+ mTimeZoneDetectorService.handleAutoTimeZoneDetectionChanged();
+ mTestHandler.assertTotalMessagesEnqueued(2);
+ mTestHandler.waitForMessagesToBeProcessed();
+ mStubbedTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneDetectionChangedCalled();
+ }
+
+ private static PhoneTimeZoneSuggestion createPhoneTimeZoneSuggestion() {
+ int slotIndex = 1234;
+ return new PhoneTimeZoneSuggestion.Builder(slotIndex)
+ .setZoneId("TestZoneId")
+ .setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET)
+ .setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE)
+ .build();
+ }
+
+ private static ManualTimeZoneSuggestion createManualTimeZoneSuggestion() {
+ return new ManualTimeZoneSuggestion("TestZoneId");
+ }
+
+ private static class StubbedTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
+
+ // Call tracking.
+ private PhoneTimeZoneSuggestion mLastPhoneSuggestion;
+ private ManualTimeZoneSuggestion mLastManualSuggestion;
+ private boolean mHandleAutoTimeZoneDetectionChangedCalled;
+ private boolean mDumpCalled;
+
+ @Override
+ public void suggestPhoneTimeZone(PhoneTimeZoneSuggestion timeZoneSuggestion) {
+ mLastPhoneSuggestion = timeZoneSuggestion;
+ }
+
+ @Override
+ public void suggestManualTimeZone(ManualTimeZoneSuggestion timeZoneSuggestion) {
+ mLastManualSuggestion = timeZoneSuggestion;
+ }
+
+ @Override
+ public void handleAutoTimeZoneDetectionChanged() {
+ mHandleAutoTimeZoneDetectionChangedCalled = true;
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String[] args) {
+ mDumpCalled = true;
+ }
+
+ void resetCallTracking() {
+ mLastPhoneSuggestion = null;
+ mLastManualSuggestion = null;
+ mHandleAutoTimeZoneDetectionChangedCalled = false;
+ mDumpCalled = false;
+ }
+
+ void verifySuggestPhoneTimeZoneCalled(PhoneTimeZoneSuggestion expectedSuggestion) {
+ assertEquals(expectedSuggestion, mLastPhoneSuggestion);
+ }
+
+ public void verifySuggestManualTimeZoneCalled(ManualTimeZoneSuggestion expectedSuggestion) {
+ assertEquals(expectedSuggestion, mLastManualSuggestion);
+ }
+
+ void verifyHandleAutoTimeZoneDetectionChangedCalled() {
+ assertTrue(mHandleAutoTimeZoneDetectionChangedCalled);
+ }
+
+ void verifyDumpCalled() {
+ assertTrue(mDumpCalled);
+ }
+ }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java
rename to services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index 2429cfc..1e38711 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -24,12 +24,12 @@
import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_HIGH;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_HIGHEST;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_LOW;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_MEDIUM;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_NONE;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_USAGE_THRESHOLD;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_HIGH;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_HIGHEST;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_LOW;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_MEDIUM;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_NONE;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_USAGE_THRESHOLD;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -41,7 +41,7 @@
import android.app.timezonedetector.PhoneTimeZoneSuggestion.MatchType;
import android.app.timezonedetector.PhoneTimeZoneSuggestion.Quality;
-import com.android.server.timezonedetector.TimeZoneDetectorStrategy.QualifiedPhoneTimeZoneSuggestion;
+import com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.QualifiedPhoneTimeZoneSuggestion;
import org.junit.Before;
import org.junit.Test;
@@ -52,9 +52,9 @@
import java.util.LinkedList;
/**
- * White-box unit tests for {@link TimeZoneDetectorStrategy}.
+ * White-box unit tests for {@link TimeZoneDetectorStrategyImpl}.
*/
-public class TimeZoneDetectorStrategyTest {
+public class TimeZoneDetectorStrategyImplTest {
/** A time zone used for initialization that does not occur elsewhere in tests. */
private static final String ARBITRARY_TIME_ZONE_ID = "Etc/UTC";
@@ -78,14 +78,14 @@
newTestCase(MATCH_TYPE_EMULATOR_ZONE_ID, QUALITY_SINGLE_ZONE, PHONE_SCORE_HIGHEST),
};
- private TimeZoneDetectorStrategy mTimeZoneDetectorStrategy;
+ private TimeZoneDetectorStrategyImpl mTimeZoneDetectorStrategy;
private FakeTimeZoneDetectorStrategyCallback mFakeTimeZoneDetectorStrategyCallback;
@Before
public void setUp() {
mFakeTimeZoneDetectorStrategyCallback = new FakeTimeZoneDetectorStrategyCallback();
mTimeZoneDetectorStrategy =
- new TimeZoneDetectorStrategy(mFakeTimeZoneDetectorStrategyCallback);
+ new TimeZoneDetectorStrategyImpl(mFakeTimeZoneDetectorStrategyCallback);
}
@Test
@@ -364,7 +364,7 @@
}
/**
- * The {@link TimeZoneDetectorStrategy.Callback} is left to detect whether changing the time
+ * The {@link TimeZoneDetectorStrategyImpl.Callback} is left to detect whether changing the time
* zone is actually necessary. This test proves that the service doesn't assume it knows the
* current setting.
*/
@@ -441,7 +441,8 @@
return new PhoneTimeZoneSuggestion.Builder(PHONE2_ID).build();
}
- static class FakeTimeZoneDetectorStrategyCallback implements TimeZoneDetectorStrategy.Callback {
+ static class FakeTimeZoneDetectorStrategyCallback
+ implements TimeZoneDetectorStrategyImpl.Callback {
private boolean mAutoTimeZoneDetectionEnabled;
private TestState<String> mTimeZoneId = new TestState<>();
@@ -560,7 +561,7 @@
Script autoTimeZoneDetectionEnabled(boolean enabled) {
mFakeTimeZoneDetectorStrategyCallback.setAutoTimeZoneDetectionEnabled(enabled);
- mTimeZoneDetectorStrategy.handleAutoTimeZoneDetectionChange();
+ mTimeZoneDetectorStrategy.handleAutoTimeZoneDetectionChanged();
return this;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
index d16c232a..47ad831 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
@@ -97,7 +97,7 @@
NotificationChannel updatedChannel =
new NotificationChannel("a", "", IMPORTANCE_HIGH);
when(mConfig.getConversationNotificationChannel(
- any(), anyInt(), eq("a"), eq(r.sbn.getShortcutId(mContext)), eq(true), eq(false)))
+ any(), anyInt(), eq("a"), eq(r.getSbn().getShortcutId(mContext)), eq(true), eq(false)))
.thenReturn(updatedChannel);
assertNull(extractor.process(r));
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 34872db..e0ee3ce 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -30,6 +30,9 @@
import static android.app.NotificationManager.IMPORTANCE_MAX;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
@@ -67,6 +70,7 @@
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -684,8 +688,9 @@
NotificationRecord nrBubble = generateMessageBubbleNotifRecord(true /* addMetadata */,
mTestNotificationChannel, 1 /* id */, "tag", groupKey, false /* isSummary */);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nrBubble.sbn.getTag(),
- nrBubble.sbn.getId(), nrBubble.sbn.getNotification(), nrBubble.sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nrBubble.getSbn().getTag(),
+ nrBubble.getSbn().getId(), nrBubble.getSbn().getNotification(),
+ nrBubble.getSbn().getUserId());
waitForIdle();
// Make sure we are a bubble
@@ -697,8 +702,9 @@
NotificationRecord nrPlain = generateMessageBubbleNotifRecord(false /* addMetadata */,
mTestNotificationChannel, 2 /* id */, "tag", groupKey, false /* isSummary */);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nrPlain.sbn.getTag(),
- nrPlain.sbn.getId(), nrPlain.sbn.getNotification(), nrPlain.sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nrPlain.getSbn().getTag(),
+ nrPlain.getSbn().getId(), nrPlain.getSbn().getNotification(),
+ nrPlain.getSbn().getUserId());
waitForIdle();
notifsAfter = mBinderService.getActiveNotifications(PKG);
@@ -711,8 +717,9 @@
if (summaryAutoCancel) {
nrSummary.getNotification().flags |= FLAG_AUTO_CANCEL;
}
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nrSummary.sbn.getTag(),
- nrSummary.sbn.getId(), nrSummary.sbn.getNotification(), nrSummary.sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nrSummary.getSbn().getTag(),
+ nrSummary.getSbn().getId(), nrSummary.getSbn().getNotification(),
+ nrSummary.getSbn().getUserId());
waitForIdle();
notifsAfter = mBinderService.getActiveNotifications(PKG);
@@ -891,7 +898,7 @@
mBinderService.createNotificationChannels(
PKG, new ParceledListSlice(Arrays.asList(channel)));
- final StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
+ final StatusBarNotification sbn = generateNotificationRecord(channel).getSbn();
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testBlockedNotifications_blockedChannel",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
@@ -909,7 +916,7 @@
mBinderService.createNotificationChannels(
PKG, new ParceledListSlice(Arrays.asList(channel)));
- final StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
+ final StatusBarNotification sbn = generateNotificationRecord(channel).getSbn();
sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
sbn.getId(), sbn.getNotification(), sbn.getUserId());
@@ -938,7 +945,7 @@
assertEquals(IMPORTANCE_NONE, mBinderService.getNotificationChannel(
PKG, mContext.getUserId(), PKG, channel.getId()).getImportance());
- StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
+ StatusBarNotification sbn = generateNotificationRecord(channel).getSbn();
sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
sbn.getId(), sbn.getNotification(), sbn.getUserId());
@@ -960,7 +967,7 @@
assertEquals(IMPORTANCE_NONE, mBinderService.getNotificationChannel(
PKG, mContext.getUserId(), PKG, channel.getId()).getImportance());
- sbn = generateNotificationRecord(channel).sbn;
+ sbn = generateNotificationRecord(channel).getSbn();
sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testEnqueuedBlockedNotifications_userBlockedChannelForegroundService",
@@ -994,7 +1001,7 @@
mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
- final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testEnqueuedBlockedNotifications_blockedApp",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
@@ -1008,7 +1015,7 @@
mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
- final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testEnqueuedBlockedNotifications_blockedAppForegroundService",
@@ -1031,7 +1038,7 @@
int id = 0;
for (String category: categories) {
final StatusBarNotification sbn =
- generateNotificationRecord(mTestNotificationChannel, ++id, "", false).sbn;
+ generateNotificationRecord(mTestNotificationChannel, ++id, "", false).getSbn();
sbn.getNotification().category = category;
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testEnqueuedRestrictedNotifications_asSystem",
@@ -1056,7 +1063,7 @@
int id = 0;
for (String category: categories) {
final StatusBarNotification sbn =
- generateNotificationRecord(mTestNotificationChannel, ++id, "", false).sbn;
+ generateNotificationRecord(mTestNotificationChannel, ++id, "", false).getSbn();
sbn.getNotification().category = category;
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testEnqueuedRestrictedNotifications_notAutomotive",
@@ -1079,7 +1086,7 @@
Notification.CATEGORY_CAR_WARNING,
Notification.CATEGORY_CAR_INFORMATION);
for (String category: categories) {
- final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
sbn.getNotification().category = category;
try {
mBinderService.enqueueNotificationWithTag(PKG, PKG,
@@ -1107,7 +1114,7 @@
Bundle bundle = new Bundle();
bundle.putInt(KEY_IMPORTANCE, IMPORTANCE_NONE);
Adjustment adjustment = new Adjustment(
- r.sbn.getPackageName(), r.getKey(), bundle, "", r.getUser().getIdentifier());
+ r.getSbn().getPackageName(), r.getKey(), bundle, "", r.getUser().getIdentifier());
mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
NotificationManagerService.PostNotificationRunnable runnable =
@@ -1142,11 +1149,11 @@
assertNull(call.old);
assertEquals(0, call.position);
assertEquals(0, call.buzzBeepBlink);
- assertEquals(PKG, call.r.sbn.getPackageName());
- assertEquals(0, call.r.sbn.getId());
- assertEquals(tag, call.r.sbn.getTag());
- assertNotNull(call.r.sbn.getInstanceId());
- assertEquals(0, call.r.sbn.getInstanceId().getId());
+ assertEquals(PKG, call.r.getSbn().getPackageName());
+ assertEquals(0, call.r.getSbn().getId());
+ assertEquals(tag, call.r.getSbn().getTag());
+ assertNotNull(call.r.getSbn().getInstanceId());
+ assertEquals(0, call.r.getSbn().getInstanceId().getId());
}
@Test
@@ -1168,14 +1175,14 @@
assertEquals(
NotificationRecordLogger.NotificationReportedEvents.NOTIFICATION_POSTED,
mNotificationRecordLogger.get(0).getUiEvent());
- assertEquals(0, mNotificationRecordLogger.get(0).r.sbn.getInstanceId().getId());
+ assertEquals(0, mNotificationRecordLogger.get(0).r.getSbn().getInstanceId().getId());
assertTrue(mNotificationRecordLogger.get(1).shouldLog());
assertEquals(
NotificationRecordLogger.NotificationReportedEvents.NOTIFICATION_UPDATED,
mNotificationRecordLogger.get(1).getUiEvent());
// Instance ID doesn't change on update of an active notification
- assertEquals(0, mNotificationRecordLogger.get(1).r.sbn.getInstanceId().getId());
+ assertEquals(0, mNotificationRecordLogger.get(1).r.getSbn().getInstanceId().getId());
}
@Test
@@ -1209,14 +1216,14 @@
assertEquals(
NotificationRecordLogger.NotificationReportedEvents.NOTIFICATION_POSTED,
mNotificationRecordLogger.get(0).getUiEvent());
- assertEquals(0, mNotificationRecordLogger.get(0).r.sbn.getInstanceId().getId());
+ assertEquals(0, mNotificationRecordLogger.get(0).r.getSbn().getInstanceId().getId());
assertTrue(mNotificationRecordLogger.get(1).shouldLog());
assertEquals(
NotificationRecordLogger.NotificationReportedEvents.NOTIFICATION_POSTED,
mNotificationRecordLogger.get(1).getUiEvent());
// New instance ID because notification was canceled before re-post
- assertEquals(1, mNotificationRecordLogger.get(1).r.sbn.getInstanceId().getId());
+ assertEquals(1, mNotificationRecordLogger.get(1).r.getSbn().getInstanceId().getId());
}
@Test
@@ -1257,7 +1264,7 @@
@Test
public void testCancelNotificationsFromListenerImmediatelyAfterEnqueue() throws Exception {
NotificationRecord r = generateNotificationRecord(null);
- final StatusBarNotification sbn = r.sbn;
+ final StatusBarNotification sbn = r.getSbn();
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testCancelNotificationsFromListenerImmediatelyAfterEnqueue",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
@@ -1271,7 +1278,7 @@
@Test
public void testCancelAllNotificationsImmediatelyAfterEnqueue() throws Exception {
- final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testCancelAllNotificationsImmediatelyAfterEnqueue",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
@@ -1286,7 +1293,7 @@
@Test
public void testCancelImmediatelyAfterEnqueueNotifiesListeners_ForegroundServiceFlag()
throws Exception {
- final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
sbn.getNotification().flags =
Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
@@ -1304,14 +1311,14 @@
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testUserInitiatedClearAll_noLeak",
- n.sbn.getId(), n.sbn.getNotification(), n.sbn.getUserId());
+ n.getSbn().getId(), n.getSbn().getNotification(), n.getSbn().getUserId());
waitForIdle();
mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
n.getUserId());
waitForIdle();
StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(n.sbn.getPackageName());
+ mBinderService.getActiveNotifications(n.getSbn().getPackageName());
assertEquals(0, notifs.length);
assertEquals(0, mService.getNotificationRecordCount());
ArgumentCaptor<NotificationStats> captor = ArgumentCaptor.forClass(NotificationStats.class);
@@ -1328,20 +1335,22 @@
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testCancelAllNotificationsCancelsChildren",
- parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId());
+ parent.getSbn().getId(), parent.getSbn().getNotification(),
+ parent.getSbn().getUserId());
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testCancelAllNotificationsCancelsChildren",
- child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId());
+ child.getSbn().getId(), child.getSbn().getNotification(),
+ child.getSbn().getUserId());
waitForIdle();
- mBinderService.cancelAllNotifications(PKG, parent.sbn.getUserId());
+ mBinderService.cancelAllNotifications(PKG, parent.getSbn().getUserId());
waitForIdle();
assertEquals(0, mService.getNotificationRecordCount());
}
@Test
public void testCancelAllNotificationsMultipleEnqueuedDoesNotCrash() throws Exception {
- final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
for (int i = 0; i < 10; i++) {
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testCancelAllNotificationsMultipleEnqueuedDoesNotCrash",
@@ -1365,20 +1374,22 @@
// fully post parent notification
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testCancelGroupSummaryMultipleEnqueuedChildrenDoesNotCrash",
- parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId());
+ parent.getSbn().getId(), parent.getSbn().getNotification(),
+ parent.getSbn().getUserId());
waitForIdle();
// enqueue the child several times
for (int i = 0; i < 10; i++) {
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testCancelGroupSummaryMultipleEnqueuedChildrenDoesNotCrash",
- child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId());
+ child.getSbn().getId(), child.getSbn().getNotification(),
+ child.getSbn().getUserId());
}
// make the parent a child, which will cancel the child notification
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testCancelGroupSummaryMultipleEnqueuedChildrenDoesNotCrash",
- parentAsChild.sbn.getId(), parentAsChild.sbn.getNotification(),
- parentAsChild.sbn.getUserId());
+ parentAsChild.getSbn().getId(), parentAsChild.getSbn().getNotification(),
+ parentAsChild.getSbn().getUserId());
waitForIdle();
assertEquals(0, mService.getNotificationRecordCount());
@@ -1395,7 +1406,7 @@
mService.mAutobundledSummaries.get(0).put("pkg", summary.getKey());
mService.updateAutobundledSummaryFlags(0, "pkg", true, false);
- assertTrue(summary.sbn.isOngoing());
+ assertTrue(summary.getSbn().isOngoing());
}
@Test
@@ -1411,12 +1422,12 @@
mService.updateAutobundledSummaryFlags(0, "pkg", false, false);
- assertFalse(summary.sbn.isOngoing());
+ assertFalse(summary.getSbn().isOngoing());
}
@Test
public void testCancelAllNotifications_IgnoreForegroundService() throws Exception {
- final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testCancelAllNotifications_IgnoreForegroundService",
@@ -1431,7 +1442,7 @@
@Test
public void testCancelAllNotifications_IgnoreOtherPackages() throws Exception {
- final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testCancelAllNotifications_IgnoreOtherPackages",
@@ -1446,7 +1457,7 @@
@Test
public void testCancelAllNotifications_NullPkgRemovesAll() throws Exception {
- final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testCancelAllNotifications_NullPkgRemovesAll",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
@@ -1460,7 +1471,7 @@
@Test
public void testCancelAllNotifications_NullPkgIgnoresUserAllNotifications() throws Exception {
- final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testCancelAllNotifications_NullPkgIgnoresUserAllNotifications",
sbn.getId(), sbn.getNotification(), UserHandle.USER_ALL);
@@ -1475,7 +1486,7 @@
@Test
public void testAppInitiatedCancelAllNotifications_CancelsNoClearFlag() throws Exception {
- final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
sbn.getNotification().flags |= Notification.FLAG_NO_CLEAR;
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testAppInitiatedCancelAllNotifications_CancelsNoClearFlag",
@@ -1497,7 +1508,7 @@
notif.getUserId(), 0, null);
waitForIdle();
StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(notif.sbn.getPackageName());
+ mBinderService.getActiveNotifications(notif.getSbn().getPackageName());
assertEquals(0, notifs.length);
}
@@ -1512,7 +1523,7 @@
notif.getUserId());
waitForIdle();
StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(notif.sbn.getPackageName());
+ mBinderService.getActiveNotifications(notif.getSbn().getPackageName());
assertEquals(1, notifs.length);
}
@@ -1534,7 +1545,7 @@
mService.getBinderService().cancelNotificationsFromListener(null, null);
waitForIdle();
StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
assertEquals(1, notifs.length);
}
@@ -1557,7 +1568,7 @@
parent.getUserId());
waitForIdle();
StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
assertEquals(1, notifs.length);
}
@@ -1582,7 +1593,7 @@
@Test
public void testCancelAfterSecondEnqueueDoesNotSpecifyForegroundFlag() throws Exception {
- final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
sbn.getNotification().flags =
Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
@@ -1616,7 +1627,7 @@
mService.getBinderService().cancelNotificationsFromListener(null, null);
waitForIdle();
StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
assertEquals(0, notifs.length);
}
@@ -1636,12 +1647,12 @@
mService.addNotification(child);
mService.addNotification(child2);
mService.addNotification(newGroup);
- String[] keys = {parent.sbn.getKey(), child.sbn.getKey(),
- child2.sbn.getKey(), newGroup.sbn.getKey()};
+ String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
+ child2.getSbn().getKey(), newGroup.getSbn().getKey()};
mService.getBinderService().cancelNotificationsFromListener(null, keys);
waitForIdle();
StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
assertEquals(1, notifs.length);
}
@@ -1664,7 +1675,7 @@
parent.getUserId());
waitForIdle();
StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
assertEquals(0, notifs.length);
}
@@ -1680,31 +1691,33 @@
final NotificationRecord group2 = generateNotificationRecord(
mTestNotificationChannel, 2, "group2", true);
mBinderService.enqueueNotificationWithTag(PKG, PKG, "testFindGroupNotificationsLocked",
- group2.sbn.getId(), group2.sbn.getNotification(), group2.sbn.getUserId());
+ group2.getSbn().getId(), group2.getSbn().getNotification(),
+ group2.getSbn().getUserId());
waitForIdle();
// should not be returned
final NotificationRecord nonGroup = generateNotificationRecord(
mTestNotificationChannel, 3, null, false);
mBinderService.enqueueNotificationWithTag(PKG, PKG, "testFindGroupNotificationsLocked",
- nonGroup.sbn.getId(), nonGroup.sbn.getNotification(), nonGroup.sbn.getUserId());
+ nonGroup.getSbn().getId(), nonGroup.getSbn().getNotification(),
+ nonGroup.getSbn().getUserId());
waitForIdle();
// same group, child, should be returned
final NotificationRecord group1Child = generateNotificationRecord(
mTestNotificationChannel, 4, "group1", false);
mBinderService.enqueueNotificationWithTag(PKG, PKG, "testFindGroupNotificationsLocked",
- group1Child.sbn.getId(),
- group1Child.sbn.getNotification(), group1Child.sbn.getUserId());
+ group1Child.getSbn().getId(),
+ group1Child.getSbn().getNotification(), group1Child.getSbn().getUserId());
waitForIdle();
List<NotificationRecord> inGroup1 =
mService.findGroupNotificationsLocked(PKG, group1.getGroupKey(),
- group1.sbn.getUserId());
+ group1.getSbn().getUserId());
assertEquals(3, inGroup1.size());
for (NotificationRecord record : inGroup1) {
assertTrue(record.getGroupKey().equals(group1.getGroupKey()));
- assertTrue(record.sbn.getId() == 1 || record.sbn.getId() == 4);
+ assertTrue(record.getSbn().getId() == 1 || record.getSbn().getId() == 4);
}
}
@@ -1718,7 +1731,7 @@
Notification.FLAG_ONGOING_EVENT, true, notif.getUserId(), 0, null);
waitForIdle();
StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(notif.sbn.getPackageName());
+ mBinderService.getActiveNotifications(notif.getSbn().getPackageName());
assertEquals(0, notifs.length);
}
@@ -1738,18 +1751,18 @@
mService.addNotification(child);
mService.addNotification(child2);
mService.addNotification(newGroup);
- String[] keys = {parent.sbn.getKey(), child.sbn.getKey(),
- child2.sbn.getKey(), newGroup.sbn.getKey()};
+ String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
+ child2.getSbn().getKey(), newGroup.getSbn().getKey()};
mService.getBinderService().cancelNotificationsFromListener(null, keys);
waitForIdle();
StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
assertEquals(0, notifs.length);
}
@Test
public void testAppInitiatedCancelAllNotifications_CancelsOnGoingFlag() throws Exception {
- final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testAppInitiatedCancelAllNotifications_CancelsOnGoingFlag",
@@ -1771,7 +1784,7 @@
notif.getUserId(), 0, null);
waitForIdle();
StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(notif.sbn.getPackageName());
+ mBinderService.getActiveNotifications(notif.getSbn().getPackageName());
assertEquals(0, notifs.length);
}
@@ -1786,7 +1799,7 @@
notif.getUserId());
waitForIdle();
StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(notif.sbn.getPackageName());
+ mBinderService.getActiveNotifications(notif.getSbn().getPackageName());
assertEquals(1, notifs.length);
}
@@ -1808,7 +1821,7 @@
mService.getBinderService().cancelNotificationsFromListener(null, null);
waitForIdle();
StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
assertEquals(1, notifs.length);
}
@@ -1828,12 +1841,12 @@
mService.addNotification(child);
mService.addNotification(child2);
mService.addNotification(newGroup);
- String[] keys = {parent.sbn.getKey(), child.sbn.getKey(),
- child2.sbn.getKey(), newGroup.sbn.getKey()};
+ String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
+ child2.getSbn().getKey(), newGroup.getSbn().getKey()};
mService.getBinderService().cancelNotificationsFromListener(null, keys);
waitForIdle();
StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
assertEquals(0, notifs.length);
}
@@ -1856,7 +1869,7 @@
parent.getUserId());
waitForIdle();
StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
assertEquals(1, notifs.length);
}
@@ -1990,7 +2003,8 @@
public void testUpdateGroupNotifyCreatorBlock() throws Exception {
NotificationChannelGroup existing = new NotificationChannelGroup("id", "name");
mService.setPreferencesHelper(mPreferencesHelper);
- when(mPreferencesHelper.getNotificationChannelGroup(eq(existing.getId()), eq(PKG), anyInt()))
+ when(mPreferencesHelper.getNotificationChannelGroup(eq(existing.getId()),
+ eq(PKG), anyInt()))
.thenReturn(existing);
NotificationChannelGroup updated = new NotificationChannelGroup("id", "name");
@@ -2013,7 +2027,8 @@
NotificationChannelGroup existing = new NotificationChannelGroup("id", "name");
existing.setBlocked(true);
mService.setPreferencesHelper(mPreferencesHelper);
- when(mPreferencesHelper.getNotificationChannelGroup(eq(existing.getId()), eq(PKG), anyInt()))
+ when(mPreferencesHelper.getNotificationChannelGroup(eq(existing.getId()),
+ eq(PKG), anyInt()))
.thenReturn(existing);
mBinderService.updateNotificationChannelGroupForPackage(
@@ -2033,7 +2048,8 @@
public void testUpdateGroupNoNotifyCreatorOtherChanges() throws Exception {
NotificationChannelGroup existing = new NotificationChannelGroup("id", "name");
mService.setPreferencesHelper(mPreferencesHelper);
- when(mPreferencesHelper.getNotificationChannelGroup(eq(existing.getId()), eq(PKG), anyInt()))
+ when(mPreferencesHelper.getNotificationChannelGroup(
+ eq(existing.getId()), eq(PKG), anyInt()))
.thenReturn(existing);
mBinderService.updateNotificationChannelGroupForPackage(
@@ -2492,7 +2508,7 @@
public void testSnoozeRunnable_snoozeGroupChild_onlyChildOfSummary() throws Exception {
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
- assertTrue(parent.sbn.getNotification().isGroupSummary());
+ assertTrue(parent.getSbn().getNotification().isGroupSummary());
final NotificationRecord child = generateNotificationRecord(
mTestNotificationChannel, 2, "group", false);
mService.addNotification(parent);
@@ -2528,7 +2544,8 @@
mTestNotificationChannel, 2, "group", false);
mBinderService.enqueueNotificationWithTag(PKG, PKG, "testPostNonGroup_noUnsnoozing",
- child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId());
+ child.getSbn().getId(), child.getSbn().getNotification(),
+ child.getSbn().getUserId());
waitForIdle();
verify(mSnoozeHelper, times(1)).repostGroupSummary(
@@ -2541,7 +2558,8 @@
mTestNotificationChannel, 2, null, false);
mBinderService.enqueueNotificationWithTag(PKG, PKG, "testPostNonGroup_noUnsnoozing",
- record.sbn.getId(), record.sbn.getNotification(), record.sbn.getUserId());
+ record.getSbn().getId(), record.getSbn().getNotification(),
+ record.getSbn().getUserId());
waitForIdle();
verify(mSnoozeHelper, never()).repostGroupSummary(anyString(), anyInt(), anyString());
@@ -2553,7 +2571,8 @@
mTestNotificationChannel, 2, "group", true);
mBinderService.enqueueNotificationWithTag(PKG, PKG, "testPostGroupSummary_noUnsnoozing",
- parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId());
+ parent.getSbn().getId(), parent.getSbn().getNotification(),
+ parent.getSbn().getUserId());
waitForIdle();
verify(mSnoozeHelper, never()).repostGroupSummary(anyString(), anyInt(), anyString());
@@ -2972,11 +2991,11 @@
NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
NotificationRecord posted = mService.findNotificationLocked(
- PKG, nr.sbn.getTag(), nr.sbn.getId(), nr.sbn.getUserId());
+ PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId());
assertFalse(posted.getNotification().isColorized());
}
@@ -2995,7 +3014,7 @@
NotificationRecord r =
generateNotificationRecord(mTestNotificationChannel, i, null, false);
mService.addNotification(r);
- sampleTagToExclude = r.sbn.getTag();
+ sampleTagToExclude = r.getSbn().getTag();
sampleIdToExclude = i;
}
@@ -3231,7 +3250,8 @@
StatusBarNotification sbn = new StatusBarNotification(preOPkg, preOPkg, 9,
"testBumpFGImportance_noChannelChangePreOApp",
- Binder.getCallingUid(), 0, nb.build(), new UserHandle(Binder.getCallingUid()), null, 0);
+ Binder.getCallingUid(), 0, nb.build(), new UserHandle(Binder.getCallingUid()), null,
+ 0);
mBinderService.enqueueNotificationWithTag(sbn.getPackageName(), sbn.getOpPkg(),
sbn.getTag(), sbn.getId(), sbn.getNotification(), sbn.getUserId());
@@ -3269,7 +3289,7 @@
mService.mNotificationDelegate.onNotificationDirectReplied(r.getKey());
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasDirectReplied());
- verify(mAssistants).notifyAssistantNotificationDirectReplyLocked(eq(r.sbn));
+ verify(mAssistants).notifyAssistantNotificationDirectReplyLocked(eq(r.getSbn()));
}
@Test
@@ -3279,12 +3299,14 @@
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, true,
NOTIFICATION_LOCATION_UNKNOWN);
- verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(true), eq((true)));
+ verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.getSbn()), eq(true),
+ eq((true)));
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, false,
NOTIFICATION_LOCATION_UNKNOWN);
- verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(true), eq((false)));
+ verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.getSbn()), eq(true),
+ eq((false)));
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
}
@@ -3296,13 +3318,14 @@
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true,
NOTIFICATION_LOCATION_UNKNOWN);
assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
- verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(false), eq((true)));
+ verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.getSbn()), eq(false),
+ eq((true)));
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, false,
NOTIFICATION_LOCATION_UNKNOWN);
assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
verify(mAssistants).notifyAssistantExpansionChangedLocked(
- eq(r.sbn), eq(false), eq((false)));
+ eq(r.getSbn()), eq(false), eq((false)));
}
@Test
@@ -3322,11 +3345,11 @@
final NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 1, 2, true);
mService.mNotificationDelegate.onNotificationVisibilityChanged(
new NotificationVisibility[] {nv}, new NotificationVisibility[]{});
- verify(mAssistants).notifyAssistantVisibilityChangedLocked(eq(r.sbn), eq(true));
+ verify(mAssistants).notifyAssistantVisibilityChangedLocked(eq(r.getSbn()), eq(true));
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasSeen());
mService.mNotificationDelegate.onNotificationVisibilityChanged(
new NotificationVisibility[] {}, new NotificationVisibility[]{nv});
- verify(mAssistants).notifyAssistantVisibilityChangedLocked(eq(r.sbn), eq(false));
+ verify(mAssistants).notifyAssistantVisibilityChangedLocked(eq(r.getSbn()), eq(false));
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasSeen());
}
@@ -3336,8 +3359,8 @@
mService.addNotification(r);
final NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 0, 1, true);
- mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.sbn.getTag(),
- r.sbn.getId(), r.getUserId(), r.getKey(), NotificationStats.DISMISSAL_AOD,
+ mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.getSbn().getTag(),
+ r.getSbn().getId(), r.getUserId(), r.getKey(), NotificationStats.DISMISSAL_AOD,
NotificationStats.DISMISS_SENTIMENT_POSITIVE, nv);
waitForIdle();
@@ -3350,8 +3373,8 @@
mService.addNotification(r);
final NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 0, 1, true);
- mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.sbn.getTag(),
- r.sbn.getId(), r.getUserId(), r.getKey(), NotificationStats.DISMISSAL_AOD,
+ mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.getSbn().getTag(),
+ r.getSbn().getId(), r.getUserId(), r.getKey(), NotificationStats.DISMISSAL_AOD,
NotificationStats.DISMISS_SENTIMENT_NEGATIVE, nv);
waitForIdle();
@@ -3373,7 +3396,7 @@
signals.putInt(Adjustment.KEY_USER_SENTIMENT,
USER_SENTIMENT_NEGATIVE);
Adjustment adjustment = new Adjustment(
- r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+ r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
mBinderService.applyAdjustmentFromAssistant(null, adjustment);
waitForIdle();
@@ -3392,7 +3415,7 @@
Bundle signals = new Bundle();
signals.putInt(KEY_IMPORTANCE, IMPORTANCE_NONE);
Adjustment adjustment = new Adjustment(
- r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+ r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
mBinderService.applyAdjustmentFromAssistant(null, adjustment);
@@ -3415,7 +3438,7 @@
signals.putInt(Adjustment.KEY_USER_SENTIMENT,
USER_SENTIMENT_NEGATIVE);
Adjustment adjustment = new Adjustment(
- r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+ r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
assertEquals(USER_SENTIMENT_NEGATIVE, r.getUserSentiment());
@@ -3433,7 +3456,7 @@
Bundle signals = new Bundle();
signals.putInt(KEY_IMPORTANCE, IMPORTANCE_LOW);
Adjustment adjustment = new Adjustment(
- r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+ r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
assertEquals(IMPORTANCE_LOW, r.getImportance());
@@ -3452,7 +3475,7 @@
signals.putInt(Adjustment.KEY_USER_SENTIMENT,
USER_SENTIMENT_NEGATIVE);
Adjustment adjustment = new Adjustment(
- r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+ r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
assertEquals(USER_SENTIMENT_NEUTRAL, r.getUserSentiment());
@@ -3472,7 +3495,7 @@
signals.putInt(Adjustment.KEY_USER_SENTIMENT,
USER_SENTIMENT_NEGATIVE);
Adjustment adjustment = new Adjustment(
- r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+ r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
mBinderService.applyAdjustmentFromAssistant(null, adjustment);
waitForIdle();
@@ -3490,7 +3513,7 @@
signals.putInt(Adjustment.KEY_USER_SENTIMENT,
USER_SENTIMENT_NEGATIVE);
Adjustment adjustment = new Adjustment(
- r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+ r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
waitForIdle();
@@ -3508,7 +3531,7 @@
signals.putInt(Adjustment.KEY_USER_SENTIMENT,
USER_SENTIMENT_NEGATIVE);
Adjustment adjustment = new Adjustment(
- r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+ r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
assertEquals(USER_SENTIMENT_NEGATIVE, r.getUserSentiment());
@@ -3622,6 +3645,33 @@
}
@Test
+ public void updateUriPermissions_posterDoesNotOwnUri() throws Exception {
+ NotificationChannel c = new NotificationChannel(
+ TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
+ c.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT);
+ Message message1 = new Message("", 0, "");
+ message1.setData("",
+ ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1));
+
+ Notification.Builder nbA = new Notification.Builder(mContext, c.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setStyle(new Notification.MessagingStyle("")
+ .addMessage(message1));
+ NotificationRecord recordA = new NotificationRecord(mContext, new StatusBarNotification(
+ PKG, PKG, 0, "tag", mUid, 0, nbA.build(), new UserHandle(mUid), null, 0), c);
+
+ doThrow(new SecurityException("no access")).when(mUgm)
+ .grantUriPermissionFromOwner(
+ any(), anyInt(), any(), any(), anyInt(), anyInt(), anyInt());
+
+ when(mUgmInternal.newUriPermissionOwner(any())).thenReturn(new Binder());
+ mService.updateUriPermissions(recordA, null, mContext.getPackageName(), USER_SYSTEM);
+
+ // yay, no crash
+ }
+
+ @Test
public void testVisitUris() throws Exception {
final Uri audioContents = Uri.parse("content://com.example/audio");
final Uri backgroundImage = Uri.parse("content://com.example/background");
@@ -4078,7 +4128,7 @@
ArgumentCaptor<List<NotificationRecord>> captorHide = ArgumentCaptor.forClass(List.class);
verify(mListeners, times(1)).notifyHiddenLocked(captorHide.capture());
assertEquals(1, captorHide.getValue().size());
- assertEquals("a", captorHide.getValue().get(0).sbn.getPackageName());
+ assertEquals("a", captorHide.getValue().get(0).getSbn().getPackageName());
// on broadcast, unhide the package
mService.simulatePackageDistractionBroadcast(
@@ -4086,7 +4136,7 @@
ArgumentCaptor<List<NotificationRecord>> captorUnhide = ArgumentCaptor.forClass(List.class);
verify(mListeners, times(1)).notifyUnhiddenLocked(captorUnhide.capture());
assertEquals(1, captorUnhide.getValue().size());
- assertEquals("a", captorUnhide.getValue().get(0).sbn.getPackageName());
+ assertEquals("a", captorUnhide.getValue().get(0).getSbn().getPackageName());
}
@Test
@@ -4107,8 +4157,8 @@
// should be called only once.
verify(mListeners, times(1)).notifyHiddenLocked(captorHide.capture());
assertEquals(2, captorHide.getValue().size());
- assertEquals("a", captorHide.getValue().get(0).sbn.getPackageName());
- assertEquals("b", captorHide.getValue().get(1).sbn.getPackageName());
+ assertEquals("a", captorHide.getValue().get(0).getSbn().getPackageName());
+ assertEquals("b", captorHide.getValue().get(1).getSbn().getPackageName());
// on broadcast, unhide the package
mService.simulatePackageDistractionBroadcast(
@@ -4118,8 +4168,8 @@
// should be called only once.
verify(mListeners, times(1)).notifyUnhiddenLocked(captorUnhide.capture());
assertEquals(2, captorUnhide.getValue().size());
- assertEquals("a", captorUnhide.getValue().get(0).sbn.getPackageName());
- assertEquals("b", captorUnhide.getValue().get(1).sbn.getPackageName());
+ assertEquals("a", captorUnhide.getValue().get(0).getSbn().getPackageName());
+ assertEquals("b", captorUnhide.getValue().get(1).getSbn().getPackageName());
}
@Test
@@ -4405,7 +4455,7 @@
ai.uid = -1;
when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(ai);
- final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
try {
mInternalService.enqueueNotification(notReal, "android", 0, 0,
"testPostFromAndroidForNonExistentPackage",
@@ -4426,7 +4476,7 @@
when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(ai);
// unlike the post case, ignore instead of throwing
- final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
mInternalService.cancelNotification(notReal, "android", 0, 0, "tag",
sbn.getId(), sbn.getUserId());
@@ -4457,7 +4507,7 @@
mService.addEnqueuedNotification(r);
mInternalService.removeForegroundServiceFlagFromNotification(
- PKG, r.sbn.getId(), r.sbn.getUserId());
+ PKG, r.getSbn().getId(), r.getSbn().getUserId());
waitForIdle();
@@ -4476,7 +4526,7 @@
mService.addNotification(r);
mInternalService.removeForegroundServiceFlagFromNotification(
- PKG, r.sbn.getId(), r.sbn.getUserId());
+ PKG, r.getSbn().getId(), r.getSbn().getUserId());
waitForIdle();
@@ -4674,7 +4724,7 @@
r.getKey(), replyIndex, reply, NOTIFICATION_LOCATION_UNKNOWN,
modifiedBeforeSending);
verify(mAssistants).notifyAssistantSuggestedReplySent(
- eq(r.sbn), eq(reply), eq(generatedByAssistant));
+ eq(r.getSbn()), eq(reply), eq(generatedByAssistant));
}
@Test
@@ -4693,7 +4743,7 @@
10, 10, r.getKey(), actionIndex, action, notificationVisibility,
generatedByAssistant);
verify(mAssistants).notifyAssistantActionClicked(
- eq(r.sbn), eq(actionIndex), eq(action), eq(generatedByAssistant));
+ eq(r.getSbn()), eq(actionIndex), eq(action), eq(generatedByAssistant));
}
@Test
@@ -4766,13 +4816,13 @@
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addNotification(r);
- StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, r.sbn.getId(),
- r.sbn.getTag(), mUid, 0,
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, r.getSbn().getId(),
+ r.getSbn().getTag(), mUid, 0,
new Notification.Builder(mContext, mTestNotificationChannel.getId()).build(),
new UserHandle(mUid), null, 0);
NotificationRecord update = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
mService.addEnqueuedNotification(update);
- assertNull(update.sbn.getNotification().getSmallIcon());
+ assertNull(update.getSbn().getNotification().getSmallIcon());
NotificationManagerService.PostNotificationRunnable runnable =
mService.new PostNotificationRunnable(update.getKey());
@@ -4944,15 +4994,15 @@
NotificationRecord nr =
generateMessageBubbleNotifRecord(mTestNotificationChannel, "testFlagBubble");
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
assertEquals(1, notifs.length);
assertTrue((notifs[0].getNotification().flags & FLAG_BUBBLE) != 0);
assertTrue(mService.getNotificationRecord(
- nr.sbn.getKey()).getNotification().isBubbleNotification());
+ nr.getSbn().getKey()).getNotification().isBubbleNotification());
}
@Test
@@ -4963,15 +5013,15 @@
NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
"testFlagBubble_noFlag_appNotAllowed");
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
assertEquals(1, notifs.length);
assertEquals((notifs[0].getNotification().flags & FLAG_BUBBLE), 0);
assertFalse(mService.getNotificationRecord(
- nr.sbn.getKey()).getNotification().isBubbleNotification());
+ nr.getSbn().getKey()).getNotification().isBubbleNotification());
}
@Test
@@ -4990,16 +5040,16 @@
NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
// Say we're foreground
- when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
+ when(mActivityManager.getPackageImportance(nr.getSbn().getPackageName())).thenReturn(
IMPORTANCE_FOREGROUND);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
// if notif isn't configured properly it doesn't get to bubble just because app is
// foreground.
assertFalse(mService.getNotificationRecord(
- nr.sbn.getKey()).getNotification().isBubbleNotification());
+ nr.getSbn().getKey()).getNotification().isBubbleNotification());
}
@Test
@@ -5010,13 +5060,13 @@
NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
"testFlagBubbleNotifs_flag_messaging");
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
// yes allowed, yes messaging, yes bubble
assertTrue(mService.getNotificationRecord(
- nr.sbn.getKey()).getNotification().isBubbleNotification());
+ nr.getSbn().getKey()).getNotification().isBubbleNotification());
}
@Test
@@ -5046,8 +5096,8 @@
sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
// yes phone call, yes person, yes foreground service, yes bubble
@@ -5079,8 +5129,8 @@
nb.build(), new UserHandle(mUid), null, 0);
NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
// yes phone call, yes person, NO foreground service, no bubble
@@ -5110,8 +5160,8 @@
sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
// yes phone call, yes foreground service, BUT NO person, no bubble
@@ -5145,8 +5195,8 @@
sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
// yes person, yes foreground service, BUT NO call, no bubble
@@ -5163,13 +5213,13 @@
"testFlagBubbleNotifs_noFlag_messaging_appNotAllowed");
// Post the notification
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
// not allowed, no bubble
assertFalse(mService.getNotificationRecord(
- nr.sbn.getKey()).getNotification().isBubbleNotification());
+ nr.getSbn().getKey()).getNotification().isBubbleNotification());
}
@Test
@@ -5187,13 +5237,13 @@
NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
// Post the notification
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
// no bubble metadata, no bubble
assertFalse(mService.getNotificationRecord(
- nr.sbn.getKey()).getNotification().isBubbleNotification());
+ nr.getSbn().getKey()).getNotification().isBubbleNotification());
}
@Test
@@ -5205,13 +5255,13 @@
"testFlagBubbleNotifs_noFlag_messaging_channelNotAllowed");
// Post the notification
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
// channel not allowed, no bubble
assertFalse(mService.getNotificationRecord(
- nr.sbn.getKey()).getNotification().isBubbleNotification());
+ nr.getSbn().getKey()).getNotification().isBubbleNotification());
}
@Test
@@ -5277,7 +5327,7 @@
NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
// yes phone call, yes person, yes foreground service, but channel not allowed, no bubble
@@ -5288,10 +5338,10 @@
@Test
public void testCancelAllNotifications_cancelsBubble() throws Exception {
final NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
- nr.sbn.getNotification().flags |= FLAG_BUBBLE;
+ nr.getSbn().getNotification().flags |= FLAG_BUBBLE;
mService.addNotification(nr);
- mBinderService.cancelAllNotifications(PKG, nr.sbn.getUserId());
+ mBinderService.cancelAllNotifications(PKG, nr.getSbn().getUserId());
waitForIdle();
StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
@@ -5302,12 +5352,13 @@
@Test
public void testAppCancelNotifications_cancelsBubbles() throws Exception {
final NotificationRecord nrBubble = generateNotificationRecord(mTestNotificationChannel);
- nrBubble.sbn.getNotification().flags |= FLAG_BUBBLE;
+ nrBubble.getSbn().getNotification().flags |= FLAG_BUBBLE;
// Post the notification
mBinderService.enqueueNotificationWithTag(PKG, PKG,
"testAppCancelNotifications_cancelsBubbles",
- nrBubble.sbn.getId(), nrBubble.sbn.getNotification(), nrBubble.sbn.getUserId());
+ nrBubble.getSbn().getId(), nrBubble.getSbn().getNotification(),
+ nrBubble.getSbn().getUserId());
waitForIdle();
StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
@@ -5315,8 +5366,8 @@
assertEquals(1, mService.getNotificationRecordCount());
mBinderService.cancelNotificationWithTag(PKG, PKG,
- "testAppCancelNotifications_cancelsBubbles", nrBubble.sbn.getId(),
- nrBubble.sbn.getUserId());
+ "testAppCancelNotifications_cancelsBubbles", nrBubble.getSbn().getId(),
+ nrBubble.getSbn().getUserId());
waitForIdle();
StatusBarNotification[] notifs2 = mBinderService.getActiveNotifications(PKG);
@@ -5328,7 +5379,7 @@
public void testCancelAllNotificationsFromListener_ignoresBubbles() throws Exception {
final NotificationRecord nrNormal = generateNotificationRecord(mTestNotificationChannel);
final NotificationRecord nrBubble = generateNotificationRecord(mTestNotificationChannel);
- nrBubble.sbn.getNotification().flags |= FLAG_BUBBLE;
+ nrBubble.getSbn().getNotification().flags |= FLAG_BUBBLE;
mService.addNotification(nrNormal);
mService.addNotification(nrBubble);
@@ -5345,12 +5396,12 @@
public void testCancelNotificationsFromListener_ignoresBubbles() throws Exception {
final NotificationRecord nrNormal = generateNotificationRecord(mTestNotificationChannel);
final NotificationRecord nrBubble = generateNotificationRecord(mTestNotificationChannel);
- nrBubble.sbn.getNotification().flags |= FLAG_BUBBLE;
+ nrBubble.getSbn().getNotification().flags |= FLAG_BUBBLE;
mService.addNotification(nrNormal);
mService.addNotification(nrBubble);
- String[] keys = {nrNormal.sbn.getKey(), nrBubble.sbn.getKey()};
+ String[] keys = {nrNormal.getSbn().getKey(), nrBubble.getSbn().getKey()};
mService.getBinderService().cancelNotificationsFromListener(null, keys);
waitForIdle();
@@ -5386,7 +5437,7 @@
Bundle signals = new Bundle();
signals.putInt(KEY_IMPORTANCE, IMPORTANCE_LOW);
signals.putInt(KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE);
- Adjustment adjustment = new Adjustment(r.sbn.getPackageName(), r.getKey(), signals,
+ Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals,
"", r.getUser().getIdentifier());
mBinderService.applyAdjustmentFromAssistant(null, adjustment);
@@ -5469,8 +5520,8 @@
NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
"testNotificationBubbleChanged_false");
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
// Reset as this is called when the notif is first sent
@@ -5499,8 +5550,8 @@
// Notif that is not a bubble
NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
1, null, false);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
// Would be a normal notification because wouldn't have met requirements to bubble
@@ -5510,9 +5561,9 @@
// Update the notification to be message style / meet bubble requirements
NotificationRecord nr2 = generateMessageBubbleNotifRecord(mTestNotificationChannel,
- nr.sbn.getTag());
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr2.sbn.getTag(),
- nr2.sbn.getId(), nr2.sbn.getNotification(), nr2.sbn.getUserId());
+ nr.getSbn().getTag());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr2.getSbn().getTag(),
+ nr2.getSbn().getId(), nr2.getSbn().getNotification(), nr2.getSbn().getUserId());
waitForIdle();
// Reset as this is called when the notif is first sent
@@ -5535,8 +5586,8 @@
// Notif that is not a bubble
NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
// Reset as this is called when the notif is first sent
@@ -5563,11 +5614,11 @@
NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel, "tag");
// Bubbles are allowed!
- setUpPrefsForBubbles(PKG, nr.sbn.getUserId(), true /* global */,
+ setUpPrefsForBubbles(PKG, nr.getSbn().getUserId(), true /* global */,
true /* app */, true /* channel */);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
// NOT suppressed
@@ -5601,7 +5652,7 @@
public void testGrantInlineReplyUriPermission_recordExists() throws Exception {
NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, 0);
mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
// A notification exists for the given record
@@ -5613,12 +5664,13 @@
Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1);
mService.mNotificationDelegate.grantInlineReplyUriPermission(
- nr.getKey(), uri, nr.sbn.getUser(), nr.sbn.getPackageName(), nr.sbn.getUid());
+ nr.getKey(), uri, nr.getSbn().getUser(), nr.getSbn().getPackageName(),
+ nr.getSbn().getUid());
// Grant permission called for the UID of SystemUI under the target user ID
verify(mUgm, times(1)).grantUriPermissionFromOwner(any(),
- eq(nr.sbn.getUid()), eq(nr.sbn.getPackageName()), eq(uri), anyInt(), anyInt(),
- eq(nr.sbn.getUserId()));
+ eq(nr.getSbn().getUid()), eq(nr.getSbn().getPackageName()), eq(uri), anyInt(),
+ anyInt(), eq(nr.getSbn().getUserId()));
}
@Test
@@ -5634,12 +5686,13 @@
int uid = 0; // sysui on primary user
mService.mNotificationDelegate.grantInlineReplyUriPermission(
- nr.getKey(), uri, nr.sbn.getUser(), nr.sbn.getPackageName(), nr.sbn.getUid());
+ nr.getKey(), uri, nr.getSbn().getUser(), nr.getSbn().getPackageName(),
+ nr.getSbn().getUid());
// Grant permission still called if no NotificationRecord exists for the given key
verify(mUgm, times(1)).grantUriPermissionFromOwner(any(),
- eq(nr.sbn.getUid()), eq(nr.sbn.getPackageName()), eq(uri), anyInt(), anyInt(),
- eq(nr.sbn.getUserId()));
+ eq(nr.getSbn().getUid()), eq(nr.getSbn().getPackageName()), eq(uri), anyInt(),
+ anyInt(), eq(nr.getSbn().getUserId()));
}
@Test
@@ -5648,7 +5701,7 @@
NotificationRecord nr =
generateNotificationRecord(mTestNotificationChannel, UserHandle.USER_ALL);
mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
// A notification exists for the given record
@@ -5660,12 +5713,13 @@
Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1);
mService.mNotificationDelegate.grantInlineReplyUriPermission(
- nr.getKey(), uri, nr.sbn.getUser(), nr.sbn.getPackageName(), nr.sbn.getUid());
+ nr.getKey(), uri, nr.getSbn().getUser(), nr.getSbn().getPackageName(),
+ nr.getSbn().getUid());
// Target user for the grant is USER_ALL instead of USER_SYSTEM
verify(mUgm, times(1)).grantUriPermissionFromOwner(any(),
- eq(nr.sbn.getUid()), eq(nr.sbn.getPackageName()), eq(uri), anyInt(), anyInt(),
- eq(UserHandle.USER_SYSTEM));
+ eq(nr.getSbn().getUid()), eq(nr.getSbn().getPackageName()), eq(uri), anyInt(),
+ anyInt(), eq(UserHandle.USER_SYSTEM));
}
@Test
@@ -5675,7 +5729,7 @@
NotificationRecord nr =
generateNotificationRecord(mTestNotificationChannel, otherUserId);
mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
// A notification exists for the given record
@@ -5698,11 +5752,11 @@
.thenReturn(otherUserUid);
mService.mNotificationDelegate.grantInlineReplyUriPermission(
- nr.getKey(), uri, nr.sbn.getUser(), nr.sbn.getPackageName(), uid);
+ nr.getKey(), uri, nr.getSbn().getUser(), nr.getSbn().getPackageName(), uid);
// Target user for the grant is USER_ALL instead of USER_SYSTEM
verify(mUgm, times(1)).grantUriPermissionFromOwner(any(),
- eq(otherUserUid), eq(nr.sbn.getPackageName()), eq(uri), anyInt(), anyInt(),
+ eq(otherUserUid), eq(nr.getSbn().getPackageName()), eq(uri), anyInt(), anyInt(),
eq(otherUserId));
}
@@ -5716,15 +5770,18 @@
// create an inline record with two uris in it
mService.mNotificationDelegate.grantInlineReplyUriPermission(
- nr.getKey(), uri1, nr.sbn.getUser(), nr.sbn.getPackageName(), nr.sbn.getUid());
+ nr.getKey(), uri1, nr.getSbn().getUser(), nr.getSbn().getPackageName(),
+ nr.getSbn().getUid());
mService.mNotificationDelegate.grantInlineReplyUriPermission(
- nr.getKey(), uri2, nr.sbn.getUser(), nr.sbn.getPackageName(), nr.sbn.getUid());
+ nr.getKey(), uri2, nr.getSbn().getUser(), nr.getSbn().getPackageName(),
+ nr.getSbn().getUid());
InlineReplyUriRecord record = mService.mInlineReplyRecordsByKey.get(nr.getKey());
assertNotNull(record); // record exists
assertEquals(record.getUris().size(), 2); // record has two uris in it
- mService.mNotificationDelegate.clearInlineReplyUriPermissions(nr.getKey(), nr.sbn.getUid());
+ mService.mNotificationDelegate.clearInlineReplyUriPermissions(nr.getKey(),
+ nr.getSbn().getUid());
// permissionOwner destroyed
verify(mUgmInternal, times(1)).revokeUriPermissionFromOwner(
@@ -5737,7 +5794,8 @@
NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, 0);
reset(mPackageManager);
- mService.mNotificationDelegate.clearInlineReplyUriPermissions(nr.getKey(), nr.sbn.getUid());
+ mService.mNotificationDelegate.clearInlineReplyUriPermissions(nr.getKey(),
+ nr.getSbn().getUid());
// no permissionOwner destroyed
verify(mUgmInternal, times(0)).revokeUriPermissionFromOwner(
@@ -5755,12 +5813,14 @@
// create an inline record a uri in it
mService.mNotificationDelegate.grantInlineReplyUriPermission(
- nr.getKey(), uri1, nr.sbn.getUser(), nr.sbn.getPackageName(), nr.sbn.getUid());
+ nr.getKey(), uri1, nr.getSbn().getUser(), nr.getSbn().getPackageName(),
+ nr.getSbn().getUid());
InlineReplyUriRecord record = mService.mInlineReplyRecordsByKey.get(nr.getKey());
assertNotNull(record); // record exists
- mService.mNotificationDelegate.clearInlineReplyUriPermissions(nr.getKey(), nr.sbn.getUid());
+ mService.mNotificationDelegate.clearInlineReplyUriPermissions(
+ nr.getKey(), nr.getSbn().getUid());
// permissionOwner destroyed for USER_SYSTEM, not USER_ALL
verify(mUgmInternal, times(1)).revokeUriPermissionFromOwner(
@@ -5778,8 +5838,8 @@
// Notification that would typically bubble
NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
"testNotificationBubbles_disabled_lowRamDevice");
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
// But we wouldn't be a bubble because the device is low ram & all bubbles are disabled.
@@ -5859,20 +5919,20 @@
NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
"testNotificationBubbles_flagAutoExpandForeground_fails_notForeground");
// Modify metadata flags
- nr.sbn.getNotification().getBubbleMetadata().setFlags(
+ nr.getSbn().getNotification().getBubbleMetadata().setFlags(
Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE
| Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION);
// Ensure we're not foreground
- when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
+ when(mActivityManager.getPackageImportance(nr.getSbn().getPackageName())).thenReturn(
IMPORTANCE_VISIBLE);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
// yes allowed, yes messaging, yes bubble
- Notification notif = mService.getNotificationRecord(nr.sbn.getKey()).getNotification();
+ Notification notif = mService.getNotificationRecord(nr.getSbn().getKey()).getNotification();
assertTrue(notif.isBubbleNotification());
// Our flags should have failed since we're not foreground
@@ -5889,20 +5949,20 @@
NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
"testNotificationBubbles_flagAutoExpandForeground_succeeds_foreground");
// Modify metadata flags
- nr.sbn.getNotification().getBubbleMetadata().setFlags(
+ nr.getSbn().getNotification().getBubbleMetadata().setFlags(
Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE
| Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION);
// Ensure we are in the foreground
- when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
+ when(mActivityManager.getPackageImportance(nr.getSbn().getPackageName())).thenReturn(
IMPORTANCE_FOREGROUND);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
// yes allowed, yes messaging, yes bubble
- Notification notif = mService.getNotificationRecord(nr.sbn.getKey()).getNotification();
+ Notification notif = mService.getNotificationRecord(nr.getSbn().getKey()).getNotification();
assertTrue(notif.isBubbleNotification());
// Our flags should have passed since we are foreground
@@ -5935,8 +5995,8 @@
when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
// Test: Send the bubble notification
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
// Verify:
@@ -5945,7 +6005,7 @@
verify(mLauncherApps, times(1)).registerCallback(launcherAppsCallback.capture(), any());
// yes allowed, yes messaging w/shortcut, yes bubble
- Notification notif = mService.getNotificationRecord(nr.sbn.getKey()).getNotification();
+ Notification notif = mService.getNotificationRecord(nr.getSbn().getKey()).getNotification();
assertTrue(notif.isBubbleNotification());
// Test: Remove the shortcut
@@ -5958,7 +6018,7 @@
verify(mLauncherApps, times(1)).unregisterCallback(launcherAppsCallback.getValue());
// We're no longer a bubble
- Notification notif2 = mService.getNotificationRecord(nr.sbn.getKey()).getNotification();
+ Notification notif2 = mService.getNotificationRecord(nr.getSbn().getKey()).getNotification();
assertFalse(notif2.isBubbleNotification());
}
@@ -5974,8 +6034,9 @@
// Dismiss summary
final NotificationVisibility nv = NotificationVisibility.obtain(nrSummary.getKey(), 1, 2,
true);
- mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, nrSummary.sbn.getTag(),
- nrSummary.sbn.getId(), nrSummary.getUserId(), nrSummary.getKey(),
+ mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG,
+ nrSummary.getSbn().getTag(),
+ nrSummary.getSbn().getId(), nrSummary.getUserId(), nrSummary.getKey(),
NotificationStats.DISMISSAL_SHADE,
NotificationStats.DISMISS_SENTIMENT_NEUTRAL, nv);
waitForIdle();
@@ -6085,8 +6146,8 @@
public void testNotificationHistory_addNoisyNotification() throws Exception {
NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
null /* tvExtender */);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
verify(mHistoryManager, times(1)).addNotification(any());
@@ -6168,4 +6229,48 @@
assertNull(mBinderService.getConversationNotificationChannel(
PKG, 0, PKG, callsParent.getId(), false, conversationId));
}
+
+ @Test
+ public void testCorrectCategory_systemOn_appCannotTurnOff() {
+ int requested = 0;
+ int system = PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS;
+
+ int actual = mService.correctCategory(requested, PRIORITY_CATEGORY_CONVERSATIONS,
+ system);
+
+ assertEquals(PRIORITY_CATEGORY_CONVERSATIONS, actual);
+ }
+
+ @Test
+ public void testCorrectCategory_systemOff_appTurnOff_noChanges() {
+ int requested = PRIORITY_CATEGORY_CALLS;
+ int system = PRIORITY_CATEGORY_CALLS;
+
+ int actual = mService.correctCategory(requested, PRIORITY_CATEGORY_CONVERSATIONS,
+ system);
+
+ assertEquals(PRIORITY_CATEGORY_CALLS, actual);
+ }
+
+ @Test
+ public void testCorrectCategory_systemOn_appTurnOn_noChanges() {
+ int requested = PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS;
+ int system = PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS;
+
+ int actual = mService.correctCategory(requested, PRIORITY_CATEGORY_CONVERSATIONS,
+ system);
+
+ assertEquals(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS, actual);
+ }
+
+ @Test
+ public void testCorrectCategory_systemOff_appCannotTurnOn() {
+ int requested = PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS;
+ int system = PRIORITY_CATEGORY_CALLS;
+
+ int actual = mService.correctCategory(requested, PRIORITY_CATEGORY_CONVERSATIONS,
+ system);
+
+ assertEquals(PRIORITY_CATEGORY_CALLS, actual);
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index c1c74da..bb84b04 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -171,7 +171,7 @@
.thenReturn(SOUND_URI);
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
- NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND);
+ NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
resetZenModeHelper();
@@ -1430,7 +1430,7 @@
// start notification policy off with mAreChannelsBypassingDnd = true, but
// RankingHelper should change to false
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
- NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND);
+ NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
assertFalse(mHelper.areChannelsBypassingDnd());
@@ -1441,7 +1441,7 @@
@Test
public void testSetupNewZenModeHelper_cannotBypass() {
// start notification policy off with mAreChannelsBypassingDnd = false
- mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0);
+ mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
assertFalse(mHelper.areChannelsBypassingDnd());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 8774b63..5018166 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -140,7 +140,7 @@
.thenReturn(SOUND_URI);
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
- NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND);
+ NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
mHelper = new RankingHelper(getContext(), mHandler, mConfig, mMockZenModeHelper,
mUsageStats, new String[] {ImportanceExtractor.class.getName()});
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 5841e59..3186d53 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -169,11 +169,11 @@
public void testCleanupContextShouldRemovePersistedRecord() {
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
mSnoozeHelper.snooze(r, "context");
- mSnoozeHelper.cleanupPersistedContext(r.sbn.getKey());
+ mSnoozeHelper.cleanupPersistedContext(r.getSbn().getKey());
assertNull(mSnoozeHelper.getSnoozeContextForUnpostedNotification(
r.getUser().getIdentifier(),
- r.sbn.getPackageName(),
- r.sbn.getKey()
+ r.getSbn().getPackageName(),
+ r.getSbn().getKey()
));
}
@@ -201,7 +201,7 @@
long actualSnoozedUntilDuration = captor.getValue() - SystemClock.elapsedRealtime();
assertTrue(Math.abs(actualSnoozedUntilDuration - 1000) < 250);
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+ UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
}
@Test
@@ -211,7 +211,7 @@
verify(mAm, never()).setExactAndAllowWhileIdle(
anyInt(), anyLong(), any(PendingIntent.class));
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+ UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
}
@Test
@@ -221,17 +221,17 @@
mSnoozeHelper.snooze(r, 1000);
mSnoozeHelper.snooze(r2 , 1000);
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+ UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+ UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey()));
- mSnoozeHelper.cancel(UserHandle.USER_SYSTEM, r.sbn.getPackageName(), "one", 1);
+ mSnoozeHelper.cancel(UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), "one", 1);
// 2 = one for each snooze, above, zero for the cancel.
verify(mAm, times(2)).cancel(any(PendingIntent.class));
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+ UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+ UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey()));
}
@Test
@@ -243,21 +243,21 @@
mSnoozeHelper.snooze(r2, 1000);
mSnoozeHelper.snooze(r3, 1000);
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+ UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+ UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey()));
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_ALL, r3.sbn.getPackageName(), r3.getKey()));
+ UserHandle.USER_ALL, r3.getSbn().getPackageName(), r3.getKey()));
mSnoozeHelper.cancel(UserHandle.USER_SYSTEM, false);
// 3 = once for each snooze above (3), only.
verify(mAm, times(3)).cancel(any(PendingIntent.class));
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+ UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+ UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey()));
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_ALL, r3.sbn.getPackageName(), r3.getKey()));
+ UserHandle.USER_ALL, r3.getSbn().getPackageName(), r3.getKey()));
}
@Test
@@ -269,21 +269,21 @@
mSnoozeHelper.snooze(r2, 1000);
mSnoozeHelper.snooze(r3, 1000);
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+ UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+ UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey()));
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r3.sbn.getPackageName(), r3.getKey()));
+ UserHandle.USER_SYSTEM, r3.getSbn().getPackageName(), r3.getKey()));
mSnoozeHelper.cancel(UserHandle.USER_SYSTEM, "pkg2");
// 3 = once for each snooze above (3), only.
verify(mAm, times(3)).cancel(any(PendingIntent.class));
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+ UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+ UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey()));
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r3.sbn.getPackageName(), r3.getKey()));
+ UserHandle.USER_SYSTEM, r3.getSbn().getPackageName(), r3.getKey()));
}
@Test
@@ -291,12 +291,12 @@
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
mSnoozeHelper.snooze(r, 1000);
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+ UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
- mSnoozeHelper.cancel(UserHandle.USER_SYSTEM, r.sbn.getPackageName(), "one", 1);
+ mSnoozeHelper.cancel(UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), "one", 1);
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+ UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
}
@Test
@@ -306,11 +306,11 @@
mSnoozeHelper.snooze(r, 1000);
mSnoozeHelper.snooze(r2 , 1000);
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+ UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+ UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey()));
- mSnoozeHelper.cancel(UserHandle.USER_SYSTEM, r.sbn.getPackageName(), "one", 1);
+ mSnoozeHelper.cancel(UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), "one", 1);
mSnoozeHelper.repost(r.getKey(), UserHandle.USER_SYSTEM);
verify(mCallback, never()).repost(UserHandle.USER_SYSTEM, r);
@@ -507,18 +507,18 @@
mSnoozeHelper.snooze(r, 1000);
mSnoozeHelper.snooze(r2, 1000);
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+ UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+ UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey()));
// clear data
mSnoozeHelper.clearData(UserHandle.USER_SYSTEM, "pkg");
// nothing snoozed; alarms canceled
assertFalse(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+ UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
assertFalse(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+ UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey()));
// twice for initial snooze, twice for canceling the snooze
verify(mAm, times(4)).cancel(any(PendingIntent.class));
}
@@ -533,21 +533,21 @@
mSnoozeHelper.snooze(r2, 1000);
mSnoozeHelper.snooze(r3, 1000);
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+ UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_ALL, r2.sbn.getPackageName(), r2.getKey()));
+ UserHandle.USER_ALL, r2.getSbn().getPackageName(), r2.getKey()));
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r3.sbn.getPackageName(), r3.getKey()));
+ UserHandle.USER_SYSTEM, r3.getSbn().getPackageName(), r3.getKey()));
// clear data
mSnoozeHelper.clearData(UserHandle.USER_SYSTEM, "pkg");
assertFalse(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+ UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_ALL, r2.sbn.getPackageName(), r2.getKey()));
+ UserHandle.USER_ALL, r2.getSbn().getPackageName(), r2.getKey()));
assertTrue(mSnoozeHelper.isSnoozed(
- UserHandle.USER_SYSTEM, r3.sbn.getPackageName(), r3.getKey()));
+ UserHandle.USER_SYSTEM, r3.getSbn().getPackageName(), r3.getKey()));
// once for each initial snooze, once for canceling one snooze
verify(mAm, times(4)).cancel(any(PendingIntent.class));
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index b654764..f7b435e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -91,6 +91,7 @@
int priorityCategories = originalPolicy.priorityCategories;
int priorityCallSenders = originalPolicy.priorityCallSenders;
int priorityMessageSenders = originalPolicy.priorityMessageSenders;
+ int priorityConversationsSenders = originalPolicy.priorityConversationSenders;
int suppressedVisualEffects = originalPolicy.suppressedVisualEffects;
priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS;
priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
@@ -99,7 +100,7 @@
suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT;
Policy expectedPolicy = new Policy(priorityCategories, priorityCallSenders,
- priorityMessageSenders, suppressedVisualEffects, 0);
+ priorityMessageSenders, suppressedVisualEffects, 0, priorityConversationsSenders);
assertEquals(expectedPolicy, config.toNotificationPolicy(zenPolicy));
}
@@ -235,6 +236,8 @@
config.areChannelsBypassingDnd = false;
config.allowCallsFrom = ZenModeConfig.SOURCE_ANYONE;
config.allowMessagesFrom = ZenModeConfig.SOURCE_ANYONE;
+ config.allowConversations = true;
+ config.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
config.suppressedVisualEffects = 0;
return config;
@@ -252,6 +255,8 @@
config.allowReminders = false;
config.allowEvents = false;
config.areChannelsBypassingDnd = false;
+ config.allowConversations = false;
+ config.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_NONE;
config.suppressedVisualEffects = 0;
return config;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
index 32f389a..fb15088 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
@@ -16,6 +16,16 @@
package com.android.server.notification;
+import static android.app.Notification.CATEGORY_CALL;
+import static android.app.Notification.CATEGORY_MESSAGE;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
+import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT;
+import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_NONE;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES;
+import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
@@ -31,11 +41,10 @@
import android.app.Notification;
import android.app.NotificationChannel;
-import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.media.AudioAttributes;
+import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.service.notification.ZenModeConfig;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -76,6 +85,16 @@
return new NotificationRecord(mContext, sbn, c);
}
+ private NotificationRecord getConversationRecord(NotificationChannel c,
+ StatusBarNotification sbn) {
+ NotificationRecord r = mock(NotificationRecord.class);
+ when(r.getCriticality()).thenReturn(CriticalNotificationExtractor.NORMAL);
+ when(r.getSbn()).thenReturn(sbn);
+ when(r.getChannel()).thenReturn(c);
+ when(r.isConversation()).thenReturn(true);
+ return r;
+ }
+
@Test
public void testIsMessage() {
NotificationRecord r = getNotificationRecord();
@@ -97,14 +116,14 @@
assertTrue(mZenModeFiltering.isAlarm(r));
r = getNotificationRecord();
- r.sbn.getNotification().category = Notification.CATEGORY_ALARM;
+ r.getSbn().getNotification().category = Notification.CATEGORY_ALARM;
assertTrue(mZenModeFiltering.isAlarm(r));
}
@Test
public void testIsAlarm_wrongCategory() {
NotificationRecord r = getNotificationRecord();
- r.sbn.getNotification().category = Notification.CATEGORY_CALL;
+ r.getSbn().getNotification().category = CATEGORY_CALL;
assertFalse(mZenModeFiltering.isAlarm(r));
}
@@ -121,10 +140,10 @@
@Test
public void testSuppressDNDInfo_yes_VisEffectsAllowed() {
NotificationRecord r = getNotificationRecord();
- when(r.sbn.getPackageName()).thenReturn("android");
- when(r.sbn.getId()).thenReturn(SystemMessage.NOTE_ZEN_UPGRADE);
+ when(r.getSbn().getPackageName()).thenReturn("android");
+ when(r.getSbn().getId()).thenReturn(SystemMessage.NOTE_ZEN_UPGRADE);
Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects()
- - SUPPRESSED_EFFECT_STATUS_BAR);
+ - SUPPRESSED_EFFECT_STATUS_BAR, 0);
assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
}
@@ -132,9 +151,9 @@
@Test
public void testSuppressDNDInfo_yes_WrongId() {
NotificationRecord r = getNotificationRecord();
- when(r.sbn.getPackageName()).thenReturn("android");
- when(r.sbn.getId()).thenReturn(SystemMessage.NOTE_ACCOUNT_CREDENTIAL_PERMISSION);
- Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects());
+ when(r.getSbn().getPackageName()).thenReturn("android");
+ when(r.getSbn().getId()).thenReturn(SystemMessage.NOTE_ACCOUNT_CREDENTIAL_PERMISSION);
+ Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects(), 0);
assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
}
@@ -142,9 +161,9 @@
@Test
public void testSuppressDNDInfo_yes_WrongPackage() {
NotificationRecord r = getNotificationRecord();
- when(r.sbn.getPackageName()).thenReturn("android2");
- when(r.sbn.getId()).thenReturn(SystemMessage.NOTE_ZEN_UPGRADE);
- Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects());
+ when(r.getSbn().getPackageName()).thenReturn("android2");
+ when(r.getSbn().getId()).thenReturn(SystemMessage.NOTE_ZEN_UPGRADE);
+ Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects(), 0);
assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
}
@@ -152,9 +171,9 @@
@Test
public void testSuppressDNDInfo_no() {
NotificationRecord r = getNotificationRecord();
- when(r.sbn.getPackageName()).thenReturn("android");
- when(r.sbn.getId()).thenReturn(SystemMessage.NOTE_ZEN_UPGRADE);
- Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects());
+ when(r.getSbn().getPackageName()).thenReturn("android");
+ when(r.getSbn().getId()).thenReturn(SystemMessage.NOTE_ZEN_UPGRADE);
+ Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects(), 0);
assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_ALARMS, policy, r));
@@ -164,7 +183,7 @@
@Test
public void testSuppressAnything_yes_ZenModeOff() {
NotificationRecord r = getNotificationRecord();
- when(r.sbn.getPackageName()).thenReturn("bananas");
+ when(r.getSbn().getPackageName()).thenReturn("bananas");
Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects());
assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_OFF, policy, r));
@@ -174,12 +193,120 @@
public void testSuppressAnything_bypass_ZenModeOn() {
NotificationRecord r = getNotificationRecord();
r.setCriticality(CriticalNotificationExtractor.CRITICAL);
- when(r.sbn.getPackageName()).thenReturn("bananas");
- Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects());
+ when(r.getSbn().getPackageName()).thenReturn("bananas");
+ Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects(), 0);
assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_NO_INTERRUPTIONS, policy, r));
r.setCriticality(CriticalNotificationExtractor.CRITICAL_LOW);
assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_NO_INTERRUPTIONS, policy, r));
}
+
+ @Test
+ public void testConversation_allAllowed() {
+ Notification n = new Notification.Builder(mContext, "a").build();
+ StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, n,
+ UserHandle.SYSTEM, null, 0);
+
+ NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+ channel.setConversationId("parent", "me, work");
+
+ NotificationRecord r = getConversationRecord(channel, sbn);
+ when(r.isConversation()).thenReturn(true);
+
+ Policy policy = new Policy(
+ PRIORITY_CATEGORY_CONVERSATIONS, 0, 0, 0, CONVERSATION_SENDERS_ANYONE);
+
+ assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
+ }
+
+ @Test
+ public void testConversation_importantAllowed_isImportant() {
+ Notification n = new Notification.Builder(mContext, "a").build();
+ StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, n,
+ UserHandle.SYSTEM, null, 0);
+
+ NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+ channel.setConversationId("parent", "me, work");
+ channel.setImportantConversation(true);
+
+ NotificationRecord r = getConversationRecord(channel, sbn);
+
+ Policy policy = new Policy(
+ PRIORITY_CATEGORY_CONVERSATIONS, 0, 0, 0, CONVERSATION_SENDERS_IMPORTANT);
+
+ assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
+ }
+
+ @Test
+ public void testConversation_importantAllowed_isNotImportant() {
+ Notification n = new Notification.Builder(mContext, "a").build();
+ StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, n,
+ UserHandle.SYSTEM, null, 0);
+
+ NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+ channel.setConversationId("parent", "me, work");
+
+ NotificationRecord r = getConversationRecord(channel, sbn);
+
+ Policy policy = new Policy(
+ PRIORITY_CATEGORY_CONVERSATIONS, 0, 0, 0, CONVERSATION_SENDERS_IMPORTANT);
+
+ assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
+ }
+
+ @Test
+ public void testConversation_noneAllowed_notCallOrMsg() {
+ Notification n = new Notification.Builder(mContext, "a").build();
+ StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, n,
+ UserHandle.SYSTEM, null, 0);
+
+ NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+ channel.setConversationId("parent", "me, work");
+
+ NotificationRecord r = getConversationRecord(channel, sbn);
+
+ Policy policy =
+ new Policy(PRIORITY_CATEGORY_CONVERSATIONS, 0, 0, 0, CONVERSATION_SENDERS_NONE);
+
+ assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
+ }
+
+ @Test
+ public void testConversation_noneAllowed_callAllowed() {
+ Notification n = new Notification.Builder(mContext, "a").build();
+ StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, n,
+ UserHandle.SYSTEM, null, 0);
+
+ NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+ channel.setConversationId("parent", "me, work");
+
+ NotificationRecord r = getConversationRecord(channel, sbn);
+ when(r.isCategory(CATEGORY_CALL)).thenReturn(true);
+
+ Policy policy =
+ new Policy(PRIORITY_CATEGORY_CALLS,
+ PRIORITY_SENDERS_ANY, 0, 0, CONVERSATION_SENDERS_NONE);
+
+ assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
+ }
+
+ @Test
+ public void testConversation_noneAllowed_msgAllowed() {
+ when(mMessagingUtil.isMessaging(any())).thenReturn(true);
+ Notification n = new Notification.Builder(mContext, "a").build();
+ StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, n,
+ UserHandle.SYSTEM, null, 0);
+
+ NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+ channel.setConversationId("parent", "me, work");
+
+ NotificationRecord r = getConversationRecord(channel, sbn);
+
+ Policy policy =
+ new Policy(PRIORITY_CATEGORY_MESSAGES,
+ 0, PRIORITY_SENDERS_ANY, 0, CONVERSATION_SENDERS_NONE);
+
+ assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 99771b9..bc2766c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -142,7 +142,8 @@
+ "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
+ "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
+ "visualScreenOff=\"true\" alarms=\"true\" "
- + "media=\"true\" system=\"false\" />\n"
+ + "media=\"true\" system=\"false\" conversations=\"true\""
+ + " conversationsFrom=\"2\"/>\n"
+ "<automatic ruleId=\"" + EVENTS_DEFAULT_RULE_ID
+ "\" enabled=\"false\" snoozing=\"false\""
+ " name=\"Event\" zen=\"1\""
@@ -218,7 +219,7 @@
public void testZenOff_NoMuteApplied() {
mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_OFF;
mZenModeHelperSpy.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS |
- Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0);
+ Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
doNothing().when(mZenModeHelperSpy).applyRestrictions(eq(false), anyBoolean(), anyInt());
@@ -232,7 +233,7 @@
public void testZenOn_AllowAlarmsMedia_NoAlarmMediaMuteApplied() {
mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS |
- Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0);
+ Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, false,
@@ -244,7 +245,7 @@
@Test
public void testZenOn_DisallowAlarmsMedia_AlarmMediaMuteApplied() {
mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+ mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, true,
AudioAttributes.USAGE_ALARM);
@@ -260,7 +261,7 @@
public void testTotalSilence() {
mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
mZenModeHelperSpy.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS |
- Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0);
+ Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
// Total silence will silence alarms, media and system noises (but not vibrations)
@@ -281,7 +282,7 @@
@Test
public void testAlarmsOnly_alarmMediaMuteNotApplied() {
mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_ALARMS;
- mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+ mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
// Alarms only mode will not silence alarms
@@ -304,7 +305,7 @@
@Test
public void testAlarmsOnly_callsMuteApplied() {
mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_ALARMS;
- mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+ mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
// Alarms only mode will silence calls despite priority-mode config
@@ -318,7 +319,7 @@
public void testAlarmsOnly_allZenConfigToggledCannotBypass_alarmMuteNotApplied() {
// Only audio attributes with SUPPRESIBLE_NEVER can bypass
mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_ALARMS;
- mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+ mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false,
@@ -330,7 +331,7 @@
// Only audio attributes with SUPPRESIBLE_NEVER can bypass
// with special case USAGE_ASSISTANCE_SONIFICATION
mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+ mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
for (int usage : AudioAttributes.SDK_USAGES) {
@@ -352,7 +353,7 @@
public void testApplyRestrictions_whitelist_priorityOnlyMode() {
mZenModeHelperSpy.setPriorityOnlyDndExemptPackages(new String[] {PKG_O});
mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+ mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
for (int usage : AudioAttributes.SDK_USAGES) {
@@ -367,7 +368,7 @@
public void testApplyRestrictions_whitelist_alarmsOnlyMode() {
mZenModeHelperSpy.setPriorityOnlyDndExemptPackages(new String[] {PKG_O});
mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_ALARMS;
- mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+ mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
for (int usage : AudioAttributes.SDK_USAGES) {
@@ -382,7 +383,7 @@
public void testApplyRestrictions_whitelist_totalSilenceMode() {
mZenModeHelperSpy.setPriorityOnlyDndExemptPackages(new String[] {PKG_O});
mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_NO_INTERRUPTIONS;
- mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+ mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
for (int usage : AudioAttributes.SDK_USAGES) {
@@ -401,7 +402,7 @@
Settings.Secure.putInt(mContentResolver, Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 1);
Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_SETTINGS_UPDATED, 0);
mZenModeHelperSpy.mIsBootComplete = true;
- mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+ mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
mZenModeHelperSpy.setZenModeSetting(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
verify(mZenModeHelperSpy, times(1)).createZenUpgradeNotification();
@@ -452,7 +453,8 @@
mZenModeHelperSpy.mConfig.allowCalls = false;
mZenModeHelperSpy.mConfig.allowMessages = false;
mZenModeHelperSpy.mConfig.allowEvents = false;
- mZenModeHelperSpy.mConfig.allowRepeatCallers= false;
+ mZenModeHelperSpy.mConfig.allowRepeatCallers = false;
+ mZenModeHelperSpy.mConfig.allowConversations = false;
// 2. apply priority only zen - verify ringer is unchanged
mZenModeHelperSpy.applyZenToRingerMode();
@@ -509,7 +511,8 @@
mZenModeHelperSpy.mConfig.allowCalls = false;
mZenModeHelperSpy.mConfig.allowMessages = false;
mZenModeHelperSpy.mConfig.allowEvents = false;
- mZenModeHelperSpy.mConfig.allowRepeatCallers= false;
+ mZenModeHelperSpy.mConfig.allowRepeatCallers = false;
+ mZenModeHelperSpy.mConfig.allowConversations = false;
ZenModeHelper.RingerModeDelegate ringerModeDelegateRingerNotMuted =
mZenModeHelperSpy.new RingerModeDelegate();
@@ -694,7 +697,9 @@
mZenModeHelperSpy.mConfig.allowCalls = true;
mZenModeHelperSpy.mConfig.allowMessages = true;
mZenModeHelperSpy.mConfig.allowEvents = true;
- mZenModeHelperSpy.mConfig.allowRepeatCallers= true;
+ mZenModeHelperSpy.mConfig.allowRepeatCallers = true;
+ mZenModeHelperSpy.mConfig.allowConversations = true;
+ mZenModeHelperSpy.mConfig.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_ANYONE;
mZenModeHelperSpy.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE;
mZenModeHelperSpy.mConfig.manualRule = new ZenModeConfig.ZenRule();
mZenModeHelperSpy.mConfig.manualRule.component = new ComponentName("a", "a");
@@ -716,7 +721,9 @@
mZenModeHelperSpy.mConfig.allowCalls = true;
mZenModeHelperSpy.mConfig.allowMessages = true;
mZenModeHelperSpy.mConfig.allowEvents = true;
- mZenModeHelperSpy.mConfig.allowRepeatCallers= true;
+ mZenModeHelperSpy.mConfig.allowRepeatCallers = true;
+ mZenModeHelperSpy.mConfig.allowConversations = true;
+ mZenModeHelperSpy.mConfig.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_ANYONE;
mZenModeHelperSpy.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE;
mZenModeHelperSpy.mConfig.manualRule = new ZenModeConfig.ZenRule();
mZenModeHelperSpy.mConfig.manualRule.zenMode =
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 399cf49..135d005 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
@@ -44,6 +45,7 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.app.BlockedAppActivity;
import com.android.internal.app.HarmfulAppWarningActivity;
import com.android.internal.app.SuspendedAppActivity;
import com.android.internal.app.UnlaunchableAppActivity;
@@ -105,6 +107,8 @@
private PackageManagerService mPackageManager;
@Mock
private ActivityManagerInternal mAmInternal;
+ @Mock
+ private LockTaskController mLockTaskController;
private ActivityStartInterceptor mInterceptor;
private ActivityInfo mAInfo = new ActivityInfo();
@@ -145,6 +149,13 @@
when(mPackageManager.getHarmfulAppWarning(TEST_PACKAGE_NAME, TEST_USER_ID))
.thenReturn(null);
+ // Mock LockTaskController
+ mAInfo.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
+ when(mService.getLockTaskController()).thenReturn(mLockTaskController);
+ when(mLockTaskController.isActivityAllowed(
+ TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT))
+ .thenReturn(true);
+
// Initialise activity info
mAInfo.applicationInfo = new ApplicationInfo();
mAInfo.packageName = mAInfo.applicationInfo.packageName = TEST_PACKAGE_NAME;
@@ -196,6 +207,18 @@
}
@Test
+ public void testInterceptLockTaskModeViolationPackage() {
+ when(mLockTaskController.isActivityAllowed(
+ TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT))
+ .thenReturn(false);
+
+ assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+
+ assertTrue(BlockedAppActivity.createIntent(TEST_USER_ID, TEST_PACKAGE_NAME)
+ .filterEquals(mInterceptor.mIntent));
+ }
+
+ @Test
public void testInterceptQuietProfile() {
// GIVEN that the user the activity is starting as is currently in quiet mode
when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 5acc0f2..956c200 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -126,7 +126,8 @@
mTarget = new TestDragDropController(mWm, mWm.mH.getLooper());
mWindow = createDropTargetWindow("Drag test window", 0);
doReturn(mWindow).when(mDisplayContent).getTouchableWinAtPointLocked(0, 0);
- when(mWm.mInputManager.transferTouchFocus(any(), any())).thenReturn(true);
+ when(mWm.mInputManager.transferTouchFocus(any(InputChannel.class),
+ any(InputChannel.class))).thenReturn(true);
mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
}
@@ -176,7 +177,8 @@
.setFormat(PixelFormat.TRANSLUCENT)
.build();
- assertTrue(mWm.mInputManager.transferTouchFocus(null, null));
+ assertTrue(mWm.mInputManager.transferTouchFocus(new InputChannel(),
+ new InputChannel()));
mToken = mTarget.performDrag(
new SurfaceSession(), 0, 0, mWindow.mClient, flag, surface, 0, 0, 0, 0, 0,
data);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index 039ff60..a137cde 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -25,10 +25,14 @@
import static android.app.StatusBarManager.DISABLE_HOME;
import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
import static android.os.Process.SYSTEM_UID;
import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
@@ -693,6 +697,45 @@
assertTrue((StatusBarManager.DISABLE2_QUICK_SETTINGS & flags.second) != 0);
}
+ @Test
+ public void testIsActivityAllowed() {
+ // WHEN lock task mode is not enabled
+ assertTrue(mLockTaskController.isActivityAllowed(
+ TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
+
+ // Start lock task mode
+ Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+ mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+
+ // WHEN LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK is not enabled
+ assertTrue(mLockTaskController.isActivityAllowed(
+ TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
+
+ // Enable LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK feature
+ mLockTaskController.updateLockTaskFeatures(
+ TEST_USER_ID, LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK);
+
+ // package with LOCK_TASK_LAUNCH_MODE_ALWAYS should always be allowed
+ assertTrue(mLockTaskController.isActivityAllowed(
+ TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_ALWAYS));
+
+ // unwhitelisted package should not be allowed
+ assertFalse(mLockTaskController.isActivityAllowed(
+ TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
+
+ // update the whitelist
+ String[] whitelist = new String[] { TEST_PACKAGE_NAME };
+ mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist);
+
+ // whitelisted package should be allowed
+ assertTrue(mLockTaskController.isActivityAllowed(
+ TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
+
+ // package with LOCK_TASK_LAUNCH_MODE_NEVER should never be allowed
+ assertFalse(mLockTaskController.isActivityAllowed(
+ TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_NEVER));
+ }
+
private Task getTask(int lockTaskAuth) {
return getTask(TEST_PACKAGE_NAME, lockTaskAuth);
}
diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java
index 14b847f..5f95bc1 100644
--- a/test-mock/src/android/test/mock/MockPackageManager.java
+++ b/test-mock/src/android/test/mock/MockPackageManager.java
@@ -1249,12 +1249,4 @@
int uid, byte[] certificate, @PackageManager.CertificateInputType int type) {
throw new UnsupportedOperationException();
}
-
- /**
- * @hide
- */
- @Override
- public String getSystemTextClassifierPackageName() {
- throw new UnsupportedOperationException();
- }
}
diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
index 2d5df4f..0628691 100644
--- a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
@@ -38,6 +38,8 @@
import android.content.Context;
import android.os.PersistableBundle;
+import androidx.test.InstrumentationRegistry;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -58,21 +60,26 @@
private static final Executor INLINE_EXECUTOR = x -> x.run();
- @Mock private Context mContext;
@Mock private IConnectivityManager mService;
@Mock private ConnectivityDiagnosticsCallback mCb;
+ private Context mContext;
private ConnectivityDiagnosticsBinder mBinder;
private ConnectivityDiagnosticsManager mManager;
+ private String mPackageName;
+
@Before
public void setUp() {
- mContext = mock(Context.class);
+ mContext = InstrumentationRegistry.getContext();
+
mService = mock(IConnectivityManager.class);
mCb = mock(ConnectivityDiagnosticsCallback.class);
mBinder = new ConnectivityDiagnosticsBinder(mCb, INLINE_EXECUTOR);
mManager = new ConnectivityDiagnosticsManager(mContext, mService);
+
+ mPackageName = mContext.getOpPackageName();
}
@After
@@ -271,7 +278,7 @@
mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
verify(mService).registerConnectivityDiagnosticsCallback(
- any(ConnectivityDiagnosticsBinder.class), eq(request));
+ any(ConnectivityDiagnosticsBinder.class), eq(request), eq(mPackageName));
assertTrue(ConnectivityDiagnosticsManager.sCallbacks.containsKey(mCb));
}
@@ -302,7 +309,7 @@
// verify that re-registering is successful
mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
verify(mService, times(2)).registerConnectivityDiagnosticsCallback(
- any(ConnectivityDiagnosticsBinder.class), eq(request));
+ any(ConnectivityDiagnosticsBinder.class), eq(request), eq(mPackageName));
assertTrue(ConnectivityDiagnosticsManager.sCallbacks.containsKey(mCb));
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 2573bba..b4f32e7 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -23,6 +23,8 @@
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport;
import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_SUPL;
@@ -119,6 +121,7 @@
import android.Manifest;
import android.annotation.NonNull;
import android.app.AlarmManager;
+import android.app.AppOpsManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -132,6 +135,7 @@
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.res.Resources;
+import android.location.LocationManager;
import android.net.CaptivePortalData;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
@@ -177,6 +181,7 @@
import android.net.util.MultinetworkPolicyTracker;
import android.os.BadParcelableException;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.Handler;
@@ -187,6 +192,7 @@
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -218,6 +224,7 @@
import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.connectivity.MockableSystemProperties;
import com.android.server.connectivity.Nat464Xlat;
+import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.Vpn;
@@ -292,6 +299,8 @@
private static final int UNREASONABLY_LONG_ALARM_WAIT_MS = 1000;
+ private static final long TIMESTAMP = 1234L;
+
private static final String CLAT_PREFIX = "v4-";
private static final String MOBILE_IFNAME = "test_rmnet_data0";
private static final String WIFI_IFNAME = "test_wlan0";
@@ -327,6 +336,8 @@
@Mock AlarmManager mAlarmManager;
@Mock IConnectivityDiagnosticsCallback mConnectivityDiagnosticsCallback;
@Mock IBinder mIBinder;
+ @Mock LocationManager mLocationManager;
+ @Mock AppOpsManager mAppOpsManager;
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -412,6 +423,8 @@
if (Context.NETWORK_STACK_SERVICE.equals(name)) return mNetworkStack;
if (Context.USER_SERVICE.equals(name)) return mUserManager;
if (Context.ALARM_SERVICE.equals(name)) return mAlarmManager;
+ if (Context.LOCATION_SERVICE.equals(name)) return mLocationManager;
+ if (Context.APP_OPS_SERVICE.equals(name)) return mAppOpsManager;
return super.getSystemService(name);
}
@@ -558,12 +571,17 @@
| NETWORK_VALIDATION_RESULT_PARTIAL;
private static final int VALIDATION_RESULT_INVALID = 0;
+ private static final long DATA_STALL_TIMESTAMP = 10L;
+ private static final int DATA_STALL_DETECTION_METHOD = 1;
+
private INetworkMonitor mNetworkMonitor;
private INetworkMonitorCallbacks mNmCallbacks;
private int mNmValidationResult = VALIDATION_RESULT_BASE;
private int mProbesCompleted;
private int mProbesSucceeded;
private String mNmValidationRedirectUrl = null;
+ private PersistableBundle mValidationExtras = PersistableBundle.EMPTY;
+ private PersistableBundle mDataStallExtras = PersistableBundle.EMPTY;
private boolean mNmProvNotificationRequested = false;
private final ConditionVariable mNetworkStatusReceived = new ConditionVariable();
@@ -631,8 +649,8 @@
}
mNmCallbacks.notifyProbeStatusChanged(mProbesCompleted, mProbesSucceeded);
- mNmCallbacks.notifyNetworkTested(
- mNmValidationResult, mNmValidationRedirectUrl);
+ mNmCallbacks.notifyNetworkTestedWithExtras(
+ mNmValidationResult, mNmValidationRedirectUrl, TIMESTAMP, mValidationExtras);
if (mNmValidationRedirectUrl != null) {
mNmCallbacks.showProvisioningNotification(
@@ -791,6 +809,11 @@
public void expectPreventReconnectReceived() {
expectPreventReconnectReceived(TIMEOUT_MS);
}
+
+ void notifyDataStallSuspected() throws Exception {
+ mNmCallbacks.notifyDataStallSuspected(
+ DATA_STALL_TIMESTAMP, DATA_STALL_DETECTION_METHOD, mDataStallExtras);
+ }
}
/**
@@ -970,6 +993,8 @@
// not inherit from NetworkAgent.
private TestNetworkAgentWrapper mMockNetworkAgent;
+ private VpnInfo mVpnInfo;
+
public MockVpn(int userId) {
super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService,
userId);
@@ -1041,6 +1066,17 @@
mConnected = false;
mConfig = null;
}
+
+ @Override
+ public synchronized VpnInfo getVpnInfo() {
+ if (mVpnInfo != null) return mVpnInfo;
+
+ return super.getVpnInfo();
+ }
+
+ private void setVpnInfo(VpnInfo vpnInfo) {
+ mVpnInfo = vpnInfo;
+ }
}
private void mockVpn(int uid) {
@@ -6400,7 +6436,7 @@
new NetworkCapabilities(), TYPE_ETHERNET, 0, NetworkRequest.Type.NONE);
try {
mService.registerConnectivityDiagnosticsCallback(
- mConnectivityDiagnosticsCallback, request);
+ mConnectivityDiagnosticsCallback, request, mContext.getPackageName());
fail("registerConnectivityDiagnosticsCallback should throw on invalid NetworkRequest");
} catch (IllegalArgumentException expected) {
}
@@ -6414,7 +6450,7 @@
when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
mService.registerConnectivityDiagnosticsCallback(
- mConnectivityDiagnosticsCallback, wifiRequest);
+ mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName());
verify(mIBinder, timeout(TIMEOUT_MS))
.linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
@@ -6438,7 +6474,7 @@
when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
mService.registerConnectivityDiagnosticsCallback(
- mConnectivityDiagnosticsCallback, wifiRequest);
+ mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName());
verify(mIBinder, timeout(TIMEOUT_MS))
.linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
@@ -6449,7 +6485,7 @@
// Register the same callback again
mService.registerConnectivityDiagnosticsCallback(
- mConnectivityDiagnosticsCallback, wifiRequest);
+ mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName());
// Block until all other events are done processing.
HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
@@ -6458,4 +6494,182 @@
mService.mConnectivityDiagnosticsCallbacks.containsKey(
mConnectivityDiagnosticsCallback));
}
+
+ @Test
+ public void testCheckConnectivityDiagnosticsPermissionsNetworkStack() throws Exception {
+ final NetworkAgentInfo naiWithoutUid =
+ new NetworkAgentInfo(
+ null, null, null, null, null, new NetworkCapabilities(), null,
+ mServiceContext, null, null, mService, null, null, null, 0);
+
+ mServiceContext.setPermission(
+ android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
+ assertTrue(
+ "NetworkStack permission not applied",
+ mService.checkConnectivityDiagnosticsPermissions(
+ Process.myPid(), Process.myUid(), naiWithoutUid,
+ mContext.getOpPackageName()));
+ }
+
+ @Test
+ public void testCheckConnectivityDiagnosticsPermissionsNoLocationPermission() throws Exception {
+ final NetworkAgentInfo naiWithoutUid =
+ new NetworkAgentInfo(
+ null, null, null, null, null, new NetworkCapabilities(), null,
+ mServiceContext, null, null, mService, null, null, null, 0);
+
+ mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+ assertFalse(
+ "ACCESS_FINE_LOCATION permission necessary for Connectivity Diagnostics",
+ mService.checkConnectivityDiagnosticsPermissions(
+ Process.myPid(), Process.myUid(), naiWithoutUid,
+ mContext.getOpPackageName()));
+ }
+
+ @Test
+ public void testCheckConnectivityDiagnosticsPermissionsActiveVpn() throws Exception {
+ final NetworkAgentInfo naiWithoutUid =
+ new NetworkAgentInfo(
+ null, null, null, null, null, new NetworkCapabilities(), null,
+ mServiceContext, null, null, mService, null, null, null, 0);
+
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+ mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+ // setUp() calls mockVpn() which adds a VPN with the Test Runner's uid. Configure it to be
+ // active
+ final VpnInfo info = new VpnInfo();
+ info.ownerUid = Process.myUid();
+ info.vpnIface = "interface";
+ mMockVpn.setVpnInfo(info);
+ assertTrue(
+ "Active VPN permission not applied",
+ mService.checkConnectivityDiagnosticsPermissions(
+ Process.myPid(), Process.myUid(), naiWithoutUid,
+ mContext.getOpPackageName()));
+ }
+
+ @Test
+ public void testCheckConnectivityDiagnosticsPermissionsNetworkAdministrator() throws Exception {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ nc.setAdministratorUids(Arrays.asList(Process.myUid()));
+ final NetworkAgentInfo naiWithUid =
+ new NetworkAgentInfo(
+ null, null, null, null, null, nc, null, mServiceContext, null, null,
+ mService, null, null, null, 0);
+
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+ mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+ // Disconnect mock vpn so the uid check on NetworkAgentInfo is tested
+ mMockVpn.disconnect();
+ assertTrue(
+ "NetworkCapabilities administrator uid permission not applied",
+ mService.checkConnectivityDiagnosticsPermissions(
+ Process.myPid(), Process.myUid(), naiWithUid, mContext.getOpPackageName()));
+ }
+
+ @Test
+ public void testCheckConnectivityDiagnosticsPermissionsFails() throws Exception {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ nc.setOwnerUid(Process.myUid());
+ nc.setAdministratorUids(Arrays.asList(Process.myUid()));
+ final NetworkAgentInfo naiWithUid =
+ new NetworkAgentInfo(
+ null, null, null, null, null, nc, null, mServiceContext, null, null,
+ mService, null, null, null, 0);
+
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+ mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+ // Use wrong pid and uid
+ assertFalse(
+ "Permissions allowed when they shouldn't be granted",
+ mService.checkConnectivityDiagnosticsPermissions(
+ Process.myPid() + 1, Process.myUid() + 1, naiWithUid,
+ mContext.getOpPackageName()));
+ }
+
+ private void setupLocationPermissions(
+ int targetSdk, boolean locationToggle, String op, String perm) throws Exception {
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.targetSdkVersion = targetSdk;
+ when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
+ .thenReturn(applicationInfo);
+
+ when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
+
+ when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), eq(mContext.getPackageName())))
+ .thenReturn(AppOpsManager.MODE_ALLOWED);
+
+ mServiceContext.setPermission(perm, PERMISSION_GRANTED);
+ }
+
+ private void setUpConnectivityDiagnosticsCallback() throws Exception {
+ final NetworkRequest request = new NetworkRequest.Builder().build();
+ when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
+
+ mServiceContext.setPermission(
+ android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
+
+ mService.registerConnectivityDiagnosticsCallback(
+ mConnectivityDiagnosticsCallback, request, mContext.getPackageName());
+
+ // Block until all other events are done processing.
+ HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+ // Connect the cell agent verify that it notifies TestNetworkCallback that it is available
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(callback);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ callback.assertNoCallback();
+ }
+
+ @Test
+ public void testConnectivityDiagnosticsCallbackOnConnectivityReport() throws Exception {
+ setUpConnectivityDiagnosticsCallback();
+
+ // Wait for onConnectivityReport to fire
+ verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS))
+ .onConnectivityReport(any(ConnectivityReport.class));
+ }
+
+ @Test
+ public void testConnectivityDiagnosticsCallbackOnDataStallSuspected() throws Exception {
+ setUpConnectivityDiagnosticsCallback();
+
+ // Trigger notifyDataStallSuspected() on the INetworkMonitorCallbacks instance in the
+ // cellular network agent
+ mCellNetworkAgent.notifyDataStallSuspected();
+
+ // Wait for onDataStallSuspected to fire
+ verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS))
+ .onDataStallSuspected(any(DataStallReport.class));
+ }
+
+ @Test
+ public void testConnectivityDiagnosticsCallbackOnConnectivityReported() throws Exception {
+ setUpConnectivityDiagnosticsCallback();
+
+ final Network n = mCellNetworkAgent.getNetwork();
+ final boolean hasConnectivity = true;
+ mService.reportNetworkConnectivity(n, hasConnectivity);
+
+ // Wait for onNetworkConnectivityReported to fire
+ verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS))
+ .onNetworkConnectivityReported(eq(n), eq(hasConnectivity));
+
+ final boolean noConnectivity = false;
+ mService.reportNetworkConnectivity(n, noConnectivity);
+
+ // Wait for onNetworkConnectivityReported to fire
+ verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS))
+ .onNetworkConnectivityReported(eq(n), eq(noConnectivity));
+ }
}
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index 0e32aee..d3958a6 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -30,6 +30,7 @@
"utils.cpp",
],
cflags: [
+ "-DSTATS_SCHEMA_LEGACY",
"-Wall",
"-Werror",
],
diff --git a/tools/stats_log_api_gen/native_writer.cpp b/tools/stats_log_api_gen/native_writer.cpp
index c7a34fe..285514d 100644
--- a/tools/stats_log_api_gen/native_writer.cpp
+++ b/tools/stats_log_api_gen/native_writer.cpp
@@ -22,15 +22,6 @@
namespace stats_log_api_gen {
#if !defined(STATS_SCHEMA_LEGACY)
-static void write_native_key_value_pairs_for_type(FILE* out, const int argIndex,
- const int typeIndex, const string& type, const string& valueFieldName) {
- fprintf(out, " for (const auto& it : arg%d_%d) {\n", argIndex, typeIndex);
- fprintf(out, " pairs.push_back("
- "{ .key = it.first, .valueType = %s, .%s = it.second });\n",
- type.c_str(), valueFieldName.c_str());
- fprintf(out, " }\n");
-
-}
static int write_native_stats_write_methods(FILE* out, const Atoms& atoms,
const AtomDecl& attributionDecl, const string& moduleName, const bool supportQ) {
@@ -41,7 +32,10 @@
continue;
}
vector<java_type_t> signature = signature_to_modules_it->first;
-
+ // Key value pairs not supported in native.
+ if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) {
+ continue;
+ }
write_native_method_signature(out, "int stats_write", signature,
attributionDecl, " {");
@@ -59,11 +53,6 @@
uidName, uidName, tagName);
break;
}
- case JAVA_TYPE_KEY_VALUE_PAIR:
- fprintf(out, " event.writeKeyValuePairs("
- "arg%d_1, arg%d_2, arg%d_3, arg%d_4);\n",
- argIndex, argIndex, argIndex, argIndex);
- break;
case JAVA_TYPE_BYTE_ARRAY:
fprintf(out, " event.writeByteArray(arg%d.arg, arg%d.arg_length);\n",
argIndex, argIndex);
@@ -85,7 +74,7 @@
fprintf(out, " event.writeString(arg%d);\n", argIndex);
break;
default:
- // Unsupported types: OBJECT, DOUBLE.
+ // Unsupported types: OBJECT, DOUBLE, KEY_VALUE_PAIRS.
fprintf(stderr, "Encountered unsupported type.");
return 1;
}
@@ -93,8 +82,8 @@
}
fprintf(out, " return event.writeToSocket();\n");
} else {
- fprintf(out, " struct stats_event* event = stats_event_obtain();\n");
- fprintf(out, " stats_event_set_atom_id(event, code);\n");
+ fprintf(out, " AStatsEvent* event = AStatsEvent_obtain();\n");
+ fprintf(out, " AStatsEvent_setAtomId(event, code);\n");
for (vector<java_type_t>::const_iterator arg = signature.begin();
arg != signature.end(); arg++) {
switch (*arg) {
@@ -102,57 +91,43 @@
const char* uidName = attributionDecl.fields.front().name.c_str();
const char* tagName = attributionDecl.fields.back().name.c_str();
fprintf(out,
- " stats_event_write_attribution_chain(event, "
+ " AStatsEvent_writeAttributionChain(event, "
"reinterpret_cast<const uint32_t*>(%s), %s.data(), "
"static_cast<uint8_t>(%s_length));\n",
uidName, tagName, uidName);
break;
}
- case JAVA_TYPE_KEY_VALUE_PAIR:
- fprintf(out, " std::vector<key_value_pair> pairs;\n");
- write_native_key_value_pairs_for_type(
- out, argIndex, 1, "INT32_TYPE", "int32Value");
- write_native_key_value_pairs_for_type(
- out, argIndex, 2, "INT64_TYPE", "int64Value");
- write_native_key_value_pairs_for_type(
- out, argIndex, 3, "STRING_TYPE", "stringValue");
- write_native_key_value_pairs_for_type(
- out, argIndex, 4, "FLOAT_TYPE", "floatValue");
- fprintf(out,
- " stats_event_write_key_value_pairs(event, pairs.data(), "
- "static_cast<uint8_t>(pairs.size()));\n");
- break;
case JAVA_TYPE_BYTE_ARRAY:
fprintf(out,
- " stats_event_write_byte_array(event, "
+ " AStatsEvent_writeByteArray(event, "
"reinterpret_cast<const uint8_t*>(arg%d.arg), arg%d.arg_length);\n",
argIndex, argIndex);
break;
case JAVA_TYPE_BOOLEAN:
- fprintf(out, " stats_event_write_bool(event, arg%d);\n", argIndex);
+ fprintf(out, " AStatsEvent_writeBool(event, arg%d);\n", argIndex);
break;
case JAVA_TYPE_INT: // Fall through.
case JAVA_TYPE_ENUM:
- fprintf(out, " stats_event_write_int32(event, arg%d);\n", argIndex);
+ fprintf(out, " AStatsEvent_writeInt32(event, arg%d);\n", argIndex);
break;
case JAVA_TYPE_FLOAT:
- fprintf(out, " stats_event_write_float(event, arg%d);\n", argIndex);
+ fprintf(out, " AStatsEvent_writeFloat(event, arg%d);\n", argIndex);
break;
case JAVA_TYPE_LONG:
- fprintf(out, " stats_event_write_int64(event, arg%d);\n", argIndex);
+ fprintf(out, " AStatsEvent_writeInt64(event, arg%d);\n", argIndex);
break;
case JAVA_TYPE_STRING:
- fprintf(out, " stats_event_write_string8(event, arg%d);\n", argIndex);
+ fprintf(out, " AStatsEvent_writeString(event, arg%d);\n", argIndex);
break;
default:
- // Unsupported types: OBJECT, DOUBLE.
+ // Unsupported types: OBJECT, DOUBLE, KEY_VALUE_PAIRS
fprintf(stderr, "Encountered unsupported type.");
return 1;
}
argIndex++;
}
- fprintf(out, " const int ret = stats_event_write(event);\n");
- fprintf(out, " stats_event_release(event);\n");
+ fprintf(out, " const int ret = AStatsEvent_write(event);\n");
+ fprintf(out, " AStatsEvent_release(event);\n");
fprintf(out, " return ret;\n");
}
fprintf(out, "}\n\n");
@@ -169,6 +144,10 @@
continue;
}
vector<java_type_t> signature = signature_it->first;
+ // Key value pairs not supported in native.
+ if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) {
+ continue;
+ }
write_native_method_signature(out, "int stats_write_non_chained", signature,
attributionDecl, " {");
@@ -210,8 +189,14 @@
if (!signature_needed_for_module(signature_to_modules_it->second, moduleName)) {
continue;
}
-
vector<java_type_t> signature = signature_to_modules_it->first;
+
+#if !defined(STATS_SCHEMA_LEGACY)
+ // Key value pairs not supported in native.
+ if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) {
+ continue;
+ }
+#endif
write_native_method_signature(out, methodName, signature, attributionDecl, ";");
}
}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 91b7df3..62d6067 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -568,14 +568,12 @@
* 2GHz band.
* @hide
*/
- @SystemApi
public static final int AP_BAND_2GHZ = 0;
/**
* 5GHz band.
* @hide
*/
- @SystemApi
public static final int AP_BAND_5GHZ = 1;
/**
@@ -583,7 +581,6 @@
* operating country code and current radio conditions.
* @hide
*/
- @SystemApi
public static final int AP_BAND_ANY = -1;
/**
@@ -593,7 +590,7 @@
*
* @hide
*/
- @SystemApi
+ @UnsupportedAppUsage
@ApBand
public int apBand = AP_BAND_2GHZ;
diff --git a/wifi/java/android/net/wifi/WifiFrameworkInitializer.java b/wifi/java/android/net/wifi/WifiFrameworkInitializer.java
index 002820b..1507199 100644
--- a/wifi/java/android/net/wifi/WifiFrameworkInitializer.java
+++ b/wifi/java/android/net/wifi/WifiFrameworkInitializer.java
@@ -102,15 +102,6 @@
}
);
SystemServiceRegistry.registerContextAwareService(
- Context.WIFI_RTT_SERVICE,
- RttManager.class,
- (context, serviceBinder) -> {
- IWifiRttManager service = IWifiRttManager.Stub.asInterface(serviceBinder);
- WifiRttManager wifiRttManager = new WifiRttManager(context, service);
- return new RttManager(context, wifiRttManager);
- }
- );
- SystemServiceRegistry.registerContextAwareService(
Context.WIFI_RTT_RANGING_SERVICE,
WifiRttManager.class,
(context, serviceBinder) -> {
@@ -118,5 +109,13 @@
return new WifiRttManager(context, service);
}
);
+ SystemServiceRegistry.registerContextAwareService(
+ Context.WIFI_RTT_SERVICE,
+ RttManager.class,
+ context -> {
+ WifiRttManager wifiRttManager = context.getSystemService(WifiRttManager.class);
+ return new RttManager(context, wifiRttManager);
+ }
+ );
}
}