Merge "Update the binder calls atom to track the calling app of a binder call."
diff --git a/Android.bp b/Android.bp
index 54b5760..56d6557 100644
--- a/Android.bp
+++ b/Android.bp
@@ -235,8 +235,7 @@
"core/java/android/os/IDeviceIdentifiersPolicyService.aidl",
"core/java/android/os/IDeviceIdleController.aidl",
"core/java/android/os/IHardwarePropertiesManager.aidl",
- "core/java/android/os/IIncidentManager.aidl",
- "core/java/android/os/IIncidentReportStatusListener.aidl",
+ ":libincident_aidl",
"core/java/android/os/IMaintenanceActivityListener.aidl",
"core/java/android/os/IMessenger.aidl",
"core/java/android/os/INetworkActivityListener.aidl",
@@ -249,8 +248,7 @@
"core/java/android/os/IRecoverySystemProgressListener.aidl",
"core/java/android/os/IRemoteCallback.aidl",
"core/java/android/os/ISchedulingPolicyService.aidl",
- "core/java/android/os/IStatsCompanionService.aidl",
- "core/java/android/os/IStatsManager.aidl",
+ ":statsd_aidl",
"core/java/android/os/ISystemUpdateManager.aidl",
"core/java/android/os/IThermalEventListener.aidl",
"core/java/android/os/IThermalService.aidl",
@@ -724,6 +722,22 @@
}
+filegroup {
+ name: "libincident_aidl",
+ srcs: [
+ "core/java/android/os/IIncidentManager.aidl",
+ "core/java/android/os/IIncidentReportStatusListener.aidl",
+ ],
+}
+
+filegroup {
+ name: "statsd_aidl",
+ srcs: [
+ "core/java/android/os/IStatsCompanionService.aidl",
+ "core/java/android/os/IStatsManager.aidl",
+ ],
+}
+
java_library {
name: "framework",
defaults: ["framework-defaults"],
@@ -934,12 +948,14 @@
"core/proto/android/os/batterytype.proto",
"core/proto/android/os/cpufreq.proto",
"core/proto/android/os/cpuinfo.proto",
+ "core/proto/android/os/data.proto",
"core/proto/android/os/kernelwake.proto",
"core/proto/android/os/pagetypeinfo.proto",
"core/proto/android/os/procrank.proto",
"core/proto/android/os/ps.proto",
"core/proto/android/os/system_properties.proto",
"core/proto/android/util/event_log_tags.proto",
+ "core/proto/android/util/log.proto",
],
// Append protoc-gen-cppstream tool's PATH otherwise aprotoc can't find the plugin tool
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index e224fa3..35d3802 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -245,10 +245,11 @@
state.pauseTiming();
final StaticLayout layout =
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
- final RecordingCanvas c = node.start(1200, 200);
+ final RecordingCanvas c = node.startRecording(1200, 200);
state.resumeTiming();
layout.draw(c);
+ node.endRecording();
}
}
@@ -261,10 +262,11 @@
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
final StaticLayout layout =
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
- final RecordingCanvas c = node.start(1200, 200);
+ final RecordingCanvas c = node.startRecording(1200, 200);
state.resumeTiming();
layout.draw(c);
+ node.endRecording();
}
}
@@ -277,10 +279,11 @@
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
final StaticLayout layout =
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
- final RecordingCanvas c = node.start(1200, 200);
+ final RecordingCanvas c = node.startRecording(1200, 200);
state.resumeTiming();
layout.draw(c);
+ node.endRecording();
}
}
@@ -293,11 +296,12 @@
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
final StaticLayout layout =
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
- final RecordingCanvas c = node.start(1200, 200);
+ final RecordingCanvas c = node.startRecording(1200, 200);
Canvas.freeTextLayoutCaches();
state.resumeTiming();
layout.draw(c);
+ node.endRecording();
}
}
@@ -310,11 +314,12 @@
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
final StaticLayout layout =
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
- final RecordingCanvas c = node.start(1200, 200);
+ final RecordingCanvas c = node.startRecording(1200, 200);
Canvas.freeTextLayoutCaches();
state.resumeTiming();
layout.draw(c);
+ node.endRecording();
}
}
@@ -328,10 +333,11 @@
mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
final StaticLayout layout =
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
- final RecordingCanvas c = node.start(1200, 200);
+ final RecordingCanvas c = node.startRecording(1200, 200);
state.resumeTiming();
layout.draw(c);
+ node.endRecording();
}
}
@@ -345,10 +351,11 @@
mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
final StaticLayout layout =
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
- final RecordingCanvas c = node.start(1200, 200);
+ final RecordingCanvas c = node.startRecording(1200, 200);
state.resumeTiming();
layout.draw(c);
+ node.endRecording();
}
}
@@ -362,11 +369,12 @@
mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
final StaticLayout layout =
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
- final RecordingCanvas c = node.start(1200, 200);
+ final RecordingCanvas c = node.startRecording(1200, 200);
Canvas.freeTextLayoutCaches();
state.resumeTiming();
layout.draw(c);
+ node.endRecording();
}
}
@@ -380,11 +388,12 @@
mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
final StaticLayout layout =
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
- final RecordingCanvas c = node.start(1200, 200);
+ final RecordingCanvas c = node.startRecording(1200, 200);
Canvas.freeTextLayoutCaches();
state.resumeTiming();
layout.draw(c);
+ node.endRecording();
}
}
diff --git a/api/current.txt b/api/current.txt
index 02070c5..f27e83c 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -42607,6 +42607,7 @@
field public static final java.lang.String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
field public static final java.lang.String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
field public static final java.lang.String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool";
+ field public static final java.lang.String KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL = "support_clir_network_default_bool";
field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
field public static final java.lang.String KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL = "support_emergency_sms_over_ims_bool";
field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
@@ -42713,6 +42714,7 @@
field public static final int CONNECTION_SECONDARY_SERVING = 2; // 0x2
field public static final int CONNECTION_UNKNOWN = 2147483647; // 0x7fffffff
field public static final android.os.Parcelable.Creator<android.telephony.CellInfo> CREATOR;
+ field public static final int UNAVAILABLE = 2147483647; // 0x7fffffff
}
public final class CellInfoCdma extends android.telephony.CellInfo implements android.os.Parcelable {
@@ -51427,6 +51429,99 @@
package android.view.textclassifier {
+ public final class ConversationActions implements android.os.Parcelable {
+ ctor public ConversationActions(java.util.List<android.view.textclassifier.ConversationActions.ConversationAction>);
+ method public int describeContents();
+ method public java.util.List<android.view.textclassifier.ConversationActions.ConversationAction> getConversationActions();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions> CREATOR;
+ field public static final java.lang.String HINT_FOR_IN_APP = "in_app";
+ field public static final java.lang.String HINT_FOR_NOTIFICATION = "notification";
+ field public static final java.lang.String TYPE_CALL_PHONE = "call_phone";
+ field public static final java.lang.String TYPE_CREATE_REMINDER = "create_reminder";
+ field public static final java.lang.String TYPE_OPEN_URL = "open_url";
+ field public static final java.lang.String TYPE_SEND_EMAIL = "send_email";
+ field public static final java.lang.String TYPE_SEND_SMS = "send_sms";
+ field public static final java.lang.String TYPE_SHARE_LOCATION = "share_location";
+ field public static final java.lang.String TYPE_TEXT_REPLY = "text_reply";
+ field public static final java.lang.String TYPE_TRACK_FLIGHT = "track_flight";
+ field public static final java.lang.String TYPE_VIEW_CALENDAR = "view_calendar";
+ field public static final java.lang.String TYPE_VIEW_MAP = "view_map";
+ }
+
+ public static final class ConversationActions.ConversationAction implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.app.RemoteAction getAction();
+ method public float getConfidenceScore();
+ method public android.os.Bundle getExtras();
+ method public java.lang.CharSequence getTextReply();
+ method public java.lang.String getType();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.ConversationAction> CREATOR;
+ }
+
+ public static final class ConversationActions.ConversationAction.Builder {
+ ctor public ConversationActions.ConversationAction.Builder(java.lang.String);
+ method public android.view.textclassifier.ConversationActions.ConversationAction build();
+ method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setAction(android.app.RemoteAction);
+ method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setConfidenceScore(float);
+ method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setExtras(android.os.Bundle);
+ method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setTextReply(java.lang.CharSequence);
+ }
+
+ public static final class ConversationActions.Message implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.app.Person getAuthor();
+ method public android.os.Bundle getExtras();
+ method public java.lang.CharSequence getText();
+ method public java.time.ZonedDateTime getTime();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.Message> CREATOR;
+ }
+
+ public static final class ConversationActions.Message.Builder {
+ ctor public ConversationActions.Message.Builder();
+ method public android.view.textclassifier.ConversationActions.Message build();
+ method public android.view.textclassifier.ConversationActions.Message.Builder setAuthor(android.app.Person);
+ method public android.view.textclassifier.ConversationActions.Message.Builder setComposeTime(java.time.ZonedDateTime);
+ method public android.view.textclassifier.ConversationActions.Message.Builder setExtras(android.os.Bundle);
+ method public android.view.textclassifier.ConversationActions.Message.Builder setText(java.lang.CharSequence);
+ }
+
+ public static final class ConversationActions.Request implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.util.List<android.view.textclassifier.ConversationActions.Message> getConversation();
+ method public java.util.List<java.lang.String> getHints();
+ method public int getMaxSuggestions();
+ method public android.view.textclassifier.ConversationActions.TypeConfig getTypeConfig();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.Request> CREATOR;
+ }
+
+ public static final class ConversationActions.Request.Builder {
+ ctor public ConversationActions.Request.Builder(java.util.List<android.view.textclassifier.ConversationActions.Message>);
+ method public android.view.textclassifier.ConversationActions.Request build();
+ method public android.view.textclassifier.ConversationActions.Request.Builder setHints(java.util.List<java.lang.String>);
+ method public android.view.textclassifier.ConversationActions.Request.Builder setMaxSuggestions(int);
+ method public android.view.textclassifier.ConversationActions.Request.Builder setTypeConfig(android.view.textclassifier.ConversationActions.TypeConfig);
+ }
+
+ public static final class ConversationActions.TypeConfig implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.util.Collection<java.lang.String> resolveTypes(java.util.Collection<java.lang.String>);
+ method public boolean shouldIncludeTypesFromTextClassifier();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.TypeConfig> CREATOR;
+ }
+
+ public static final class ConversationActions.TypeConfig.Builder {
+ ctor public ConversationActions.TypeConfig.Builder();
+ method public android.view.textclassifier.ConversationActions.TypeConfig build();
+ method public android.view.textclassifier.ConversationActions.TypeConfig.Builder includeTypesFromTextClassifier(boolean);
+ method public android.view.textclassifier.ConversationActions.TypeConfig.Builder setExcludedTypes(java.util.Collection<java.lang.String>);
+ method public android.view.textclassifier.ConversationActions.TypeConfig.Builder setIncludedTypes(java.util.Collection<java.lang.String>);
+ }
+
public final class SelectionEvent implements android.os.Parcelable {
method public static android.view.textclassifier.SelectionEvent createSelectionActionEvent(int, int, int);
method public static android.view.textclassifier.SelectionEvent createSelectionActionEvent(int, int, int, android.view.textclassifier.TextClassification);
@@ -51481,6 +51576,7 @@
method public float getConfidenceScore(java.lang.String);
method public java.lang.String getEntity(int);
method public int getEntityCount();
+ method public android.os.Bundle getExtras();
method public deprecated android.graphics.drawable.Drawable getIcon();
method public java.lang.String getId();
method public deprecated android.content.Intent getIntent();
@@ -51496,6 +51592,7 @@
method public android.view.textclassifier.TextClassification.Builder addAction(android.app.RemoteAction);
method public android.view.textclassifier.TextClassification build();
method public android.view.textclassifier.TextClassification.Builder setEntityType(java.lang.String, float);
+ method public android.view.textclassifier.TextClassification.Builder setExtras(android.os.Bundle);
method public deprecated android.view.textclassifier.TextClassification.Builder setIcon(android.graphics.drawable.Drawable);
method public android.view.textclassifier.TextClassification.Builder setId(java.lang.String);
method public deprecated android.view.textclassifier.TextClassification.Builder setIntent(android.content.Intent);
@@ -51508,6 +51605,7 @@
method public int describeContents();
method public android.os.LocaleList getDefaultLocales();
method public int getEndIndex();
+ method public android.os.Bundle getExtras();
method public java.time.ZonedDateTime getReferenceTime();
method public int getStartIndex();
method public java.lang.CharSequence getText();
@@ -51519,6 +51617,7 @@
ctor public TextClassification.Request.Builder(java.lang.CharSequence, int, int);
method public android.view.textclassifier.TextClassification.Request build();
method public android.view.textclassifier.TextClassification.Request.Builder setDefaultLocales(android.os.LocaleList);
+ method public android.view.textclassifier.TextClassification.Request.Builder setExtras(android.os.Bundle);
method public android.view.textclassifier.TextClassification.Request.Builder setReferenceTime(java.time.ZonedDateTime);
}
@@ -51563,6 +51662,7 @@
method public default int getMaxGenerateLinksTextLength();
method public default boolean isDestroyed();
method public default void onSelectionEvent(android.view.textclassifier.SelectionEvent);
+ method public default android.view.textclassifier.ConversationActions suggestConversationActions(android.view.textclassifier.ConversationActions.Request);
method public default android.view.textclassifier.TextSelection suggestSelection(android.view.textclassifier.TextSelection.Request);
method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList);
field public static final java.lang.String HINT_TEXT_IS_EDITABLE = "android.text_is_editable";
diff --git a/api/system-current.txt b/api/system-current.txt
index 7f58330..b1cbfa6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -93,6 +93,7 @@
field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
field public static final java.lang.String LOCK_DEVICE = "android.permission.LOCK_DEVICE";
field public static final java.lang.String LOOP_RADIO = "android.permission.LOOP_RADIO";
+ field public static final java.lang.String MANAGE_ACCESSIBILITY = "android.permission.MANAGE_ACCESSIBILITY";
field public static final java.lang.String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
field public static final java.lang.String MANAGE_APP_OPS_RESTRICTIONS = "android.permission.MANAGE_APP_OPS_RESTRICTIONS";
field public static final java.lang.String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS";
@@ -870,9 +871,11 @@
method public int getAppStandbyBucket(java.lang.String);
method public java.util.Map<java.lang.String, java.lang.Integer> getAppStandbyBuckets();
method public void registerAppUsageObserver(int, java.lang.String[], long, java.util.concurrent.TimeUnit, android.app.PendingIntent);
+ method public void registerUsageSessionObserver(int, java.lang.String[], long, java.util.concurrent.TimeUnit, long, java.util.concurrent.TimeUnit, android.app.PendingIntent, android.app.PendingIntent);
method public void setAppStandbyBucket(java.lang.String, int);
method public void setAppStandbyBuckets(java.util.Map<java.lang.String, java.lang.Integer>);
method public void unregisterAppUsageObserver(int);
+ method public void unregisterUsageSessionObserver(int);
method public void whitelistAppTemporarily(java.lang.String, long, android.os.UserHandle);
field public static final java.lang.String EXTRA_OBSERVER_ID = "android.app.usage.extra.OBSERVER_ID";
field public static final java.lang.String EXTRA_TIME_LIMIT = "android.app.usage.extra.TIME_LIMIT";
@@ -6830,6 +6833,14 @@
}
+package android.view.accessibility {
+
+ public final class AccessibilityManager {
+ method public void performAccessibilityShortcut();
+ }
+
+}
+
package android.webkit {
public abstract class CookieManager {
diff --git a/api/test-current.txt b/api/test-current.txt
index 8f08c71..d453395 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -960,6 +960,7 @@
public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
field public static final java.lang.String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled";
+ field public static final java.lang.String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE = "accessibility_shortcut_target_service";
field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
field public static final java.lang.String AUTOFILL_SERVICE = "autofill_service";
field public static final java.lang.String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT = "autofill_user_data_max_category_count";
@@ -1611,6 +1612,17 @@
package android.view.accessibility {
+ public final class AccessibilityManager {
+ method public void addAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener, android.os.Handler);
+ method public java.lang.String getAccessibilityShortcutService();
+ method public void performAccessibilityShortcut();
+ method public void removeAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
+ }
+
+ public static abstract interface AccessibilityManager.AccessibilityServicesStateChangeListener {
+ method public abstract void onAccessibilityServicesStateChanged(android.view.accessibility.AccessibilityManager);
+ }
+
public class AccessibilityNodeInfo implements android.os.Parcelable {
method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
method public void writeToParcelNoRecycle(android.os.Parcel, int);
diff --git a/cmds/incident/Android.bp b/cmds/incident/Android.bp
new file mode 100644
index 0000000..2a5ec5b
--- /dev/null
+++ b/cmds/incident/Android.bp
@@ -0,0 +1,46 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_binary {
+ name: "incident",
+
+ srcs: [
+ "main.cpp",
+ ":incident_sections",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "liblog",
+ "libutils",
+ "libincident",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-missing-field-initializers",
+ "-Wno-unused-variable",
+ "-Wunused-parameter",
+ ],
+}
+
+genrule {
+ name: "incident_sections",
+ tools: ["incident-section-gen"],
+ out: ["incident_sections.cpp"],
+ cmd: "$(location incident-section-gen) incident > $(out)",
+}
diff --git a/cmds/incident/Android.mk b/cmds/incident/Android.mk
deleted file mode 100644
index 8615f9b..0000000
--- a/cmds/incident/Android.mk
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- main.cpp
-
-LOCAL_MODULE := incident
-
-LOCAL_SHARED_LIBRARIES := \
- libbase \
- libbinder \
- libcutils \
- liblog \
- libutils \
- libincident
-
-LOCAL_CFLAGS += \
- -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
-
-LOCAL_MODULE_CLASS := EXECUTABLES
-gen_src_dir := $(local-generated-sources-dir)
-
-gen := $(gen_src_dir)/incident_sections.cpp
-$(gen): $(HOST_OUT_EXECUTABLES)/incident-section-gen
-$(gen): PRIVATE_CUSTOM_TOOL = \
- $(HOST_OUT_EXECUTABLES)/incident-section-gen incident > $@
-$(gen): $(HOST_OUT_EXECUTABLES)/incident-section-gen
- $(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(gen)
-
-gen_src_dir:=
-gen:=
-
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/incidentd/Android.bp b/cmds/incidentd/Android.bp
new file mode 100644
index 0000000..1e970f4
--- /dev/null
+++ b/cmds/incidentd/Android.bp
@@ -0,0 +1,117 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// =========
+// incidentd
+// =========
+
+cc_binary {
+ name: "incidentd",
+
+ srcs: [
+ "src/**/*.cpp",
+ ":incidentd_section_list",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-missing-field-initializers",
+ "-Wno-unused-variable",
+ "-Wunused-parameter",
+
+ // Allow implicit fallthrough in IncidentService.cpp:85 until it is fixed.
+ "-Wno-error=implicit-fallthrough",
+
+ // optimize for size (protobuf glop can get big)
+ "-Os",
+ //"-g",
+ //"-O0",
+ ],
+
+ local_include_dirs: ["src"],
+ generated_headers: ["gen-platform-proto-constants"],
+
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libdebuggerd_client",
+ "libdumputils",
+ "libincident",
+ "liblog",
+ "libprotoutil",
+ "libservices",
+ "libutils",
+ ],
+
+ init_rc: ["incidentd.rc"],
+}
+
+// ==============
+// incidentd_test
+// ==============
+
+cc_test {
+ name: "incidentd_test",
+ test_suites: ["device-tests"],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ "-Wno-unused-variable",
+ "-Wunused-parameter",
+
+ // Allow implicit fallthrough in IncidentService.cpp:85 until it is fixed.
+ "-Wno-error=implicit-fallthrough",
+ ],
+
+ local_include_dirs: ["src"],
+ generated_headers: ["gen-platform-proto-constants"],
+
+ srcs: [
+ "tests/**/*.cpp",
+ "src/PrivacyBuffer.cpp",
+ "src/FdBuffer.cpp",
+ "src/Privacy.cpp",
+ "src/Reporter.cpp",
+ "src/Section.cpp",
+ "src/Throttler.cpp",
+ "src/incidentd_util.cpp",
+ "src/report_directory.cpp",
+ ],
+
+ data: ["testdata/**/*"],
+
+ static_libs: ["libgmock"],
+
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libdebuggerd_client",
+ "libdumputils",
+ "libincident",
+ "liblog",
+ "libprotobuf-cpp-lite",
+ "libprotoutil",
+ "libservices",
+ "libutils",
+ ],
+}
+
+genrule {
+ name: "incidentd_section_list",
+ tools: ["incident-section-gen"],
+ out: ["section_list.cpp"],
+ cmd: "$(location incident-section-gen) incidentd > $(out)",
+}
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
deleted file mode 100644
index eba5586..0000000
--- a/cmds/incidentd/Android.mk
+++ /dev/null
@@ -1,156 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-# proto files used in incidentd to generate cppstream proto headers.
-PROTO_FILES:= \
- frameworks/base/core/proto/android/os/backtrace.proto \
- frameworks/base/core/proto/android/os/data.proto \
- frameworks/base/core/proto/android/util/log.proto
-
-# ========= #
-# incidentd #
-# ========= #
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := incidentd
-
-LOCAL_SRC_FILES := $(call all-cpp-files-under, src) \
-
-LOCAL_CFLAGS += \
- -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
-
-# Allow implicit fallthrough in IncidentService.cpp:85 until it is fixed.
-LOCAL_CFLAGS += -Wno-error=implicit-fallthrough
-
-ifeq (debug,)
- LOCAL_CFLAGS += \
- -g -O0
-else
- # optimize for size (protobuf glop can get big)
- LOCAL_CFLAGS += \
- -Os
-endif
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
-
-LOCAL_SHARED_LIBRARIES := \
- libbase \
- libbinder \
- libdebuggerd_client \
- libdumputils \
- libincident \
- liblog \
- libprotoutil \
- libservices \
- libutils
-
-LOCAL_MODULE_CLASS := EXECUTABLES
-
-gen_src_dir := $(local-generated-sources-dir)
-
-# generate section_list.cpp
-GEN_LIST := $(gen_src_dir)/src/section_list.cpp
-$(GEN_LIST): $(HOST_OUT_EXECUTABLES)/incident-section-gen
-$(GEN_LIST): PRIVATE_CUSTOM_TOOL = \
- $(HOST_OUT_EXECUTABLES)/incident-section-gen incidentd > $@
-$(GEN_LIST): $(HOST_OUT_EXECUTABLES)/incident-section-gen
- $(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN_LIST)
-GEN_LIST:=
-
-# generate cppstream proto, add proto files to PROTO_FILES
-GEN_PROTO := $(gen_src_dir)/proto.timestamp
-$(GEN_PROTO): $(HOST_OUT_EXECUTABLES)/aprotoc $(HOST_OUT_EXECUTABLES)/protoc-gen-cppstream $(PROTO_FILES)
-$(GEN_PROTO): PRIVATE_GEN_SRC_DIR := $(gen_src_dir)
-$(GEN_PROTO): PRIVATE_CUSTOM_TOOL = \
- $(HOST_OUT_EXECUTABLES)/aprotoc --plugin=protoc-gen-cppstream=$(HOST_OUT_EXECUTABLES)/protoc-gen-cppstream \
- --cppstream_out=$(PRIVATE_GEN_SRC_DIR) -Iexternal/protobuf/src -I . \
- $(PROTO_FILES) \
- && touch $@
-$(GEN_PROTO): $(HOST_OUT_EXECUTABLES)/aprotoc
- $(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN_PROTO)
-GEN_PROTO:=
-
-gen_src_dir:=
-
-LOCAL_INIT_RC := incidentd.rc
-
-include $(BUILD_EXECUTABLE)
-
-# ============== #
-# incidentd_test #
-# ============== #
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := incidentd_test
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_CFLAGS := -Werror -Wall -Wno-unused-variable -Wunused-parameter
-
-# Allow implicit fallthrough in IncidentService.cpp:85 until it is fixed.
-LOCAL_CFLAGS += -Wno-error=implicit-fallthrough
-
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
-
-LOCAL_SRC_FILES := $(call all-cpp-files-under, tests) \
- src/PrivacyBuffer.cpp \
- src/FdBuffer.cpp \
- src/Privacy.cpp \
- src/Reporter.cpp \
- src/Section.cpp \
- src/Throttler.cpp \
- src/incidentd_util.cpp \
- src/report_directory.cpp \
-
-LOCAL_STATIC_LIBRARIES := \
- libgmock \
-
-LOCAL_SHARED_LIBRARIES := \
- libbase \
- libbinder \
- libdebuggerd_client \
- libdumputils \
- libincident \
- liblog \
- libprotobuf-cpp-lite \
- libprotoutil \
- libservices \
- libutils \
-
-LOCAL_TEST_DATA := $(call find-test-data-in-subdirs, $(LOCAL_PATH), *, testdata)
-
-LOCAL_MODULE_CLASS := NATIVE_TESTS
-gen_src_dir := $(local-generated-sources-dir)
-# generate cppstream proto for testing
-GEN_PROTO := $(gen_src_dir)/test.proto.timestamp
-$(GEN_PROTO): $(HOST_OUT_EXECUTABLES)/aprotoc $(HOST_OUT_EXECUTABLES)/protoc-gen-cppstream $(PROTO_FILES)
-$(GEN_PROTO): PRIVATE_GEN_SRC_DIR := $(gen_src_dir)
-$(GEN_PROTO): PRIVATE_CUSTOM_TOOL = \
- $(HOST_OUT_EXECUTABLES)/aprotoc --plugin=protoc-gen-cppstream=$(HOST_OUT_EXECUTABLES)/protoc-gen-cppstream \
- --cppstream_out=$(PRIVATE_GEN_SRC_DIR) -Iexternal/protobuf/src -I . \
- $(PROTO_FILES) \
- && touch $@
-$(GEN_PROTO): $(HOST_OUT_EXECUTABLES)/aprotoc
- $(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN_PROTO)
-GEN_PROTO:=
-
-gen_src_dir:=
-
-include $(BUILD_NATIVE_TEST)
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 94203f4f..02dde5a 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -1,5 +1,5 @@
//
-// Copyright (C) 2015 The Android Open Source Project
+// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -42,6 +42,277 @@
}
+cc_defaults {
+ name: "statsd_defaults",
+ aidl: {
+ include_dirs: ["frameworks/base/core/java"],
+ },
+
+ srcs: [
+ ":statsd_aidl",
+ "src/statsd_config.proto",
+ "src/FieldValue.cpp",
+ "src/hash.cpp",
+ "src/stats_log_util.cpp",
+ "src/anomaly/AlarmMonitor.cpp",
+ "src/anomaly/AlarmTracker.cpp",
+ "src/anomaly/AnomalyTracker.cpp",
+ "src/anomaly/DurationAnomalyTracker.cpp",
+ "src/anomaly/subscriber_util.cpp",
+ "src/condition/CombinationConditionTracker.cpp",
+ "src/condition/condition_util.cpp",
+ "src/condition/SimpleConditionTracker.cpp",
+ "src/condition/ConditionWizard.cpp",
+ "src/condition/StateTracker.cpp",
+ "src/config/ConfigKey.cpp",
+ "src/config/ConfigListener.cpp",
+ "src/config/ConfigManager.cpp",
+ "src/external/Perfetto.cpp",
+ "src/external/Perfprofd.cpp",
+ "src/external/StatsPuller.cpp",
+ "src/external/StatsCompanionServicePuller.cpp",
+ "src/external/SubsystemSleepStatePuller.cpp",
+ "src/external/ResourceHealthManagerPuller.cpp",
+ "src/external/ResourceThermalManagerPuller.cpp",
+ "src/external/StatsPullerManager.cpp",
+ "src/external/puller_util.cpp",
+ "src/logd/LogEvent.cpp",
+ "src/logd/LogListener.cpp",
+ "src/matchers/CombinationLogMatchingTracker.cpp",
+ "src/matchers/EventMatcherWizard.cpp",
+ "src/matchers/matcher_util.cpp",
+ "src/matchers/SimpleLogMatchingTracker.cpp",
+ "src/metrics/MetricProducer.cpp",
+ "src/metrics/EventMetricProducer.cpp",
+ "src/metrics/CountMetricProducer.cpp",
+ "src/metrics/DurationMetricProducer.cpp",
+ "src/metrics/duration_helper/OringDurationTracker.cpp",
+ "src/metrics/duration_helper/MaxDurationTracker.cpp",
+ "src/metrics/ValueMetricProducer.cpp",
+ "src/metrics/GaugeMetricProducer.cpp",
+ "src/metrics/MetricsManager.cpp",
+ "src/metrics/metrics_manager_util.cpp",
+ "src/packages/UidMap.cpp",
+ "src/storage/StorageManager.cpp",
+ "src/StatsLogProcessor.cpp",
+ "src/StatsService.cpp",
+ "src/statscompanion_util.cpp",
+ "src/subscriber/IncidentdReporter.cpp",
+ "src/subscriber/SubscriberReporter.cpp",
+ "src/HashableDimensionKey.cpp",
+ "src/guardrail/StatsdStats.cpp",
+ "src/socket/StatsSocketListener.cpp",
+ "src/shell/ShellSubscriber.cpp",
+ "src/shell/shell_config.proto",
+
+ ":perfprofd_aidl",
+ ],
+
+ local_include_dirs: [
+ "src",
+ ],
+
+ static_libs: [
+ "libhealthhalutils",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libincident",
+ "liblog",
+ "libutils",
+ "libservices",
+ "libprotoutil",
+ "libstatslog",
+ "libhardware",
+ "libhardware_legacy",
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "android.frameworks.stats@1.0",
+ "android.hardware.health@2.0",
+ "android.hardware.power@1.0",
+ "android.hardware.power@1.1",
+ "android.hardware.thermal@1.0",
+ "libpackagelistparser",
+ "libsysutils",
+ "libcutils",
+ ],
+}
+
+// =========
+// statsd
+// =========
+
+cc_binary {
+ name: "statsd",
+ defaults: ["statsd_defaults"],
+
+ srcs: ["src/main.cpp"],
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wno-unused-parameter",
+ // optimize for size (protobuf glop can get big)
+ "-Os",
+ // "-g",
+ // "-O0",
+ ],
+
+ product_variables: {
+ eng: {
+ // Enable sanitizer ONLY on eng builds
+ //sanitize: {
+ // address: true,
+ //},
+ },
+ debuggable: {
+ // Add a flag to enable stats log printing from statsd on debug builds.
+ cflags: ["-DVERY_VERBOSE_PRINTING"],
+ },
+ },
+
+ proto: {
+ type: "lite",
+ },
+
+ shared_libs: ["libgtest_prod"],
+
+ init_rc: ["statsd.rc"],
+}
+
+// ==============
+// statsd_test
+// ==============
+
+cc_test {
+ name: "statsd_test",
+ defaults: ["statsd_defaults"],
+ test_suites: ["device-tests"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-missing-field-initializers",
+ "-Wno-unused-variable",
+ "-Wno-unused-function",
+ "-Wno-unused-parameter",
+ ],
+
+ srcs: [
+ "src/atom_field_options.proto",
+ "src/atoms.proto",
+ "src/stats_log.proto",
+ "src/shell/shell_data.proto",
+ "tests/AlarmMonitor_test.cpp",
+ "tests/anomaly/AlarmTracker_test.cpp",
+ "tests/anomaly/AnomalyTracker_test.cpp",
+ "tests/ConfigManager_test.cpp",
+ "tests/external/puller_util_test.cpp",
+ "tests/indexed_priority_queue_test.cpp",
+ "tests/LogEntryMatcher_test.cpp",
+ "tests/LogEvent_test.cpp",
+ "tests/MetricsManager_test.cpp",
+ "tests/StatsLogProcessor_test.cpp",
+ "tests/StatsService_test.cpp",
+ "tests/UidMap_test.cpp",
+ "tests/FieldValue_test.cpp",
+ "tests/condition/CombinationConditionTracker_test.cpp",
+ "tests/condition/SimpleConditionTracker_test.cpp",
+ "tests/condition/StateTracker_test.cpp",
+ "tests/metrics/OringDurationTracker_test.cpp",
+ "tests/metrics/MaxDurationTracker_test.cpp",
+ "tests/metrics/CountMetricProducer_test.cpp",
+ "tests/metrics/DurationMetricProducer_test.cpp",
+ "tests/metrics/EventMetricProducer_test.cpp",
+ "tests/metrics/ValueMetricProducer_test.cpp",
+ "tests/metrics/GaugeMetricProducer_test.cpp",
+ "tests/guardrail/StatsdStats_test.cpp",
+ "tests/metrics/metrics_test_helper.cpp",
+ "tests/statsd_test_util.cpp",
+ "tests/e2e/WakelockDuration_e2e_test.cpp",
+ "tests/e2e/MetricActivation_e2e_test.cpp",
+ "tests/e2e/MetricConditionLink_e2e_test.cpp",
+ "tests/e2e/Alarm_e2e_test.cpp",
+ "tests/e2e/Attribution_e2e_test.cpp",
+ "tests/e2e/GaugeMetric_e2e_push_test.cpp",
+ "tests/e2e/GaugeMetric_e2e_pull_test.cpp",
+ "tests/e2e/ValueMetric_pull_e2e_test.cpp",
+ "tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp",
+ "tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp",
+ "tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp",
+ "tests/e2e/Anomaly_count_e2e_test.cpp",
+ "tests/e2e/Anomaly_duration_sum_e2e_test.cpp",
+ "tests/e2e/ConfigTtl_e2e_test.cpp",
+ "tests/e2e/PartialBucket_e2e_test.cpp",
+ "tests/shell/ShellSubscriber_test.cpp",
+ ],
+
+ static_libs: [
+ "libgmock",
+ "libplatformprotos",
+ ],
+
+ proto: {
+ type: "full",
+ include_dirs: ["external/protobuf/src"],
+ },
+
+ shared_libs: ["libprotobuf-cpp-full"],
+
+}
+
+//#############################
+// statsd micro benchmark
+//#############################
+
+cc_benchmark {
+ name: "statsd_benchmark",
+ defaults: ["statsd_defaults"],
+
+ srcs: [
+ "src/atom_field_options.proto",
+ "src/atoms.proto",
+ "src/stats_log.proto",
+ "benchmark/main.cpp",
+ "benchmark/hello_world_benchmark.cpp",
+ "benchmark/log_event_benchmark.cpp",
+ "benchmark/stats_write_benchmark.cpp",
+ "benchmark/filter_value_benchmark.cpp",
+ "benchmark/get_dimensions_for_condition_benchmark.cpp",
+ "benchmark/metric_util.cpp",
+ "benchmark/duration_metric_benchmark.cpp",
+ ],
+
+ proto: {
+ type: "full",
+ include_dirs: ["external/protobuf/src"],
+ },
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ "-Wno-unused-variable",
+ "-Wno-unused-function",
+
+ // Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374
+ "-Wno-varargs"
+ ],
+
+ static_libs: [
+ "libplatformprotos",
+ ],
+
+ shared_libs: [
+ "libgtest_prod",
+ "libstatslog",
+ "libprotobuf-cpp-full",
+ ],
+}
// ==== java proto device library (for test only) ==============================
java_library {
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
deleted file mode 100644
index 5818f5d..0000000
--- a/cmds/statsd/Android.mk
+++ /dev/null
@@ -1,319 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-statsd_common_src := \
- ../../core/java/android/os/IStatsCompanionService.aidl \
- ../../core/java/android/os/IStatsManager.aidl \
- src/statsd_config.proto \
- src/FieldValue.cpp \
- src/hash.cpp \
- src/stats_log_util.cpp \
- src/anomaly/AlarmMonitor.cpp \
- src/anomaly/AlarmTracker.cpp \
- src/anomaly/AnomalyTracker.cpp \
- src/anomaly/DurationAnomalyTracker.cpp \
- src/anomaly/subscriber_util.cpp \
- src/condition/CombinationConditionTracker.cpp \
- src/condition/condition_util.cpp \
- src/condition/SimpleConditionTracker.cpp \
- src/condition/ConditionWizard.cpp \
- src/condition/StateTracker.cpp \
- src/config/ConfigKey.cpp \
- src/config/ConfigListener.cpp \
- src/config/ConfigManager.cpp \
- src/external/Perfetto.cpp \
- src/external/Perfprofd.cpp \
- src/external/StatsPuller.cpp \
- src/external/StatsCompanionServicePuller.cpp \
- src/external/SubsystemSleepStatePuller.cpp \
- src/external/ResourceHealthManagerPuller.cpp \
- src/external/ResourceThermalManagerPuller.cpp \
- src/external/StatsPullerManager.cpp \
- src/external/puller_util.cpp \
- src/logd/LogEvent.cpp \
- src/logd/LogListener.cpp \
- src/matchers/CombinationLogMatchingTracker.cpp \
- src/matchers/EventMatcherWizard.cpp \
- src/matchers/matcher_util.cpp \
- src/matchers/SimpleLogMatchingTracker.cpp \
- src/metrics/MetricProducer.cpp \
- src/metrics/EventMetricProducer.cpp \
- src/metrics/CountMetricProducer.cpp \
- src/metrics/DurationMetricProducer.cpp \
- src/metrics/duration_helper/OringDurationTracker.cpp \
- src/metrics/duration_helper/MaxDurationTracker.cpp \
- src/metrics/ValueMetricProducer.cpp \
- src/metrics/GaugeMetricProducer.cpp \
- src/metrics/MetricsManager.cpp \
- src/metrics/metrics_manager_util.cpp \
- src/packages/UidMap.cpp \
- src/storage/StorageManager.cpp \
- src/StatsLogProcessor.cpp \
- src/StatsService.cpp \
- src/statscompanion_util.cpp \
- src/subscriber/IncidentdReporter.cpp \
- src/subscriber/SubscriberReporter.cpp \
- src/HashableDimensionKey.cpp \
- src/guardrail/StatsdStats.cpp \
- src/socket/StatsSocketListener.cpp \
- src/shell/ShellSubscriber.cpp \
- src/shell/shell_config.proto
-
-# TODO(b/110563449): Once statsd is using a blueprint file, migrate to the proper filegroups.
-statsd_common_src += \
- ../../../../system/extras/perfprofd/binder_interface/aidl/android/os/IPerfProfd.aidl
-
-statsd_common_c_includes := \
- $(LOCAL_PATH)/src \
- $(LOCAL_PATH)/../../libs/services/include
-
-statsd_common_aidl_includes := \
- $(LOCAL_PATH)/../../core/java
-
-statsd_common_static_libraries := \
- libhealthhalutils
-
-statsd_common_shared_libraries := \
- libbase \
- libbinder \
- libincident \
- liblog \
- libutils \
- libservices \
- libprotoutil \
- libstatslog \
- libhardware \
- libhardware_legacy \
- libhidlbase \
- libhidltransport \
- libhwbinder \
- android.frameworks.stats@1.0 \
- android.hardware.health@2.0 \
- android.hardware.power@1.0 \
- android.hardware.power@1.1 \
- android.hardware.thermal@1.0 \
- libpackagelistparser \
- libsysutils \
- libcutils
-
-# =========
-# statsd
-# =========
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := statsd
-
-LOCAL_SRC_FILES := \
- $(statsd_common_src) \
- src/main.cpp
-
-LOCAL_CFLAGS += \
- -Wall \
- -Wextra \
- -Werror \
- -Wno-unused-parameter
-
-ifeq (debug,)
- LOCAL_CFLAGS += \
- -g -O0
-else
- # optimize for size (protobuf glop can get big)
- LOCAL_CFLAGS += \
- -Os
-endif
-LOCAL_PROTOC_OPTIMIZE_TYPE := lite
-
-LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
-LOCAL_C_INCLUDES += $(statsd_common_c_includes)
-
-LOCAL_STATIC_LIBRARIES := $(statsd_common_static_libraries)
-
-LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
- libgtest_prod
-
-LOCAL_MODULE_CLASS := EXECUTABLES
-
-# Enable sanitizer ONLY on eng builds.
-#ifeq ($(TARGET_BUILD_VARIANT),eng)
-# LOCAL_CLANG := true
-# LOCAL_SANITIZE := address
-#endif
-
-# Add a flag to enable stats log printing from statsd on debug builds.
-ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
- LOCAL_CFLAGS += \
- -DVERY_VERBOSE_PRINTING
-endif
-
-LOCAL_INIT_RC := statsd.rc
-
-include $(BUILD_EXECUTABLE)
-
-
-# ==============
-# statsd_test
-# ==============
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := statsd_test
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
-LOCAL_C_INCLUDES += $(statsd_common_c_includes)
-
-LOCAL_CFLAGS += \
- -Wall \
- -Werror \
- -Wno-missing-field-initializers \
- -Wno-unused-variable \
- -Wno-unused-function \
- -Wno-unused-parameter
-
-LOCAL_SRC_FILES := \
- $(statsd_common_src) \
- src/atom_field_options.proto \
- src/atoms.proto \
- src/stats_log.proto \
- src/shell/shell_data.proto \
- tests/AlarmMonitor_test.cpp \
- tests/anomaly/AlarmTracker_test.cpp \
- tests/anomaly/AnomalyTracker_test.cpp \
- tests/ConfigManager_test.cpp \
- tests/external/puller_util_test.cpp \
- tests/indexed_priority_queue_test.cpp \
- tests/LogEntryMatcher_test.cpp \
- tests/LogEvent_test.cpp \
- tests/MetricsManager_test.cpp \
- tests/StatsLogProcessor_test.cpp \
- tests/StatsService_test.cpp \
- tests/UidMap_test.cpp \
- tests/FieldValue_test.cpp \
- tests/condition/CombinationConditionTracker_test.cpp \
- tests/condition/SimpleConditionTracker_test.cpp \
- tests/condition/StateTracker_test.cpp \
- tests/metrics/OringDurationTracker_test.cpp \
- tests/metrics/MaxDurationTracker_test.cpp \
- tests/metrics/CountMetricProducer_test.cpp \
- tests/metrics/DurationMetricProducer_test.cpp \
- tests/metrics/EventMetricProducer_test.cpp \
- tests/metrics/ValueMetricProducer_test.cpp \
- tests/metrics/GaugeMetricProducer_test.cpp \
- tests/guardrail/StatsdStats_test.cpp \
- tests/metrics/metrics_test_helper.cpp \
- tests/statsd_test_util.cpp \
- tests/e2e/WakelockDuration_e2e_test.cpp \
- tests/e2e/MetricActivation_e2e_test.cpp \
- tests/e2e/MetricConditionLink_e2e_test.cpp \
- tests/e2e/Alarm_e2e_test.cpp \
- tests/e2e/Attribution_e2e_test.cpp \
- tests/e2e/GaugeMetric_e2e_push_test.cpp \
- tests/e2e/GaugeMetric_e2e_pull_test.cpp \
- tests/e2e/ValueMetric_pull_e2e_test.cpp \
- tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp \
- tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp \
- tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp \
- tests/e2e/Anomaly_count_e2e_test.cpp \
- tests/e2e/Anomaly_duration_sum_e2e_test.cpp \
- tests/e2e/ConfigTtl_e2e_test.cpp \
- tests/e2e/PartialBucket_e2e_test.cpp \
- tests/shell/ShellSubscriber_test.cpp
-
-LOCAL_STATIC_LIBRARIES := \
- $(statsd_common_static_libraries) \
- libgmock \
- libplatformprotos
-
-LOCAL_PROTOC_OPTIMIZE_TYPE := full
-
-LOCAL_PROTOC_FLAGS := \
- -Iexternal/protobuf/src
-
-LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
- libprotobuf-cpp-full
-
-include $(BUILD_NATIVE_TEST)
-
-##############################
-# statsd micro benchmark
-##############################
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := statsd_benchmark
-
-LOCAL_SRC_FILES := $(statsd_common_src) \
- src/atom_field_options.proto \
- src/atoms.proto \
- src/stats_log.proto \
- benchmark/main.cpp \
- benchmark/hello_world_benchmark.cpp \
- benchmark/log_event_benchmark.cpp \
- benchmark/stats_write_benchmark.cpp \
- benchmark/filter_value_benchmark.cpp \
- benchmark/get_dimensions_for_condition_benchmark.cpp \
- benchmark/metric_util.cpp \
- benchmark/duration_metric_benchmark.cpp
-
-LOCAL_STATIC_LIBRARIES := \
- $(statsd_common_static_libraries)
-
-LOCAL_PROTOC_OPTIMIZE_TYPE := full
-
-LOCAL_PROTOC_FLAGS := \
- -Iexternal/protobuf/src
-
-LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
- libprotobuf-cpp-full
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- platformprotoslite
-
-LOCAL_C_INCLUDES := $(statsd_common_c_includes)
-
-LOCAL_CFLAGS := -Wall \
- -Werror \
- -Wno-unused-parameter \
- -Wno-unused-variable \
- -Wno-unused-function \
-
-# Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374
-LOCAL_CFLAGS += -Wno-varargs
-
-LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
-
-LOCAL_STATIC_LIBRARIES := \
- $(statsd_common_static_libraries) \
- libplatformprotos
-
-LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
- libgtest_prod \
- libstatslog
-
-LOCAL_MODULE_TAGS := eng tests
-
-include $(BUILD_NATIVE_BENCHMARK)
-
-
-statsd_common_src:=
-statsd_common_aidl_includes:=
-statsd_common_c_includes:=
-statsd_common_static_libraries:=
-statsd_common_shared_libraries:=
-
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/cmds/statsd/tools/Android.mk b/cmds/statsd/tools/Android.mk
deleted file mode 100644
index 7253c96..0000000
--- a/cmds/statsd/tools/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-#Include the sub-makefiles
-include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/cmds/statsd/tools/dogfood/Android.bp b/cmds/statsd/tools/dogfood/Android.bp
new file mode 100644
index 0000000..bb494a6
--- /dev/null
+++ b/cmds/statsd/tools/dogfood/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+
+android_app {
+ name: "StatsdDogfood",
+ platform_apis: true,
+
+ srcs: ["src/**/*.java"],
+
+ resource_dirs: ["res"],
+ static_libs: [
+ "platformprotoslite",
+ "statsdprotolite",
+ ],
+
+ privileged: true,
+ dex_preopt: {
+ enabled: false,
+ },
+ certificate: "platform",
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/cmds/statsd/tools/dogfood/Android.mk b/cmds/statsd/tools/dogfood/Android.mk
deleted file mode 100644
index baf235b..0000000
--- a/cmds/statsd/tools/dogfood/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-#
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_PACKAGE_NAME := StatsdDogfood
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite \
- statsdprotolite
-
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_DEX_PREOPT := false
-LOCAL_CERTIFICATE := platform
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
diff --git a/cmds/statsd/tools/loadtest/Android.bp b/cmds/statsd/tools/loadtest/Android.bp
new file mode 100644
index 0000000..bf87fc5
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+
+android_app {
+ name: "StatsdLoadtest",
+ platform_apis: true,
+
+ srcs: ["src/**/*.java"],
+
+ resource_dirs: ["res"],
+ static_libs: [
+ "platformprotoslite",
+ "statsdprotolite",
+ ],
+
+ certificate: "platform",
+ privileged: true,
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/cmds/statsd/tools/loadtest/Android.mk b/cmds/statsd/tools/loadtest/Android.mk
deleted file mode 100644
index 219cd95..0000000
--- a/cmds/statsd/tools/loadtest/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-#
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_PACKAGE_NAME := StatsdLoadtest
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite \
- statsdprotolite
-
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_DEX_PREOPT := false
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 8f82ee7..a4f1db3 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -34,6 +34,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.UserManager;
+import android.provider.Settings;
import android.util.ArrayMap;
import com.android.internal.app.IAppOpsActiveCallback;
@@ -1584,6 +1585,26 @@
* @hide
*/
public static int opToDefaultMode(int op) {
+ // STOPSHIP b/118520006: Hardcode the default values once the feature is stable.
+ switch (op) {
+ // SMS permissions
+ case AppOpsManager.OP_SEND_SMS:
+ case AppOpsManager.OP_RECEIVE_SMS:
+ case AppOpsManager.OP_READ_SMS:
+ case AppOpsManager.OP_RECEIVE_WAP_PUSH:
+ case AppOpsManager.OP_RECEIVE_MMS:
+ case AppOpsManager.OP_READ_CELL_BROADCASTS:
+ // CallLog permissions
+ case AppOpsManager.OP_READ_CALL_LOG:
+ case AppOpsManager.OP_WRITE_CALL_LOG:
+ case AppOpsManager.OP_PROCESS_OUTGOING_CALLS: {
+ // ActivityThread.currentApplication() is never null
+ if (Settings.Global.getInt(ActivityThread.currentApplication().getContentResolver(),
+ Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, 0) == 1) {
+ return AppOpsManager.MODE_DEFAULT;
+ }
+ }
+ }
return sOpDefaultMode[op];
}
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 9713527..4d52263 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -51,4 +51,8 @@
void registerAppUsageObserver(int observerId, in String[] packages, long timeLimitMs,
in PendingIntent callback, String callingPackage);
void unregisterAppUsageObserver(int observerId, String callingPackage);
+ void registerUsageSessionObserver(int sessionObserverId, in String[] observed, long timeLimitMs,
+ long sessionThresholdTimeMs, in PendingIntent limitReachedCallbackIntent,
+ in PendingIntent sessionEndCallbackIntent, String callingPackage);
+ void unregisterUsageSessionObserver(int sessionObserverId, String callingPackage);
}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index dbb00eb..6d7400e 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
@@ -595,7 +596,7 @@
* exceeded by the group of apps. The delivered Intent will also contain
* the extras {@link #EXTRA_OBSERVER_ID}, {@link #EXTRA_TIME_LIMIT} and
* {@link #EXTRA_TIME_USED}. Cannot be null.
- * @throws SecurityException if the caller doesn't have the OBSERVE_APP_USAGE permission or
+ * @throws SecurityException if the caller doesn't have the OBSERVE_APP_USAGE permission and
* is not the profile owner of this user.
*/
@SystemApi
@@ -616,7 +617,7 @@
* to any observer registered by this application. Unregistering an observer that was already
* unregistered or never registered will have no effect.
* @param observerId The id of the observer that was previously registered.
- * @throws SecurityException if the caller doesn't have the OBSERVE_APP_USAGE permission or is
+ * @throws SecurityException if the caller doesn't have the OBSERVE_APP_USAGE permission and is
* not the profile owner of this user.
*/
@SystemApi
@@ -629,6 +630,81 @@
}
}
+ /**
+ * Register a usage session observer that receives a callback on the provided {@code
+ * limitReachedCallbackIntent} when the sum of usages of apps in the packages array exceeds
+ * the {@code timeLimit} specified within a usage session. After the {@code timeLimit} has
+ * been reached, the usage session observer will receive a callback on the provided {@code
+ * sessionEndCallbackIntent} when the usage session ends. Registering another session
+ * observer against a {@code sessionObserverId} that has already been registered will
+ * override the previous session observer.
+ *
+ * @param sessionObserverId A unique id associated with the group of apps to be
+ * monitored. There can be multiple groups with common
+ * packages and different time limits.
+ * @param packages The list of packages to observe for foreground activity time. Cannot be null
+ * and must include at least one package.
+ * @param timeLimit The total time the set of apps can be used continuously before the {@code
+ * limitReachedCallbackIntent} is delivered. Must be at least one minute.
+ * @param timeUnit The unit for time specified in {@code timeLimit}. Cannot be null.
+ * @param sessionThresholdTime The time that can take place between usage sessions before the
+ * next session is considered a new session. Must be non-negative.
+ * @param sessionThresholdTimeUnit The unit for time specified in {@code sessionThreshold}.
+ * Cannot be null.
+ * @param limitReachedCallbackIntent The {@link PendingIntent} that will be dispatched when the
+ * time limit is exceeded by the group of apps. The delivered
+ * Intent will also contain the extras {@link
+ * #EXTRA_OBSERVER_ID}, {@link #EXTRA_TIME_LIMIT} and {@link
+ * #EXTRA_TIME_USED}. Cannot be null.
+ * @param sessionEndCallbackIntent The {@link PendingIntent} that will be dispatched when the
+ * session has ended after the time limit has been exceeded. The
+ * session is considered at its end after the {@code observed}
+ * usage has stopped and an additional {@code
+ * sessionThresholdTime} has passed. The delivered Intent will
+ * also contain the extras {@link #EXTRA_OBSERVER_ID} and {@link
+ * #EXTRA_TIME_USED}. Can be null.
+ * @throws SecurityException if the caller doesn't have the OBSERVE_APP_USAGE permission and
+ * is not the profile owner of this user.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE)
+ public void registerUsageSessionObserver(int sessionObserverId, @NonNull String[] packages,
+ long timeLimit, @NonNull TimeUnit timeUnit, long sessionThresholdTime,
+ @NonNull TimeUnit sessionThresholdTimeUnit,
+ @NonNull PendingIntent limitReachedCallbackIntent,
+ @Nullable PendingIntent sessionEndCallbackIntent) {
+ try {
+ mService.registerUsageSessionObserver(sessionObserverId, packages,
+ timeUnit.toMillis(timeLimit),
+ sessionThresholdTimeUnit.toMillis(sessionThresholdTime),
+ limitReachedCallbackIntent, sessionEndCallbackIntent,
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unregister the usage session observer specified by the {@code sessionObserverId}. This will
+ * only apply to any app session observer registered by this application. Unregistering an
+ * observer that was already unregistered or never registered will have no effect.
+ *
+ * @param sessionObserverId The id of the observer that was previously registered.
+ * @throws SecurityException if the caller doesn't have the OBSERVE_APP_USAGE permission and
+ * is not the profile owner of this user.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE)
+ public void unregisterUsageSessionObserver(int sessionObserverId) {
+ try {
+ mService.unregisterUsageSessionObserver(sessionObserverId, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** @hide */
public static String reasonToString(int standbyReason) {
StringBuilder sb = new StringBuilder();
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index e270fc2..5447f59 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -811,14 +811,19 @@
* packet needs to be subtracted from the root UID on the base interface both for tx
* and rx traffic (http://b/12249687, http:/b/33681750).
*
+ * As for eBPF, the per uid stats is collected by different hook, the rx packets on base
+ * interface will not be counted. Thus, the adjustment on root uid is only needed in tx
+ * direction.
+ *
* <p>This method will behave fine if {@code stackedIfaces} is an non-synchronized but add-only
* {@code ConcurrentHashMap}
* @param baseTraffic Traffic on the base interfaces. Will be mutated.
* @param stackedTraffic Stats with traffic stacked on top of our ifaces. Will also be mutated.
* @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
+ * @param useBpfStats True if eBPF is in use.
*/
public static void apply464xlatAdjustments(NetworkStats baseTraffic,
- NetworkStats stackedTraffic, Map<String, String> stackedIfaces) {
+ NetworkStats stackedTraffic, Map<String, String> stackedIfaces, boolean useBpfStats) {
// Total 464xlat traffic to subtract from uid 0 on all base interfaces.
// stackedIfaces may grow afterwards, but NetworkStats will just be resized automatically.
final NetworkStats adjustments = new NetworkStats(0, stackedIfaces.size());
@@ -837,15 +842,20 @@
continue;
}
// Subtract any 464lat traffic seen for the root UID on the current base interface.
+ // However, for eBPF, the per uid stats is collected by different hook, the rx packets
+ // on base interface will not be counted. Thus, the adjustment on root uid is only
+ // needed in tx direction.
adjust.iface = baseIface;
- adjust.rxBytes = -(entry.rxBytes + entry.rxPackets * IPV4V6_HEADER_DELTA);
+ if (!useBpfStats) {
+ adjust.rxBytes = -(entry.rxBytes + entry.rxPackets * IPV4V6_HEADER_DELTA);
+ adjust.rxPackets = -entry.rxPackets;
+ }
adjust.txBytes = -(entry.txBytes + entry.txPackets * IPV4V6_HEADER_DELTA);
- adjust.rxPackets = -entry.rxPackets;
adjust.txPackets = -entry.txPackets;
adjustments.combineValues(adjust);
- // For 464xlat traffic, xt_qtaguid only counts the bytes of the native IPv4 packet sent
- // on the stacked interface with prefix "v4-" and drops the IPv6 header size after
+ // For 464xlat traffic, per uid stats only counts the bytes of the native IPv4 packet
+ // sent on the stacked interface with prefix "v4-" and drops the IPv6 header size after
// unwrapping. To account correctly for on-the-wire traffic, add the 20 additional bytes
// difference for all packets (http://b/12249687, http:/b/33681750).
entry.rxBytes += entry.rxPackets * IPV4V6_HEADER_DELTA;
@@ -864,8 +874,8 @@
* base and stacked traffic.
* @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
*/
- public void apply464xlatAdjustments(Map<String, String> stackedIfaces) {
- apply464xlatAdjustments(this, this, stackedIfaces);
+ public void apply464xlatAdjustments(Map<String, String> stackedIfaces, boolean useBpfStats) {
+ apply464xlatAdjustments(this, this, stackedIfaces, useBpfStats);
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 45e6343..f58624f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6038,6 +6038,7 @@
* shortcut. Must be its flattened {@link ComponentName}.
* @hide
*/
+ @TestApi
public static final String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE =
"accessibility_shortcut_target_service";
@@ -7396,8 +7397,8 @@
"call_screening_default_component";
/**
- * Specifies the package name currently configured to be the default application to perform
- * the user-defined call redirection service with Telecom.
+ * Specifies the component name currently configured to be the default application to
+ * perform the user-defined call redirection service with Telecom.
* @hide
*/
@UnsupportedAppUsage
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 3ab8a0a..e5fd292 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1269,29 +1269,47 @@
* scrolling on the specified line.
*/
public float getLineLeft(int line) {
- int dir = getParagraphDirection(line);
- Alignment align = getParagraphAlignment(line);
+ final int dir = getParagraphDirection(line);
+ final Alignment align = getParagraphAlignment(line);
- if (align == Alignment.ALIGN_LEFT) {
- return 0;
- } else if (align == Alignment.ALIGN_NORMAL) {
- if (dir == DIR_RIGHT_TO_LEFT)
- return getParagraphRight(line) - getLineMax(line);
- else
- return 0;
- } else if (align == Alignment.ALIGN_RIGHT) {
- return mWidth - getLineMax(line);
- } else if (align == Alignment.ALIGN_OPPOSITE) {
- if (dir == DIR_RIGHT_TO_LEFT)
- return 0;
- else
+ // First convert combinations of alignment and direction settings to
+ // three basic cases: ALIGN_LEFT, ALIGN_RIGHT and ALIGN_CENTER.
+ // For unexpected cases, it will fallback to ALIGN_LEFT.
+ final Alignment resultAlign;
+ switch(align) {
+ case ALIGN_NORMAL:
+ resultAlign =
+ dir == DIR_RIGHT_TO_LEFT ? Alignment.ALIGN_RIGHT : Alignment.ALIGN_LEFT;
+ break;
+ case ALIGN_OPPOSITE:
+ resultAlign =
+ dir == DIR_RIGHT_TO_LEFT ? Alignment.ALIGN_LEFT : Alignment.ALIGN_RIGHT;
+ break;
+ case ALIGN_CENTER:
+ resultAlign = Alignment.ALIGN_CENTER;
+ break;
+ case ALIGN_RIGHT:
+ resultAlign = Alignment.ALIGN_RIGHT;
+ break;
+ default: /* align == Alignment.ALIGN_LEFT */
+ resultAlign = Alignment.ALIGN_LEFT;
+ }
+
+ // Here we must use getLineMax() to do the computation, because it maybe overridden by
+ // derived class. And also note that line max equals the width of the text in that line
+ // plus the leading margin.
+ switch (resultAlign) {
+ case ALIGN_CENTER:
+ final int left = getParagraphLeft(line);
+ final float max = getLineMax(line);
+ // This computation only works when mWidth equals leadingMargin plus
+ // the width of text in this line. If this condition doesn't meet anymore,
+ // please change here too.
+ return (float) Math.floor(left + (mWidth - max) / 2);
+ case ALIGN_RIGHT:
return mWidth - getLineMax(line);
- } else { /* align == Alignment.ALIGN_CENTER */
- int left = getParagraphLeft(line);
- int right = getParagraphRight(line);
- int max = ((int) getLineMax(line)) & ~1;
-
- return left + ((right - left) - max) / 2;
+ default: /* resultAlign == Alignment.ALIGN_LEFT */
+ return 0;
}
}
@@ -1300,29 +1318,40 @@
* scrolling on the specified line.
*/
public float getLineRight(int line) {
- int dir = getParagraphDirection(line);
- Alignment align = getParagraphAlignment(line);
+ final int dir = getParagraphDirection(line);
+ final Alignment align = getParagraphAlignment(line);
- if (align == Alignment.ALIGN_LEFT) {
- return getParagraphLeft(line) + getLineMax(line);
- } else if (align == Alignment.ALIGN_NORMAL) {
- if (dir == DIR_RIGHT_TO_LEFT)
+ final Alignment resultAlign;
+ switch(align) {
+ case ALIGN_NORMAL:
+ resultAlign =
+ dir == DIR_RIGHT_TO_LEFT ? Alignment.ALIGN_RIGHT : Alignment.ALIGN_LEFT;
+ break;
+ case ALIGN_OPPOSITE:
+ resultAlign =
+ dir == DIR_RIGHT_TO_LEFT ? Alignment.ALIGN_LEFT : Alignment.ALIGN_RIGHT;
+ break;
+ case ALIGN_CENTER:
+ resultAlign = Alignment.ALIGN_CENTER;
+ break;
+ case ALIGN_RIGHT:
+ resultAlign = Alignment.ALIGN_RIGHT;
+ break;
+ default: /* align == Alignment.ALIGN_LEFT */
+ resultAlign = Alignment.ALIGN_LEFT;
+ }
+
+ switch (resultAlign) {
+ case ALIGN_CENTER:
+ final int right = getParagraphRight(line);
+ final float max = getLineMax(line);
+ // This computation only works when mWidth equals leadingMargin plus width of the
+ // text in this line. If this condition doesn't meet anymore, please change here.
+ return (float) Math.ceil(right - (mWidth - max) / 2);
+ case ALIGN_RIGHT:
return mWidth;
- else
- return getParagraphLeft(line) + getLineMax(line);
- } else if (align == Alignment.ALIGN_RIGHT) {
- return mWidth;
- } else if (align == Alignment.ALIGN_OPPOSITE) {
- if (dir == DIR_RIGHT_TO_LEFT)
+ default: /* resultAlign == Alignment.ALIGN_LEFT */
return getLineMax(line);
- else
- return mWidth;
- } else { /* align == Alignment.ALIGN_CENTER */
- int left = getParagraphLeft(line);
- int right = getParagraphRight(line);
- int max = ((int) getLineMax(line)) & ~1;
-
- return right - ((right - left) - max) / 2;
}
}
@@ -1671,7 +1700,7 @@
* Return the vertical position of the baseline of the specified line.
*/
public final int getLineBaseline(int line) {
- // getLineTop(line+1) == getLineTop(line)
+ // getLineTop(line+1) == getLineBottom(line)
return getLineTop(line+1) - getLineDescent(line);
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index e88682e..a7d0cfb 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -23,8 +23,11 @@
import android.accessibilityservice.AccessibilityServiceInfo.FeedbackType;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -204,6 +207,7 @@
*
* @hide
*/
+ @TestApi
public interface AccessibilityServicesStateChangeListener {
/**
@@ -778,6 +782,7 @@
* for a callback on the process's main handler.
* @hide
*/
+ @TestApi
public void addAccessibilityServicesStateChangeListener(
@NonNull AccessibilityServicesStateChangeListener listener, @Nullable Handler handler) {
synchronized (mLock) {
@@ -793,6 +798,7 @@
*
* @hide
*/
+ @TestApi
public void removeAccessibilityServicesStateChangeListener(
@NonNull AccessibilityServicesStateChangeListener listener) {
synchronized (mLock) {
@@ -1056,6 +1062,9 @@
*
* @hide
*/
+ @SystemApi
+ @TestApi
+ @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
public void performAccessibilityShortcut() {
final IAccessibilityManager service;
synchronized (mLock) {
@@ -1139,6 +1148,30 @@
}
}
+ /**
+ * Get the component name of the service currently assigned to the accessibility shortcut.
+ *
+ * @return The flattened component name
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
+ @Nullable
+ public String getAccessibilityShortcutService() {
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ }
+ if (service != null) {
+ try {
+ return service.getAccessibilityShortcutService();
+ } catch (RemoteException re) {
+ re.rethrowFromSystemServer();
+ }
+ }
+ return null;
+ }
+
private IAccessibilityManager getServiceLocked() {
if (mService == null) {
tryConnectToServiceLocked(null);
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 3e2ef18..61a8a1da 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -67,9 +67,12 @@
void notifyAccessibilityButtonVisibilityChanged(boolean available);
- // Requires WRITE_SECURE_SETTINGS
+ // Requires Manifest.permission.MANAGE_ACCESSIBILITY
void performAccessibilityShortcut();
+ // Requires Manifest.permission.MANAGE_ACCESSIBILITY
+ String getAccessibilityShortcutService();
+
// System process only
boolean sendFingerprintGesture(int gestureKeyCode);
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
new file mode 100644
index 0000000..5fcf227
--- /dev/null
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -0,0 +1,779 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.textclassifier;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.app.Person;
+import android.app.RemoteAction;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.SpannedString;
+import android.util.ArraySet;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Represents a list of actions suggested by a {@link TextClassifier} on a given conversation.
+ *
+ * @see TextClassifier#suggestConversationActions(Request)
+ */
+public final class ConversationActions implements Parcelable {
+
+ public static final Creator<ConversationActions> CREATOR =
+ new Creator<ConversationActions>() {
+ @Override
+ public ConversationActions createFromParcel(Parcel in) {
+ return new ConversationActions(in);
+ }
+
+ @Override
+ public ConversationActions[] newArray(int size) {
+ return new ConversationActions[size];
+ }
+ };
+
+ /** @hide */
+ @Retention(SOURCE)
+ @StringDef(
+ value = {
+ TYPE_VIEW_CALENDAR,
+ TYPE_VIEW_MAP,
+ TYPE_TRACK_FLIGHT,
+ TYPE_OPEN_URL,
+ TYPE_SEND_SMS,
+ TYPE_CALL_PHONE,
+ TYPE_SEND_EMAIL,
+ TYPE_TEXT_REPLY,
+ TYPE_CREATE_REMINDER,
+ TYPE_SHARE_LOCATION
+ },
+ prefix = "TYPE_")
+ public @interface ActionType {}
+
+ /**
+ * Indicates an action to view a calendar at a specified time.
+ */
+ public static final String TYPE_VIEW_CALENDAR = "view_calendar";
+ /**
+ * Indicates an action to view the map at a specified location.
+ */
+ public static final String TYPE_VIEW_MAP = "view_map";
+ /**
+ * Indicates an action to track a flight.
+ */
+ public static final String TYPE_TRACK_FLIGHT = "track_flight";
+ /**
+ * Indicates an action to open an URL.
+ */
+ public static final String TYPE_OPEN_URL = "open_url";
+ /**
+ * Indicates an action to send a SMS.
+ */
+ public static final String TYPE_SEND_SMS = "send_sms";
+ /**
+ * Indicates an action to call a phone number.
+ */
+ public static final String TYPE_CALL_PHONE = "call_phone";
+ /**
+ * Indicates an action to send an email.
+ */
+ public static final String TYPE_SEND_EMAIL = "send_email";
+ /**
+ * Indicates an action to reply with a text message.
+ */
+ public static final String TYPE_TEXT_REPLY = "text_reply";
+ /**
+ * Indicates an action to create a reminder.
+ */
+ public static final String TYPE_CREATE_REMINDER = "create_reminder";
+ /**
+ * Indicates an action to reply with a location.
+ */
+ public static final String TYPE_SHARE_LOCATION = "share_location";
+
+ /** @hide */
+ @Retention(SOURCE)
+ @StringDef(
+ value = {
+ HINT_FOR_NOTIFICATION,
+ HINT_FOR_IN_APP,
+ },
+ prefix = "HINT_")
+ public @interface Hint {}
+ /**
+ * To indicate the generated actions will be used within the app.
+ */
+ public static final String HINT_FOR_IN_APP = "in_app";
+ /**
+ * To indicate the generated actions will be used for notification.
+ */
+ public static final String HINT_FOR_NOTIFICATION = "notification";
+
+ private List<ConversationAction> mConversationActions;
+
+ /** Constructs a {@link ConversationActions} object. */
+ public ConversationActions(@NonNull List<ConversationAction> conversationActions) {
+ mConversationActions =
+ Collections.unmodifiableList(Preconditions.checkNotNull(conversationActions));
+ }
+
+ private ConversationActions(Parcel in) {
+ mConversationActions =
+ Collections.unmodifiableList(in.createTypedArrayList(ConversationAction.CREATOR));
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeTypedList(mConversationActions);
+ }
+
+ /** Returns an immutable list of {@link ConversationAction} objects. */
+ @NonNull
+ public List<ConversationAction> getConversationActions() {
+ return mConversationActions;
+ }
+
+ /** Represents the action suggested by a {@link TextClassifier} on a given conversation. */
+ public static final class ConversationAction implements Parcelable {
+
+ public static final Creator<ConversationAction> CREATOR =
+ new Creator<ConversationAction>() {
+ @Override
+ public ConversationAction createFromParcel(Parcel in) {
+ return new ConversationAction(in);
+ }
+
+ @Override
+ public ConversationAction[] newArray(int size) {
+ return new ConversationAction[size];
+ }
+ };
+
+ @NonNull
+ @ActionType
+ private final String mType;
+ @NonNull
+ private final CharSequence mTextReply;
+ @Nullable
+ private final RemoteAction mAction;
+
+ @FloatRange(from = 0, to = 1)
+ private final float mScore;
+
+ @NonNull
+ private final Bundle mExtras;
+
+ private ConversationAction(
+ @NonNull String type,
+ @Nullable RemoteAction action,
+ @Nullable CharSequence textReply,
+ float score,
+ @NonNull Bundle extras) {
+ mType = Preconditions.checkNotNull(type);
+ mAction = action;
+ mTextReply = textReply;
+ mScore = score;
+ mExtras = Preconditions.checkNotNull(extras);
+ }
+
+ private ConversationAction(Parcel in) {
+ mType = in.readString();
+ mAction = in.readParcelable(null);
+ mTextReply = in.readCharSequence();
+ mScore = in.readFloat();
+ mExtras = in.readBundle();
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeString(mType);
+ parcel.writeParcelable(mAction, flags);
+ parcel.writeCharSequence(mTextReply);
+ parcel.writeFloat(mScore);
+ parcel.writeBundle(mExtras);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ @ActionType
+ /** Returns the type of this action, for example, {@link #TYPE_VIEW_CALENDAR}. */
+ public String getType() {
+ return mType;
+ }
+
+ @Nullable
+ /**
+ * Returns a RemoteAction object, which contains the icon, label and a PendingIntent, for
+ * the specified action type.
+ */
+ public RemoteAction getAction() {
+ return mAction;
+ }
+
+ /**
+ * Returns the confidence score for the specified action. The value ranges from 0 (low
+ * confidence) to 1 (high confidence).
+ */
+ @FloatRange(from = 0, to = 1)
+ public float getConfidenceScore() {
+ return mScore;
+ }
+
+ /**
+ * Returns the text reply that could be sent as a reply to the given conversation.
+ * <p>
+ * This is only available when the type of the action is {@link #TYPE_TEXT_REPLY}.
+ */
+ @Nullable
+ public CharSequence getTextReply() {
+ return mTextReply;
+ }
+
+ /**
+ * Returns the extended data related to this conversation action.
+ *
+ * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
+ * prefer to hold a reference to the returned bundle rather than frequently calling this
+ * method.
+ */
+ @NonNull
+ public Bundle getExtras() {
+ return mExtras.deepCopy();
+ }
+
+ /** Builder class to construct {@link ConversationAction}. */
+ public static final class Builder {
+ @Nullable
+ @ActionType
+ private String mType;
+ @Nullable
+ private RemoteAction mAction;
+ @Nullable
+ private CharSequence mTextReply;
+ private float mScore;
+ @Nullable
+ private Bundle mExtras;
+
+ public Builder(@NonNull @ActionType String actionType) {
+ mType = Preconditions.checkNotNull(actionType);
+ }
+
+ /**
+ * Sets an action that may be performed on the given conversation.
+ */
+ @NonNull
+ public Builder setAction(@Nullable RemoteAction action) {
+ mAction = action;
+ return this;
+ }
+
+ /**
+ * Sets a text reply that may be performed on the given conversation.
+ */
+ @NonNull
+ public Builder setTextReply(@Nullable CharSequence textReply) {
+ mTextReply = textReply;
+ return this;
+ }
+
+ /** Sets the confident score. */
+ @NonNull
+ public Builder setConfidenceScore(@FloatRange(from = 0, to = 1) float score) {
+ mScore = score;
+ return this;
+ }
+
+ /**
+ * Sets the extended data for the conversation action object.
+ */
+ @NonNull
+ public Builder setExtras(@Nullable Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /** Builds the {@link ConversationAction} object. */
+ @NonNull
+ public ConversationAction build() {
+ return new ConversationAction(
+ mType,
+ mAction,
+ mTextReply,
+ mScore,
+ mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
+ }
+ }
+ }
+
+ /** Represents a message in the conversation. */
+ public static final class Message implements Parcelable {
+ @Nullable
+ private final Person mAuthor;
+ @Nullable
+ private final ZonedDateTime mComposeTime;
+ @Nullable
+ private final CharSequence mText;
+ @NonNull
+ private final Bundle mExtras;
+
+ private Message(
+ @Nullable Person author,
+ @Nullable ZonedDateTime composeTime,
+ @Nullable CharSequence text,
+ @NonNull Bundle bundle) {
+ mAuthor = author;
+ mComposeTime = composeTime;
+ mText = text;
+ mExtras = Preconditions.checkNotNull(bundle);
+ }
+
+ private Message(Parcel in) {
+ mAuthor = in.readParcelable(null);
+ mComposeTime =
+ in.readInt() == 0
+ ? null
+ : ZonedDateTime.parse(
+ in.readString(), DateTimeFormatter.ISO_ZONED_DATE_TIME);
+ mText = in.readCharSequence();
+ mExtras = in.readBundle();
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeParcelable(mAuthor, flags);
+ parcel.writeInt(mComposeTime != null ? 1 : 0);
+ if (mComposeTime != null) {
+ parcel.writeString(mComposeTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
+ }
+ parcel.writeCharSequence(mText);
+ parcel.writeBundle(mExtras);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<Message> CREATOR =
+ new Creator<Message>() {
+ @Override
+ public Message createFromParcel(Parcel in) {
+ return new Message(in);
+ }
+
+ @Override
+ public Message[] newArray(int size) {
+ return new Message[size];
+ }
+ };
+
+ /** Returns the person that composed the message. */
+ @Nullable
+ public Person getAuthor() {
+ return mAuthor;
+ }
+
+ /** Returns the compose time of the message. */
+ @Nullable
+ public ZonedDateTime getTime() {
+ return mComposeTime;
+ }
+
+ /** Returns the text of the message. */
+ @Nullable
+ public CharSequence getText() {
+ return mText;
+ }
+
+ /**
+ * Returns the extended data related to this conversation action.
+ *
+ * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
+ * prefer to hold a reference to the returned bundle rather than frequently calling this
+ * method.
+ */
+ @NonNull
+ public Bundle getExtras() {
+ return mExtras.deepCopy();
+ }
+
+ /** Builder class to construct a {@link Message} */
+ public static final class Builder {
+ @Nullable
+ private Person mAuthor;
+ @Nullable
+ private ZonedDateTime mComposeTime;
+ @Nullable
+ private CharSequence mText;
+ @Nullable
+ private Bundle mExtras;
+
+ /** Sets the person who composed this message. */
+ @NonNull
+ public Builder setAuthor(@Nullable Person author) {
+ mAuthor = author;
+ return this;
+ }
+
+ /** Sets the text of this message */
+ @NonNull
+ public Builder setText(@Nullable CharSequence text) {
+ mText = text;
+ return this;
+ }
+
+ /** Sets the compose time of this message */
+ @NonNull
+ public Builder setComposeTime(@Nullable ZonedDateTime composeTime) {
+ mComposeTime = composeTime;
+ return this;
+ }
+
+ /** Sets a set of extended data to the message. */
+ @NonNull
+ public Builder setExtras(@Nullable Bundle bundle) {
+ this.mExtras = bundle;
+ return this;
+ }
+
+ /** Builds the {@link Message} object. */
+ @NonNull
+ public Message build() {
+ return new Message(
+ mAuthor,
+ mComposeTime,
+ mText == null ? null : new SpannedString(mText),
+ mExtras == null ? new Bundle() : mExtras.deepCopy());
+ }
+ }
+ }
+
+ /** Configuration object for specifying what action types to identify. */
+ public static final class TypeConfig implements Parcelable {
+ @NonNull
+ @ActionType
+ private final Set<String> mExcludedTypes;
+ @NonNull
+ @ActionType
+ private final Set<String> mIncludedTypes;
+ private final boolean mIncludeTypesFromTextClassifier;
+
+ private TypeConfig(
+ @NonNull Set<String> includedTypes,
+ @NonNull Set<String> excludedTypes,
+ boolean includeTypesFromTextClassifier) {
+ mIncludedTypes = Preconditions.checkNotNull(includedTypes);
+ mExcludedTypes = Preconditions.checkNotNull(excludedTypes);
+ mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier;
+ }
+
+ private TypeConfig(Parcel in) {
+ mIncludedTypes = new ArraySet<>(in.createStringArrayList());
+ mExcludedTypes = new ArraySet<>(in.createStringArrayList());
+ mIncludeTypesFromTextClassifier = in.readByte() != 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeStringList(new ArrayList<>(mIncludedTypes));
+ parcel.writeStringList(new ArrayList<>(mExcludedTypes));
+ parcel.writeByte((byte) (mIncludeTypesFromTextClassifier ? 1 : 0));
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<TypeConfig> CREATOR =
+ new Creator<TypeConfig>() {
+ @Override
+ public TypeConfig createFromParcel(Parcel in) {
+ return new TypeConfig(in);
+ }
+
+ @Override
+ public TypeConfig[] newArray(int size) {
+ return new TypeConfig[size];
+ }
+ };
+
+ /**
+ * Returns a final list of types that the text classifier should look for.
+ *
+ * <p>NOTE: This method is intended for use by a text classifier.
+ *
+ * @param defaultTypes types the text classifier thinks should be included before factoring
+ * in the included/excluded types given by the client.
+ */
+ @NonNull
+ public Collection<String> resolveTypes(@Nullable Collection<String> defaultTypes) {
+ Set<String> types = new ArraySet<>();
+ if (mIncludeTypesFromTextClassifier && defaultTypes != null) {
+ types.addAll(defaultTypes);
+ }
+ types.addAll(mIncludedTypes);
+ types.removeAll(mExcludedTypes);
+ return Collections.unmodifiableCollection(types);
+ }
+
+ /**
+ * Return whether the client allows the text classifier to include its own list of default
+ * types. If this function returns {@code true}, the text classifier can consider specifying
+ * a default list of entity types in {@link #resolveTypes(Collection)}.
+ *
+ * <p>NOTE: This method is intended for use by a text classifier.
+ *
+ * @see #resolveTypes(Collection)
+ */
+ public boolean shouldIncludeTypesFromTextClassifier() {
+ return mIncludeTypesFromTextClassifier;
+ }
+
+ /** Builder class to construct the {@link TypeConfig} object. */
+ public static final class Builder {
+ @Nullable
+ private Collection<String> mExcludedTypes;
+ @Nullable
+ private Collection<String> mIncludedTypes;
+ private boolean mIncludeTypesFromTextClassifier = true;
+
+ /**
+ * Sets a collection of types that are explicitly included, for example, {@link
+ * #TYPE_VIEW_CALENDAR}.
+ */
+ @NonNull
+ public Builder setIncludedTypes(
+ @Nullable @ActionType Collection<String> includedTypes) {
+ mIncludedTypes = includedTypes;
+ return this;
+ }
+
+ /**
+ * Sets a collection of types that are explicitly excluded, for example, {@link
+ * #TYPE_VIEW_CALENDAR}.
+ */
+ @NonNull
+ public Builder setExcludedTypes(
+ @Nullable @ActionType Collection<String> excludedTypes) {
+ mExcludedTypes = excludedTypes;
+ return this;
+ }
+
+ /**
+ * Specifies whether or not to include the types suggested by the text classifier. By
+ * default, it is included.
+ */
+ @NonNull
+ public Builder includeTypesFromTextClassifier(boolean includeTypesFromTextClassifier) {
+ mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier;
+ return this;
+ }
+
+ /**
+ * Combines all of the options that have been set and returns a new {@link TypeConfig}
+ * object.
+ */
+ @NonNull
+ public TypeConfig build() {
+ return new TypeConfig(
+ mIncludedTypes == null
+ ? Collections.emptySet()
+ : new ArraySet<>(mIncludedTypes),
+ mExcludedTypes == null
+ ? Collections.emptySet()
+ : new ArraySet<>(mExcludedTypes),
+ mIncludeTypesFromTextClassifier);
+ }
+ }
+ }
+
+ /**
+ * A request object for generating conversation action suggestions.
+ *
+ * @see TextClassifier#suggestConversationActions(Request)
+ */
+ public static final class Request implements Parcelable {
+ @NonNull
+ private final List<Message> mConversation;
+ @NonNull
+ private final TypeConfig mTypeConfig;
+ private final int mMaxSuggestions;
+ @NonNull
+ @Hint
+ private final List<String> mHints;
+
+ private Request(
+ @NonNull List<Message> conversation,
+ @NonNull TypeConfig typeConfig,
+ int maxSuggestions,
+ @Nullable @Hint List<String> hints) {
+ mConversation = Preconditions.checkNotNull(conversation);
+ mTypeConfig = Preconditions.checkNotNull(typeConfig);
+ mMaxSuggestions = maxSuggestions;
+ mHints = hints;
+ }
+
+ private Request(Parcel in) {
+ List<Message> conversation = new ArrayList<>();
+ in.readParcelableList(conversation, null);
+ mConversation = Collections.unmodifiableList(conversation);
+ mTypeConfig = in.readParcelable(null);
+ mMaxSuggestions = in.readInt();
+ List<String> hints = new ArrayList<>();
+ in.readStringList(hints);
+ mHints = Collections.unmodifiableList(hints);
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeParcelableList(mConversation, flags);
+ parcel.writeParcelable(mTypeConfig, flags);
+ parcel.writeInt(mMaxSuggestions);
+ parcel.writeStringList(mHints);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<Request> CREATOR =
+ new Creator<Request>() {
+ @Override
+ public Request createFromParcel(Parcel in) {
+ return new Request(in);
+ }
+
+ @Override
+ public Request[] newArray(int size) {
+ return new Request[size];
+ }
+ };
+
+ /** Returns the type config. */
+ @NonNull
+ public TypeConfig getTypeConfig() {
+ return mTypeConfig;
+ }
+
+ /** Returns an immutable list of messages that make up the conversation. */
+ @NonNull
+ public List<Message> getConversation() {
+ return mConversation;
+ }
+
+ /**
+ * Return the maximal number of suggestions the caller wants, value 0 means no restriction.
+ */
+ @IntRange(from = 0)
+ public int getMaxSuggestions() {
+ return mMaxSuggestions;
+ }
+
+ /** Returns an immutable list of hints */
+ @Nullable
+ @Hint
+ public List<String> getHints() {
+ return mHints;
+ }
+
+ /** Builder object to construct the {@link Request} object. */
+ public static final class Builder {
+ @NonNull
+ private List<Message> mConversation;
+ @Nullable
+ private TypeConfig mTypeConfig;
+ private int mMaxSuggestions;
+ @Nullable
+ @Hint
+ private List<String> mHints;
+
+ /**
+ * Constructs a builder.
+ *
+ * @param conversation the conversation that the text classifier is going to generate
+ * actions for.
+ */
+ public Builder(@NonNull List<Message> conversation) {
+ mConversation = Preconditions.checkNotNull(conversation);
+ }
+
+ /**
+ * Sets the hints to help text classifier to generate actions. It could be used to help
+ * text classifier to infer what types of actions the caller may be interested in.
+ */
+ public Builder setHints(@Nullable @Hint List<String> hints) {
+ mHints = hints;
+ return this;
+ }
+
+ /** Sets the type config. */
+ @NonNull
+ public Builder setTypeConfig(@Nullable TypeConfig typeConfig) {
+ mTypeConfig = typeConfig;
+ return this;
+ }
+
+ /** Sets the maximum number of suggestions you want.
+ * <p>
+ * Value 0 means no restriction.
+ */
+ @NonNull
+ public Builder setMaxSuggestions(@IntRange(from = 0) int maxSuggestions) {
+ mMaxSuggestions = Preconditions.checkArgumentNonnegative(maxSuggestions);
+ return this;
+ }
+
+ /** Builds the {@link Request} object. */
+ @NonNull
+ public Request build() {
+ return new Request(
+ Collections.unmodifiableList(mConversation),
+ mTypeConfig == null ? new TypeConfig.Builder().build() : mTypeConfig,
+ mMaxSuggestions,
+ mHints == null
+ ? Collections.emptyList()
+ : Collections.unmodifiableList(mHints));
+ }
+ }
+ }
+}
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 9511a9e..d972bb8 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -33,6 +33,7 @@
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
+import android.os.Bundle;
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
@@ -126,6 +127,7 @@
@NonNull private final List<RemoteAction> mActions;
@NonNull private final EntityConfidence mEntityConfidence;
@Nullable private final String mId;
+ @NonNull private final Bundle mExtras;
private TextClassification(
@Nullable String text,
@@ -135,7 +137,8 @@
@Nullable OnClickListener legacyOnClickListener,
@NonNull List<RemoteAction> actions,
@NonNull Map<String, Float> entityConfidence,
- @Nullable String id) {
+ @Nullable String id,
+ @NonNull Bundle extras) {
mText = text;
mLegacyIcon = legacyIcon;
mLegacyLabel = legacyLabel;
@@ -144,6 +147,7 @@
mActions = Collections.unmodifiableList(actions);
mEntityConfidence = new EntityConfidence(entityConfidence);
mId = id;
+ mExtras = extras;
}
/**
@@ -255,6 +259,18 @@
return mId;
}
+ /**
+ * Returns the extended data.
+ *
+ * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
+ * prefer to hold a reference to the returned bundle rather than frequently calling this
+ * method.
+ */
+ @NonNull
+ public Bundle getExtras() {
+ return mExtras.deepCopy();
+ }
+
@Override
public String toString() {
return String.format(Locale.US,
@@ -359,6 +375,7 @@
@Nullable private Intent mLegacyIntent;
@Nullable private OnClickListener mLegacyOnClickListener;
@Nullable private String mId;
+ @Nullable private Bundle mExtras;
/**
* Sets the classified text.
@@ -471,12 +488,22 @@
}
/**
+ * Sets the extended data.
+ */
+ @NonNull
+ public Builder setExtras(@Nullable Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
* Builds and returns a {@link TextClassification} object.
*/
@NonNull
public TextClassification build() {
return new TextClassification(mText, mLegacyIcon, mLegacyLabel, mLegacyIntent,
- mLegacyOnClickListener, mActions, mEntityConfidence, mId);
+ mLegacyOnClickListener, mActions, mEntityConfidence, mId,
+ mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
}
}
@@ -490,18 +517,21 @@
private final int mEndIndex;
@Nullable private final LocaleList mDefaultLocales;
@Nullable private final ZonedDateTime mReferenceTime;
+ @NonNull private final Bundle mExtras;
private Request(
CharSequence text,
int startIndex,
int endIndex,
LocaleList defaultLocales,
- ZonedDateTime referenceTime) {
+ ZonedDateTime referenceTime,
+ Bundle extras) {
mText = text;
mStartIndex = startIndex;
mEndIndex = endIndex;
mDefaultLocales = defaultLocales;
mReferenceTime = referenceTime;
+ mExtras = extras;
}
/**
@@ -548,6 +578,18 @@
}
/**
+ * Returns the extended data.
+ *
+ * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
+ * prefer to hold a reference to the returned bundle rather than frequently calling this
+ * method.
+ */
+ @NonNull
+ public Bundle getExtras() {
+ return mExtras.deepCopy();
+ }
+
+ /**
* A builder for building TextClassification requests.
*/
public static final class Builder {
@@ -555,6 +597,7 @@
private final CharSequence mText;
private final int mStartIndex;
private final int mEndIndex;
+ private Bundle mExtras;
@Nullable private LocaleList mDefaultLocales;
@Nullable private ZonedDateTime mReferenceTime;
@@ -602,11 +645,23 @@
}
/**
+ * Sets the extended data.
+ *
+ * @return this builder
+ */
+ @NonNull
+ public Builder setExtras(@Nullable Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
* Builds and returns the request object.
*/
@NonNull
public Request build() {
- return new Request(mText, mStartIndex, mEndIndex, mDefaultLocales, mReferenceTime);
+ return new Request(mText, mStartIndex, mEndIndex, mDefaultLocales, mReferenceTime,
+ mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
}
}
@@ -628,6 +683,7 @@
if (mReferenceTime != null) {
dest.writeString(mReferenceTime.toString());
}
+ dest.writeBundle(mExtras);
}
public static final Parcelable.Creator<Request> CREATOR =
@@ -649,6 +705,7 @@
mEndIndex = in.readInt();
mDefaultLocales = in.readInt() == 0 ? null : LocaleList.CREATOR.createFromParcel(in);
mReferenceTime = in.readInt() == 0 ? null : ZonedDateTime.parse(in.readString());
+ mExtras = in.readBundle();
}
}
@@ -664,6 +721,7 @@
dest.writeTypedList(mActions);
mEntityConfidence.writeToParcel(dest, flags);
dest.writeString(mId);
+ dest.writeBundle(mExtras);
}
public static final Parcelable.Creator<TextClassification> CREATOR =
@@ -695,6 +753,7 @@
mLegacyIntent = null; // mLegacyIntent is not parcelled.
mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
mId = in.readString();
+ mExtras = in.readBundle();
}
// Best effort attempt to try to load a drawable from the provided icon.
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 2e92f14..e675744 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -325,6 +325,17 @@
}
/**
+ * Suggests and returns a list of actions according to the given conversation.
+ */
+ @WorkerThread
+ default ConversationActions suggestConversationActions(
+ @NonNull ConversationActions.Request request) {
+ Preconditions.checkNotNull(request);
+ Utils.checkMainThread();
+ return new ConversationActions(Collections.emptyList());
+ }
+
+ /**
* Reports a selection event.
*
* <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index d1c2799..0a7cff6 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -113,11 +113,12 @@
/**
* Applies 464xlat adjustments with ifaces noted with {@link #noteStackedIface(String, String)}.
- * @see NetworkStats#apply464xlatAdjustments(NetworkStats, NetworkStats, Map)
+ * @see NetworkStats#apply464xlatAdjustments(NetworkStats, NetworkStats, Map, boolean)
*/
public static void apply464xlatAdjustments(NetworkStats baseTraffic,
- NetworkStats stackedTraffic) {
- NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, sStackedIfaces);
+ NetworkStats stackedTraffic, boolean useBpfStats) {
+ NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, sStackedIfaces,
+ useBpfStats);
}
@VisibleForTesting
@@ -263,7 +264,7 @@
// No locking here: apply464xlatAdjustments behaves fine with an add-only ConcurrentHashMap.
// TODO: remove this and only apply adjustments in NetworkStatsService.
- stats.apply464xlatAdjustments(sStackedIfaces);
+ stats.apply464xlatAdjustments(sStackedIfaces, mUseBpfStats);
return stats;
}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 98b7b5d..65213c0 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -216,6 +216,11 @@
*/
native protected static void nativeUnmountStorageOnInit();
+ private static void callPostForkSystemServerHooks() {
+ // SystemServer specific post fork hooks run before child post fork hooks.
+ VM_HOOKS.postForkSystemServer();
+ }
+
private static void callPostForkChildHooks(int runtimeFlags, boolean isSystemServer,
boolean isZygote, String instructionSet) {
VM_HOOKS.postForkChild(runtimeFlags, isSystemServer, isZygote, instructionSet);
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 3e04bb3..4e20e29 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -84,6 +84,7 @@
static const char kIsolatedStorage[] = "persist.sys.isolated_storage";
static const char kZygoteClassName[] = "com/android/internal/os/Zygote";
static jclass gZygoteClass;
+static jmethodID gCallPostForkSystemServerHooks;
static jmethodID gCallPostForkChildHooks;
static bool g_is_security_enforced = true;
@@ -886,6 +887,18 @@
// Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
UnsetChldSignalHandler();
+ if (is_system_server) {
+ env->CallStaticVoidMethod(gZygoteClass, gCallPostForkSystemServerHooks);
+ if (env->ExceptionCheck()) {
+ fail_fn("Error calling post fork system server hooks.");
+ }
+ // TODO(oth): Remove hardcoded label here (b/117874058).
+ static const char* kSystemServerLabel = "u:r:system_server:s0";
+ if (selinux_android_setcon(kSystemServerLabel) != 0) {
+ fail_fn(CREATE_ERROR("selinux_android_setcon(%s)", kSystemServerLabel));
+ }
+ }
+
env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
is_system_server, is_child_zygote, instructionSet);
if (env->ExceptionCheck()) {
@@ -1181,6 +1194,9 @@
int register_com_android_internal_os_Zygote(JNIEnv* env) {
gZygoteClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, kZygoteClassName));
+ gCallPostForkSystemServerHooks = GetStaticMethodIDOrDie(env, gZygoteClass,
+ "callPostForkSystemServerHooks",
+ "()V");
gCallPostForkChildHooks = GetStaticMethodIDOrDie(env, gZygoteClass, "callPostForkChildHooks",
"(IZZLjava/lang/String;)V");
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index cd6bf66..e728ead 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4199,6 +4199,11 @@
<permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND"
android:protectionLevel="signature" />
+ <!-- @SystemApi Allows modifying accessibility state.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_ACCESSIBILITY"
+ android:protectionLevel="signature|setup" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
index de863d7..91a5440 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
@@ -29,6 +29,7 @@
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
+import android.os.Bundle;
import android.os.LocaleList;
import android.os.Parcel;
import android.support.test.InstrumentationRegistry;
@@ -48,6 +49,13 @@
@RunWith(AndroidJUnit4.class)
public class TextClassificationTest {
+ private static final String BUNDLE_KEY = "key";
+ private static final String BUNDLE_VALUE = "value";
+ private static final Bundle BUNDLE = new Bundle();
+ static {
+ BUNDLE.putString(BUNDLE_KEY, BUNDLE_VALUE);
+ }
+
public Icon generateTestIcon(int width, int height, int colorValue) {
final int numPixels = width * height;
final int[] colors = new int[numPixels];
@@ -89,6 +97,7 @@
.setEntityType(TextClassifier.TYPE_ADDRESS, 0.3f)
.setEntityType(TextClassifier.TYPE_PHONE, 0.7f)
.setId(id)
+ .setExtras(BUNDLE)
.build();
// Parcel and unparcel
@@ -119,6 +128,9 @@
assertEquals(TextClassifier.TYPE_ADDRESS, result.getEntity(1));
assertEquals(0.7f, result.getConfidenceScore(TextClassifier.TYPE_PHONE), 1e-7f);
assertEquals(0.3f, result.getConfidenceScore(TextClassifier.TYPE_ADDRESS), 1e-7f);
+
+ // Extras
+ assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
}
@Test
@@ -182,6 +194,7 @@
new TextClassification.Request.Builder(text, 0, text.length())
.setDefaultLocales(new LocaleList(Locale.US, Locale.GERMANY))
.setReferenceTime(referenceTime)
+ .setExtras(BUNDLE)
.build();
// Parcel and unparcel.
@@ -197,5 +210,6 @@
assertEquals(referenceTime, result.getReferenceTime());
assertEquals("en-US,de-DE", result.getDefaultLocales().toLanguageTags());
assertEquals(referenceTime, result.getReferenceTime());
+ assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
}
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 28e92db..8964ee3 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -313,6 +313,7 @@
<permission name="android.permission.INSTALL_PACKAGES"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.LOCAL_MAC_ADDRESS"/>
+ <permission name="android.permission.MANAGE_ACCESSIBILITY"/>
<permission name="android.permission.MANAGE_ACTIVITY_STACKS"/>
<permission name="android.permission.MANAGE_DEVICE_ADMINS"/>
<permission name="android.permission.MANAGE_USB"/>
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
index 3efe655..6f78651 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -48,9 +48,6 @@
* </p>
*/
public class MeasuredText {
- private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
- MeasuredText.class.getClassLoader(), nGetReleaseFunc(), 1024);
-
private long mNativePtr;
private @NonNull char[] mChars;
@@ -166,6 +163,9 @@
* Note: The appendStyle and appendReplacementRun should be called to cover the text length.
*/
public static class Builder {
+ private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+ MeasuredText.class.getClassLoader(), nGetReleaseFunc(), 1024);
+
private long mNativePtr;
private final @NonNull char[] mText;
diff --git a/libs/incident/Android.bp b/libs/incident/Android.bp
new file mode 100644
index 0000000..0619a9c
--- /dev/null
+++ b/libs/incident/Android.bp
@@ -0,0 +1,50 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+ name: "libincident",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-missing-field-initializers",
+ "-Wno-unused-variable",
+ "-Wunused-parameter",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "liblog",
+ "libutils",
+ ],
+
+ aidl: {
+ include_dirs: ["frameworks/base/core/java"],
+ export_aidl_headers: true,
+ },
+
+ srcs: [
+ ":libincident_aidl",
+ "proto/android/os/header.proto",
+ "proto/android/os/metadata.proto",
+ "src/IncidentReportArgs.cpp",
+ ],
+
+ proto: {
+ type: "lite",
+ export_proto_headers: true,
+ },
+
+ export_include_dirs: ["include"],
+}
diff --git a/libs/incident/Android.mk b/libs/incident/Android.mk
deleted file mode 100644
index 08c8346..0000000
--- a/libs/incident/Android.mk
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libincident
-
-LOCAL_CFLAGS := \
- -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
-
-LOCAL_SHARED_LIBRARIES := \
- libbinder \
- liblog \
- libutils
-
-LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/../../core/java
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/include
-
-LOCAL_SRC_FILES := \
- ../../core/java/android/os/IIncidentManager.aidl \
- ../../core/java/android/os/IIncidentReportStatusListener.aidl \
- proto/android/os/header.proto \
- proto/android/os/metadata.proto \
- src/IncidentReportArgs.cpp
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_PROTO_OPTIMIZE_TYPE := lite
-
-include $(BUILD_SHARED_LIBRARY)
-
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
index a842229..3152e65 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
@@ -127,4 +127,17 @@
default void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice,
int state, int bluetoothProfile) {
}
+
+ /**
+ * Called when ACL connection state is changed. It listens to
+ * {@link android.bluetooth.BluetoothDevice#ACTION_ACL_CONNECTED} and {@link
+ * android.bluetooth.BluetoothDevice#ACTION_ACL_DISCONNECTED}
+ *
+ * @param cachedDevice Bluetooth device that changed
+ * @param state the Bluetooth device connection state, the possible values are:
+ * {@link android.bluetooth.BluetoothAdapter#STATE_DISCONNECTED},
+ * {@link android.bluetooth.BluetoothAdapter#STATE_CONNECTED}
+ */
+ default void onAclConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 022bf69..2b7babd0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -119,6 +119,10 @@
addHandler(TelephonyManager.ACTION_PHONE_STATE_CHANGED,
new AudioModeChangedHandler());
+ // ACL connection changed broadcasts
+ addHandler(BluetoothDevice.ACTION_ACL_CONNECTED, new AclStateChangedHandler());
+ addHandler(BluetoothDevice.ACTION_ACL_DISCONNECTED, new AclStateChangedHandler());
+
registerAdapterIntentReceiver();
}
@@ -236,6 +240,15 @@
}
}
+ private void dispatchAclStateChanged(CachedBluetoothDevice activeDevice,
+ int state) {
+ synchronized (mCallbacks) {
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onAclConnectionStateChanged(activeDevice, state);
+ }
+ }
+ }
+
@VisibleForTesting
void addHandler(String action, Handler handler) {
mHandlerMap.put(action, handler);
@@ -447,6 +460,32 @@
}
}
+ private class AclStateChangedHandler implements Handler {
+ @Override
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ final String action = intent.getAction();
+ if (action == null) {
+ Log.w(TAG, "AclStateChangedHandler: action is null");
+ return;
+ }
+ final CachedBluetoothDevice activeDevice = mDeviceManager.findDevice(device);
+ final int state;
+ switch (action) {
+ case BluetoothDevice.ACTION_ACL_CONNECTED:
+ state = BluetoothAdapter.STATE_CONNECTED;
+ break;
+ case BluetoothDevice.ACTION_ACL_DISCONNECTED:
+ state = BluetoothAdapter.STATE_DISCONNECTED;
+ break;
+ default:
+ Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action);
+ return;
+
+ }
+ dispatchAclStateChanged(activeDevice, state);
+ }
+ }
+
private class AudioModeChangedHandler implements Handler {
@Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index 020234c6..c147d5e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -19,7 +19,10 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
@@ -50,6 +53,8 @@
private BluetoothCallback mBluetoothCallback;
@Mock
private CachedBluetoothDevice mCachedBluetoothDevice;
+ @Mock
+ private BluetoothDevice mBluetoothDevice;
private Context mContext;
private Intent mIntent;
@@ -62,6 +67,7 @@
mBluetoothEventManager = new BluetoothEventManager(mLocalAdapter,
mCachedDeviceManager, mContext, /* handler= */ null, /* userHandle= */ null);
+ when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice);
}
@Test
@@ -126,4 +132,28 @@
verify(mBluetoothCallback).onProfileConnectionStateChanged(mCachedBluetoothDevice,
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
}
+
+ @Test
+ public void dispatchAclConnectionStateChanged_aclDisconnected_shouldDispatchCallback() {
+ mBluetoothEventManager.registerCallback(mBluetoothCallback);
+ mIntent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+ mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
+ mContext.sendBroadcast(mIntent);
+
+ verify(mBluetoothCallback).onAclConnectionStateChanged(mCachedBluetoothDevice,
+ BluetoothAdapter.STATE_DISCONNECTED);
+ }
+
+ @Test
+ public void dispatchAclConnectionStateChanged_aclConnected_shouldDispatchCallback() {
+ mBluetoothEventManager.registerCallback(mBluetoothCallback);
+ mIntent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
+ mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
+ mContext.sendBroadcast(mIntent);
+
+ verify(mBluetoothCallback).onAclConnectionStateChanged(mCachedBluetoothDevice,
+ BluetoothAdapter.STATE_CONNECTED);
+ }
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index bf4374a..bca3530 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -116,6 +116,7 @@
public boolean isTransient = false;
public String expandedAccessibilityClassName;
public SlashState slash;
+ public boolean handlesLongClick = true;
public boolean copyTo(State other) {
if (other == null) throw new IllegalArgumentException();
@@ -133,7 +134,8 @@
|| !Objects.equals(other.state, state)
|| !Objects.equals(other.isTransient, isTransient)
|| !Objects.equals(other.dualTarget, dualTarget)
- || !Objects.equals(other.slash, slash);
+ || !Objects.equals(other.slash, slash)
+ || !Objects.equals(other.handlesLongClick, handlesLongClick);
other.icon = icon;
other.iconSupplier = iconSupplier;
other.label = label;
@@ -146,6 +148,7 @@
other.dualTarget = dualTarget;
other.isTransient = isTransient;
other.slash = slash != null ? slash.copy() : null;
+ other.handlesLongClick = handlesLongClick;
return changed;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index d42127e..0638998 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -194,6 +194,7 @@
}
setClickable(state.state != Tile.STATE_UNAVAILABLE);
+ setLongClickable(state.handlesLongClick);
mIcon.setIcon(state, allowAnimations);
setContentDescription(state.contentDescription);
@@ -287,10 +288,14 @@
info.setText(label);
info.setChecked(b);
info.setCheckable(true);
- info.addAction(
- new AccessibilityNodeInfo.AccessibilityAction(
- AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.getId(),
- getResources().getString(R.string.accessibility_long_click_tile)));
+ if (isLongClickable()) {
+ info.addAction(
+ new AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.AccessibilityAction
+ .ACTION_LONG_CLICK.getId(),
+ getResources().getString(
+ R.string.accessibility_long_click_tile)));
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index f2ead1c..d7ac253 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -18,7 +18,6 @@
import android.app.ActivityManager;
import android.content.Intent;
-import android.graphics.drawable.Drawable;
import android.provider.MediaStore;
import android.service.quicksettings.Tile;
import android.widget.Switch;
@@ -50,7 +49,9 @@
@Override
public BooleanState newTileState() {
- return new BooleanState();
+ BooleanState state = new BooleanState();
+ state.handlesLongClick = false;
+ return state;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 38011d9..427d169 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -15,6 +15,7 @@
*/
package com.android.systemui.statusbar;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import android.app.ActivityManager;
@@ -292,7 +293,8 @@
return false;
}
return mShowLockscreenNotifications
- && !getEntryManager().getNotificationData().isAmbient(sbn.getKey());
+ && getEntryManager().getNotificationData().getImportance(sbn.getKey())
+ >= IMPORTANCE_DEFAULT;
}
private void setShowLockscreenNotifications(boolean show) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index c560301..df99a9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -48,6 +48,7 @@
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -124,7 +125,8 @@
// Dismiss action to be launched when we stop dozing or the keyguard is gone.
private DismissWithActionRequest mPendingWakeupAction;
- private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+ private final KeyguardMonitorImpl mKeyguardMonitor =
+ (KeyguardMonitorImpl) Dependency.get(KeyguardMonitor.class);
private final NotificationMediaManager mMediaManager =
Dependency.get(NotificationMediaManager.class);
@@ -202,6 +204,8 @@
public void show(Bundle options) {
mShowing = true;
mStatusBarWindowController.setKeyguardShowing(true);
+ mKeyguardMonitor.notifyKeyguardState(
+ mShowing, mKeyguardMonitor.isSecure(), mKeyguardMonitor.isOccluded());
reset(true /* hideBouncerWhenShowing */);
StatsLog.write(StatsLog.KEYGUARD_STATE_CHANGED,
StatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
@@ -424,6 +428,8 @@
*/
public void hide(long startTime, long fadeoutDuration) {
mShowing = false;
+ mKeyguardMonitor.notifyKeyguardState(
+ mShowing, mKeyguardMonitor.isSecure(), mKeyguardMonitor.isOccluded());
launchPendingWakeupAction();
if (KeyguardUpdateMonitor.getInstance(mContext).needsSlowUnlockTransition()) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index fbceade..097405a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2068,7 +2068,9 @@
&& componentNameToEnable.equals(userState.mServiceToEnableWithShortcut)) {
return false;
}
+
userState.mServiceToEnableWithShortcut = componentNameToEnable;
+ scheduleNotifyClientsOfServicesStateChange(userState);
return true;
}
@@ -2343,10 +2345,10 @@
@Override
public void performAccessibilityShortcut() {
if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)
- && (mContext.checkCallingPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ && (mContext.checkCallingPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
!= PackageManager.PERMISSION_GRANTED)) {
throw new SecurityException(
- "performAccessibilityShortcut requires the WRITE_SECURE_SETTINGS permission");
+ "performAccessibilityShortcut requires the MANAGE_ACCESSIBILITY permission");
}
final Map<ComponentName, ToggleableFrameworkFeatureInfo> frameworkFeatureMap =
AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
@@ -2381,6 +2383,19 @@
}
};
+ @Override
+ public String getAccessibilityShortcutService() {
+ if (mContext.checkCallingPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "getAccessibilityShortcutService requires the MANAGE_ACCESSIBILITY permission");
+ }
+ synchronized(mLock) {
+ final UserState userState = getUserStateLocked(mCurrentUserId);
+ return userState.mServiceToEnableWithShortcut.flattenToString();
+ }
+ }
+
/**
* Enables accessibility service specified by {@param componentName} for the {@param userId}.
*/
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index a72470d..05293b5 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -373,7 +373,8 @@
return getOrCreateStack(windowingMode, activityType, onTop);
}
- private int getNextStackId() {
+ @VisibleForTesting
+ int getNextStackId() {
return sNextFreeStackId++;
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 026c5cc..0049e22 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -48,41 +48,7 @@
import static com.android.server.am.ActivityDisplay.POSITION_BOTTOM;
import static com.android.server.am.ActivityDisplay.POSITION_TOP;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ALL;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_APP;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONTAINERS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SAVED_STATE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STATES;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_APP;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONTAINERS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SAVED_STATE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_USER_LEAVING;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
-import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
import static com.android.server.am.ActivityStack.ActivityState.FINISHING;
@@ -102,8 +68,42 @@
import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
-
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_APP;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONTAINERS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SAVED_STATE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_APP;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONTAINERS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SAVED_STATE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_USER_LEAVING;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+
import static java.lang.Integer.MAX_VALUE;
import android.app.Activity;
@@ -1807,13 +1807,6 @@
return false;
}
- final ActivityRecord top = topRunningActivityLocked();
- if (top == null && isInStackLocked(starting) == null && !isTopStackOnDisplay()) {
- // Shouldn't be visible if you don't have any running activities, not starting one, and
- // not the top stack on display.
- return false;
- }
-
final ActivityDisplay display = getDisplay();
boolean gotSplitScreenStack = false;
boolean gotOpaqueSplitScreenPrimary = false;
@@ -1822,9 +1815,16 @@
final boolean isAssistantType = isActivityTypeAssistant();
for (int i = display.getChildCount() - 1; i >= 0; --i) {
final ActivityStack other = display.getChildAt(i);
+ final boolean hasRunningActivities = other.topRunningActivityLocked() != null;
if (other == this) {
- // Should be visible if there is no other stack occluding it.
- return true;
+ // Should be visible if there is no other stack occluding it, unless it doesn't
+ // have any running activities, not starting one and not home stack.
+ return hasRunningActivities || isInStackLocked(starting) != null
+ || isActivityTypeHome();
+ }
+
+ if (!hasRunningActivities) {
+ continue;
}
final int otherWindowingMode = other.getWindowingMode();
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 6d3a3b6..dc3bfbc 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -1627,7 +1627,8 @@
// fold tethering stats and operations into uid snapshot
final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_UID);
tetherSnapshot.filter(UID_ALL, ifaces, TAG_ALL);
- NetworkStatsFactory.apply464xlatAdjustments(uidSnapshot, tetherSnapshot);
+ NetworkStatsFactory.apply464xlatAdjustments(uidSnapshot, tetherSnapshot,
+ mUseBpfTrafficStats);
uidSnapshot.combineAllValues(tetherSnapshot);
final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
@@ -1637,7 +1638,8 @@
final NetworkStats vtStats = telephonyManager.getVtDataUsage(STATS_PER_UID);
if (vtStats != null) {
vtStats.filter(UID_ALL, ifaces, TAG_ALL);
- NetworkStatsFactory.apply464xlatAdjustments(uidSnapshot, vtStats);
+ NetworkStatsFactory.apply464xlatAdjustments(uidSnapshot, vtStats,
+ mUseBpfTrafficStats);
uidSnapshot.combineAllValues(vtStats);
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
index 0da5742..59c4067 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
@@ -60,8 +60,7 @@
public void testLastFocusedStackIsUpdatedWhenMovingStack() {
// Create a stack at bottom.
final ActivityDisplay display = mSupervisor.getDefaultDisplay();
- final ActivityStack stack = display.createStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, !ON_TOP);
+ final ActivityStack stack = new StackBuilder(mSupervisor).setOnTop(!ON_TOP).build();
final ActivityStack prevFocusedStack = display.getFocusedStack();
stack.moveToFront("moveStackToFront");
@@ -140,16 +139,14 @@
*/
@Test
public void testTopRunningActivity() {
- // Create stack to hold focus.
final ActivityDisplay display = mSupervisor.getDefaultDisplay();
- final ActivityStack emptyStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
final KeyguardController keyguard = mSupervisor.getKeyguardController();
- final ActivityStack stack = mSupervisor.getDefaultDisplay().createStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
- .setStack(stack).build();
+ final ActivityStack stack = new StackBuilder(mSupervisor).build();
+ final ActivityRecord activity = stack.getTopActivity();
+
+ // Create empty stack on top.
+ final ActivityStack emptyStack =
+ new StackBuilder(mSupervisor).setCreateActivity(false).build();
// Make sure the top running activity is not affected when keyguard is not locked.
assertTopRunningActivity(activity, display);
@@ -159,8 +156,8 @@
assertEquals(activity, display.topRunningActivity());
assertNull(display.topRunningActivity(true /* considerKeyguardState */));
- // Change focus to stack with activity.
- stack.moveToFront("focusChangeToTestStack");
+ // Move stack with activity to top.
+ stack.moveToFront("testStackToFront");
assertEquals(stack, display.getFocusedStack());
assertEquals(activity, display.topRunningActivity());
assertNull(display.topRunningActivity(true /* considerKeyguardState */));
@@ -175,11 +172,10 @@
// Ensure the show when locked activity is returned.
assertTopRunningActivity(showWhenLockedActivity, display);
- // Change focus back to empty stack.
- emptyStack.moveToFront("focusChangeToEmptyStack");
- assertEquals(emptyStack, display.getFocusedStack());
- // If there is no running activity in focused stack, the running activity in next focusable
- // stack should be returned.
+ // Move empty stack to front. The running activity in focusable stack which below the
+ // empty stack should be returned.
+ emptyStack.moveToFront("emptyStackToFront");
+ assertEquals(stack, display.getFocusedStack());
assertTopRunningActivity(showWhenLockedActivity, display);
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index ffc7fa2..1015008 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -79,10 +79,9 @@
super.setUp();
setupActivityTaskManagerService();
- mStack = mSupervisor.getDefaultDisplay().createStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
- mActivity = new ActivityBuilder(mService).setTask(mTask).build();
+ mStack = new StackBuilder(mSupervisor).build();
+ mTask = mStack.getChildAt(0);
+ mActivity = mTask.getTopActivity();
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 2c993d3..bdceec7 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -359,15 +359,12 @@
public void testFindTaskToMoveToFrontWhenRecentsOnTop() throws Exception {
// Create stack/task on default display.
final ActivityDisplay display = mSupervisor.getDefaultDisplay();
- final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, false /* onTop */);
- final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+ final TestActivityStack targetStack = new StackBuilder(mSupervisor).setOnTop(false).build();
+ final TaskRecord targetTask = targetStack.getChildAt(0);
// Create Recents on top of the display.
- final ActivityStack stack = display.createStack(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_RECENTS, true /* onTop */);
- final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
- new ActivityBuilder(mService).setTask(task).build();
+ final ActivityStack stack =
+ new StackBuilder(mSupervisor).setActivityType(ACTIVITY_TYPE_RECENTS).build();
final String reason = "findTaskToMoveToFront";
mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 53f67af..ea33de74 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -26,8 +26,8 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
-import static com.android.server.am.ActivityStack.ActivityState.FINISHING;
import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
@@ -361,14 +361,12 @@
translucentStack.topRunningActivityLocked();
topRunningTranslucentActivity.finishing = true;
- // Home shouldn't be visible since its activity is marked as finishing and it isn't the top
- // of the stack list.
- assertFalse(homeStack.shouldBeVisible(null /* starting */));
+ // Home stack should be visible even there are no running activities.
+ assertTrue(homeStack.shouldBeVisible(null /* starting */));
// Home should be visible if we are starting an activity within it.
assertTrue(homeStack.shouldBeVisible(topRunningHomeActivity /* starting */));
- // The translucent stack should be visible since it is the top of the stack list even though
- // it has its activity marked as finishing.
- assertTrue(translucentStack.shouldBeVisible(null /* starting */));
+ // The translucent stack shouldn't be visible since its activity marked as finishing.
+ assertFalse(translucentStack.shouldBeVisible(null /* starting */));
}
@Test
@@ -672,9 +670,10 @@
// There is still an activity1 in stack1 so the activity2 should be added to finishing list
// that will be destroyed until idle.
+ stack2.getTopActivity().visible = true;
final ActivityRecord activity2 = finishCurrentActivity(stack2);
- assertEquals(FINISHING, activity2.getState());
- assertTrue(mSupervisor.mFinishingActivities.contains(activity2));
+ assertEquals(STOPPING, activity2.getState());
+ assertTrue(mSupervisor.mStoppingActivities.contains(activity2));
// The display becomes empty. Since there is no next activity to be idle, the activity
// should be destroyed immediately with updating configuration to restore original state.
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 094241e..adf861b 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -39,14 +39,6 @@
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
-import android.content.pm.PackageManagerInternal;
-import com.android.server.uri.UriGrantsManagerInternal;
-import com.android.server.wm.ActivityTaskManagerInternal;
-import com.android.server.wm.DisplayWindowController;
-
-import org.junit.Rule;
-import org.mockito.invocation.InvocationOnMock;
-
import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.Context;
@@ -54,6 +46,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
@@ -73,7 +66,10 @@
import com.android.server.AppOpsService;
import com.android.server.AttributeCache;
import com.android.server.ServiceThread;
+import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.AppWindowContainerController;
+import com.android.server.wm.DisplayWindowController;
import com.android.server.wm.PinnedStackWindowController;
import com.android.server.wm.RootWindowContainerController;
import com.android.server.wm.StackWindowController;
@@ -83,7 +79,9 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
import java.io.File;
import java.util.List;
@@ -608,23 +606,9 @@
@Override
<T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
int stackId, boolean onTop) {
- if (windowingMode == WINDOWING_MODE_PINNED) {
- return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop) {
- @Override
- Rect getDefaultPictureInPictureBounds(float aspectRatio) {
- return new Rect(50, 50, 100, 100);
- }
-
- @Override
- PinnedStackWindowController createStackWindowController(int displayId,
- boolean onTop, Rect outBounds) {
- return mock(PinnedStackWindowController.class);
- }
- };
- } else {
- return (T) new TestActivityStack(
- this, stackId, mSupervisor, windowingMode, activityType, onTop);
- }
+ return new StackBuilder(mSupervisor).setDisplay(this)
+ .setWindowingMode(windowingMode).setActivityType(activityType)
+ .setStackId(stackId).setOnTop(onTop).setCreateActivity(false).build();
}
@Override
@@ -677,8 +661,19 @@
private int mSupportsSplitScreen = SUPPORTS_SPLIT_SCREEN_UNSET;
TestActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
- int windowingMode, int activityType, boolean onTop) {
+ int windowingMode, int activityType, boolean onTop, boolean createActivity) {
super(display, stackId, supervisor, windowingMode, activityType, onTop);
+ if (createActivity) {
+ new ActivityBuilder(mService).setCreateTask(true).setStack(this).build();
+ if (onTop) {
+ // We move the task to front again in order to regain focus after activity
+ // added to the stack. Or {@link ActivityDisplay#mPreferredTopFocusableStack}
+ // could be other stacks (e.g. home stack).
+ moveToFront("createActivityStack");
+ } else {
+ moveToBack("createActivityStack", null);
+ }
+ }
}
@Override
@@ -750,4 +745,71 @@
ActivityOptions options) {
}
}
+
+ protected static class StackBuilder {
+ private final ActivityStackSupervisor mSupervisor;
+ private ActivityDisplay mDisplay;
+ private int mStackId = -1;
+ private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
+ private int mActivityType = ACTIVITY_TYPE_STANDARD;
+ private boolean mOnTop = true;
+ private boolean mCreateActivity = true;
+
+ StackBuilder(ActivityStackSupervisor supervisor) {
+ mSupervisor = supervisor;
+ mDisplay = mSupervisor.getDefaultDisplay();
+ }
+
+ StackBuilder setWindowingMode(int windowingMode) {
+ mWindowingMode = windowingMode;
+ return this;
+ }
+
+ StackBuilder setActivityType(int activityType) {
+ mActivityType = activityType;
+ return this;
+ }
+
+ StackBuilder setStackId(int stackId) {
+ mStackId = stackId;
+ return this;
+ }
+
+ StackBuilder setDisplay(ActivityDisplay display) {
+ mDisplay = display;
+ return this;
+ }
+
+ StackBuilder setOnTop(boolean onTop) {
+ mOnTop = onTop;
+ return this;
+ }
+
+ StackBuilder setCreateActivity(boolean createActivity) {
+ mCreateActivity = createActivity;
+ return this;
+ }
+
+ <T extends ActivityStack> T build() {
+ final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId();
+ if (mWindowingMode == WINDOWING_MODE_PINNED) {
+ return (T) new PinnedActivityStack(mDisplay, stackId, mSupervisor, mOnTop) {
+ @Override
+ Rect getDefaultPictureInPictureBounds(float aspectRatio) {
+ return new Rect(50, 50, 100, 100);
+ }
+
+ @Override
+ PinnedStackWindowController createStackWindowController(int displayId,
+ boolean onTop, Rect outBounds) {
+ return mock(PinnedStackWindowController.class);
+ }
+ };
+ } else {
+ return (T) new TestActivityStack(mDisplay, stackId, mSupervisor, mWindowingMode,
+ mActivityType, mOnTop, mCreateActivity);
+ }
+ }
+
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 27e8c63..129b835 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -883,7 +883,7 @@
MyTestActivityStack(ActivityDisplay display, ActivityStackSupervisor supervisor) {
super(display, LAST_STACK_ID++, supervisor, WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, true);
+ ACTIVITY_TYPE_STANDARD, true /* onTop */, false /* createActivity */);
mDisplay = display;
}
diff --git a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
index aa3046f..849a411 100644
--- a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
@@ -16,9 +16,7 @@
package com.android.server.am;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -30,7 +28,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.platform.test.annotations.Presubmit;
-import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
@@ -72,8 +69,8 @@
final int numStacks = 2;
for (int stackIndex = 0; stackIndex < numStacks; stackIndex++) {
- final ActivityStack stack = new TestActivityStack(display, stackIndex, mSupervisor,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true);
+ final ActivityStack stack =
+ new StackBuilder(mSupervisor).setCreateActivity(false).build();
display.addChild(stack, POSITION_BOTTOM);
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
index 047addd..793d6b0 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
@@ -61,6 +61,7 @@
private static final long TIME_30_MIN = 30 * 60_000L;
private static final long TIME_10_MIN = 10 * 60_000L;
+ private static final long TIME_1_MIN = 10 * 60_000L;
private static final long MAX_OBSERVER_PER_UID = 10;
private static final long MIN_TIME_LIMIT = 4_000L;
@@ -77,7 +78,8 @@
PKG_GAME1, PKG_GAME2
};
- private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
+ private CountDownLatch mLimitReachedLatch = new CountDownLatch(1);
+ private CountDownLatch mSessionEndLatch = new CountDownLatch(1);
private AppTimeLimitController mController;
@@ -85,18 +87,24 @@
private long mUptimeMillis;
- AppTimeLimitController.OnLimitReachedListener mListener
- = new AppTimeLimitController.OnLimitReachedListener() {
+ AppTimeLimitController.TimeLimitCallbackListener mListener =
+ new AppTimeLimitController.TimeLimitCallbackListener() {
+ @Override
+ public void onLimitReached(int observerId, int userId, long timeLimit,
+ long timeElapsed,
+ PendingIntent callbackIntent) {
+ mLimitReachedLatch.countDown();
+ }
- @Override
- public void onLimitReached(int observerId, int userId, long timeLimit, long timeElapsed,
- PendingIntent callbackIntent) {
- mCountDownLatch.countDown();
- }
- };
+ @Override
+ public void onSessionEnd(int observerId, int userId, long timeElapsed,
+ PendingIntent callbackIntent) {
+ mSessionEndLatch.countDown();
+ }
+ };
class MyAppTimeLimitController extends AppTimeLimitController {
- MyAppTimeLimitController(AppTimeLimitController.OnLimitReachedListener listener,
+ MyAppTimeLimitController(AppTimeLimitController.TimeLimitCallbackListener listener,
Looper looper) {
super(listener, looper);
}
@@ -107,7 +115,12 @@
}
@Override
- protected long getObserverPerUidLimit() {
+ protected long getAppUsageObserverPerUidLimit() {
+ return MAX_OBSERVER_PER_UID;
+ }
+
+ @Override
+ protected long getUsageSessionObserverPerUidLimit() {
return MAX_OBSERVER_PER_UID;
}
@@ -129,188 +142,551 @@
mThread.quit();
}
- /** Verify observer is added */
+ /** Verify app usage observer is added */
@Test
- public void testAddObserver() {
- addObserver(OBS_ID1, GROUP1, TIME_30_MIN);
- assertTrue("Observer wasn't added", hasObserver(OBS_ID1));
- addObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN);
- assertTrue("Observer wasn't added", hasObserver(OBS_ID2));
- assertTrue("Observer wasn't added", hasObserver(OBS_ID1));
+ public void testAppUsageObserver_AddObserver() {
+ addAppUsageObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ assertTrue("Observer wasn't added", hasAppUsageObserver(UID, OBS_ID1));
+ addAppUsageObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN);
+ assertTrue("Observer wasn't added", hasAppUsageObserver(UID, OBS_ID2));
+ assertTrue("Observer wasn't added", hasAppUsageObserver(UID, OBS_ID1));
}
- /** Verify observer is removed */
+ /** Verify usage session observer is added */
@Test
- public void testRemoveObserver() {
- addObserver(OBS_ID1, GROUP1, TIME_30_MIN);
- assertTrue("Observer wasn't added", hasObserver(OBS_ID1));
- mController.removeObserver(UID, OBS_ID1, USER_ID);
- assertFalse("Observer wasn't removed", hasObserver(OBS_ID1));
+ public void testUsageSessionObserver_AddObserver() {
+ addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
+ assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID1));
+ addUsageSessionObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN, TIME_1_MIN);
+ assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID2));
+ assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID1));
+ }
+
+ /** Verify app usage observer is removed */
+ @Test
+ public void testAppUsageObserver_RemoveObserver() {
+ addAppUsageObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ assertTrue("Observer wasn't added", hasAppUsageObserver(UID, OBS_ID1));
+ mController.removeAppUsageObserver(UID, OBS_ID1, USER_ID);
+ assertFalse("Observer wasn't removed", hasAppUsageObserver(UID, OBS_ID1));
+ }
+
+ /** Verify usage session observer is removed */
+ @Test
+ public void testUsageSessionObserver_RemoveObserver() {
+ addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
+ assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID1));
+ mController.removeUsageSessionObserver(UID, OBS_ID1, USER_ID);
+ assertFalse("Observer wasn't removed", hasUsageSessionObserver(UID, OBS_ID1));
}
/** Re-adding an observer should result in only one copy */
@Test
- public void testObserverReAdd() {
- addObserver(OBS_ID1, GROUP1, TIME_30_MIN);
- assertTrue("Observer wasn't added", hasObserver(OBS_ID1));
- addObserver(OBS_ID1, GROUP1, TIME_10_MIN);
+ public void testAppUsageObserver_ObserverReAdd() {
+ addAppUsageObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ assertTrue("Observer wasn't added", hasAppUsageObserver(UID, OBS_ID1));
+ addAppUsageObserver(OBS_ID1, GROUP1, TIME_10_MIN);
assertTrue("Observer wasn't added",
- mController.getObserverGroup(OBS_ID1, USER_ID).timeLimit == TIME_10_MIN);
- mController.removeObserver(UID, OBS_ID1, USER_ID);
- assertFalse("Observer wasn't removed", hasObserver(OBS_ID1));
+ mController.getAppUsageGroup(UID, OBS_ID1).getTimeLimitMs() == TIME_10_MIN);
+ mController.removeAppUsageObserver(UID, OBS_ID1, USER_ID);
+ assertFalse("Observer wasn't removed", hasAppUsageObserver(UID, OBS_ID1));
+ }
+
+ /** Re-adding an observer should result in only one copy */
+ @Test
+ public void testUsageSessionObserver_ObserverReAdd() {
+ addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
+ assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID1));
+ addUsageSessionObserver(OBS_ID1, GROUP1, TIME_10_MIN, TIME_1_MIN);
+ assertTrue("Observer wasn't added",
+ mController.getSessionUsageGroup(UID, OBS_ID1).getTimeLimitMs() == TIME_10_MIN);
+ mController.removeUsageSessionObserver(UID, OBS_ID1, USER_ID);
+ assertFalse("Observer wasn't removed", hasUsageSessionObserver(UID, OBS_ID1));
+ }
+
+ /** Different type observers can be registered to the same observerId value */
+ @Test
+ public void testAllObservers_ExclusiveObserverIds() {
+ addAppUsageObserver(OBS_ID1, GROUP1, TIME_10_MIN);
+ addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
+ assertTrue("Observer wasn't added", hasAppUsageObserver(UID, OBS_ID1));
+ assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID1));
+
+ AppTimeLimitController.UsageGroup appUsageGroup = mController.getAppUsageGroup(UID,
+ OBS_ID1);
+ AppTimeLimitController.UsageGroup sessionUsageGroup = mController.getSessionUsageGroup(UID,
+ OBS_ID1);
+
+ // Verify data still intact
+ assertEquals(TIME_10_MIN, appUsageGroup.getTimeLimitMs());
+ assertEquals(TIME_30_MIN, sessionUsageGroup.getTimeLimitMs());
}
/** Verify that usage across different apps within a group are added up */
@Test
- public void testAccumulation() throws Exception {
+ public void testAppUsageObserver_Accumulation() throws Exception {
setTime(0L);
- addObserver(OBS_ID1, GROUP1, TIME_30_MIN);
- moveToForeground(PKG_SOC1);
+ addAppUsageObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ startUsage(PKG_SOC1);
// Add 10 mins
setTime(TIME_10_MIN);
- moveToBackground(PKG_SOC1);
+ stopUsage(PKG_SOC1);
- long timeRemaining = mController.getObserverGroup(OBS_ID1, USER_ID).timeRemaining;
+ AppTimeLimitController.UsageGroup group = mController.getAppUsageGroup(UID, OBS_ID1);
+
+ long timeRemaining = group.getTimeLimitMs() - group.getUsageTimeMs();
assertEquals(TIME_10_MIN * 2, timeRemaining);
- moveToForeground(PKG_SOC1);
+ startUsage(PKG_SOC1);
setTime(TIME_10_MIN * 2);
- moveToBackground(PKG_SOC1);
+ stopUsage(PKG_SOC1);
- timeRemaining = mController.getObserverGroup(OBS_ID1, USER_ID).timeRemaining;
+ timeRemaining = group.getTimeLimitMs() - group.getUsageTimeMs();
assertEquals(TIME_10_MIN, timeRemaining);
setTime(TIME_30_MIN);
- assertFalse(mCountDownLatch.await(100L, TimeUnit.MILLISECONDS));
+ assertFalse(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
// Add a different package in the group
- moveToForeground(PKG_GAME1);
+ startUsage(PKG_GAME1);
setTime(TIME_30_MIN + TIME_10_MIN);
- moveToBackground(PKG_GAME1);
+ stopUsage(PKG_GAME1);
- assertEquals(0, mController.getObserverGroup(OBS_ID1, USER_ID).timeRemaining);
- assertTrue(mCountDownLatch.await(100L, TimeUnit.MILLISECONDS));
+ assertEquals(0, group.getTimeLimitMs() - group.getUsageTimeMs());
+ assertTrue(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
+ }
+
+ /** Verify that usage across different apps within a group are added up */
+ @Test
+ public void testUsageSessionObserver_Accumulation() throws Exception {
+ setTime(0L);
+ addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
+ startUsage(PKG_SOC1);
+ // Add 10 mins
+ setTime(TIME_10_MIN);
+ stopUsage(PKG_SOC1);
+
+ AppTimeLimitController.UsageGroup group = mController.getSessionUsageGroup(UID, OBS_ID1);
+
+ long timeRemaining = group.getTimeLimitMs() - group.getUsageTimeMs();
+ assertEquals(TIME_10_MIN * 2, timeRemaining);
+
+ startUsage(PKG_SOC1);
+ setTime(TIME_10_MIN * 2);
+ stopUsage(PKG_SOC1);
+
+ timeRemaining = group.getTimeLimitMs() - group.getUsageTimeMs();
+ assertEquals(TIME_10_MIN, timeRemaining);
+
+ setTime(TIME_30_MIN);
+
+ assertFalse(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
+
+ // Add a different package in the group
+ startUsage(PKG_GAME1);
+ setTime(TIME_30_MIN + TIME_10_MIN);
+ stopUsage(PKG_GAME1);
+
+ assertEquals(0, group.getTimeLimitMs() - group.getUsageTimeMs());
+ assertTrue(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
}
/** Verify that time limit does not get triggered due to a different app */
@Test
- public void testTimeoutOtherApp() throws Exception {
+ public void testAppUsageObserver_TimeoutOtherApp() throws Exception {
setTime(0L);
- addObserver(OBS_ID1, GROUP1, 4_000L);
- moveToForeground(PKG_SOC2);
- assertFalse(mCountDownLatch.await(6_000L, TimeUnit.MILLISECONDS));
+ addAppUsageObserver(OBS_ID1, GROUP1, 4_000L);
+ startUsage(PKG_SOC2);
+ assertFalse(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
setTime(6_000L);
- moveToBackground(PKG_SOC2);
- assertFalse(mCountDownLatch.await(100L, TimeUnit.MILLISECONDS));
+ stopUsage(PKG_SOC2);
+ assertFalse(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
+ }
+
+ /** Verify that time limit does not get triggered due to a different app */
+ @Test
+ public void testUsageSessionObserver_TimeoutOtherApp() throws Exception {
+ setTime(0L);
+ addUsageSessionObserver(OBS_ID1, GROUP1, 4_000L, 1_000L);
+ startUsage(PKG_SOC2);
+ assertFalse(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
+ setTime(6_000L);
+ stopUsage(PKG_SOC2);
+ assertFalse(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
+
}
/** Verify the timeout message is delivered at the right time */
@Test
- public void testTimeout() throws Exception {
+ public void testAppUsageObserver_Timeout() throws Exception {
setTime(0L);
- addObserver(OBS_ID1, GROUP1, 4_000L);
- moveToForeground(PKG_SOC1);
+ addAppUsageObserver(OBS_ID1, GROUP1, 4_000L);
+ startUsage(PKG_SOC1);
setTime(6_000L);
- assertTrue(mCountDownLatch.await(6_000L, TimeUnit.MILLISECONDS));
- moveToBackground(PKG_SOC1);
+ assertTrue(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
+ stopUsage(PKG_SOC1);
// Verify that the observer was removed
- assertFalse(hasObserver(OBS_ID1));
+ assertFalse(hasAppUsageObserver(UID, OBS_ID1));
+ }
+
+ /** Verify the timeout message is delivered at the right time */
+ @Test
+ public void testUsageSessionObserver_Timeout() throws Exception {
+ setTime(0L);
+ addUsageSessionObserver(OBS_ID1, GROUP1, 4_000L, 1_000L);
+ startUsage(PKG_SOC1);
+ setTime(6_000L);
+ assertTrue(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
+ stopUsage(PKG_SOC1);
+ // Usage has stopped, Session should end in a second. Verify session end occurs in a second
+ // (+/- 100ms, which is hopefully not too slim a margin)
+ assertFalse(mSessionEndLatch.await(900L, TimeUnit.MILLISECONDS));
+ assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
+ // Verify that the observer was not removed
+ assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
}
/** If an app was already running, make sure it is partially counted towards the time limit */
@Test
- public void testAlreadyRunning() throws Exception {
+ public void testAppUsageObserver_AlreadyRunning() throws Exception {
setTime(TIME_10_MIN);
- moveToForeground(PKG_GAME1);
+ startUsage(PKG_GAME1);
setTime(TIME_30_MIN);
- addObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN);
+ addAppUsageObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN);
setTime(TIME_30_MIN + TIME_10_MIN);
- moveToBackground(PKG_GAME1);
- assertFalse(mCountDownLatch.await(1000L, TimeUnit.MILLISECONDS));
+ stopUsage(PKG_GAME1);
+ assertFalse(mLimitReachedLatch.await(1_000L, TimeUnit.MILLISECONDS));
- moveToForeground(PKG_GAME2);
+ startUsage(PKG_GAME2);
setTime(TIME_30_MIN + TIME_30_MIN);
- moveToBackground(PKG_GAME2);
- assertTrue(mCountDownLatch.await(1000L, TimeUnit.MILLISECONDS));
+ stopUsage(PKG_GAME2);
+ assertTrue(mLimitReachedLatch.await(1_000L, TimeUnit.MILLISECONDS));
// Verify that the observer was removed
- assertFalse(hasObserver(OBS_ID2));
+ assertFalse(hasAppUsageObserver(UID, OBS_ID2));
+ }
+
+ /** If an app was already running, make sure it is partially counted towards the time limit */
+ @Test
+ public void testUsageSessionObserver_AlreadyRunning() throws Exception {
+ setTime(TIME_10_MIN);
+ startUsage(PKG_GAME1);
+ setTime(TIME_30_MIN);
+ addUsageSessionObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN, TIME_1_MIN);
+ setTime(TIME_30_MIN + TIME_10_MIN);
+ stopUsage(PKG_GAME1);
+ assertFalse(mLimitReachedLatch.await(1_000L, TimeUnit.MILLISECONDS));
+
+ startUsage(PKG_GAME2);
+ setTime(TIME_30_MIN + TIME_30_MIN);
+ stopUsage(PKG_GAME2);
+ assertTrue(mLimitReachedLatch.await(1_000L, TimeUnit.MILLISECONDS));
+ // Verify that the observer was removed
+ assertTrue(hasUsageSessionObserver(UID, OBS_ID2));
}
/** If watched app is already running, verify the timeout callback happens at the right time */
@Test
- public void testAlreadyRunningTimeout() throws Exception {
+ public void testAppUsageObserver_AlreadyRunningTimeout() throws Exception {
setTime(0);
- moveToForeground(PKG_SOC1);
+ startUsage(PKG_SOC1);
setTime(TIME_10_MIN);
// 10 second time limit
- addObserver(OBS_ID1, GROUP_SOC, 10_000L);
+ addAppUsageObserver(OBS_ID1, GROUP_SOC, 10_000L);
setTime(TIME_10_MIN + 5_000L);
// Shouldn't call back in 6 seconds
- assertFalse(mCountDownLatch.await(6_000L, TimeUnit.MILLISECONDS));
+ assertFalse(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
setTime(TIME_10_MIN + 10_000L);
// Should call back by 11 seconds (6 earlier + 5 now)
- assertTrue(mCountDownLatch.await(5_000L, TimeUnit.MILLISECONDS));
+ assertTrue(mLimitReachedLatch.await(5_000L, TimeUnit.MILLISECONDS));
// Verify that the observer was removed
- assertFalse(hasObserver(OBS_ID1));
+ assertFalse(hasAppUsageObserver(UID, OBS_ID1));
}
- /** Verify that App Time Limit Controller will limit the number of observerIds */
+ /** If watched app is already running, verify the timeout callback happens at the right time */
@Test
- public void testMaxObserverLimit() throws Exception {
+ public void testUsageSessionObserver_AlreadyRunningTimeout() throws Exception {
+ setTime(0);
+ startUsage(PKG_SOC1);
+ setTime(TIME_10_MIN);
+ // 10 second time limit
+ addUsageSessionObserver(OBS_ID1, GROUP_SOC, 10_000L, 1_000L);
+ setTime(TIME_10_MIN + 5_000L);
+ // Shouldn't call back in 6 seconds
+ assertFalse(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
+ setTime(TIME_10_MIN + 10_000L);
+ // Should call back by 11 seconds (6 earlier + 5 now)
+ assertTrue(mLimitReachedLatch.await(5_000L, TimeUnit.MILLISECONDS));
+ stopUsage(PKG_SOC1);
+ // Usage has stopped, Session should end in a second. Verify session end occurs in a second
+ // (+/- 100ms, which is hopefully not too slim a margin)
+ assertFalse(mSessionEndLatch.await(900L, TimeUnit.MILLISECONDS));
+ assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
+ // Verify that the observer was removed
+ assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
+ }
+
+ /**
+ * Verify that App Time Limit Controller will limit the number of observerIds for app usage
+ * observers
+ */
+ @Test
+ public void testAppUsageObserver_MaxObserverLimit() throws Exception {
boolean receivedException = false;
int ANOTHER_UID = UID + 1;
- addObserver(OBS_ID1, GROUP1, TIME_30_MIN);
- addObserver(OBS_ID2, GROUP1, TIME_30_MIN);
- addObserver(OBS_ID3, GROUP1, TIME_30_MIN);
- addObserver(OBS_ID4, GROUP1, TIME_30_MIN);
- addObserver(OBS_ID5, GROUP1, TIME_30_MIN);
- addObserver(OBS_ID6, GROUP1, TIME_30_MIN);
- addObserver(OBS_ID7, GROUP1, TIME_30_MIN);
- addObserver(OBS_ID8, GROUP1, TIME_30_MIN);
- addObserver(OBS_ID9, GROUP1, TIME_30_MIN);
- addObserver(OBS_ID10, GROUP1, TIME_30_MIN);
+ addAppUsageObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ addAppUsageObserver(OBS_ID2, GROUP1, TIME_30_MIN);
+ addAppUsageObserver(OBS_ID3, GROUP1, TIME_30_MIN);
+ addAppUsageObserver(OBS_ID4, GROUP1, TIME_30_MIN);
+ addAppUsageObserver(OBS_ID5, GROUP1, TIME_30_MIN);
+ addAppUsageObserver(OBS_ID6, GROUP1, TIME_30_MIN);
+ addAppUsageObserver(OBS_ID7, GROUP1, TIME_30_MIN);
+ addAppUsageObserver(OBS_ID8, GROUP1, TIME_30_MIN);
+ addAppUsageObserver(OBS_ID9, GROUP1, TIME_30_MIN);
+ addAppUsageObserver(OBS_ID10, GROUP1, TIME_30_MIN);
// Readding an observer should not cause an IllegalStateException
- addObserver(OBS_ID5, GROUP1, TIME_30_MIN);
+ addAppUsageObserver(OBS_ID5, GROUP1, TIME_30_MIN);
// Adding an observer for a different uid shouldn't cause an IllegalStateException
- mController.addObserver(ANOTHER_UID, OBS_ID11, GROUP1, TIME_30_MIN, null, USER_ID);
+ mController.addAppUsageObserver(ANOTHER_UID, OBS_ID11, GROUP1, TIME_30_MIN, null, USER_ID);
try {
- addObserver(OBS_ID11, GROUP1, TIME_30_MIN);
+ addAppUsageObserver(OBS_ID11, GROUP1, TIME_30_MIN);
} catch (IllegalStateException ise) {
receivedException = true;
}
assertTrue("Should have caused an IllegalStateException", receivedException);
}
- /** Verify that addObserver minimum time limit is one minute */
+ /**
+ * Verify that App Time Limit Controller will limit the number of observerIds for usage session
+ * observers
+ */
@Test
- public void testMinimumTimeLimit() throws Exception {
+ public void testUsageSessionObserver_MaxObserverLimit() throws Exception {
+ addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
+ boolean receivedException = false;
+ int ANOTHER_UID = UID + 1;
+ addUsageSessionObserver(OBS_ID2, GROUP1, TIME_30_MIN, TIME_1_MIN);
+ addUsageSessionObserver(OBS_ID3, GROUP1, TIME_30_MIN, TIME_1_MIN);
+ addUsageSessionObserver(OBS_ID4, GROUP1, TIME_30_MIN, TIME_1_MIN);
+ addUsageSessionObserver(OBS_ID5, GROUP1, TIME_30_MIN, TIME_1_MIN);
+ addUsageSessionObserver(OBS_ID6, GROUP1, TIME_30_MIN, TIME_1_MIN);
+ addUsageSessionObserver(OBS_ID7, GROUP1, TIME_30_MIN, TIME_1_MIN);
+ addUsageSessionObserver(OBS_ID8, GROUP1, TIME_30_MIN, TIME_1_MIN);
+ addUsageSessionObserver(OBS_ID9, GROUP1, TIME_30_MIN, TIME_1_MIN);
+ addUsageSessionObserver(OBS_ID10, GROUP1, TIME_30_MIN, TIME_1_MIN);
+ // Readding an observer should not cause an IllegalStateException
+ addUsageSessionObserver(OBS_ID5, GROUP1, TIME_30_MIN, TIME_1_MIN);
+ // Adding an observer for a different uid shouldn't cause an IllegalStateException
+ mController.addUsageSessionObserver(ANOTHER_UID, OBS_ID11, GROUP1, TIME_30_MIN, TIME_1_MIN,
+ null, null, USER_ID);
+ try {
+ addUsageSessionObserver(OBS_ID11, GROUP1, TIME_30_MIN, TIME_1_MIN);
+ } catch (IllegalStateException ise) {
+ receivedException = true;
+ }
+ assertTrue("Should have caused an IllegalStateException", receivedException);
+ }
+
+ /** Verify that addAppUsageObserver minimum time limit is one minute */
+ @Test
+ public void testAppUsageObserver_MinimumTimeLimit() throws Exception {
boolean receivedException = false;
// adding an observer with a one minute time limit should not cause an exception
- addObserver(OBS_ID1, GROUP1, MIN_TIME_LIMIT);
+ addAppUsageObserver(OBS_ID1, GROUP1, MIN_TIME_LIMIT);
try {
- addObserver(OBS_ID1, GROUP1, MIN_TIME_LIMIT - 1);
+ addAppUsageObserver(OBS_ID1, GROUP1, MIN_TIME_LIMIT - 1);
} catch (IllegalArgumentException iae) {
receivedException = true;
}
assertTrue("Should have caused an IllegalArgumentException", receivedException);
}
- private void moveToForeground(String packageName) {
- mController.moveToForeground(packageName, "class", USER_ID);
+ /** Verify that addUsageSessionObserver minimum time limit is one minute */
+ @Test
+ public void testUsageSessionObserver_MinimumTimeLimit() throws Exception {
+ boolean receivedException = false;
+ // test also for session observers
+ addUsageSessionObserver(OBS_ID10, GROUP1, MIN_TIME_LIMIT, TIME_1_MIN);
+ try {
+ addUsageSessionObserver(OBS_ID10, GROUP1, MIN_TIME_LIMIT - 1, TIME_1_MIN);
+ } catch (IllegalArgumentException iae) {
+ receivedException = true;
+ }
+ assertTrue("Should have caused an IllegalArgumentException", receivedException);
}
- private void moveToBackground(String packageName) {
- mController.moveToBackground(packageName, "class", USER_ID);
+ /** Verify that concurrent usage from multiple apps in the same group will counted correctly */
+ @Test
+ public void testAppUsageObserver_ConcurrentUsage() throws Exception {
+ setTime(0L);
+ addAppUsageObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ AppTimeLimitController.UsageGroup group = mController.getAppUsageGroup(UID, OBS_ID1);
+ startUsage(PKG_SOC1);
+ // Add 10 mins
+ setTime(TIME_10_MIN);
+
+ // Add a different package in the group will first package is still in use
+ startUsage(PKG_GAME1);
+ setTime(TIME_10_MIN * 2);
+ // Stop first package usage
+ stopUsage(PKG_SOC1);
+
+ setTime(TIME_30_MIN);
+ stopUsage(PKG_GAME1);
+
+ assertEquals(TIME_30_MIN, group.getUsageTimeMs());
+ assertTrue(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
}
- private void addObserver(int observerId, String[] packages, long timeLimit) {
- mController.addObserver(UID, observerId, packages, timeLimit, null, USER_ID);
+ /** Verify that concurrent usage from multiple apps in the same group will counted correctly */
+ @Test
+ public void testUsageSessionObserver_ConcurrentUsage() throws Exception {
+ setTime(0L);
+ addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
+ AppTimeLimitController.UsageGroup group = mController.getSessionUsageGroup(UID, OBS_ID1);
+ startUsage(PKG_SOC1);
+ // Add 10 mins
+ setTime(TIME_10_MIN);
+
+ // Add a different package in the group will first package is still in use
+ startUsage(PKG_GAME1);
+ setTime(TIME_10_MIN * 2);
+ // Stop first package usage
+ stopUsage(PKG_SOC1);
+
+ setTime(TIME_30_MIN);
+ stopUsage(PKG_GAME1);
+
+ assertEquals(TIME_30_MIN, group.getUsageTimeMs());
+ assertTrue(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
}
- /** Is there still an observer by that id */
- private boolean hasObserver(int observerId) {
- return mController.getObserverGroup(observerId, USER_ID) != null;
+ /** Verify that a session will continue if usage starts again within the session threshold */
+ @Test
+ public void testUsageSessionObserver_ContinueSession() throws Exception {
+ setTime(0L);
+ addUsageSessionObserver(OBS_ID1, GROUP1, 10_000L, 2_000L);
+ startUsage(PKG_SOC1);
+ setTime(6_000L);
+ stopUsage(PKG_SOC1);
+ // Wait momentarily, Session should not end
+ assertFalse(mSessionEndLatch.await(1_000L, TimeUnit.MILLISECONDS));
+
+ setTime(7_000L);
+ startUsage(PKG_SOC1);
+ setTime(10_500L);
+ stopUsage(PKG_SOC1);
+ // Total usage time has not reached the limit. Time limit callback should not fire yet
+ assertFalse(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
+
+ setTime(10_600L);
+ startUsage(PKG_SOC1);
+ setTime(12_000L);
+ assertTrue(mLimitReachedLatch.await(1_000L, TimeUnit.MILLISECONDS));
+ stopUsage(PKG_SOC1);
+ // Usage has stopped, Session should end in 2 seconds. Verify session end occurs
+ // (+/- 100ms, which is hopefully not too slim a margin)
+ assertFalse(mSessionEndLatch.await(1_900L, TimeUnit.MILLISECONDS));
+ assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
+ // Verify that the observer was not removed
+ assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
+ }
+
+ /** Verify that a new session will start if next usage starts after the session threshold */
+ @Test
+ public void testUsageSessionObserver_NewSession() throws Exception {
+ setTime(0L);
+ addUsageSessionObserver(OBS_ID1, GROUP1, 10_000L, 1_000L);
+ startUsage(PKG_SOC1);
+ setTime(6_000L);
+ stopUsage(PKG_SOC1);
+ // Wait for longer than the session threshold. Session end callback should not be triggered
+ // because the usage timelimit hasn't been triggered.
+ assertFalse(mSessionEndLatch.await(1_500L, TimeUnit.MILLISECONDS));
+
+ setTime(7_500L);
+ // This should be the start of a new session
+ startUsage(PKG_SOC1);
+ setTime(16_000L);
+ stopUsage(PKG_SOC1);
+ // Total usage has exceed the timelimit, but current session time has not
+ assertFalse(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
+
+ setTime(16_100L);
+ startUsage(PKG_SOC1);
+ setTime(18_000L);
+ assertTrue(mLimitReachedLatch.await(2000L, TimeUnit.MILLISECONDS));
+ stopUsage(PKG_SOC1);
+ // Usage has stopped, Session should end in 2 seconds. Verify session end occurs
+ // (+/- 100ms, which is hopefully not too slim a margin)
+ assertFalse(mSessionEndLatch.await(900L, TimeUnit.MILLISECONDS));
+ assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
+ // Verify that the observer was not removed
+ assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
+ }
+
+ /** Verify that the callbacks will be triggered for multiple sessions */
+ @Test
+ public void testUsageSessionObserver_RepeatSessions() throws Exception {
+ setTime(0L);
+ addUsageSessionObserver(OBS_ID1, GROUP1, 10_000L, 1_000L);
+ startUsage(PKG_SOC1);
+ setTime(9_000L);
+ stopUsage(PKG_SOC1);
+ // Stutter usage here, to reduce real world time needed trigger limit reached callback
+ startUsage(PKG_SOC1);
+ setTime(11_000L);
+ assertTrue(mLimitReachedLatch.await(2_000L, TimeUnit.MILLISECONDS));
+ stopUsage(PKG_SOC1);
+ // Usage has stopped, Session should end in 1 seconds. Verify session end occurs
+ // (+/- 100ms, which is hopefully not too slim a margin)
+ assertFalse(mSessionEndLatch.await(900L, TimeUnit.MILLISECONDS));
+ assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
+
+ // Rearm the countdown latches
+ mLimitReachedLatch = new CountDownLatch(1);
+ mSessionEndLatch = new CountDownLatch(1);
+
+ // New session start
+ setTime(20_000L);
+ startUsage(PKG_SOC1);
+ setTime(29_000L);
+ stopUsage(PKG_SOC1);
+ startUsage(PKG_SOC1);
+ setTime(31_000L);
+ assertTrue(mLimitReachedLatch.await(2_000L, TimeUnit.MILLISECONDS));
+ stopUsage(PKG_SOC1);
+ assertFalse(mSessionEndLatch.await(900L, TimeUnit.MILLISECONDS));
+ assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
+ assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
+ }
+
+ private void startUsage(String packageName) {
+ mController.noteUsageStart(packageName, USER_ID);
+ }
+
+ private void stopUsage(String packageName) {
+ mController.noteUsageStop(packageName, USER_ID);
+ }
+
+ private void addAppUsageObserver(int observerId, String[] packages, long timeLimit) {
+ mController.addAppUsageObserver(UID, observerId, packages, timeLimit, null, USER_ID);
+ }
+
+ private void addUsageSessionObserver(int observerId, String[] packages, long timeLimit,
+ long sessionThreshold) {
+ mController.addUsageSessionObserver(UID, observerId, packages, timeLimit, sessionThreshold,
+ null, null, USER_ID);
+ }
+
+ /** Is there still an app usage observer by that id */
+ private boolean hasAppUsageObserver(int uid, int observerId) {
+ return mController.getAppUsageGroup(uid, observerId) != null;
+ }
+
+ /** Is there still an usage session observer by that id */
+ private boolean hasUsageSessionObserver(int uid, int observerId) {
+ return mController.getSessionUsageGroup(uid, observerId) != null;
}
private void setTime(long time) {
diff --git a/services/usage/java/com/android/server/usage/AppTimeLimitController.java b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
index 5916b04c..eaaf9b2 100644
--- a/services/usage/java/com/android/server/usage/AppTimeLimitController.java
+++ b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
@@ -22,17 +22,16 @@
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
-import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
@@ -57,72 +56,432 @@
private final MyHandler mHandler;
- private OnLimitReachedListener mListener;
+ private TimeLimitCallbackListener mListener;
private static final long MAX_OBSERVER_PER_UID = 1000;
private static final long ONE_MINUTE = 60_000L;
+ /** Collection of data for each user that has reported usage */
@GuardedBy("mLock")
private final SparseArray<UserData> mUsers = new SparseArray<>();
- private static class UserData {
+ /**
+ * Collection of data for each app that is registering observers
+ * WARNING: Entries are currently not removed, based on the assumption there are a small
+ * fixed number of apps on device that can register observers.
+ */
+ @GuardedBy("mLock")
+ private final SparseArray<ObserverAppData> mObserverApps = new SparseArray<>();
+
+ private class UserData {
/** userId of the user */
- private @UserIdInt int userId;
+ private @UserIdInt
+ int userId;
- /** The app that is currently in the foreground */
- private String currentForegroundedPackage;
+ /** Set of the currently active entities */
+ private final ArraySet<String> currentlyActive = new ArraySet<>();
- /** The time when the current app came to the foreground */
- private long currentForegroundedTime;
-
- /** Map from package name for quick lookup */
- private ArrayMap<String, ArrayList<TimeLimitGroup>> packageMap = new ArrayMap<>();
-
- /** Map of observerId to details of the time limit group */
- private SparseArray<TimeLimitGroup> groups = new SparseArray<>();
-
- /** Map of the number of observerIds registered by uid */
- private SparseIntArray observerIdCounts = new SparseIntArray();
+ /** Map from entity name for quick lookup */
+ private final ArrayMap<String, ArrayList<UsageGroup>> observedMap = new ArrayMap<>();
private UserData(@UserIdInt int userId) {
this.userId = userId;
}
+
+ @GuardedBy("mLock")
+ boolean isActive(String[] entities) {
+ // TODO: Consider using a bloom filter here if number of actives becomes large
+ final int size = entities.length;
+ for (int i = 0; i < size; i++) {
+ if (currentlyActive.contains(entities[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @GuardedBy("mLock")
+ void addUsageGroup(UsageGroup group) {
+ final int size = group.mObserved.length;
+ for (int i = 0; i < size; i++) {
+ ArrayList<UsageGroup> list = observedMap.get(group.mObserved[i]);
+ if (list == null) {
+ list = new ArrayList<>();
+ observedMap.put(group.mObserved[i], list);
+ }
+ list.add(group);
+ }
+ }
+
+ @GuardedBy("mLock")
+ void removeUsageGroup(UsageGroup group) {
+ final int size = group.mObserved.length;
+ for (int i = 0; i < size; i++) {
+ final ArrayList<UsageGroup> list = observedMap.get(group.mObserved[i]);
+ if (list != null) {
+ list.remove(group);
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ void dump(PrintWriter pw) {
+ pw.print(" userId=");
+ pw.println(userId);
+ pw.print(" Currently Active:");
+ final int nActive = currentlyActive.size();
+ for (int i = 0; i < nActive; i++) {
+ pw.print(currentlyActive.valueAt(i));
+ pw.print(", ");
+ }
+ pw.println();
+ pw.print(" Observed Entities:");
+ final int nEntities = currentlyActive.size();
+ for (int i = 0; i < nEntities; i++) {
+ pw.print(observedMap.keyAt(i));
+ pw.print(", ");
+ }
+ pw.println();
+ }
+ }
+
+
+ private class ObserverAppData {
+ /** uid of the observing app */
+ private int uid;
+
+ /** Map of observerId to details of the time limit group */
+ SparseArray<AppUsageGroup> appUsageGroups = new SparseArray<>();
+
+ /** Map of observerId to details of the time limit group */
+ SparseArray<SessionUsageGroup> sessionUsageGroups = new SparseArray<>();
+
+ private ObserverAppData(int uid) {
+ this.uid = uid;
+ }
+
+ @GuardedBy("mLock")
+ void removeAppUsageGroup(int observerId) {
+ appUsageGroups.remove(observerId);
+ }
+
+ @GuardedBy("mLock")
+ void removeSessionUsageGroup(int observerId) {
+ sessionUsageGroups.remove(observerId);
+ }
+
+
+ @GuardedBy("mLock")
+ void dump(PrintWriter pw) {
+ pw.print(" uid=");
+ pw.println(uid);
+ pw.println(" App Usage Groups:");
+ final int nAppUsageGroups = appUsageGroups.size();
+ for (int i = 0; i < nAppUsageGroups; i++) {
+ appUsageGroups.valueAt(i).dump(pw);
+ pw.println();
+ }
+ pw.println(" Session Usage Groups:");
+ final int nSessionUsageGroups = appUsageGroups.size();
+ for (int i = 0; i < nSessionUsageGroups; i++) {
+ sessionUsageGroups.valueAt(i).dump(pw);
+ pw.println();
+ }
+ }
}
/**
* Listener interface for being informed when an app group's time limit is reached.
*/
- public interface OnLimitReachedListener {
+ public interface TimeLimitCallbackListener {
/**
* Time limit for a group, keyed by the observerId, has been reached.
- * @param observerId The observerId of the group whose limit was reached
- * @param userId The userId
- * @param timeLimit The original time limit in milliseconds
- * @param timeElapsed How much time was actually spent on apps in the group, in milliseconds
+ *
+ * @param observerId The observerId of the group whose limit was reached
+ * @param userId The userId
+ * @param timeLimit The original time limit in milliseconds
+ * @param timeElapsed How much time was actually spent on apps in the group, in
+ * milliseconds
* @param callbackIntent The PendingIntent to send when the limit is reached
*/
public void onLimitReached(int observerId, @UserIdInt int userId, long timeLimit,
long timeElapsed, PendingIntent callbackIntent);
+
+ /**
+ * Session ended for a group, keyed by the observerId, after limit was reached.
+ *
+ * @param observerId The observerId of the group whose limit was reached
+ * @param userId The userId
+ * @param timeElapsed How much time was actually spent on apps in the group, in
+ * milliseconds
+ * @param callbackIntent The PendingIntent to send when the limit is reached
+ */
+ public void onSessionEnd(int observerId, @UserIdInt int userId, long timeElapsed,
+ PendingIntent callbackIntent);
}
- static class TimeLimitGroup {
- int requestingUid;
- int observerId;
- String[] packages;
- long timeLimit;
- long timeRequested;
- long timeRemaining;
- PendingIntent callbackIntent;
- String currentPackage;
- long timeCurrentPackageStarted;
- int userId;
+ abstract class UsageGroup {
+ protected int mObserverId;
+ protected String[] mObserved;
+ protected long mTimeLimitMs;
+ protected long mUsageTimeMs;
+ protected int mActives;
+ protected long mLastKnownUsageTimeMs;
+ protected WeakReference<UserData> mUserRef;
+ protected WeakReference<ObserverAppData> mObserverAppRef;
+ protected PendingIntent mLimitReachedCallback;
+
+ UsageGroup(UserData user, ObserverAppData observerApp, int observerId, String[] observed,
+ long timeLimitMs, PendingIntent limitReachedCallback) {
+ mUserRef = new WeakReference<>(user);
+ mObserverAppRef = new WeakReference<>(observerApp);
+ mObserverId = observerId;
+ mObserved = observed;
+ mTimeLimitMs = timeLimitMs;
+ mLimitReachedCallback = limitReachedCallback;
+ }
+
+ @GuardedBy("mLock")
+ public long getTimeLimitMs() { return mTimeLimitMs; }
+
+ @GuardedBy("mLock")
+ public long getUsageTimeMs() { return mUsageTimeMs; }
+
+ @GuardedBy("mLock")
+ public void remove() {
+ UserData user = mUserRef.get();
+ if (user != null) {
+ user.removeUsageGroup(this);
+ }
+ // Clear the callback, so any racy inflight message will do nothing
+ mLimitReachedCallback = null;
+ }
+
+ @GuardedBy("mLock")
+ void noteUsageStart(long startTimeMs) {
+ noteUsageStart(startTimeMs, startTimeMs);
+ }
+
+ @GuardedBy("mLock")
+ void noteUsageStart(long startTimeMs, long currentTimeMs) {
+ if (mActives++ == 0) {
+ mLastKnownUsageTimeMs = startTimeMs;
+ final long timeRemaining =
+ mTimeLimitMs - mUsageTimeMs + currentTimeMs - startTimeMs;
+ if (timeRemaining > 0) {
+ if (DEBUG) {
+ Slog.d(TAG, "Posting timeout for " + mObserverId + " for "
+ + timeRemaining + "ms");
+ }
+ postCheckTimeoutLocked(this, timeRemaining);
+ }
+ } else {
+ if (mActives > mObserved.length) {
+ // Try to get to a sane state and log the issue
+ mActives = mObserved.length;
+ final UserData user = mUserRef.get();
+ if (user == null) return;
+ final Object[] array = user.currentlyActive.toArray();
+ Slog.e(TAG,
+ "Too many noted usage starts! Observed entities: " + Arrays.toString(
+ mObserved) + " Active Entities: " + Arrays.toString(array));
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ void noteUsageStop(long stopTimeMs) {
+ if (--mActives == 0) {
+ final boolean limitNotCrossed = mUsageTimeMs < mTimeLimitMs;
+ mUsageTimeMs += stopTimeMs - mLastKnownUsageTimeMs;
+ if (limitNotCrossed && mUsageTimeMs >= mTimeLimitMs) {
+ // Crossed the limit
+ if (DEBUG) Slog.d(TAG, "MTB informing group obs=" + mObserverId);
+ postInformLimitReachedListenerLocked(this);
+ }
+ cancelCheckTimeoutLocked(this);
+ } else {
+ if (mActives < 0) {
+ // Try to get to a sane state and log the issue
+ mActives = 0;
+ final UserData user = mUserRef.get();
+ if (user == null) return;
+ final Object[] array = user.currentlyActive.toArray();
+ Slog.e(TAG,
+ "Too many noted usage stops! Observed entities: " + Arrays.toString(
+ mObserved) + " Active Entities: " + Arrays.toString(array));
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ void checkTimeout(long currentTimeMs) {
+ final UserData user = mUserRef.get();
+ if (user == null) return;
+
+ long timeRemainingMs = mTimeLimitMs - mUsageTimeMs;
+
+ if (DEBUG) Slog.d(TAG, "checkTimeout timeRemaining=" + timeRemainingMs);
+
+ // Already reached the limit, no need to report again
+ if (timeRemainingMs <= 0) return;
+
+ if (DEBUG) {
+ Slog.d(TAG, "checkTimeout");
+ }
+
+ // Double check that at least one entity in this group is currently active
+ if (user.isActive(mObserved)) {
+ if (DEBUG) {
+ Slog.d(TAG, "checkTimeout group is active");
+ }
+ final long timeUsedMs = currentTimeMs - mLastKnownUsageTimeMs;
+ if (timeRemainingMs <= timeUsedMs) {
+ if (DEBUG) Slog.d(TAG, "checkTimeout : Time limit reached");
+ // Hit the limit, set timeRemaining to zero to avoid checking again
+ mUsageTimeMs += timeUsedMs;
+ mLastKnownUsageTimeMs = currentTimeMs;
+ AppTimeLimitController.this.postInformLimitReachedListenerLocked(this);
+ } else {
+ if (DEBUG) Slog.d(TAG, "checkTimeout : Some more time remaining");
+ AppTimeLimitController.this.postCheckTimeoutLocked(this,
+ timeRemainingMs - timeUsedMs);
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ public void onLimitReached() {
+ UserData user = mUserRef.get();
+ if (user == null) return;
+ if (mListener != null) {
+ mListener.onLimitReached(mObserverId, user.userId, mTimeLimitMs, mUsageTimeMs,
+ mLimitReachedCallback);
+ }
+ }
+
+ @GuardedBy("mLock")
+ void dump(PrintWriter pw) {
+ pw.print(" Group id=");
+ pw.print(mObserverId);
+ pw.print(" timeLimit=");
+ pw.print(mTimeLimitMs);
+ pw.print(" used=");
+ pw.print(mUsageTimeMs);
+ pw.print(" lastKnownUsage=");
+ pw.print(mLastKnownUsageTimeMs);
+ pw.print(" mActives=");
+ pw.print(mActives);
+ pw.print(" observed=");
+ pw.print(Arrays.toString(mObserved));
+ }
}
+ class AppUsageGroup extends UsageGroup {
+ public AppUsageGroup(UserData user, ObserverAppData observerApp, int observerId,
+ String[] observed, long timeLimitMs, PendingIntent limitReachedCallback) {
+ super(user, observerApp, observerId, observed, timeLimitMs, limitReachedCallback);
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ public void remove() {
+ super.remove();
+ ObserverAppData observerApp = mObserverAppRef.get();
+ if (observerApp != null) {
+ observerApp.removeAppUsageGroup(mObserverId);
+ }
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ public void onLimitReached() {
+ super.onLimitReached();
+ // Unregister since the limit has been met and observer was informed.
+ remove();
+ }
+ }
+
+ class SessionUsageGroup extends UsageGroup {
+ private long mLastUsageEndTimeMs;
+ private long mNewSessionThresholdMs;
+ private PendingIntent mSessionEndCallback;
+
+ public SessionUsageGroup(UserData user, ObserverAppData observerApp, int observerId,
+ String[] observed, long timeLimitMs, PendingIntent limitReachedCallback,
+ long newSessionThresholdMs, PendingIntent sessionEndCallback) {
+ super(user, observerApp, observerId, observed, timeLimitMs, limitReachedCallback);
+ this.mNewSessionThresholdMs = newSessionThresholdMs;
+ this.mSessionEndCallback = sessionEndCallback;
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ public void remove() {
+ super.remove();
+ ObserverAppData observerApp = mObserverAppRef.get();
+ if (observerApp != null) {
+ observerApp.removeSessionUsageGroup(mObserverId);
+ }
+ // Clear the callback, so any racy inflight messages will do nothing
+ mSessionEndCallback = null;
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ public void noteUsageStart(long startTimeMs, long currentTimeMs) {
+ if (mActives == 0) {
+ if (startTimeMs - mLastUsageEndTimeMs > mNewSessionThresholdMs) {
+ // New session has started, clear usage time.
+ mUsageTimeMs = 0;
+ }
+ AppTimeLimitController.this.cancelInformSessionEndListener(this);
+ }
+ super.noteUsageStart(startTimeMs, currentTimeMs);
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ public void noteUsageStop(long stopTimeMs) {
+ super.noteUsageStop(stopTimeMs);
+ if (mActives == 0) {
+ mLastUsageEndTimeMs = stopTimeMs;
+ if (mUsageTimeMs >= mTimeLimitMs) {
+ // Usage has ended. Schedule the session end callback to be triggered once
+ // the new session threshold has been reached
+ AppTimeLimitController.this.postInformSessionEndListenerLocked(this,
+ mNewSessionThresholdMs);
+ }
+
+ }
+ }
+
+ @GuardedBy("mLock")
+ public void onSessionEnd() {
+ UserData user = mUserRef.get();
+ if (user == null) return;
+ if (mListener != null) {
+ mListener.onSessionEnd(mObserverId, user.userId, mUsageTimeMs, mSessionEndCallback);
+ }
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ void dump(PrintWriter pw) {
+ super.dump(pw);
+ pw.print(" lastUsageEndTime=");
+ pw.print(mLastUsageEndTimeMs);
+ pw.print(" newSessionThreshold=");
+ pw.print(mNewSessionThresholdMs);
+ }
+ }
+
+
private class MyHandler extends Handler {
-
static final int MSG_CHECK_TIMEOUT = 1;
- static final int MSG_INFORM_LISTENER = 2;
+ static final int MSG_INFORM_LIMIT_REACHED_LISTENER = 2;
+ static final int MSG_INFORM_SESSION_END = 3;
MyHandler(Looper looper) {
super(looper);
@@ -132,10 +491,19 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_CHECK_TIMEOUT:
- checkTimeout((TimeLimitGroup) msg.obj);
+ synchronized (mLock) {
+ ((UsageGroup) msg.obj).checkTimeout(getUptimeMillis());
+ }
break;
- case MSG_INFORM_LISTENER:
- informListener((TimeLimitGroup) msg.obj);
+ case MSG_INFORM_LIMIT_REACHED_LISTENER:
+ synchronized (mLock) {
+ ((UsageGroup) msg.obj).onLimitReached();
+ }
+ break;
+ case MSG_INFORM_SESSION_END:
+ synchronized (mLock) {
+ ((SessionUsageGroup) msg.obj).onSessionEnd();
+ }
break;
default:
super.handleMessage(msg);
@@ -144,7 +512,7 @@
}
}
- public AppTimeLimitController(OnLimitReachedListener listener, Looper looper) {
+ public AppTimeLimitController(TimeLimitCallbackListener listener, Looper looper) {
mHandler = new MyHandler(looper);
mListener = listener;
}
@@ -157,7 +525,13 @@
/** Overrideable for testing purposes */
@VisibleForTesting
- protected long getObserverPerUidLimit() {
+ protected long getAppUsageObserverPerUidLimit() {
+ return MAX_OBSERVER_PER_UID;
+ }
+
+ /** Overrideable for testing purposes */
+ @VisibleForTesting
+ protected long getUsageSessionObserverPerUidLimit() {
return MAX_OBSERVER_PER_UID;
}
@@ -167,6 +541,21 @@
return ONE_MINUTE;
}
+ @VisibleForTesting
+ AppUsageGroup getAppUsageGroup(int observerAppUid, int observerId) {
+ synchronized (mLock) {
+ return getOrCreateObserverAppDataLocked(observerAppUid).appUsageGroups.get(observerId);
+ }
+ }
+
+ @VisibleForTesting
+ SessionUsageGroup getSessionUsageGroup(int observerAppUid, int observerId) {
+ synchronized (mLock) {
+ return getOrCreateObserverAppDataLocked(observerAppUid).sessionUsageGroups.get(
+ observerId);
+ }
+ }
+
/** Returns an existing UserData object for the given userId, or creates one */
@GuardedBy("mLock")
private UserData getOrCreateUserDataLocked(int userId) {
@@ -178,6 +567,17 @@
return userData;
}
+ /** Returns an existing ObserverAppData object for the given uid, or creates one */
+ @GuardedBy("mLock")
+ private ObserverAppData getOrCreateObserverAppDataLocked(int uid) {
+ ObserverAppData appData = mObserverApps.get(uid);
+ if (appData == null) {
+ appData = new ObserverAppData(uid);
+ mObserverApps.put(uid, appData);
+ }
+ return appData;
+ }
+
/** Clean up data if user is removed */
public void onUserRemoved(int userId) {
synchronized (mLock) {
@@ -187,300 +587,219 @@
}
/**
- * Registers an observer with the given details. Existing observer with the same observerId
- * is removed.
+ * Check if group has any currently active entities.
*/
- public void addObserver(int requestingUid, int observerId, String[] packages, long timeLimit,
- PendingIntent callbackIntent, @UserIdInt int userId) {
+ @GuardedBy("mLock")
+ private void noteActiveLocked(UserData user, UsageGroup group, long currentTimeMs) {
+ // TODO: Consider using a bloom filter here if number of actives becomes large
+ final int size = group.mObserved.length;
+ for (int i = 0; i < size; i++) {
+ if (user.currentlyActive.contains(group.mObserved[i])) {
+ // Entity is currently active. Start group's usage.
+ group.noteUsageStart(currentTimeMs);
+ }
+ }
+ }
+ /**
+ * Registers an app usage observer with the given details.
+ * Existing app usage observer with the same observerId will be removed.
+ */
+ public void addAppUsageObserver(int requestingUid, int observerId, String[] observed,
+ long timeLimit, PendingIntent callbackIntent, @UserIdInt int userId) {
if (timeLimit < getMinTimeLimit()) {
throw new IllegalArgumentException("Time limit must be >= " + getMinTimeLimit());
}
synchronized (mLock) {
UserData user = getOrCreateUserDataLocked(userId);
- removeObserverLocked(user, requestingUid, observerId, /*readding =*/ true);
-
- final int observerIdCount = user.observerIdCounts.get(requestingUid, 0);
- if (observerIdCount >= getObserverPerUidLimit()) {
- throw new IllegalStateException(
- "Too many observers added by uid " + requestingUid);
+ ObserverAppData observerApp = getOrCreateObserverAppDataLocked(requestingUid);
+ AppUsageGroup group = observerApp.appUsageGroups.get(observerId);
+ if (group != null) {
+ // Remove previous app usage group associated with observerId
+ observerApp.appUsageGroups.get(observerId).remove();
}
- user.observerIdCounts.put(requestingUid, observerIdCount + 1);
- TimeLimitGroup group = new TimeLimitGroup();
- group.observerId = observerId;
- group.callbackIntent = callbackIntent;
- group.packages = packages;
- group.timeLimit = timeLimit;
- group.timeRemaining = group.timeLimit;
- group.timeRequested = getUptimeMillis();
- group.requestingUid = requestingUid;
- group.timeCurrentPackageStarted = -1L;
- group.userId = userId;
-
- user.groups.append(observerId, group);
-
- addGroupToPackageMapLocked(user, packages, group);
+ final int observerIdCount = observerApp.appUsageGroups.size();
+ if (observerIdCount >= getAppUsageObserverPerUidLimit()) {
+ throw new IllegalStateException(
+ "Too many app usage observers added by uid " + requestingUid);
+ }
+ group = new AppUsageGroup(user, observerApp, observerId, observed, timeLimit,
+ callbackIntent);
+ observerApp.appUsageGroups.append(observerId, group);
if (DEBUG) {
- Slog.d(TAG, "addObserver " + packages + " for " + timeLimit);
+ Slog.d(TAG, "addObserver " + observed + " for " + timeLimit);
}
- // Handle the case where a target package is already in the foreground when observer
- // is added.
- if (user.currentForegroundedPackage != null && inPackageList(group.packages,
- user.currentForegroundedPackage)) {
- group.timeCurrentPackageStarted = group.timeRequested;
- group.currentPackage = user.currentForegroundedPackage;
- if (group.timeRemaining > 0) {
- postCheckTimeoutLocked(group, group.timeRemaining);
- }
- }
+
+ user.addUsageGroup(group);
+ noteActiveLocked(user, group, getUptimeMillis());
}
}
/**
* Remove a registered observer by observerId and calling uid.
+ *
* @param requestingUid The calling uid
- * @param observerId The unique observer id for this user
- * @param userId The user id of the observer
+ * @param observerId The unique observer id for this user
+ * @param userId The user id of the observer
*/
- public void removeObserver(int requestingUid, int observerId, @UserIdInt int userId) {
+ public void removeAppUsageObserver(int requestingUid, int observerId, @UserIdInt int userId) {
+ synchronized (mLock) {
+ ObserverAppData observerApp = getOrCreateObserverAppDataLocked(requestingUid);
+ observerApp.appUsageGroups.get(observerId).remove();
+ }
+ }
+
+
+ /**
+ * Registers a usage session observer with the given details.
+ * Existing usage session observer with the same observerId will be removed.
+ */
+ public void addUsageSessionObserver(int requestingUid, int observerId, String[] observed,
+ long timeLimit, long sessionThresholdTime,
+ PendingIntent limitReachedCallbackIntent, PendingIntent sessionEndCallbackIntent,
+ @UserIdInt int userId) {
+ if (timeLimit < getMinTimeLimit()) {
+ throw new IllegalArgumentException("Time limit must be >= " + getMinTimeLimit());
+ }
synchronized (mLock) {
UserData user = getOrCreateUserDataLocked(userId);
- removeObserverLocked(user, requestingUid, observerId, /*readding =*/ false);
+ ObserverAppData observerApp = getOrCreateObserverAppDataLocked(requestingUid);
+ SessionUsageGroup group = observerApp.sessionUsageGroups.get(observerId);
+ if (group != null) {
+ // Remove previous app usage group associated with observerId
+ observerApp.sessionUsageGroups.get(observerId).remove();
+ }
+
+ final int observerIdCount = observerApp.sessionUsageGroups.size();
+ if (observerIdCount >= getUsageSessionObserverPerUidLimit()) {
+ throw new IllegalStateException(
+ "Too many app usage observers added by uid " + requestingUid);
+ }
+ group = new SessionUsageGroup(user, observerApp, observerId, observed, timeLimit,
+ limitReachedCallbackIntent, sessionThresholdTime, sessionEndCallbackIntent);
+ observerApp.sessionUsageGroups.append(observerId, group);
+
+ user.addUsageGroup(group);
+ noteActiveLocked(user, group, getUptimeMillis());
}
}
- @VisibleForTesting
- TimeLimitGroup getObserverGroup(int observerId, int userId) {
+ /**
+ * Remove a registered observer by observerId and calling uid.
+ *
+ * @param requestingUid The calling uid
+ * @param observerId The unique observer id for this user
+ * @param userId The user id of the observer
+ */
+ public void removeUsageSessionObserver(int requestingUid, int observerId,
+ @UserIdInt int userId) {
synchronized (mLock) {
- return getOrCreateUserDataLocked(userId).groups.get(observerId);
+ ObserverAppData observerApp = getOrCreateObserverAppDataLocked(requestingUid);
+ observerApp.sessionUsageGroups.get(observerId).remove();
}
}
- private static boolean inPackageList(String[] packages, String packageName) {
- return ArrayUtils.contains(packages, packageName);
+ /**
+ * Called when an entity becomes active.
+ *
+ * @param name The entity that became active
+ * @param userId The user
+ */
+ public void noteUsageStart(String name, int userId) throws IllegalArgumentException {
+ synchronized (mLock) {
+ UserData user = getOrCreateUserDataLocked(userId);
+ if (DEBUG) Slog.d(TAG, "Usage entity " + name + " became active");
+ if (user.currentlyActive.contains(name)) {
+ throw new IllegalArgumentException(
+ "Unable to start usage for " + name + ", already in use");
+ }
+ final long currentTime = getUptimeMillis();
+
+ // Add to the list of active entities
+ user.currentlyActive.add(name);
+
+ ArrayList<UsageGroup> groups = user.observedMap.get(name);
+ if (groups == null) return;
+
+ final int size = groups.size();
+ for (int i = 0; i < size; i++) {
+ UsageGroup group = groups.get(i);
+ group.noteUsageStart(currentTime);
+ }
+ }
+ }
+
+ /**
+ * Called when an entity becomes inactive.
+ *
+ * @param name The entity that became inactive
+ * @param userId The user
+ */
+ public void noteUsageStop(String name, int userId) throws IllegalArgumentException {
+ synchronized (mLock) {
+ UserData user = getOrCreateUserDataLocked(userId);
+ if (DEBUG) Slog.d(TAG, "Usage entity " + name + " became inactive");
+ if (!user.currentlyActive.remove(name)) {
+ throw new IllegalArgumentException(
+ "Unable to stop usage for " + name + ", not in use");
+ }
+ final long currentTime = getUptimeMillis();
+
+ // Check if any of the groups need to watch for this entity
+ ArrayList<UsageGroup> groups = user.observedMap.get(name);
+ if (groups == null) return;
+
+ final int size = groups.size();
+ for (int i = 0; i < size; i++) {
+ UsageGroup group = groups.get(i);
+ group.noteUsageStop(currentTime);
+ }
+ }
}
@GuardedBy("mLock")
- private void removeObserverLocked(UserData user, int requestingUid, int observerId,
- boolean readding) {
- TimeLimitGroup group = user.groups.get(observerId);
- if (group != null && group.requestingUid == requestingUid) {
- removeGroupFromPackageMapLocked(user, group);
- user.groups.remove(observerId);
- mHandler.removeMessages(MyHandler.MSG_CHECK_TIMEOUT, group);
- final int observerIdCount = user.observerIdCounts.get(requestingUid);
- if (observerIdCount <= 1 && !readding) {
- user.observerIdCounts.delete(requestingUid);
- } else {
- user.observerIdCounts.put(requestingUid, observerIdCount - 1);
- }
- }
- }
-
- /**
- * Called when an app has moved to the foreground.
- * @param packageName The app that is foregrounded
- * @param className The className of the activity
- * @param userId The user
- */
- public void moveToForeground(String packageName, String className, int userId) {
- synchronized (mLock) {
- UserData user = getOrCreateUserDataLocked(userId);
- if (DEBUG) Slog.d(TAG, "Setting mCurrentForegroundedPackage to " + packageName);
- // Note the current foreground package
- user.currentForegroundedPackage = packageName;
- user.currentForegroundedTime = getUptimeMillis();
-
- // Check if any of the groups need to watch for this package
- maybeWatchForPackageLocked(user, packageName, user.currentForegroundedTime);
- }
- }
-
- /**
- * Called when an app is sent to the background.
- *
- * @param packageName
- * @param className
- * @param userId
- */
- public void moveToBackground(String packageName, String className, int userId) {
- synchronized (mLock) {
- UserData user = getOrCreateUserDataLocked(userId);
- if (!TextUtils.equals(user.currentForegroundedPackage, packageName)) {
- Slog.w(TAG, "Eh? Last foregrounded package = " + user.currentForegroundedPackage
- + " and now backgrounded = " + packageName);
- return;
- }
- final long stopTime = getUptimeMillis();
-
- // Add up the usage time to all groups that contain the package
- ArrayList<TimeLimitGroup> groups = user.packageMap.get(packageName);
- if (groups != null) {
- final int size = groups.size();
- for (int i = 0; i < size; i++) {
- final TimeLimitGroup group = groups.get(i);
- // Don't continue to send
- if (group.timeRemaining <= 0) continue;
-
- final long startTime = Math.max(user.currentForegroundedTime,
- group.timeRequested);
- long diff = stopTime - startTime;
- group.timeRemaining -= diff;
- if (group.timeRemaining <= 0) {
- if (DEBUG) Slog.d(TAG, "MTB informing group obs=" + group.observerId);
- postInformListenerLocked(group);
- }
- // Reset indicators that observer was added when package was already fg
- group.currentPackage = null;
- group.timeCurrentPackageStarted = -1L;
- mHandler.removeMessages(MyHandler.MSG_CHECK_TIMEOUT, group);
- }
- }
- user.currentForegroundedPackage = null;
- }
- }
-
- private void postInformListenerLocked(TimeLimitGroup group) {
- mHandler.sendMessage(mHandler.obtainMessage(MyHandler.MSG_INFORM_LISTENER,
+ private void postInformLimitReachedListenerLocked(UsageGroup group) {
+ mHandler.sendMessage(mHandler.obtainMessage(MyHandler.MSG_INFORM_LIMIT_REACHED_LISTENER,
group));
}
- /**
- * Inform the observer and unregister it, as the limit has been reached.
- * @param group the observed group
- */
- private void informListener(TimeLimitGroup group) {
- if (mListener != null) {
- mListener.onLimitReached(group.observerId, group.userId, group.timeLimit,
- group.timeLimit - group.timeRemaining, group.callbackIntent);
- }
- // Unregister since the limit has been met and observer was informed.
- synchronized (mLock) {
- UserData user = getOrCreateUserDataLocked(group.userId);
- removeObserverLocked(user, group.requestingUid, group.observerId, false);
- }
- }
-
- /** Check if any of the groups care about this package and set up delayed messages */
@GuardedBy("mLock")
- private void maybeWatchForPackageLocked(UserData user, String packageName, long uptimeMillis) {
- ArrayList<TimeLimitGroup> groups = user.packageMap.get(packageName);
- if (groups == null) return;
-
- final int size = groups.size();
- for (int i = 0; i < size; i++) {
- TimeLimitGroup group = groups.get(i);
- if (group.timeRemaining > 0) {
- group.timeCurrentPackageStarted = uptimeMillis;
- group.currentPackage = packageName;
- if (DEBUG) {
- Slog.d(TAG, "Posting timeout for " + packageName + " for "
- + group.timeRemaining + "ms");
- }
- postCheckTimeoutLocked(group, group.timeRemaining);
- }
- }
+ private void postInformSessionEndListenerLocked(SessionUsageGroup group, long timeout) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MyHandler.MSG_INFORM_SESSION_END, group),
+ timeout);
}
- private void addGroupToPackageMapLocked(UserData user, String[] packages,
- TimeLimitGroup group) {
- for (int i = 0; i < packages.length; i++) {
- ArrayList<TimeLimitGroup> list = user.packageMap.get(packages[i]);
- if (list == null) {
- list = new ArrayList<>();
- user.packageMap.put(packages[i], list);
- }
- list.add(group);
- }
+ @GuardedBy("mLock")
+ private void cancelInformSessionEndListener(SessionUsageGroup group) {
+ mHandler.removeMessages(MyHandler.MSG_INFORM_SESSION_END, group);
}
- /**
- * Remove the group reference from the package to group mapping, which is 1 to many.
- * @param group The group to remove from the package map.
- */
- private void removeGroupFromPackageMapLocked(UserData user, TimeLimitGroup group) {
- final int mapSize = user.packageMap.size();
- for (int i = 0; i < mapSize; i++) {
- ArrayList<TimeLimitGroup> list = user.packageMap.valueAt(i);
- list.remove(group);
- }
- }
-
- private void postCheckTimeoutLocked(TimeLimitGroup group, long timeout) {
+ @GuardedBy("mLock")
+ private void postCheckTimeoutLocked(UsageGroup group, long timeout) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(MyHandler.MSG_CHECK_TIMEOUT, group),
timeout);
}
- /**
- * See if the given group has reached the timeout if the current foreground app is included
- * and it exceeds the time remaining.
- * @param group the group of packages to check
- */
- void checkTimeout(TimeLimitGroup group) {
- // For each package in the group, check if any of the currently foregrounded apps are adding
- // up to hit the limit and inform the observer
- synchronized (mLock) {
- UserData user = getOrCreateUserDataLocked(group.userId);
- // This group doesn't exist anymore, nothing to see here.
- if (user.groups.get(group.observerId) != group) return;
-
- if (DEBUG) Slog.d(TAG, "checkTimeout timeRemaining=" + group.timeRemaining);
-
- // Already reached the limit, no need to report again
- if (group.timeRemaining <= 0) return;
-
- if (DEBUG) {
- Slog.d(TAG, "checkTimeout foregroundedPackage="
- + user.currentForegroundedPackage);
- }
-
- if (inPackageList(group.packages, user.currentForegroundedPackage)) {
- if (DEBUG) {
- Slog.d(TAG, "checkTimeout package in foreground="
- + user.currentForegroundedPackage);
- }
- if (group.timeCurrentPackageStarted < 0) {
- Slog.w(TAG, "startTime was not set correctly for " + group);
- }
- final long timeInForeground = getUptimeMillis() - group.timeCurrentPackageStarted;
- if (group.timeRemaining <= timeInForeground) {
- if (DEBUG) Slog.d(TAG, "checkTimeout : Time limit reached");
- // Hit the limit, set timeRemaining to zero to avoid checking again
- group.timeRemaining -= timeInForeground;
- postInformListenerLocked(group);
- // Reset
- group.timeCurrentPackageStarted = -1L;
- group.currentPackage = null;
- } else {
- if (DEBUG) Slog.d(TAG, "checkTimeout : Some more time remaining");
- postCheckTimeoutLocked(group, group.timeRemaining - timeInForeground);
- }
- }
- }
+ @GuardedBy("mLock")
+ private void cancelCheckTimeoutLocked(UsageGroup group) {
+ mHandler.removeMessages(MyHandler.MSG_CHECK_TIMEOUT, group);
}
void dump(PrintWriter pw) {
synchronized (mLock) {
pw.println("\n App Time Limits");
- int nUsers = mUsers.size();
+ final int nUsers = mUsers.size();
for (int i = 0; i < nUsers; i++) {
- UserData user = mUsers.valueAt(i);
- pw.print(" User "); pw.println(user.userId);
- int nGroups = user.groups.size();
- for (int j = 0; j < nGroups; j++) {
- TimeLimitGroup group = user.groups.valueAt(j);
- pw.print(" Group id="); pw.print(group.observerId);
- pw.print(" timeLimit="); pw.print(group.timeLimit);
- pw.print(" remaining="); pw.print(group.timeRemaining);
- pw.print(" currentPackage="); pw.print(group.currentPackage);
- pw.print(" timeCurrentPkgStarted="); pw.print(group.timeCurrentPackageStarted);
- pw.print(" packages="); pw.println(Arrays.toString(group.packages));
- }
- pw.println();
- pw.print(" currentForegroundedPackage=");
- pw.println(user.currentForegroundedPackage);
+ pw.print(" User ");
+ mUsers.valueAt(i).dump(pw);
+ }
+ pw.println();
+ final int nObserverApps = mObserverApps.size();
+ for (int i = 0; i < nObserverApps; i++) {
+ pw.print(" Observer App ");
+ mObserverApps.valueAt(i).dump(pw);
}
}
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index dd1ddfa..2621252 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -165,16 +165,36 @@
mAppStandby = new AppStandbyController(getContext(), BackgroundThread.get().getLooper());
mAppTimeLimit = new AppTimeLimitController(
- (observerId, userId, timeLimit, timeElapsed, callbackIntent) -> {
- Intent intent = new Intent();
- intent.putExtra(UsageStatsManager.EXTRA_OBSERVER_ID, observerId);
- intent.putExtra(UsageStatsManager.EXTRA_TIME_LIMIT, timeLimit);
- intent.putExtra(UsageStatsManager.EXTRA_TIME_USED, timeElapsed);
- try {
- callbackIntent.send(getContext(), 0, intent);
- } catch (PendingIntent.CanceledException e) {
- Slog.w(TAG, "Couldn't deliver callback: "
- + callbackIntent);
+ new AppTimeLimitController.TimeLimitCallbackListener() {
+ @Override
+ public void onLimitReached(int observerId, int userId, long timeLimit,
+ long timeElapsed, PendingIntent callbackIntent) {
+ if (callbackIntent == null) return;
+ Intent intent = new Intent();
+ intent.putExtra(UsageStatsManager.EXTRA_OBSERVER_ID, observerId);
+ intent.putExtra(UsageStatsManager.EXTRA_TIME_LIMIT, timeLimit);
+ intent.putExtra(UsageStatsManager.EXTRA_TIME_USED, timeElapsed);
+ try {
+ callbackIntent.send(getContext(), 0, intent);
+ } catch (PendingIntent.CanceledException e) {
+ Slog.w(TAG, "Couldn't deliver callback: "
+ + callbackIntent);
+ }
+ }
+
+ @Override
+ public void onSessionEnd(int observerId, int userId, long timeElapsed,
+ PendingIntent callbackIntent) {
+ if (callbackIntent == null) return;
+ Intent intent = new Intent();
+ intent.putExtra(UsageStatsManager.EXTRA_OBSERVER_ID, observerId);
+ intent.putExtra(UsageStatsManager.EXTRA_TIME_USED, timeElapsed);
+ try {
+ callbackIntent.send(getContext(), 0, intent);
+ } catch (PendingIntent.CanceledException e) {
+ Slog.w(TAG, "Couldn't deliver callback: "
+ + callbackIntent);
+ }
}
}, mHandler.getLooper());
@@ -412,12 +432,18 @@
mAppStandby.reportEvent(event, elapsedRealtime, userId);
switch (event.mEventType) {
case Event.MOVE_TO_FOREGROUND:
- mAppTimeLimit.moveToForeground(event.getPackageName(), event.getClassName(),
- userId);
+ try {
+ mAppTimeLimit.noteUsageStart(event.getPackageName(), userId);
+ } catch (IllegalArgumentException iae) {
+ Slog.e(TAG, "Failed to note usage start", iae);
+ }
break;
case Event.MOVE_TO_BACKGROUND:
- mAppTimeLimit.moveToBackground(event.getPackageName(), event.getClassName(),
- userId);
+ try {
+ mAppTimeLimit.noteUsageStop(event.getPackageName(), userId);
+ } catch (IllegalArgumentException iae) {
+ Slog.e(TAG, "Failed to note usage stop", iae);
+ }
break;
}
}
@@ -1151,16 +1177,70 @@
Binder.restoreCallingIdentity(token);
}
}
+
+ @Override
+ public void registerUsageSessionObserver(int sessionObserverId, String[] observed,
+ long timeLimitMs, long sessionThresholdTimeMs,
+ PendingIntent limitReachedCallbackIntent, PendingIntent sessionEndCallbackIntent,
+ String callingPackage) {
+ if (!hasObserverPermission(callingPackage)) {
+ throw new SecurityException("Caller doesn't have OBSERVE_APP_USAGE permission");
+ }
+
+ if (observed == null || observed.length == 0) {
+ throw new IllegalArgumentException("Must specify at least one observed entity");
+ }
+ if (limitReachedCallbackIntent == null) {
+ throw new NullPointerException("limitReachedCallbackIntent can't be null");
+ }
+ final int callingUid = Binder.getCallingUid();
+ final int userId = UserHandle.getUserId(callingUid);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ UsageStatsService.this.registerUsageSessionObserver(callingUid, sessionObserverId,
+ observed, timeLimitMs, sessionThresholdTimeMs, limitReachedCallbackIntent,
+ sessionEndCallbackIntent, userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void unregisterUsageSessionObserver(int sessionObserverId, String callingPackage) {
+ if (!hasObserverPermission(callingPackage)) {
+ throw new SecurityException("Caller doesn't have OBSERVE_APP_USAGE permission");
+ }
+
+ final int callingUid = Binder.getCallingUid();
+ final int userId = UserHandle.getUserId(callingUid);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ UsageStatsService.this.unregisterUsageSessionObserver(callingUid, sessionObserverId, userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
}
void registerAppUsageObserver(int callingUid, int observerId, String[] packages,
long timeLimitMs, PendingIntent callbackIntent, int userId) {
- mAppTimeLimit.addObserver(callingUid, observerId, packages, timeLimitMs, callbackIntent,
+ mAppTimeLimit.addAppUsageObserver(callingUid, observerId, packages, timeLimitMs, callbackIntent,
userId);
}
void unregisterAppUsageObserver(int callingUid, int observerId, int userId) {
- mAppTimeLimit.removeObserver(callingUid, observerId, userId);
+ mAppTimeLimit.removeAppUsageObserver(callingUid, observerId, userId);
+ }
+
+ void registerUsageSessionObserver(int callingUid, int observerId, String[] observed,
+ long timeLimitMs, long sessionThresholdTime, PendingIntent limitReachedCallbackIntent,
+ PendingIntent sessionEndCallbackIntent, int userId) {
+ mAppTimeLimit.addUsageSessionObserver(callingUid, observerId, observed, timeLimitMs,
+ sessionThresholdTime, limitReachedCallbackIntent, sessionEndCallbackIntent, userId);
+ }
+
+ void unregisterUsageSessionObserver(int callingUid, int sessionObserverId, int userId) {
+ mAppTimeLimit.removeUsageSessionObserver(callingUid, sessionObserverId, userId);
}
/**
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 66918eb..f4b5f86 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2231,6 +2231,13 @@
public static final String KEY_CALL_WAITING_OVER_UT_WARNING_BOOL =
"call_waiting_over_ut_warning_bool";
+ /**
+ * Flag indicating whether to support "Network default" option in Caller ID settings for Calling
+ * Line Identification Restriction (CLIR).
+ */
+ public static final String KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL =
+ "support_clir_network_default_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -2579,6 +2586,7 @@
sDefaults.putBoolean(KEY_CALL_BARRING_OVER_UT_WARNING_BOOL, false);
sDefaults.putBoolean(KEY_CALLER_ID_OVER_UT_WARNING_BOOL, false);
sDefaults.putBoolean(KEY_CALL_WAITING_OVER_UT_WARNING_BOOL, false);
+ sDefaults.putBoolean(KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL, true);
}
/**
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 9218bdc..598f567 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -16,7 +16,6 @@
package android.telephony;
-import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.telephony.cdma.CdmaCellLocation;
@@ -56,11 +55,11 @@
*/
public CellIdentityCdma() {
super(TAG, CellInfo.TYPE_CDMA, null, null, null, null);
- mNetworkId = Integer.MAX_VALUE;
- mSystemId = Integer.MAX_VALUE;
- mBasestationId = Integer.MAX_VALUE;
- mLongitude = Integer.MAX_VALUE;
- mLatitude = Integer.MAX_VALUE;
+ mNetworkId = CellInfo.UNAVAILABLE;
+ mSystemId = CellInfo.UNAVAILABLE;
+ mBasestationId = CellInfo.UNAVAILABLE;
+ mLongitude = CellInfo.UNAVAILABLE;
+ mLatitude = CellInfo.UNAVAILABLE;
}
/**
@@ -104,7 +103,7 @@
mLongitude = lon;
mLatitude = lat;
} else {
- mLongitude = mLatitude = Integer.MAX_VALUE;
+ mLongitude = mLatitude = CellInfo.UNAVAILABLE;
}
}
@@ -130,21 +129,24 @@
}
/**
- * @return Network Id 0..65535, Integer.MAX_VALUE if unknown
+ * @return Network Id 0..65535, {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}
+ * if unavailable.
*/
public int getNetworkId() {
return mNetworkId;
}
/**
- * @return System Id 0..32767, Integer.MAX_VALUE if unknown
+ * @return System Id 0..32767, {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}
+ * if unavailable.
*/
public int getSystemId() {
return mSystemId;
}
/**
- * @return Base Station Id 0..65535, Integer.MAX_VALUE if unknown
+ * @return Base Station Id 0..65535, {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}
+ * if unavailable.
*/
public int getBasestationId() {
return mBasestationId;
@@ -155,7 +157,7 @@
* specified in 3GPP2 C.S0005-A v6.0. It is represented in units
* of 0.25 seconds and ranges from -2592000 to 2592000, both
* values inclusive (corresponding to a range of -180
- * to +180 degrees). Integer.MAX_VALUE if unknown.
+ * to +180 degrees). {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
public int getLongitude() {
return mLongitude;
@@ -166,7 +168,7 @@
* specified in 3GPP2 C.S0005-A v6.0. It is represented in units
* of 0.25 seconds and ranges from -1296000 to 1296000, both
* values inclusive (corresponding to a range of -90
- * to +90 degrees). Integer.MAX_VALUE if unknown.
+ * to +90 degrees). {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
public int getLatitude() {
return mLatitude;
@@ -182,10 +184,10 @@
@Override
public CdmaCellLocation asCellLocation() {
CdmaCellLocation cl = new CdmaCellLocation();
- int bsid = mBasestationId != Integer.MAX_VALUE ? mBasestationId : -1;
- int sid = mSystemId != Integer.MAX_VALUE ? mSystemId : -1;
- int nid = mNetworkId != Integer.MAX_VALUE ? mNetworkId : -1;
- // lat and long already use Integer.MAX_VALUE for invalid/unknown
+ int bsid = mBasestationId != CellInfo.UNAVAILABLE ? mBasestationId : -1;
+ int sid = mSystemId != CellInfo.UNAVAILABLE ? mSystemId : -1;
+ int nid = mNetworkId != CellInfo.UNAVAILABLE ? mNetworkId : -1;
+ // lat and long already use CellInfo.UNAVAILABLE for invalid/unknown
cl.setCellLocationData(bsid, mLatitude, mLongitude, sid, nid);
return cl;
}
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index cb9dbf3..04c28e5 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -16,7 +16,6 @@
package android.telephony;
-import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.telephony.gsm.GsmCellLocation;
@@ -48,10 +47,10 @@
@UnsupportedAppUsage
public CellIdentityGsm() {
super(TAG, CellInfo.TYPE_GSM, null, null, null, null);
- mLac = Integer.MAX_VALUE;
- mCid = Integer.MAX_VALUE;
- mArfcn = Integer.MAX_VALUE;
- mBsic = Integer.MAX_VALUE;
+ mLac = CellInfo.UNAVAILABLE;
+ mCid = CellInfo.UNAVAILABLE;
+ mArfcn = CellInfo.UNAVAILABLE;
+ mBsic = CellInfo.UNAVAILABLE;
}
/**
* public constructor
@@ -63,7 +62,7 @@
* @hide
*/
public CellIdentityGsm(int mcc, int mnc, int lac, int cid) {
- this(lac, cid, Integer.MAX_VALUE, Integer.MAX_VALUE,
+ this(lac, cid, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
String.valueOf(mcc), String.valueOf(mnc), null, null);
}
@@ -103,7 +102,7 @@
mArfcn = arfcn;
// In RIL BSIC is a UINT8, so 0xFF is the 'INVALID' designator
// for inbound parcels
- mBsic = (bsic == 0xFF) ? Integer.MAX_VALUE : bsic;
+ mBsic = (bsic == 0xFF) ? CellInfo.UNAVAILABLE : bsic;
}
private CellIdentityGsm(CellIdentityGsm cid) {
@@ -116,69 +115,73 @@
}
/**
- * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown
+ * @return 3-digit Mobile Country Code, 0..999,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
* @deprecated Use {@link #getMccString} instead.
*/
@Deprecated
public int getMcc() {
- return (mMccStr != null) ? Integer.valueOf(mMccStr) : Integer.MAX_VALUE;
+ return (mMccStr != null) ? Integer.valueOf(mMccStr) : CellInfo.UNAVAILABLE;
}
/**
- * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown
+ * @return 2 or 3-digit Mobile Network Code, 0..999,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
* @deprecated Use {@link #getMncString} instead.
*/
@Deprecated
public int getMnc() {
- return (mMncStr != null) ? Integer.valueOf(mMncStr) : Integer.MAX_VALUE;
+ return (mMncStr != null) ? Integer.valueOf(mMncStr) : CellInfo.UNAVAILABLE;
}
/**
- * @return 16-bit Location Area Code, 0..65535, Integer.MAX_VALUE if unknown
+ * @return 16-bit Location Area Code, 0..65535,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
public int getLac() {
return mLac;
}
/**
- * @return CID
- * Either 16-bit GSM Cell Identity described
- * in TS 27.007, 0..65535, Integer.MAX_VALUE if unknown
+ * @return 16-bit GSM Cell Identity described in TS 27.007, 0..65535,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
public int getCid() {
return mCid;
}
/**
- * @return 16-bit GSM Absolute RF Channel Number, Integer.MAX_VALUE if unknown
+ * @return 16-bit GSM Absolute RF Channel Number,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
public int getArfcn() {
return mArfcn;
}
/**
- * @return 6-bit Base Station Identity Code, Integer.MAX_VALUE if unknown
+ * @return 6-bit Base Station Identity Code,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
public int getBsic() {
return mBsic;
}
/**
- * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown
+ * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown.
*/
public String getMobileNetworkOperator() {
return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
}
/**
- * @return Mobile Country Code in string format, null if unknown
+ * @return Mobile Country Code in string format, null if unavailable.
*/
public String getMccString() {
return mMccStr;
}
/**
- * @return Mobile Network Code in string format, null if unknown
+ * @return Mobile Network Code in string format, null if unavailable.
*/
public String getMncString() {
return mMncStr;
@@ -192,19 +195,19 @@
/**
* @deprecated Primary Scrambling Code is not applicable to GSM.
- * @return Integer.MAX_VALUE, undefined for GSM
+ * @return {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} - undefined for GSM
*/
@Deprecated
public int getPsc() {
- return Integer.MAX_VALUE;
+ return CellInfo.UNAVAILABLE;
}
/** @hide */
@Override
public GsmCellLocation asCellLocation() {
GsmCellLocation cl = new GsmCellLocation();
- int lac = mLac != Integer.MAX_VALUE ? mLac : -1;
- int cid = mCid != Integer.MAX_VALUE ? mCid : -1;
+ int lac = mLac != CellInfo.UNAVAILABLE ? mLac : -1;
+ int cid = mCid != CellInfo.UNAVAILABLE ? mCid : -1;
cl.setLacAndCid(lac, cid);
cl.setPsc(-1);
return cl;
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index b44e891..04b6a6c 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -16,7 +16,6 @@
package android.telephony;
-import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.telephony.gsm.GsmCellLocation;
@@ -49,11 +48,11 @@
@UnsupportedAppUsage
public CellIdentityLte() {
super(TAG, CellInfo.TYPE_LTE, null, null, null, null);
- mCi = Integer.MAX_VALUE;
- mPci = Integer.MAX_VALUE;
- mTac = Integer.MAX_VALUE;
- mEarfcn = Integer.MAX_VALUE;
- mBandwidth = Integer.MAX_VALUE;
+ mCi = CellInfo.UNAVAILABLE;
+ mPci = CellInfo.UNAVAILABLE;
+ mTac = CellInfo.UNAVAILABLE;
+ mEarfcn = CellInfo.UNAVAILABLE;
+ mBandwidth = CellInfo.UNAVAILABLE;
}
/**
@@ -68,7 +67,7 @@
*/
@UnsupportedAppUsage
public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac) {
- this(ci, pci, tac, Integer.MAX_VALUE, Integer.MAX_VALUE, String.valueOf(mcc),
+ this(ci, pci, tac, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, String.valueOf(mcc),
String.valueOf(mnc), null, null);
}
@@ -84,7 +83,7 @@
* @hide
*/
public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac, int earfcn) {
- this(ci, pci, tac, earfcn, Integer.MAX_VALUE, String.valueOf(mcc), String.valueOf(mnc),
+ this(ci, pci, tac, earfcn, CellInfo.UNAVAILABLE, String.valueOf(mcc), String.valueOf(mnc),
null, null);
}
@@ -122,74 +121,81 @@
}
/**
- * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown
+ * @return 3-digit Mobile Country Code, 0..999,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
* @deprecated Use {@link #getMccString} instead.
*/
@Deprecated
public int getMcc() {
- return (mMccStr != null) ? Integer.valueOf(mMccStr) : Integer.MAX_VALUE;
+ return (mMccStr != null) ? Integer.valueOf(mMccStr) : CellInfo.UNAVAILABLE;
}
/**
- * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown
+ * @return 2 or 3-digit Mobile Network Code, 0..999,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
* @deprecated Use {@link #getMncString} instead.
*/
@Deprecated
public int getMnc() {
- return (mMncStr != null) ? Integer.valueOf(mMncStr) : Integer.MAX_VALUE;
+ return (mMncStr != null) ? Integer.valueOf(mMncStr) : CellInfo.UNAVAILABLE;
}
/**
- * @return 28-bit Cell Identity, Integer.MAX_VALUE if unknown
+ * @return 28-bit Cell Identity,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
public int getCi() {
return mCi;
}
/**
- * @return Physical Cell Id 0..503, Integer.MAX_VALUE if unknown
+ * @return Physical Cell Id 0..503,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
public int getPci() {
return mPci;
}
/**
- * @return 16-bit Tracking Area Code, Integer.MAX_VALUE if unknown
+ * @return 16-bit Tracking Area Code,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
public int getTac() {
return mTac;
}
/**
- * @return 18-bit Absolute RF Channel Number, Integer.MAX_VALUE if unknown
+ * @return 18-bit Absolute RF Channel Number,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
public int getEarfcn() {
return mEarfcn;
}
/**
- * @return Cell bandwidth in kHz, Integer.MAX_VALUE if unknown
+ * @return Cell bandwidth in kHz,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
public int getBandwidth() {
return mBandwidth;
}
/**
- * @return Mobile Country Code in string format, null if unknown
+ * @return Mobile Country Code in string format, null if unavailable.
*/
public String getMccString() {
return mMccStr;
}
/**
- * @return Mobile Network Code in string format, null if unknown
+ * @return Mobile Network Code in string format, null if unavailable.
*/
public String getMncString() {
return mMncStr;
}
/**
- * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown
+ * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown.
*/
public String getMobileNetworkOperator() {
return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
@@ -216,8 +222,8 @@
@Override
public GsmCellLocation asCellLocation() {
GsmCellLocation cl = new GsmCellLocation();
- int tac = mTac != Integer.MAX_VALUE ? mTac : -1;
- int cid = mCi != Integer.MAX_VALUE ? mCi : -1;
+ int tac = mTac != CellInfo.UNAVAILABLE ? mTac : -1;
+ int cid = mCi != CellInfo.UNAVAILABLE ? mCi : -1;
cl.setLacAndCid(tac, cid);
cl.setPsc(0);
return cl;
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index bc83de1..8b1c1b9 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -28,11 +28,12 @@
private static final String TAG = CellIdentityTdscdma.class.getSimpleName();
private static final boolean DBG = false;
- // 16-bit Location Area Code, 0..65535, INT_MAX if unknown.
+ // 16-bit Location Area Code, 0..65535, CellInfo.UNAVAILABLE if unknown.
private final int mLac;
- // 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown.
+ // 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, CellInfo.UNAVAILABLE
+ // if unknown.
private final int mCid;
- // 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown.
+ // 8-bit Cell Parameters ID described in TS 25.331, 0..127, CellInfo.UNAVAILABLE if unknown.
private final int mCpid;
// 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
private final int mUarfcn;
@@ -42,18 +43,20 @@
*/
public CellIdentityTdscdma() {
super(TAG, CellInfo.TYPE_TDSCDMA, null, null, null, null);
- mLac = Integer.MAX_VALUE;
- mCid = Integer.MAX_VALUE;
- mCpid = Integer.MAX_VALUE;
- mUarfcn = Integer.MAX_VALUE;
+ mLac = CellInfo.UNAVAILABLE;
+ mCid = CellInfo.UNAVAILABLE;
+ mCpid = CellInfo.UNAVAILABLE;
+ mUarfcn = CellInfo.UNAVAILABLE;
}
/**
* @param mcc 3-digit Mobile Country Code, 0..999
* @param mnc 2 or 3-digit Mobile Network Code, 0..999
- * @param lac 16-bit Location Area Code, 0..65535, INT_MAX if unknown
- * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
- * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
+ * @param lac 16-bit Location Area Code, 0..65535, CellInfo.UNAVAILABLE if unknown
+ * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, CellInfo.
+ * UNAVAILABLE if unknown
+ * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, CellInfo.UNAVAILABLE
+ * if unknown
* @param uarfcn 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
*
* @hide
@@ -65,9 +68,11 @@
/**
* @param mcc 3-digit Mobile Country Code in string format
* @param mnc 2 or 3-digit Mobile Network Code in string format
- * @param lac 16-bit Location Area Code, 0..65535, INT_MAX if unknown
- * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
- * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
+ * @param lac 16-bit Location Area Code, 0..65535, CellInfo.UNAVAILABLE if unknown
+ * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455,
+ * CellInfo.UNAVAILABLE if unknown
+ * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127,
+ * CellInfo.UNAVAILABLE if unknown
* @param uarfcn 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
* @param alphal long alpha Operator Name String or Enhanced Operator Name String
* @param alphas short alpha Operator Name String or Enhanced Operator Name String
@@ -116,21 +121,24 @@
}
/**
- * @return 16-bit Location Area Code, 0..65535, INT_MAX if unknown
+ * @return 16-bit Location Area Code, 0..65535,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
public int getLac() {
return mLac;
}
/**
- * @return 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
+ * @return 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
public int getCid() {
return mCid;
}
/**
- * @return 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
+ * @return 8-bit Cell Parameters ID described in TS 25.331, 0..127,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
public int getCpid() {
return mCpid;
@@ -146,8 +154,8 @@
@Override
public GsmCellLocation asCellLocation() {
GsmCellLocation cl = new GsmCellLocation();
- int lac = mLac != Integer.MAX_VALUE ? mLac : -1;
- int cid = mCid != Integer.MAX_VALUE ? mCid : -1;
+ int lac = mLac != CellInfo.UNAVAILABLE ? mLac : -1;
+ int cid = mCid != CellInfo.UNAVAILABLE ? mCid : -1;
cl.setLacAndCid(lac, cid);
cl.setPsc(-1); // There is no PSC for TD-SCDMA; not using this for CPI to stem shenanigans
return cl;
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 727d990..3416ffe 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -16,7 +16,6 @@
package android.telephony;
-import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.telephony.gsm.GsmCellLocation;
@@ -46,10 +45,10 @@
*/
public CellIdentityWcdma() {
super(TAG, CellInfo.TYPE_WCDMA, null, null, null, null);
- mLac = Integer.MAX_VALUE;
- mCid = Integer.MAX_VALUE;
- mPsc = Integer.MAX_VALUE;
- mUarfcn = Integer.MAX_VALUE;
+ mLac = CellInfo.UNAVAILABLE;
+ mCid = CellInfo.UNAVAILABLE;
+ mPsc = CellInfo.UNAVAILABLE;
+ mUarfcn = CellInfo.UNAVAILABLE;
}
/**
* public constructor
@@ -62,7 +61,7 @@
* @hide
*/
public CellIdentityWcdma (int mcc, int mnc, int lac, int cid, int psc) {
- this(lac, cid, psc, Integer.MAX_VALUE, String.valueOf(mcc), String.valueOf(mnc),
+ this(lac, cid, psc, CellInfo.UNAVAILABLE, String.valueOf(mcc), String.valueOf(mnc),
null, null);
}
@@ -113,25 +112,28 @@
}
/**
- * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown
+ * @return 3-digit Mobile Country Code, 0..999,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
* @deprecated Use {@link #getMccString} instead.
*/
@Deprecated
public int getMcc() {
- return (mMccStr != null) ? Integer.valueOf(mMccStr) : Integer.MAX_VALUE;
+ return (mMccStr != null) ? Integer.valueOf(mMccStr) : CellInfo.UNAVAILABLE;
}
/**
- * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown
+ * @return 2 or 3-digit Mobile Network Code, 0..999,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
* @deprecated Use {@link #getMncString} instead.
*/
@Deprecated
public int getMnc() {
- return (mMncStr != null) ? Integer.valueOf(mMncStr) : Integer.MAX_VALUE;
+ return (mMncStr != null) ? Integer.valueOf(mMncStr) : CellInfo.UNAVAILABLE;
}
/**
- * @return 16-bit Location Area Code, 0..65535, Integer.MAX_VALUE if unknown
+ * @return 16-bit Location Area Code, 0..65535,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
public int getLac() {
return mLac;
@@ -139,29 +141,30 @@
/**
* @return CID
- * 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, Integer.MAX_VALUE if unknown
+ * 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
public int getCid() {
return mCid;
}
/**
- * @return 9-bit UMTS Primary Scrambling Code described in TS 25.331, 0..511, Integer.MAX_VALUE
- * if unknown
+ * @return 9-bit UMTS Primary Scrambling Code described in TS 25.331, 0..511,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
public int getPsc() {
return mPsc;
}
/**
- * @return Mobile Country Code in string version, null if unknown
+ * @return Mobile Country Code in string version, null if unavailable.
*/
public String getMccString() {
return mMccStr;
}
/**
- * @return Mobile Network Code in string version, null if unknown
+ * @return Mobile Network Code in string version, null if unavailable.
*/
public String getMncString() {
return mMncStr;
@@ -180,7 +183,8 @@
}
/**
- * @return 16-bit UMTS Absolute RF Channel Number, Integer.MAX_VALUE if unknown
+ * @return 16-bit UMTS Absolute RF Channel Number,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
public int getUarfcn() {
return mUarfcn;
@@ -196,9 +200,9 @@
@Override
public GsmCellLocation asCellLocation() {
GsmCellLocation cl = new GsmCellLocation();
- int lac = mLac != Integer.MAX_VALUE ? mLac : -1;
- int cid = mCid != Integer.MAX_VALUE ? mCid : -1;
- int psc = mPsc != Integer.MAX_VALUE ? mPsc : -1;
+ int lac = mLac != CellInfo.UNAVAILABLE ? mLac : -1;
+ int cid = mCid != CellInfo.UNAVAILABLE ? mCid : -1;
+ int psc = mPsc != CellInfo.UNAVAILABLE ? mPsc : -1;
cl.setLacAndCid(lac, cid);
cl.setPsc(psc);
@@ -280,4 +284,4 @@
protected static CellIdentityWcdma createFromParcelBody(Parcel in) {
return new CellIdentityWcdma(in);
}
-}
\ No newline at end of file
+}
diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index 94e4293..1c63e82 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -33,6 +33,11 @@
public abstract class CellInfo implements Parcelable {
/**
+ * This value indicates that the integer field is unreported.
+ */
+ public static final int UNAVAILABLE = Integer.MAX_VALUE;
+
+ /**
* Cell identity type
* @hide
*/
diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java
index aa6b207..5123052 100644
--- a/telephony/java/android/telephony/CellSignalStrengthCdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java
@@ -51,8 +51,9 @@
* <p>Note that this HAL is inconsistent with UMTS-based radio techs as the value indicating
* that a field is unreported is negative, rather than a large(r) positive number.
* <p>Also note that to keep the public-facing methods of this class consistent with others,
- * unreported values are coerced to Integer.MAX_VALUE rather than left as -1, which is
- * a departure from SignalStrength, which is stuck with the values it currently reports.
+ * unreported values are coerced to {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}
+ * rather than left as -1, which is a departure from SignalStrength, which is stuck with the
+ * values it currently reports.
*
* @param cdmaDbm negative of the CDMA signal strength value or -1 if invalid.
* @param cdmaEcio negative of the CDMA pilot/noise ratio or -1 if invalid.
@@ -65,12 +66,12 @@
int evdoSnr) {
// The values here were lifted from SignalStrength.validateInput()
// FIXME: Combine all checking and setting logic between this and SignalStrength.
- mCdmaDbm = ((cdmaDbm > 0) && (cdmaDbm < 120)) ? -cdmaDbm : Integer.MAX_VALUE;
- mCdmaEcio = ((cdmaEcio > 0) && (cdmaEcio < 160)) ? -cdmaEcio : Integer.MAX_VALUE;
+ mCdmaDbm = ((cdmaDbm > 0) && (cdmaDbm < 120)) ? -cdmaDbm : CellInfo.UNAVAILABLE;
+ mCdmaEcio = ((cdmaEcio > 0) && (cdmaEcio < 160)) ? -cdmaEcio : CellInfo.UNAVAILABLE;
- mEvdoDbm = ((evdoDbm > 0) && (evdoDbm < 120)) ? -evdoDbm : Integer.MAX_VALUE;
- mEvdoEcio = ((evdoEcio > 0) && (evdoEcio < 160)) ? -evdoEcio : Integer.MAX_VALUE;
- mEvdoSnr = ((evdoSnr > 0) && (evdoSnr <= 8)) ? evdoSnr : Integer.MAX_VALUE;
+ mEvdoDbm = ((evdoDbm > 0) && (evdoDbm < 120)) ? -evdoDbm : CellInfo.UNAVAILABLE;
+ mEvdoEcio = ((evdoEcio > 0) && (evdoEcio < 160)) ? -evdoEcio : CellInfo.UNAVAILABLE;
+ mEvdoSnr = ((evdoSnr > 0) && (evdoSnr <= 8)) ? evdoSnr : CellInfo.UNAVAILABLE;
}
/** @hide */
@@ -96,11 +97,11 @@
/** @hide */
@Override
public void setDefaultValues() {
- mCdmaDbm = Integer.MAX_VALUE;
- mCdmaEcio = Integer.MAX_VALUE;
- mEvdoDbm = Integer.MAX_VALUE;
- mEvdoEcio = Integer.MAX_VALUE;
- mEvdoSnr = Integer.MAX_VALUE;
+ mCdmaDbm = CellInfo.UNAVAILABLE;
+ mCdmaEcio = CellInfo.UNAVAILABLE;
+ mEvdoDbm = CellInfo.UNAVAILABLE;
+ mEvdoEcio = CellInfo.UNAVAILABLE;
+ mEvdoSnr = CellInfo.UNAVAILABLE;
}
/**
@@ -139,7 +140,7 @@
int cdmaAsuLevel;
int ecioAsuLevel;
- if (cdmaDbm == Integer.MAX_VALUE) cdmaAsuLevel = 99;
+ if (cdmaDbm == CellInfo.UNAVAILABLE) cdmaAsuLevel = 99;
else if (cdmaDbm >= -75) cdmaAsuLevel = 16;
else if (cdmaDbm >= -82) cdmaAsuLevel = 8;
else if (cdmaDbm >= -90) cdmaAsuLevel = 4;
@@ -148,7 +149,7 @@
else cdmaAsuLevel = 99;
// Ec/Io are in dB*10
- if (cdmaEcio == Integer.MAX_VALUE) ecioAsuLevel = 99;
+ if (cdmaEcio == CellInfo.UNAVAILABLE) ecioAsuLevel = 99;
else if (cdmaEcio >= -90) ecioAsuLevel = 16;
else if (cdmaEcio >= -100) ecioAsuLevel = 8;
else if (cdmaEcio >= -115) ecioAsuLevel = 4;
@@ -170,7 +171,7 @@
int levelDbm;
int levelEcio;
- if (cdmaDbm == Integer.MAX_VALUE) levelDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ if (cdmaDbm == CellInfo.UNAVAILABLE) levelDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
else if (cdmaDbm >= -75) levelDbm = SIGNAL_STRENGTH_GREAT;
else if (cdmaDbm >= -85) levelDbm = SIGNAL_STRENGTH_GOOD;
else if (cdmaDbm >= -95) levelDbm = SIGNAL_STRENGTH_MODERATE;
@@ -178,7 +179,7 @@
else levelDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
// Ec/Io are in dB*10
- if (cdmaEcio == Integer.MAX_VALUE) levelEcio = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ if (cdmaEcio == CellInfo.UNAVAILABLE) levelEcio = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
else if (cdmaEcio >= -90) levelEcio = SIGNAL_STRENGTH_GREAT;
else if (cdmaEcio >= -110) levelEcio = SIGNAL_STRENGTH_GOOD;
else if (cdmaEcio >= -130) levelEcio = SIGNAL_STRENGTH_MODERATE;
@@ -199,14 +200,14 @@
int levelEvdoDbm;
int levelEvdoSnr;
- if (evdoDbm == Integer.MAX_VALUE) levelEvdoDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ if (evdoDbm == CellInfo.UNAVAILABLE) levelEvdoDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
else if (evdoDbm >= -65) levelEvdoDbm = SIGNAL_STRENGTH_GREAT;
else if (evdoDbm >= -75) levelEvdoDbm = SIGNAL_STRENGTH_GOOD;
else if (evdoDbm >= -90) levelEvdoDbm = SIGNAL_STRENGTH_MODERATE;
else if (evdoDbm >= -105) levelEvdoDbm = SIGNAL_STRENGTH_POOR;
else levelEvdoDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- if (evdoSnr == Integer.MAX_VALUE) levelEvdoSnr = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ if (evdoSnr == CellInfo.UNAVAILABLE) levelEvdoSnr = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
else if (evdoSnr >= 7) levelEvdoSnr = SIGNAL_STRENGTH_GREAT;
else if (evdoSnr >= 5) levelEvdoSnr = SIGNAL_STRENGTH_GOOD;
else if (evdoSnr >= 3) levelEvdoSnr = SIGNAL_STRENGTH_MODERATE;
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index 1e8d119..e906f46 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -40,7 +40,7 @@
@UnsupportedAppUsage
private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
@UnsupportedAppUsage
- private int mTimingAdvance; // range from 0-219 or Integer.MAX_VALUE if unknown
+ private int mTimingAdvance; // range from 0-219 or CellInfo.UNAVAILABLE if unknown
/** @hide */
@UnsupportedAppUsage
@@ -50,7 +50,7 @@
/** @hide */
public CellSignalStrengthGsm(int ss, int ber) {
- this(ss, ber, Integer.MAX_VALUE);
+ this(ss, ber, CellInfo.UNAVAILABLE);
}
/** @hide */
@@ -81,9 +81,9 @@
/** @hide */
@Override
public void setDefaultValues() {
- mSignalStrength = Integer.MAX_VALUE;
- mBitErrorRate = Integer.MAX_VALUE;
- mTimingAdvance = Integer.MAX_VALUE;
+ mSignalStrength = CellInfo.UNAVAILABLE;
+ mBitErrorRate = CellInfo.UNAVAILABLE;
+ mTimingAdvance = CellInfo.UNAVAILABLE;
}
/**
@@ -112,8 +112,9 @@
/**
* Get the GSM timing advance between 0..219 symbols (normally 0..63).
- * Integer.MAX_VALUE is reported when there is no RR connection.
- * Refer to 3GPP 45.010 Sec 5.8
+ * <p>{@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} is reported when there is no RR
+ * connection. Refer to 3GPP 45.010 Sec 5.8.
+ *
* @return the current GSM timing advance, if available.
*/
public int getTimingAdvance() {
@@ -128,11 +129,11 @@
int dBm;
int level = mSignalStrength;
- int asu = (level == 99 ? Integer.MAX_VALUE : level);
- if (asu != Integer.MAX_VALUE) {
+ int asu = (level == 99 ? CellInfo.UNAVAILABLE : level);
+ if (asu != CellInfo.UNAVAILABLE) {
dBm = -113 + (2 * asu);
} else {
- dBm = Integer.MAX_VALUE;
+ dBm = CellInfo.UNAVAILABLE;
}
if (DBG) log("getDbm=" + dBm);
return dBm;
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index ed7d4b2..d6856b3 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -85,12 +85,12 @@
/** @hide */
@Override
public void setDefaultValues() {
- mSignalStrength = Integer.MAX_VALUE;
- mRsrp = Integer.MAX_VALUE;
- mRsrq = Integer.MAX_VALUE;
- mRssnr = Integer.MAX_VALUE;
- mCqi = Integer.MAX_VALUE;
- mTimingAdvance = Integer.MAX_VALUE;
+ mSignalStrength = CellInfo.UNAVAILABLE;
+ mRsrp = CellInfo.UNAVAILABLE;
+ mRsrq = CellInfo.UNAVAILABLE;
+ mRssnr = CellInfo.UNAVAILABLE;
+ mCqi = CellInfo.UNAVAILABLE;
+ mTimingAdvance = CellInfo.UNAVAILABLE;
}
/**
@@ -104,26 +104,27 @@
int levelRsrp = 0;
int levelRssnr = 0;
- if (mRsrp == Integer.MAX_VALUE) levelRsrp = 0;
+ if (mRsrp == CellInfo.UNAVAILABLE) levelRsrp = 0;
else if (mRsrp >= -95) levelRsrp = SIGNAL_STRENGTH_GREAT;
else if (mRsrp >= -105) levelRsrp = SIGNAL_STRENGTH_GOOD;
else if (mRsrp >= -115) levelRsrp = SIGNAL_STRENGTH_MODERATE;
else levelRsrp = SIGNAL_STRENGTH_POOR;
// See RIL_LTE_SignalStrength in ril.h
- if (mRssnr == Integer.MAX_VALUE) levelRssnr = 0;
+ if (mRssnr == CellInfo.UNAVAILABLE) levelRssnr = 0;
else if (mRssnr >= 45) levelRssnr = SIGNAL_STRENGTH_GREAT;
else if (mRssnr >= 10) levelRssnr = SIGNAL_STRENGTH_GOOD;
else if (mRssnr >= -30) levelRssnr = SIGNAL_STRENGTH_MODERATE;
else levelRssnr = SIGNAL_STRENGTH_POOR;
int level;
- if (mRsrp == Integer.MAX_VALUE)
+ if (mRsrp == CellInfo.UNAVAILABLE) {
level = levelRssnr;
- else if (mRssnr == Integer.MAX_VALUE)
+ } else if (mRssnr == CellInfo.UNAVAILABLE) {
level = levelRsrp;
- else
+ } else {
level = (levelRssnr < levelRsrp) ? levelRssnr : levelRsrp;
+ }
if (DBG) log("Lte rsrp level: " + levelRsrp
+ " snr level: " + levelRssnr + " level: " + level);
@@ -133,7 +134,8 @@
/**
* Get reference signal received quality
*
- * @return the RSRQ if available or Integer.MAX_VALUE if unavailable.
+ * @return the RSRQ if available or
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
public int getRsrq() {
return mRsrq;
@@ -142,7 +144,8 @@
/**
* Get reference signal signal-to-noise ratio
*
- * @return the RSSNR if available or Integer.MAX_VALUE if unavailable.
+ * @return the RSSNR if available or
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
public int getRssnr() {
return mRssnr;
@@ -160,7 +163,8 @@
/**
* Get channel quality indicator
*
- * @return the CQI if available or Integer.MAX_VALUE if unavailable.
+ * @return the CQI if available or
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
public int getCqi() {
return mCqi;
@@ -184,7 +188,7 @@
public int getAsuLevel() {
int lteAsuLevel = 99;
int lteDbm = getDbm();
- if (lteDbm == Integer.MAX_VALUE) lteAsuLevel = 99;
+ if (lteDbm == CellInfo.UNAVAILABLE) lteAsuLevel = 99;
else if (lteDbm <= -140) lteAsuLevel = 0;
else if (lteDbm >= -43) lteAsuLevel = 97;
else lteAsuLevel = lteDbm + 140;
@@ -194,10 +198,11 @@
/**
* Get the timing advance value for LTE, as a value in range of 0..1282.
- * Integer.MAX_VALUE is reported when there is no active RRC
- * connection. Refer to 3GPP 36.213 Sec 4.2.3
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} is reported when there is no
+ * active RRC connection. Refer to 3GPP 36.213 Sec 4.2.3
*
- * @return the LTE timing advance if available or Integer.MAX_VALUE if unavailable.
+ * @return the LTE timing advance if available or
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
public int getTimingAdvance() {
return mTimingAdvance;
@@ -252,8 +257,8 @@
// Need to multiply rsrp and rsrq by -1
// to ensure consistency when reading values written here
// unless the values are invalid
- dest.writeInt(mRsrp * (mRsrp != Integer.MAX_VALUE ? -1 : 1));
- dest.writeInt(mRsrq * (mRsrq != Integer.MAX_VALUE ? -1 : 1));
+ dest.writeInt(mRsrp * (mRsrp != CellInfo.UNAVAILABLE ? -1 : 1));
+ dest.writeInt(mRsrq * (mRsrq != CellInfo.UNAVAILABLE ? -1 : 1));
dest.writeInt(mRssnr);
dest.writeInt(mCqi);
dest.writeInt(mTimingAdvance);
@@ -268,9 +273,9 @@
// rsrp and rsrq are written into the parcel as positive values.
// Need to convert into negative values unless the values are invalid
mRsrp = in.readInt();
- if (mRsrp != Integer.MAX_VALUE) mRsrp *= -1;
+ if (mRsrp != CellInfo.UNAVAILABLE) mRsrp *= -1;
mRsrq = in.readInt();
- if (mRsrq != Integer.MAX_VALUE) mRsrq *= -1;
+ if (mRsrq != CellInfo.UNAVAILABLE) mRsrq *= -1;
mRssnr = in.readInt();
mCqi = in.readInt();
mTimingAdvance = in.readInt();
diff --git a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
index 41859a3..4d040cc 100644
--- a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
@@ -36,11 +36,11 @@
private static final int TDSCDMA_SIGNAL_STRENGTH_MODERATE = 5;
private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
- // or Integer.MAX_VALUE if unknown
+ // or CellInfo.UNAVAILABLE if unknown
private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
- // Integer.MAX_VALUE if unknown
- private int mRscp; // Pilot power (0-96, 255) as defined in TS 27.007 8.69 or Integer.MAX_VALUE
- // if unknown
+ // CellInfo.UNAVAILABLE if unknown
+ private int mRscp; // Pilot power (0-96, 255) as defined in TS 27.007 8.69 or
+ // CellInfo.UNAVAILABLE if unknown
/** @hide */
public CellSignalStrengthTdscdma() {
@@ -75,9 +75,9 @@
/** @hide */
@Override
public void setDefaultValues() {
- mSignalStrength = Integer.MAX_VALUE;
- mBitErrorRate = Integer.MAX_VALUE;
- mRscp = Integer.MAX_VALUE;
+ mSignalStrength = CellInfo.UNAVAILABLE;
+ mBitErrorRate = CellInfo.UNAVAILABLE;
+ mRscp = CellInfo.UNAVAILABLE;
}
/**
@@ -118,11 +118,11 @@
int dBm;
int level = mSignalStrength;
- int asu = (level == 99 ? Integer.MAX_VALUE : level);
- if (asu != Integer.MAX_VALUE) {
+ int asu = (level == 99 ? CellInfo.UNAVAILABLE : level);
+ if (asu != CellInfo.UNAVAILABLE) {
dBm = -113 + (2 * asu);
} else {
- dBm = Integer.MAX_VALUE;
+ dBm = CellInfo.UNAVAILABLE;
}
if (DBG) log("getDbm=" + dBm);
return dBm;
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index 66e0882..0048cbd 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -37,14 +37,14 @@
@UnsupportedAppUsage
private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
- // or Integer.MAX_VALUE if unknown
+ // or CellInfo.UNAVAILABLE if unknown
@UnsupportedAppUsage
private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
- // Integer.MAX_VALUE if unknown
+ // CellInfo.UNAVAILABLE if unknown
private int mRscp; // bit error rate (0-96, 255) as defined in TS 27.007 8.69 or
- // Integer.MAX_VALUE if unknown
+ // CellInfo.UNAVAILABLE if unknown
private int mEcNo; // signal to noise radio (0-49, 255) as defined in TS 27.007 8.69 or
- // Integer.MAX_VALUE if unknown
+ // CellInfo.UNAVAILABLE if unknown
/** @hide */
public CellSignalStrengthWcdma() {
@@ -81,10 +81,10 @@
/** @hide */
@Override
public void setDefaultValues() {
- mSignalStrength = Integer.MAX_VALUE;
- mBitErrorRate = Integer.MAX_VALUE;
- mRscp = Integer.MAX_VALUE;
- mEcNo = Integer.MAX_VALUE;
+ mSignalStrength = CellInfo.UNAVAILABLE;
+ mBitErrorRate = CellInfo.UNAVAILABLE;
+ mRscp = CellInfo.UNAVAILABLE;
+ mEcNo = CellInfo.UNAVAILABLE;
}
/**
@@ -119,11 +119,11 @@
int dBm;
int level = mSignalStrength;
- int asu = (level == 99 ? Integer.MAX_VALUE : level);
- if (asu != Integer.MAX_VALUE) {
+ int asu = (level == 99 ? CellInfo.UNAVAILABLE : level);
+ if (asu != CellInfo.UNAVAILABLE) {
dBm = -113 + (2 * asu);
} else {
- dBm = Integer.MAX_VALUE;
+ dBm = CellInfo.UNAVAILABLE;
}
if (DBG) log("getDbm=" + dBm);
return dBm;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 0459667..0ba18ee 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -43,6 +43,7 @@
import android.net.INetworkPolicyManager;
import android.net.NetworkCapabilities;
import android.net.Uri;
+import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -781,8 +782,13 @@
IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() {
@Override
public void onSubscriptionsChanged() {
- if (DBG) log("onOpportunisticSubscriptionsChanged callback received.");
- mExecutor.execute(() -> onOpportunisticSubscriptionsChanged());
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (DBG) log("onOpportunisticSubscriptionsChanged callback received.");
+ mExecutor.execute(() -> onOpportunisticSubscriptionsChanged());
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
};
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index 8f18d07..d6dbf5a 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -39,6 +39,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.net.NetworkStats.Entry;
import android.os.Process;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.filters.SmallTest;
@@ -785,7 +786,38 @@
ArrayMap<String, String> stackedIface = new ArrayMap<>();
stackedIface.put(v4Iface, baseIface);
- NetworkStats.Entry otherEntry = new NetworkStats.Entry(
+ // Ipv4 traffic sent/received by an app on stacked interface.
+ final NetworkStats.Entry appEntry = new NetworkStats.Entry(
+ v4Iface, appUid, SET_DEFAULT, TAG_NONE,
+ 30501490 /* rxBytes */,
+ 22401 /* rxPackets */,
+ 876235 /* txBytes */,
+ 13805 /* txPackets */,
+ 0 /* operations */);
+
+ // Traffic measured for the root uid on the base interface if eBPF is in use.
+ // Incorrectly includes appEntry's bytes and packets, plus IPv4-IPv6 translation
+ // overhead (20 bytes per packet), only for TX traffic.
+ final NetworkStats.Entry ebpfRootUidEntry = new NetworkStats.Entry(
+ baseIface, rootUid, SET_DEFAULT, TAG_NONE,
+ 163577 /* rxBytes */,
+ 187 /* rxPackets */,
+ 1169942 /* txBytes */,
+ 13902 /* txPackets */,
+ 0 /* operations */);
+
+ // Traffic measured for the root uid on the base interface if xt_qtaguid is in use.
+ // Incorrectly includes appEntry's bytes and packets, plus IPv4-IPv6 translation
+ // overhead (20 bytes per packet), in both directions.
+ final NetworkStats.Entry xtRootUidEntry = new NetworkStats.Entry(
+ baseIface, rootUid, SET_DEFAULT, TAG_NONE,
+ 31113087 /* rxBytes */,
+ 22588 /* rxPackets */,
+ 1169942 /* txBytes */,
+ 13902 /* txPackets */,
+ 0 /* operations */);
+
+ final NetworkStats.Entry otherEntry = new NetworkStats.Entry(
otherIface, appUid, SET_DEFAULT, TAG_NONE,
2600 /* rxBytes */,
2 /* rxPackets */,
@@ -793,39 +825,41 @@
3 /* txPackets */,
0 /* operations */);
- NetworkStats stats = new NetworkStats(TEST_START, 3)
- .addValues(v4Iface, appUid, SET_DEFAULT, TAG_NONE,
- 30501490 /* rxBytes */,
- 22401 /* rxPackets */,
- 876235 /* txBytes */,
- 13805 /* txPackets */,
- 0 /* operations */)
- .addValues(baseIface, rootUid, SET_DEFAULT, TAG_NONE,
- 31113087,
- 22588,
- 1169942,
- 13902,
- 0)
+ final NetworkStats statsXt = new NetworkStats(TEST_START, 3)
+ .addValues(appEntry)
+ .addValues(xtRootUidEntry)
.addValues(otherEntry);
- stats.apply464xlatAdjustments(stackedIface);
+ final NetworkStats statsEbpf = new NetworkStats(TEST_START, 3)
+ .addValues(appEntry)
+ .addValues(ebpfRootUidEntry)
+ .addValues(otherEntry);
- assertEquals(3, stats.size());
- assertValues(stats, 0, v4Iface, appUid, SET_DEFAULT, TAG_NONE,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
+ statsXt.apply464xlatAdjustments(stackedIface, false);
+ statsEbpf.apply464xlatAdjustments(stackedIface, true);
+
+ assertEquals(3, statsXt.size());
+ assertEquals(3, statsEbpf.size());
+ final NetworkStats.Entry expectedAppUid = new NetworkStats.Entry(
+ v4Iface, appUid, SET_DEFAULT, TAG_NONE,
30949510,
22401,
1152335,
13805,
0);
- assertValues(stats, 1, baseIface, 0, SET_DEFAULT, TAG_NONE,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
+ final NetworkStats.Entry expectedRootUid = new NetworkStats.Entry(
+ baseIface, 0, SET_DEFAULT, TAG_NONE,
163577,
187,
17607,
97,
0);
- assertEquals(otherEntry, stats.getValues(2, null));
+ assertEquals(expectedAppUid, statsXt.getValues(0, null));
+ assertEquals(expectedRootUid, statsXt.getValues(1, null));
+ assertEquals(otherEntry, statsXt.getValues(2, null));
+ assertEquals(expectedAppUid, statsEbpf.getValues(0, null));
+ assertEquals(expectedRootUid, statsEbpf.getValues(1, null));
+ assertEquals(otherEntry, statsEbpf.getValues(2, null));
}
@Test
@@ -850,7 +884,7 @@
.addValues(secondEntry);
// Empty map: no adjustment
- stats.apply464xlatAdjustments(new ArrayMap<>());
+ stats.apply464xlatAdjustments(new ArrayMap<>(), false);
assertEquals(2, stats.size());
assertEquals(firstEntry, stats.getValues(0, null));