summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt8
-rw-r--r--api/system-current.txt18
-rw-r--r--cmds/statsd/src/atoms.proto23
-rw-r--r--core/java/android/app/Activity.java23
-rw-r--r--core/java/android/app/ActivityThread.java61
-rw-r--r--core/java/android/app/ClientTransactionHandler.java10
-rw-r--r--core/java/android/app/IActivityManager.aidl1
-rw-r--r--core/java/android/app/PendingIntent.java13
-rw-r--r--core/java/android/app/servertransaction/TopResumedActivityChangeItem.java112
-rw-r--r--core/java/android/hardware/HardwareBuffer.java34
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java53
-rw-r--r--core/java/android/hardware/camera2/impl/CameraMetadataNative.java35
-rw-r--r--core/java/android/hardware/camera2/params/StreamConfigurationMap.java127
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java29
-rw-r--r--core/java/android/provider/DeviceConfig.java30
-rw-r--r--core/java/android/provider/Settings.java69
-rw-r--r--core/java/android/view/ImeInsetsSourceConsumer.java157
-rw-r--r--core/java/android/view/InsetsController.java30
-rw-r--r--core/java/android/view/InsetsSourceConsumer.java16
-rw-r--r--core/java/android/view/InsetsState.java2
-rw-r--r--core/java/android/view/SurfaceView.java2
-rw-r--r--core/java/android/view/ViewRootImpl.java5
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureManager.java9
-rw-r--r--core/java/android/view/contentcapture/IContentCaptureManager.aidl14
-rw-r--r--core/java/android/view/contentcapture/MainContentCaptureSession.java6
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java65
-rw-r--r--core/java/android/view/inputmethod/InputMethodSystemProperty.java14
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java39
-rw-r--r--core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl5
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java37
-rw-r--r--core/java/com/android/internal/view/IInputMethodClient.aidl4
-rw-r--r--core/jni/Android.bp1
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android_hardware_HardwareBuffer.cpp17
-rw-r--r--core/jni/android_media_AudioAttributes.cpp205
-rw-r--r--core/jni/android_media_AudioAttributes.h70
-rw-r--r--core/jni/android_media_AudioRecord.cpp41
-rw-r--r--core/jni/android_media_AudioSystem.cpp65
-rw-r--r--core/jni/android_media_AudioTrack.cpp52
-rw-r--r--core/jni/android_view_Surface.cpp11
-rw-r--r--core/jni/com_android_internal_os_AtomicDirectory.cpp2
-rw-r--r--core/jni/include/android_runtime/android_view_Surface.h3
-rw-r--r--core/proto/android/app/job/enums.proto3
-rw-r--r--core/proto/android/bluetooth/hci/enums.proto2
-rw-r--r--core/proto/android/server/job/enums.proto43
-rw-r--r--core/proto/android/server/jobscheduler.proto19
-rw-r--r--core/res/res/values-night/themes_device_defaults.xml4
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java3
-rw-r--r--core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java145
-rw-r--r--core/tests/coretests/src/android/view/InsetsStateTest.java10
-rw-r--r--graphics/java/android/graphics/ImageFormat.java9
-rw-r--r--keystore/java/android/security/Credentials.java61
-rw-r--r--media/apex/java/android/media/MediaPlayer2.java9
-rw-r--r--media/java/android/media/ImageReader.java47
-rw-r--r--media/java/android/media/ImageUtils.java2
-rw-r--r--media/java/android/media/tv/TvContract.java11
-rw-r--r--media/jni/Android.bp33
-rw-r--r--media/jni/android_media_ImageReader.cpp21
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java23
-rw-r--r--packages/SystemUI/res/drawable/smart_reply_button_background.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml6
-rw-r--r--packages/SystemUI/res/values/strings.xml5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java24
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java16
-rw-r--r--services/core/java/com/android/server/AlarmManagerInternal.java12
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java36
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java11
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerDebugConfig.java1
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java52
-rw-r--r--services/core/java/com/android/server/am/BroadcastConstants.java151
-rw-r--r--services/core/java/com/android/server/am/BroadcastDispatcher.java687
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java311
-rw-r--r--services/core/java/com/android/server/am/BroadcastRecord.java54
-rw-r--r--services/core/java/com/android/server/attention/AttentionManagerService.java22
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java66
-rw-r--r--services/core/java/com/android/server/job/controllers/JobStatus.java83
-rw-r--r--services/core/java/com/android/server/wm/ActivityMetricsLogger.java52
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java22
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java1
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java1
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java105
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java5
-rw-r--r--services/core/java/com/android/server/wm/RootActivityContainer.java24
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java10
-rw-r--r--telephony/java/android/telephony/ServiceState.java5
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java22
-rw-r--r--telephony/java/android/telephony/data/DataCallResponse.java30
-rw-r--r--telephony/java/android/telephony/data/DataProfile.java88
-rw-r--r--telephony/java/com/android/internal/telephony/RILConstants.java20
-rw-r--r--tests/ActivityTests/AndroidManifest.xml1
-rw-r--r--tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java100
-rw-r--r--tests/ActivityTests/src/com/google/android/test/activity/SlowReceiver.java47
-rw-r--r--tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java81
-rw-r--r--tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java105
-rw-r--r--tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java238
-rw-r--r--tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java85
-rw-r--r--tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java7
-rw-r--r--tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java51
-rw-r--r--tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/IntEnum.java.txt45
-rw-r--r--tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/IntFlag.java.txt43
-rw-r--r--tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt4
-rw-r--r--tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt10
-rw-r--r--tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt8
-rw-r--r--tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt30
-rw-r--r--tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SuppliedAttributeId.java.txt10
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java12
114 files changed, 4085 insertions, 788 deletions
diff --git a/api/current.txt b/api/current.txt
index 361ee74becdd..3d77ffbc450d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3802,6 +3802,7 @@ package android.app {
method @Deprecated public void onStateNotSaved();
method @CallSuper protected void onStop();
method protected void onTitleChanged(CharSequence, int);
+ method public void onTopResumedActivityChanged(boolean);
method public boolean onTouchEvent(android.view.MotionEvent);
method public boolean onTrackballEvent(android.view.MotionEvent);
method public void onTrimMemory(int);
@@ -14167,6 +14168,7 @@ package android.graphics {
ctor public ImageFormat();
method public static int getBitsPerPixel(int);
field public static final int DEPTH16 = 1144402265; // 0x44363159
+ field public static final int DEPTH_JPEG = 1768253795; // 0x69656963
field public static final int DEPTH_POINT_CLOUD = 257; // 0x101
field public static final int FLEX_RGBA_8888 = 42; // 0x2a
field public static final int FLEX_RGB_888 = 41; // 0x29
@@ -16171,6 +16173,7 @@ package android.hardware {
method public long getUsage();
method public int getWidth();
method public boolean isClosed();
+ method public static boolean isSupported(int, int, int, int, long);
method public void writeToParcel(android.os.Parcel, int);
field public static final int BLOB = 33; // 0x21
field public static final android.os.Parcelable.Creator<android.hardware.HardwareBuffer> CREATOR;
@@ -27681,6 +27684,7 @@ package android.media.tv {
field public static final String TYPE_DVB_T2 = "TYPE_DVB_T2";
field public static final String TYPE_ISDB_C = "TYPE_ISDB_C";
field public static final String TYPE_ISDB_S = "TYPE_ISDB_S";
+ field public static final String TYPE_ISDB_S3 = "TYPE_ISDB_S3";
field public static final String TYPE_ISDB_T = "TYPE_ISDB_T";
field public static final String TYPE_ISDB_TB = "TYPE_ISDB_TB";
field public static final String TYPE_NTSC = "TYPE_NTSC";
@@ -38649,6 +38653,7 @@ package android.provider {
field public static final String ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS";
field public static final String ACTION_PRINT_SETTINGS = "android.settings.ACTION_PRINT_SETTINGS";
field public static final String ACTION_PRIVACY_SETTINGS = "android.settings.PRIVACY_SETTINGS";
+ field public static final String ACTION_PROCESS_WIFI_EASY_CONNECT_QR_CODE = "android.settings.PROCESS_WIFI_EASY_CONNECT_QR_CODE";
field public static final String ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS";
field public static final String ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
field public static final String ACTION_REQUEST_SET_AUTOFILL_SERVICE = "android.settings.REQUEST_SET_AUTOFILL_SERVICE";
@@ -38682,6 +38687,7 @@ package android.provider {
field public static final String EXTRA_DO_NOT_DISTURB_MODE_ENABLED = "android.settings.extra.do_not_disturb_mode_enabled";
field public static final String EXTRA_DO_NOT_DISTURB_MODE_MINUTES = "android.settings.extra.do_not_disturb_mode_minutes";
field public static final String EXTRA_INPUT_METHOD_ID = "input_method_id";
+ field public static final String EXTRA_QR_CODE = "android.provider.extra.QR_CODE";
field public static final String EXTRA_SUB_ID = "android.provider.extra.SUB_ID";
field public static final String INTENT_CATEGORY_USAGE_ACCESS_CONFIG = "android.intent.category.USAGE_ACCESS_CONFIG";
field public static final String METADATA_USAGE_ACCESS_REASON = "android.settings.metadata.USAGE_ACCESS_REASON";
@@ -45013,6 +45019,7 @@ package android.telephony {
method @Deprecated public void setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean);
method public boolean updateAvailableNetworks(java.util.List<android.telephony.AvailableNetworkInfo>);
field public static final String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL";
+ field public static final String ACTION_NETWORK_COUNTRY_CHANGED = "android.telephony.action.NETWORK_COUNTRY_CHANGED";
field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
field public static final String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
field public static final String ACTION_SECRET_CODE = "android.telephony.action.SECRET_CODE";
@@ -45051,6 +45058,7 @@ package android.telephony {
field public static final String EXTRA_INCOMING_NUMBER = "incoming_number";
field public static final String EXTRA_IS_REFRESH = "android.telephony.extra.IS_REFRESH";
field public static final String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT = "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
+ field public static final String EXTRA_NETWORK_COUNTRY = "android.telephony.extra.NETWORK_COUNTRY";
field public static final String EXTRA_NOTIFICATION_COUNT = "android.telephony.extra.NOTIFICATION_COUNT";
field public static final String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telephony.extra.PHONE_ACCOUNT_HANDLE";
field public static final String EXTRA_PRECISE_CARRIER_ID = "android.telephony.extra.PRECISE_CARRIER_ID";
diff --git a/api/system-current.txt b/api/system-current.txt
index 66e3642a8c05..e0af359c999b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5743,6 +5743,12 @@ package android.provider {
field public static final String NAMESPACE = "activity_manager";
}
+ public static interface DeviceConfig.AttentionManagerService {
+ field public static final String NAMESPACE = "attention_manager_service";
+ field public static final String PROPERTY_COMPONENT_NAME = "component_name";
+ field public static final String PROPERTY_SERVICE_ENABLED = "service_enabled";
+ }
+
public static interface DeviceConfig.FsiBoot {
field public static final String NAMESPACE = "fsi_boot";
field public static final String OOB_ENABLED = "oob_enabled";
@@ -5751,8 +5757,8 @@ package android.provider {
public static interface DeviceConfig.IntelligenceAttention {
field public static final String NAMESPACE = "intelligence_attention";
- field public static final String PROPERTY_ATTENTION_CHECK_ENABLED = "attention_check_enabled";
- field public static final String PROPERTY_ATTENTION_CHECK_SETTINGS = "attention_check_settings";
+ field public static final String PROPERTY_ATTENTION_ENABLED = "attention_enabled";
+ field public static final String PROPERTY_ATTENTION_SETTINGS = "attention_settings";
}
public static interface DeviceConfig.OnPropertyChangedListener {
@@ -7927,7 +7933,7 @@ package android.telephony {
package android.telephony.data {
public final class DataCallResponse implements android.os.Parcelable {
- ctor public DataCallResponse(int, int, int, int, @Nullable String, @Nullable String, @Nullable java.util.List<android.net.LinkAddress>, @Nullable java.util.List<java.net.InetAddress>, @Nullable java.util.List<java.net.InetAddress>, @Nullable java.util.List<java.lang.String>, int);
+ ctor public DataCallResponse(int, int, int, int, int, @Nullable String, @Nullable java.util.List<android.net.LinkAddress>, @Nullable java.util.List<java.net.InetAddress>, @Nullable java.util.List<java.net.InetAddress>, @Nullable java.util.List<java.lang.String>, int);
ctor public DataCallResponse(android.os.Parcel);
method public int describeContents();
method public int getActive();
@@ -7938,9 +7944,9 @@ package android.telephony.data {
method @NonNull public String getIfname();
method public int getMtu();
method @NonNull public java.util.List<java.lang.String> getPcscfs();
+ method public int getProtocolType();
method public int getStatus();
method public int getSuggestedRetryTime();
- method @NonNull public String getType();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR;
}
@@ -7954,8 +7960,8 @@ package android.telephony.data {
method public int getMtu();
method public String getPassword();
method public int getProfileId();
- method public String getProtocol();
- method public String getRoamingProtocol();
+ method public int getProtocol();
+ method public int getRoamingProtocol();
method public int getSupportedApnTypesBitmap();
method public int getType();
method public String getUserName();
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 8e56bef6856e..e84eced8bb1a 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -33,6 +33,7 @@ import "frameworks/base/core/proto/android/net/networkcapabilities.proto";
import "frameworks/base/core/proto/android/os/enums.proto";
import "frameworks/base/core/proto/android/server/connectivity/data_stall_event.proto";
import "frameworks/base/core/proto/android/server/enums.proto";
+import "frameworks/base/core/proto/android/server/job/enums.proto";
import "frameworks/base/core/proto/android/server/location/enums.proto";
import "frameworks/base/core/proto/android/service/procstats_enum.proto";
import "frameworks/base/core/proto/android/service/usb.proto";
@@ -213,6 +214,7 @@ message Atom {
WatchdogRollbackOccurred watchdog_rollback_occurred = 147;
BiometricHalDeathReported biometric_hal_death_reported = 148;
BubbleUIChanged bubble_ui_changed = 149;
+ ScheduledJobConstraintChanged scheduled_job_constraint_changed = 150;
}
// Pulled events will start at field 10000.
@@ -4699,3 +4701,24 @@ message BubbleUIChanged {
optional float normalized_x_position = 7;
optional float normalized_y_position = 8;
}
+
+/**
+ * Logs that a constraint for a scheduled job has changed.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/job/controllers/JobStatus.java
+ */
+message ScheduledJobConstraintChanged {
+ repeated AttributionNode attribution_node = 1;
+
+ // Name of the job.
+ optional string job_name = 2;
+
+ optional com.android.server.job.ConstraintEnum constraint = 3;
+
+ enum State {
+ UNSATISFIED = 0;
+ SATISFIED = 1;
+ }
+ optional State state = 4;
+}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 92f47e767d2e..e77e212cb46e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1808,6 +1808,29 @@ public class Activity extends ContextThemeWrapper
mCalled = true;
}
+ /**
+ * Called when activity gets or looses the top resumed position in the system.
+ *
+ * <p>Starting with {@link android.os.Build.VERSION_CODES#Q} multiple activities can be resumed
+ * at the same time in multi-window and multi-display modes. This callback should be used
+ * instead of {@link #onResume()} as an indication that the activity can try to open
+ * exclusive-access devices like camera.</p>
+ *
+ * <p>It will always be delivered after the activity was resumed and before it is paused. In
+ * some cases it might be skipped and activity can go straight from {@link #onResume()} to
+ * {@link #onPause()} without receiving the top resumed state.</p>
+ *
+ * @param isTopResumedActivity {@code true} if it's the topmost resumed activity in the system,
+ * {@code false} otherwise. A call with this as {@code true} will
+ * always be followed by another one with {@code false}.
+ *
+ * @see #onResume()
+ * @see #onPause()
+ * @see #onWindowFocusChanged(boolean)
+ */
+ public void onTopResumedActivityChanged(boolean isTopResumedActivity) {
+ }
+
void setVoiceInteractor(IVoiceInteractor voiceInteractor) {
if (mVoiceInteractor != null) {
for (Request activeRequest: mVoiceInteractor.getActiveRequests()) {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ee3d27c7dac8..03a09ee04ea1 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -449,6 +449,14 @@ public final class ActivityThread extends ClientTransactionHandler {
ViewRootImpl.ActivityConfigCallback configCallback;
ActivityClientRecord nextIdle;
+ // Indicates whether this activity is currently the topmost resumed one in the system.
+ // This holds the last reported value from server.
+ boolean isTopResumedActivity;
+ // This holds the value last sent to the activity. This is needed, because an update from
+ // server may come at random time, but we always need to report changes between ON_RESUME
+ // and ON_PAUSE to the app.
+ boolean lastReportedTopResumedState;
+
ProfilerInfo profilerInfo;
@UnsupportedAppUsage
@@ -3295,16 +3303,14 @@ public final class ActivityThread extends ClientTransactionHandler {
final boolean resumed = !r.paused;
if (resumed) {
r.activity.mTemporaryPause = true;
- mInstrumentation.callActivityOnPause(r.activity);
+ performPauseActivityIfNeeded(r, "performNewIntents");
}
checkAndBlockForNetworkAccess();
deliverNewIntents(r, intents);
if (resumed) {
- r.activity.performResume(false, "performNewIntents");
+ performResumeActivity(token, false, "performNewIntents");
r.activity.mTemporaryPause = false;
- }
-
- if (r.paused && andPause) {
+ } else if (andPause) {
// In this case the activity was in the paused state when we delivered the intent,
// to guarantee onResume gets called after onNewIntent we temporarily resume the
// activity and pause again as the caller wanted.
@@ -3957,6 +3963,8 @@ public final class ActivityThread extends ClientTransactionHandler {
r.state = null;
r.persistentState = null;
r.setState(ON_RESUME);
+
+ reportTopResumedActivityChanged(r, r.isTopResumedActivity);
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException("Unable to resume activity "
@@ -4111,6 +4119,45 @@ public final class ActivityThread extends ClientTransactionHandler {
Looper.myQueue().addIdleHandler(new Idler());
}
+
+ @Override
+ public void handleTopResumedActivityChanged(IBinder token, boolean onTop, String reason) {
+ ActivityClientRecord r = mActivities.get(token);
+ if (r == null || r.activity == null) {
+ Slog.w(TAG, "Not found target activity to report position change for token: " + token);
+ return;
+ }
+
+ if (DEBUG_ORDER) {
+ Slog.d(TAG, "Received position change to top: " + onTop + " for activity: " + r);
+ }
+
+ if (r.isTopResumedActivity == onTop) {
+ throw new IllegalStateException("Activity top position already set to onTop=" + onTop);
+ }
+
+ r.isTopResumedActivity = onTop;
+
+ if (r.getLifecycleState() == ON_RESUME) {
+ reportTopResumedActivityChanged(r, onTop);
+ } else {
+ if (DEBUG_ORDER) {
+ Slog.d(TAG, "Won't deliver top position change in state=" + r.getLifecycleState());
+ }
+ }
+ }
+
+ /**
+ * Call {@link Activity#onTopResumedActivityChanged(boolean)} if its top resumed state changed
+ * since the last report.
+ */
+ private void reportTopResumedActivityChanged(ActivityClientRecord r, boolean onTop) {
+ if (r.lastReportedTopResumedState != onTop) {
+ r.lastReportedTopResumedState = onTop;
+ r.activity.onTopResumedActivityChanged(onTop);
+ }
+ }
+
@Override
public void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
int configChanges, PendingTransactionActions pendingActions, String reason) {
@@ -4202,6 +4249,10 @@ public final class ActivityThread extends ClientTransactionHandler {
return;
}
+ // Always reporting top resumed position loss when pausing an activity. If necessary, it
+ // will be restored in performResumeActivity().
+ reportTopResumedActivityChanged(r, false /* onTop */);
+
try {
r.activity.mCalled = false;
mInstrumentation.callActivityOnPause(r.activity);
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 07dbb6bee9fd..70badfae4a20 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -105,6 +105,16 @@ public abstract class ClientTransactionHandler {
boolean isForward, String reason);
/**
+ * Notify the activity about top resumed state change.
+ * @param token Target activity token.
+ * @param isTopResumedActivity Current state of the activity, {@code true} if it's the
+ * topmost resumed activity in the system, {@code false} otherwise.
+ * @param reason Reason for performing this operation.
+ */
+ public abstract void handleTopResumedActivityChanged(IBinder token,
+ boolean isTopResumedActivity, String reason);
+
+ /**
* Stop the activity.
* @param token Target activity token.
* @param show Flag indicating whether activity is still shown.
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index fb65da14a087..412d7f45986c 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -277,6 +277,7 @@ interface IActivityManager {
void unstableProviderDied(in IBinder connection);
boolean isIntentSenderAnActivity(in IIntentSender sender);
boolean isIntentSenderAForegroundService(in IIntentSender sender);
+ boolean isIntentSenderABroadcast(in IIntentSender sender);
int startActivityAsUser(in IApplicationThread caller, in String callingPackage,
in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
int requestCode, int flags, in ProfilerInfo profilerInfo,
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 75d95b2c2508..55014ebf1fcb 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -1113,6 +1113,19 @@ public final class PendingIntent implements Parcelable {
/**
* @hide
+ * Check whether this PendingIntent will launch an Activity.
+ */
+ public boolean isBroadcast() {
+ try {
+ return ActivityManager.getService()
+ .isIntentSenderABroadcast(mTarget);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
* Return the Intent of this PendingIntent.
*/
@UnsupportedAppUsage
diff --git a/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
new file mode 100644
index 000000000000..4064a02e54a8
--- /dev/null
+++ b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.servertransaction;
+
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Trace;
+
+/**
+ * Top resumed activity changed callback.
+ * @hide
+ */
+public class TopResumedActivityChangeItem extends ClientTransactionItem {
+
+ private boolean mOnTop;
+
+ @Override
+ public void execute(ClientTransactionHandler client, IBinder token,
+ PendingTransactionActions pendingActions) {
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "topResumedActivityChangeItem");
+ client.handleTopResumedActivityChanged(token, mOnTop, "topResumedActivityChangeItem");
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+
+ // ObjectPoolItem implementation
+
+ private TopResumedActivityChangeItem() {}
+
+ /** Obtain an instance initialized with provided params. */
+ public static TopResumedActivityChangeItem obtain(boolean onTop) {
+ TopResumedActivityChangeItem instance =
+ ObjectPool.obtain(TopResumedActivityChangeItem.class);
+ if (instance == null) {
+ instance = new TopResumedActivityChangeItem();
+ }
+ instance.mOnTop = onTop;
+
+ return instance;
+ }
+
+ @Override
+ public void recycle() {
+ mOnTop = false;
+ ObjectPool.recycle(this);
+ }
+
+
+ // Parcelable implementation
+
+ /** Write to Parcel. */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeBoolean(mOnTop);
+ }
+
+ /** Read from Parcel. */
+ private TopResumedActivityChangeItem(Parcel in) {
+ mOnTop = in.readBoolean();
+ }
+
+ public static final Creator<TopResumedActivityChangeItem> CREATOR =
+ new Creator<TopResumedActivityChangeItem>() {
+ public TopResumedActivityChangeItem createFromParcel(Parcel in) {
+ return new TopResumedActivityChangeItem(in);
+ }
+
+ public TopResumedActivityChangeItem[] newArray(int size) {
+ return new TopResumedActivityChangeItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final TopResumedActivityChangeItem other = (TopResumedActivityChangeItem) o;
+ return mOnTop == other.mOnTop;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + (mOnTop ? 1 : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "TopResumedActivityChangeItem{onTop=" + mOnTop + "}";
+ }
+}
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index d2c0e7be5900..5d4928cd4ab3 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -182,6 +182,38 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable {
}
/**
+ * Queries whether the given buffer description is supported by the system. If this returns
+ * true, then the allocation may succeed until resource exhaustion occurs. If this returns
+ * false then this combination will never succeed.
+ *
+ * @param width The width in pixels of the buffer
+ * @param height The height in pixels of the buffer
+ * @param format The @Format of each pixel
+ * @param layers The number of layers in the buffer
+ * @param usage The @Usage flags describing how the buffer will be used
+ * @return True if the combination is supported, false otherwise.
+ */
+ public static boolean isSupported(int width, int height, @Format int format, int layers,
+ @Usage long usage) {
+ if (!HardwareBuffer.isSupportedFormat(format)) {
+ throw new IllegalArgumentException("Invalid pixel format " + format);
+ }
+ if (width <= 0) {
+ throw new IllegalArgumentException("Invalid width " + width);
+ }
+ if (height <= 0) {
+ throw new IllegalArgumentException("Invalid height " + height);
+ }
+ if (layers <= 0) {
+ throw new IllegalArgumentException("Invalid layer count " + layers);
+ }
+ if (format == BLOB && height != 1) {
+ throw new IllegalArgumentException("Height must be 1 when using the BLOB format");
+ }
+ return nIsSupported(width, height, format, layers, usage);
+ }
+
+ /**
* Private use only. See {@link #create(int, int, int, int, long)}. May also be
* called from JNI using an already allocated native <code>HardwareBuffer</code>.
*/
@@ -386,4 +418,6 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable {
private static native int nGetLayers(long nativeObject);
@FastNative
private static native long nGetUsage(long nativeObject);
+ private static native boolean nIsSupported(int width, int height, int format, int layers,
+ long usage);
}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 97583089736e..425b9561dbc1 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -3721,6 +3721,59 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
new Key<android.hardware.camera2.params.RecommendedStreamConfiguration[]>("android.depth.availableRecommendedDepthStreamConfigurations", android.hardware.camera2.params.RecommendedStreamConfiguration[].class);
/**
+ * <p>The available dynamic depth dataspace stream
+ * configurations that this camera device supports
+ * (i.e. format, width, height, output/input stream).</p>
+ * <p>These are output stream configurations for use with
+ * dataSpace DYNAMIC_DEPTH. The configurations are
+ * listed as <code>(format, width, height, input?)</code> tuples.</p>
+ * <p>Only devices that support depth output for at least
+ * the HAL_PIXEL_FORMAT_Y16 dense depth map along with
+ * HAL_PIXEL_FORMAT_BLOB with the same size or size with
+ * the same aspect ratio can have dynamic depth dataspace
+ * stream configuration. {@link CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE android.depth.depthIsExclusive} also
+ * needs to be set to FALSE.</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ *
+ * @see CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE
+ * @hide
+ */
+ public static final Key<android.hardware.camera2.params.StreamConfiguration[]> DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS =
+ new Key<android.hardware.camera2.params.StreamConfiguration[]>("android.depth.availableDynamicDepthStreamConfigurations", android.hardware.camera2.params.StreamConfiguration[].class);
+
+ /**
+ * <p>This lists the minimum frame duration for each
+ * format/size combination for dynamic depth output streams.</p>
+ * <p>This should correspond to the frame duration when only that
+ * stream is active, with all processing (typically in android.*.mode)
+ * set to either OFF or FAST.</p>
+ * <p>When multiple streams are used in a request, the minimum frame
+ * duration will be max(individual stream min durations).</p>
+ * <p>The minimum frame duration of a stream (of a particular format, size)
+ * is the same regardless of whether the stream is input or output.</p>
+ * <p><b>Units</b>: (format, width, height, ns) x n</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * @hide
+ */
+ public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS =
+ new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.depth.availableDynamicDepthMinFrameDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+ /**
+ * <p>This lists the maximum stall duration for each
+ * output format/size combination for dynamic depth streams.</p>
+ * <p>A stall duration is how much extra time would get added
+ * to the normal minimum frame duration for a repeating request
+ * that has streams with non-zero stall.</p>
+ * <p>All dynamic depth output streams may have a nonzero stall
+ * duration.</p>
+ * <p><b>Units</b>: (format, width, height, ns) x n</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * @hide
+ */
+ public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS =
+ new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.depth.availableDynamicDepthStallDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+ /**
* <p>String containing the ids of the underlying physical cameras.</p>
* <p>For a logical camera, this is concatenation of all underlying physical camera IDs.
* The null terminator for physical camera ID must be preserved so that the whole string
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index c527ab4c6730..7877a4d51313 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -1119,6 +1119,8 @@ public class CameraMetadataNative implements Parcelable {
continue;
}
+ // Dynamic depth streams involve alot of SW processing and currently cannot be
+ // recommended.
StreamConfigurationMap map = null;
switch (i) {
case RecommendedStreamConfigurationMap.USECASE_PREVIEW:
@@ -1127,28 +1129,44 @@ public class CameraMetadataNative implements Parcelable {
map = new StreamConfigurationMap(scData.streamConfigurationArray,
scData.minDurationArray, scData.stallDurationArray,
/*depthconfiguration*/ null, /*depthminduration*/ null,
- /*depthstallduration*/ null, /*highspeedvideoconfigurations*/ null,
+ /*depthstallduration*/ null,
+ /*dynamicDepthConfigurations*/ null,
+ /*dynamicDepthMinFrameDurations*/ null,
+ /*dynamicDepthStallDurations*/ null,
+ /*highspeedvideoconfigurations*/ null,
/*inputoutputformatsmap*/ null, listHighResolution, supportsPrivate[i]);
break;
case RecommendedStreamConfigurationMap.USECASE_RECORD:
map = new StreamConfigurationMap(scData.streamConfigurationArray,
scData.minDurationArray, scData.stallDurationArray,
/*depthconfiguration*/ null, /*depthminduration*/ null,
- /*depthstallduration*/ null, highSpeedVideoConfigurations,
+ /*depthstallduration*/ null,
+ /*dynamicDepthConfigurations*/ null,
+ /*dynamicDepthMinFrameDurations*/ null,
+ /*dynamicDepthStallDurations*/ null,
+ highSpeedVideoConfigurations,
/*inputoutputformatsmap*/ null, listHighResolution, supportsPrivate[i]);
break;
case RecommendedStreamConfigurationMap.USECASE_ZSL:
map = new StreamConfigurationMap(scData.streamConfigurationArray,
scData.minDurationArray, scData.stallDurationArray,
depthScData.streamConfigurationArray, depthScData.minDurationArray,
- depthScData.stallDurationArray, /*highSpeedVideoConfigurations*/ null,
+ depthScData.stallDurationArray,
+ /*dynamicDepthConfigurations*/ null,
+ /*dynamicDepthMinFrameDurations*/ null,
+ /*dynamicDepthStallDurations*/ null,
+ /*highSpeedVideoConfigurations*/ null,
inputOutputFormatsMap, listHighResolution, supportsPrivate[i]);
break;
default:
map = new StreamConfigurationMap(scData.streamConfigurationArray,
scData.minDurationArray, scData.stallDurationArray,
depthScData.streamConfigurationArray, depthScData.minDurationArray,
- depthScData.stallDurationArray, /*highSpeedVideoConfigurations*/ null,
+ depthScData.stallDurationArray,
+ /*dynamicDepthConfigurations*/ null,
+ /*dynamicDepthMinFrameDurations*/ null,
+ /*dynamicDepthStallDurations*/ null,
+ /*highSpeedVideoConfigurations*/ null,
/*inputOutputFormatsMap*/ null, listHighResolution, supportsPrivate[i]);
}
@@ -1206,6 +1224,12 @@ public class CameraMetadataNative implements Parcelable {
CameraCharacteristics.DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS);
StreamConfigurationDuration[] depthStallDurations = getBase(
CameraCharacteristics.DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS);
+ StreamConfiguration[] dynamicDepthConfigurations = getBase(
+ CameraCharacteristics.DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS);
+ StreamConfigurationDuration[] dynamicDepthMinFrameDurations = getBase(
+ CameraCharacteristics.DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS);
+ StreamConfigurationDuration[] dynamicDepthStallDurations = getBase(
+ CameraCharacteristics.DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS);
HighSpeedVideoConfiguration[] highSpeedVideoConfigurations = getBase(
CameraCharacteristics.CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS);
ReprocessFormatsMap inputOutputFormatsMap = getBase(
@@ -1214,7 +1238,8 @@ public class CameraMetadataNative implements Parcelable {
return new StreamConfigurationMap(
configurations, minFrameDurations, stallDurations,
depthConfigurations, depthMinFrameDurations, depthStallDurations,
- highSpeedVideoConfigurations, inputOutputFormatsMap,
+ dynamicDepthConfigurations, dynamicDepthMinFrameDurations,
+ dynamicDepthStallDurations, highSpeedVideoConfigurations, inputOutputFormatsMap,
listHighResolution);
}
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index dd052a8db1d9..a22e008a65fd 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -95,13 +95,17 @@ public final class StreamConfigurationMap {
StreamConfiguration[] depthConfigurations,
StreamConfigurationDuration[] depthMinFrameDurations,
StreamConfigurationDuration[] depthStallDurations,
+ StreamConfiguration[] dynamicDepthConfigurations,
+ StreamConfigurationDuration[] dynamicDepthMinFrameDurations,
+ StreamConfigurationDuration[] dynamicDepthStallDurations,
HighSpeedVideoConfiguration[] highSpeedVideoConfigurations,
ReprocessFormatsMap inputOutputFormatsMap,
boolean listHighResolution) {
this(configurations, minFrameDurations, stallDurations,
depthConfigurations, depthMinFrameDurations, depthStallDurations,
- highSpeedVideoConfigurations, inputOutputFormatsMap, listHighResolution,
- /*enforceImplementationDefined*/ true);
+ dynamicDepthConfigurations, dynamicDepthMinFrameDurations,
+ dynamicDepthStallDurations, highSpeedVideoConfigurations, inputOutputFormatsMap,
+ listHighResolution, /*enforceImplementationDefined*/ true);
}
/**
@@ -131,6 +135,9 @@ public final class StreamConfigurationMap {
StreamConfiguration[] depthConfigurations,
StreamConfigurationDuration[] depthMinFrameDurations,
StreamConfigurationDuration[] depthStallDurations,
+ StreamConfiguration[] dynamicDepthConfigurations,
+ StreamConfigurationDuration[] dynamicDepthMinFrameDurations,
+ StreamConfigurationDuration[] dynamicDepthStallDurations,
HighSpeedVideoConfiguration[] highSpeedVideoConfigurations,
ReprocessFormatsMap inputOutputFormatsMap,
boolean listHighResolution,
@@ -163,6 +170,19 @@ public final class StreamConfigurationMap {
"depthStallDurations");
}
+ if (dynamicDepthConfigurations == null) {
+ mDynamicDepthConfigurations = new StreamConfiguration[0];
+ mDynamicDepthMinFrameDurations = new StreamConfigurationDuration[0];
+ mDynamicDepthStallDurations = new StreamConfigurationDuration[0];
+ } else {
+ mDynamicDepthConfigurations = checkArrayElementsNotNull(dynamicDepthConfigurations,
+ "dynamicDepthConfigurations");
+ mDynamicDepthMinFrameDurations = checkArrayElementsNotNull(
+ dynamicDepthMinFrameDurations, "dynamicDepthMinFrameDurations");
+ mDynamicDepthStallDurations = checkArrayElementsNotNull(dynamicDepthStallDurations,
+ "dynamicDepthStallDurations");
+ }
+
if (highSpeedVideoConfigurations == null) {
mHighSpeedVideoConfigurations = new HighSpeedVideoConfiguration[0];
} else {
@@ -205,6 +225,15 @@ public final class StreamConfigurationMap {
mDepthOutputFormats.put(config.getFormat(),
mDepthOutputFormats.get(config.getFormat()) + 1);
}
+ for (StreamConfiguration config : mDynamicDepthConfigurations) {
+ if (!config.isOutput()) {
+ // Ignoring input configs
+ continue;
+ }
+
+ mDynamicDepthOutputFormats.put(config.getFormat(),
+ mDynamicDepthOutputFormats.get(config.getFormat()) + 1);
+ }
if (configurations != null && enforceImplementationDefined &&
mOutputFormats.indexOfKey(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) < 0) {
@@ -335,6 +364,8 @@ public final class StreamConfigurationMap {
int dataspace = imageFormatToDataspace(format);
if (dataspace == HAL_DATASPACE_DEPTH) {
return mDepthOutputFormats.indexOfKey(internalFormat) >= 0;
+ } else if (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) {
+ return mDynamicDepthOutputFormats.indexOfKey(internalFormat) >= 0;
} else {
return getFormatsMap(/*output*/true).indexOfKey(internalFormat) >= 0;
}
@@ -446,7 +477,9 @@ public final class StreamConfigurationMap {
boolean isFlexible = SurfaceUtils.isFlexibleConsumer(surface);
StreamConfiguration[] configs =
- surfaceDataspace != HAL_DATASPACE_DEPTH ? mConfigurations : mDepthConfigurations;
+ surfaceDataspace == HAL_DATASPACE_DEPTH ? mDepthConfigurations :
+ surfaceDataspace == HAL_DATASPACE_DYNAMIC_DEPTH ? mDynamicDepthConfigurations :
+ mConfigurations;
for (StreamConfiguration config : configs) {
if (config.getFormat() == surfaceFormat && config.isOutput()) {
// Matching format, either need exact size match, or a flexible consumer
@@ -479,7 +512,9 @@ public final class StreamConfigurationMap {
int dataspace = imageFormatToDataspace(format);
StreamConfiguration[] configs =
- dataspace != HAL_DATASPACE_DEPTH ? mConfigurations : mDepthConfigurations;
+ dataspace == HAL_DATASPACE_DEPTH ? mDepthConfigurations :
+ dataspace == HAL_DATASPACE_DYNAMIC_DEPTH ? mDynamicDepthConfigurations :
+ mConfigurations;
for (StreamConfiguration config : configs) {
if ((config.getFormat() == internalFormat) && config.isOutput() &&
config.getSize().equals(size)) {
@@ -992,6 +1027,12 @@ public final class StreamConfigurationMap {
Arrays.equals(mMinFrameDurations, other.mMinFrameDurations) &&
Arrays.equals(mStallDurations, other.mStallDurations) &&
Arrays.equals(mDepthConfigurations, other.mDepthConfigurations) &&
+ Arrays.equals(mDepthMinFrameDurations, other.mDepthMinFrameDurations) &&
+ Arrays.equals(mDepthStallDurations, other.mDepthStallDurations) &&
+ Arrays.equals(mDynamicDepthConfigurations, other.mDynamicDepthConfigurations) &&
+ Arrays.equals(mDynamicDepthMinFrameDurations,
+ other.mDynamicDepthMinFrameDurations) &&
+ Arrays.equals(mDynamicDepthStallDurations, other.mDynamicDepthStallDurations) &&
Arrays.equals(mHighSpeedVideoConfigurations,
other.mHighSpeedVideoConfigurations);
}
@@ -1005,9 +1046,10 @@ public final class StreamConfigurationMap {
public int hashCode() {
// XX: do we care about order?
return HashCodeHelpers.hashCodeGeneric(
- mConfigurations, mMinFrameDurations,
- mStallDurations,
- mDepthConfigurations, mHighSpeedVideoConfigurations);
+ mConfigurations, mMinFrameDurations, mStallDurations,
+ mDepthConfigurations, mDepthMinFrameDurations, mDepthStallDurations,
+ mDynamicDepthConfigurations, mDynamicDepthMinFrameDurations,
+ mDynamicDepthStallDurations, mHighSpeedVideoConfigurations);
}
// Check that the argument is supported by #getOutputFormats or #getInputFormats
@@ -1022,6 +1064,10 @@ public final class StreamConfigurationMap {
if (mDepthOutputFormats.indexOfKey(internalFormat) >= 0) {
return format;
}
+ } else if (internalDataspace == HAL_DATASPACE_DYNAMIC_DEPTH) {
+ if (mDynamicDepthOutputFormats.indexOfKey(internalFormat) >= 0) {
+ return format;
+ }
} else {
if (mAllOutputFormats.indexOfKey(internalFormat) >= 0) {
return format;
@@ -1245,6 +1291,7 @@ public final class StreamConfigurationMap {
switch (format) {
case ImageFormat.JPEG:
case ImageFormat.DEPTH_POINT_CLOUD:
+ case ImageFormat.DEPTH_JPEG:
return HAL_PIXEL_FORMAT_BLOB;
case ImageFormat.DEPTH16:
return HAL_PIXEL_FORMAT_Y16;
@@ -1264,6 +1311,7 @@ public final class StreamConfigurationMap {
* <li>ImageFormat.JPEG => HAL_DATASPACE_V0_JFIF
* <li>ImageFormat.DEPTH_POINT_CLOUD => HAL_DATASPACE_DEPTH
* <li>ImageFormat.DEPTH16 => HAL_DATASPACE_DEPTH
+ * <li>ImageFormat.DEPTH_JPEG => HAL_DATASPACE_DYNAMIC_DEPTH
* <li>others => HAL_DATASPACE_UNKNOWN
* </ul>
* </p>
@@ -1293,6 +1341,8 @@ public final class StreamConfigurationMap {
case ImageFormat.DEPTH16:
case ImageFormat.RAW_DEPTH:
return HAL_DATASPACE_DEPTH;
+ case ImageFormat.DEPTH_JPEG:
+ return HAL_DATASPACE_DYNAMIC_DEPTH;
default:
return HAL_DATASPACE_UNKNOWN;
}
@@ -1343,12 +1393,16 @@ public final class StreamConfigurationMap {
SparseIntArray formatsMap =
!output ? mInputFormats :
dataspace == HAL_DATASPACE_DEPTH ? mDepthOutputFormats :
+ dataspace == HAL_DATASPACE_DYNAMIC_DEPTH ? mDynamicDepthOutputFormats :
highRes ? mHighResOutputFormats :
mOutputFormats;
int sizesCount = formatsMap.get(format);
- if ( ((!output || dataspace == HAL_DATASPACE_DEPTH) && sizesCount == 0) ||
- (output && dataspace != HAL_DATASPACE_DEPTH && mAllOutputFormats.get(format) == 0)) {
+ if ( ((!output || (dataspace == HAL_DATASPACE_DEPTH ||
+ dataspace == HAL_DATASPACE_DYNAMIC_DEPTH)) && sizesCount == 0) ||
+ (output && (dataspace != HAL_DATASPACE_DEPTH &&
+ dataspace != HAL_DATASPACE_DYNAMIC_DEPTH) &&
+ mAllOutputFormats.get(format) == 0)) {
// Only throw if this is really not supported at all
throw new IllegalArgumentException("format not available");
}
@@ -1357,9 +1411,13 @@ public final class StreamConfigurationMap {
int sizeIndex = 0;
StreamConfiguration[] configurations =
- (dataspace == HAL_DATASPACE_DEPTH) ? mDepthConfigurations : mConfigurations;
+ (dataspace == HAL_DATASPACE_DEPTH) ? mDepthConfigurations :
+ (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ? mDynamicDepthConfigurations :
+ mConfigurations;
StreamConfigurationDuration[] minFrameDurations =
- (dataspace == HAL_DATASPACE_DEPTH) ? mDepthMinFrameDurations : mMinFrameDurations;
+ (dataspace == HAL_DATASPACE_DEPTH) ? mDepthMinFrameDurations :
+ (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ? mDynamicDepthMinFrameDurations :
+ mMinFrameDurations;
for (StreamConfiguration config : configurations) {
int fmt = config.getFormat();
@@ -1386,7 +1444,21 @@ public final class StreamConfigurationMap {
}
}
- if (sizeIndex != sizesCount) {
+ // Dynamic depth streams can have both fast and also high res modes.
+ if ((sizeIndex != sizesCount) && (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH)) {
+
+ if (sizeIndex > sizesCount) {
+ throw new AssertionError(
+ "Too many dynamic depth sizes (expected " + sizesCount + ", actual " +
+ sizeIndex + ")");
+ }
+
+ if (sizeIndex <= 0) {
+ sizes = new Size[0];
+ } else {
+ sizes = Arrays.copyOf(sizes, sizeIndex);
+ }
+ } else if (sizeIndex != sizesCount) {
throw new AssertionError(
"Too few sizes (expected " + sizesCount + ", actual " + sizeIndex + ")");
}
@@ -1409,6 +1481,10 @@ public final class StreamConfigurationMap {
for (int j = 0; j < mDepthOutputFormats.size(); j++) {
formats[i++] = depthFormatToPublic(mDepthOutputFormats.keyAt(j));
}
+ if (mDynamicDepthOutputFormats.size() > 0) {
+ // Only one publicly dynamic depth format is available.
+ formats[i++] = ImageFormat.DEPTH_JPEG;
+ }
}
if (formats.length != i) {
throw new AssertionError("Too few formats " + i + ", expected " + formats.length);
@@ -1451,11 +1527,13 @@ public final class StreamConfigurationMap {
private StreamConfigurationDuration[] getDurations(int duration, int dataspace) {
switch (duration) {
case DURATION_MIN_FRAME:
- return (dataspace == HAL_DATASPACE_DEPTH) ?
- mDepthMinFrameDurations : mMinFrameDurations;
+ return (dataspace == HAL_DATASPACE_DEPTH) ? mDepthMinFrameDurations :
+ (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ?
+ mDynamicDepthMinFrameDurations : mMinFrameDurations;
case DURATION_STALL:
- return (dataspace == HAL_DATASPACE_DEPTH) ?
- mDepthStallDurations : mStallDurations;
+ return (dataspace == HAL_DATASPACE_DEPTH) ? mDepthStallDurations :
+ (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ? mDynamicDepthStallDurations :
+ mStallDurations;
default:
throw new IllegalArgumentException("duration was invalid");
}
@@ -1467,6 +1545,7 @@ public final class StreamConfigurationMap {
int size = formatsMap.size();
if (output) {
size += mDepthOutputFormats.size();
+ size += mDynamicDepthOutputFormats.size();
}
return size;
@@ -1486,10 +1565,11 @@ public final class StreamConfigurationMap {
return false;
}
- private boolean isSupportedInternalConfiguration(int format, int dataspace,
- Size size) {
+ private boolean isSupportedInternalConfiguration(int format, int dataspace, Size size) {
StreamConfiguration[] configurations =
- (dataspace == HAL_DATASPACE_DEPTH) ? mDepthConfigurations : mConfigurations;
+ (dataspace == HAL_DATASPACE_DEPTH) ? mDepthConfigurations :
+ (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ? mDynamicDepthConfigurations :
+ mConfigurations;
for (int i = 0; i < configurations.length; i++) {
if (configurations[i].getFormat() == format &&
@@ -1681,6 +1761,8 @@ public final class StreamConfigurationMap {
return "DEPTH16";
case ImageFormat.DEPTH_POINT_CLOUD:
return "DEPTH_POINT_CLOUD";
+ case ImageFormat.DEPTH_JPEG:
+ return "DEPTH_JPEG";
case ImageFormat.RAW_DEPTH:
return "RAW_DEPTH";
case ImageFormat.PRIVATE:
@@ -1712,6 +1794,7 @@ public final class StreamConfigurationMap {
(1 << HAL_DATASPACE_RANGE_SHIFT);
private static final int HAL_DATASPACE_DEPTH = 0x1000;
+ private static final int HAL_DATASPACE_DYNAMIC_DEPTH = 0x1002;
private static final long DURATION_20FPS_NS = 50000000L;
/**
@@ -1728,6 +1811,10 @@ public final class StreamConfigurationMap {
private final StreamConfigurationDuration[] mDepthMinFrameDurations;
private final StreamConfigurationDuration[] mDepthStallDurations;
+ private final StreamConfiguration[] mDynamicDepthConfigurations;
+ private final StreamConfigurationDuration[] mDynamicDepthMinFrameDurations;
+ private final StreamConfigurationDuration[] mDynamicDepthStallDurations;
+
private final HighSpeedVideoConfiguration[] mHighSpeedVideoConfigurations;
private final ReprocessFormatsMap mInputOutputFormatsMap;
@@ -1745,6 +1832,8 @@ public final class StreamConfigurationMap {
private final SparseIntArray mInputFormats = new SparseIntArray();
/** internal format -> num depth output sizes mapping, for HAL_DATASPACE_DEPTH */
private final SparseIntArray mDepthOutputFormats = new SparseIntArray();
+ /** internal format -> num dynamic depth output sizes mapping, for HAL_DATASPACE_DYNAMIC_DEPTH */
+ private final SparseIntArray mDynamicDepthOutputFormats = new SparseIntArray();
/** High speed video Size -> FPS range count mapping*/
private final HashMap</*HighSpeedVideoSize*/Size, /*Count*/Integer> mHighSpeedVideoSizeMap =
new HashMap<Size, Integer>();
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 5b3ad77dbf43..333cfbd400dd 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -597,6 +597,7 @@ public class InputMethodService extends AbstractInputMethodService {
Log.v(TAG, "Making IME window invisible");
}
setImeWindowStatus(IME_ACTIVE | IME_INVISIBLE, mBackDisposition);
+ applyVisibilityInInsetsConsumer(false /* setVisible */);
onPreRenderedWindowVisibilityChanged(false /* setVisible */);
} else {
mShowInputFlags = 0;
@@ -625,10 +626,10 @@ public class InputMethodService extends AbstractInputMethodService {
? mDecorViewVisible && mWindowVisible : isInputViewShown();
if (dispatchOnShowInputRequested(flags, false)) {
if (mIsPreRendered) {
- // TODO: notify visibility to insets consumer.
if (DEBUG) {
Log.v(TAG, "Making IME window visible");
}
+ applyVisibilityInInsetsConsumer(true /* setVisible */);
onPreRenderedWindowVisibilityChanged(true /* setVisible */);
} else {
showWindow(true);
@@ -1887,10 +1888,23 @@ public class InputMethodService extends AbstractInputMethodService {
if (DEBUG) Log.v(TAG, "showWindow: draw decorView!");
mWindow.show();
}
+ maybeNotifyPreRendered();
mDecorViewWasVisible = true;
mInShowWindow = false;
}
+ /**
+ * Notify {@link android.view.ImeInsetsSourceConsumer} if IME has been pre-rendered
+ * for current EditorInfo, when pre-rendering is enabled.
+ */
+ private void maybeNotifyPreRendered() {
+ if (!mCanPreRender || !mIsPreRendered) {
+ return;
+ }
+ mPrivOps.reportPreRendered(getCurrentInputEditorInfo());
+ }
+
+
private boolean prepareWindow(boolean showInput) {
boolean doShowInput = false;
mDecorViewVisible = true;
@@ -1942,6 +1956,18 @@ public class InputMethodService extends AbstractInputMethodService {
}
}
+ /**
+ * Apply the IME visibility in {@link android.view.ImeInsetsSourceConsumer} when
+ * pre-rendering is enabled.
+ * @param setVisible {@code true} to make it visible, false to hide it.
+ */
+ private void applyVisibilityInInsetsConsumer(boolean setVisible) {
+ if (!mIsPreRendered) {
+ return;
+ }
+ mPrivOps.applyImeVisibility(setVisible);
+ }
+
private void finishViews(boolean finishingInput) {
if (mInputViewStarted) {
if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
@@ -2081,6 +2107,7 @@ public class InputMethodService extends AbstractInputMethodService {
// When IME is not pre-rendered, this will actually show the IME.
if (DEBUG) Log.v(TAG, "showWindow: draw decorView!");
mWindow.show();
+ maybeNotifyPreRendered();
mDecorViewWasVisible = true;
mInShowWindow = false;
} else {
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 148dd910ef69..d58e00af5054 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -112,14 +112,12 @@ public final class DeviceConfig {
@SystemApi
public interface IntelligenceAttention {
String NAMESPACE = "intelligence_attention";
- /**
- * If {@code true}, enables the attention check.
- */
- String PROPERTY_ATTENTION_CHECK_ENABLED = "attention_check_enabled";
- /**
- * Settings for performing the attention check.
- */
- String PROPERTY_ATTENTION_CHECK_SETTINGS = "attention_check_settings";
+
+ /** If {@code true}, enables the attention features. */
+ String PROPERTY_ATTENTION_ENABLED = "attention_enabled";
+
+ /** Settings for the attention features. */
+ String PROPERTY_ATTENTION_SETTINGS = "attention_settings";
}
/**
@@ -182,6 +180,22 @@ public final class DeviceConfig {
}
/**
+ * Namespace for {@link AttentionManagerService} related features.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface AttentionManagerService {
+ String NAMESPACE = "attention_manager_service";
+
+ /** If {@code true}, enables {@link AttentionManagerService} features. */
+ String PROPERTY_SERVICE_ENABLED = "service_enabled";
+
+ /** Allows a CTS to inject a fake implementation. */
+ String PROPERTY_COMPONENT_NAME = "component_name";
+ }
+
+ /**
* Namespace for storage-related features.
*
* @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 195e72c71e55..927c729758b5 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -486,6 +486,37 @@ public final class Settings {
"android.settings.WIFI_IP_SETTINGS";
/**
+ * Activity Action: Show setting page to process an Easy Connect (Wi-Fi DPP) QR code and start
+ * configuration. This intent should be used when you want to use this device to take on the
+ * configurator role for an IoT/other device. When provided with a valid DPP URI string Settings
+ * will open a wifi selection screen for the user to indicate which network they would like
+ * to configure the device specified in the DPP URI string for and carry them through the rest
+ * of the flow for provisioning the device.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you safeguard
+ * against this by checking WifiManager.isEasyConnectSupported();
+ * <p>
+ * Input:
+ * The following keys in the bundle with their associated value.
+ * <ul>
+ * <li>"qrCode": Standard Easy Connect (Wi-Fi DPP) URI bootstrapping information as a
+ * string.</li>
+ * </ul>
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_PROCESS_WIFI_EASY_CONNECT_QR_CODE =
+ "android.settings.PROCESS_WIFI_EASY_CONNECT_QR_CODE";
+
+ /**
+ * An extra to put in the bundle for {@link #ACTION_PROCESS_WIFI_EASY_CONNECT_QR_CODE} intents.
+ * It must contain properly formatted Easy Connect (Wi-Fi DPP) URI bootstrapping information for
+ * the process to proceed.
+ */
+ public static final String EXTRA_QR_CODE = "android.provider.extra.QR_CODE";
+
+ /**
* Activity Action: Show settings to allow configuration of data and view data usage.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
@@ -11768,6 +11799,44 @@ public final class Settings {
public static final String SYNC_MANAGER_CONSTANTS = "sync_manager_constants";
/**
+ * Broadcast dispatch tuning parameters specific to foreground broadcasts.
+ *
+ * This is encoded as a key=value list, separated by commas. Ex: "foo=1,bar=true"
+ *
+ * The following keys are supported:
+ * <pre>
+ * bcast_timeout (long)
+ * bcast_slow_time (long)
+ * bcast_deferral (long)
+ * bcast_deferral_decay_factor (float)
+ * bcast_deferral_floor (long)
+ * </pre>
+ *
+ * @hide
+ */
+ public static final String BROADCAST_FG_CONSTANTS = "bcast_fg_constants";
+
+ /**
+ * Broadcast dispatch tuning parameters specific to background broadcasts.
+ *
+ * This is encoded as a key=value list, separated by commas. Ex: "foo=1,bar=true".
+ * See {@link #BROADCAST_FG_CONSTANTS} for the list of supported keys.
+ *
+ * @hide
+ */
+ public static final String BROADCAST_BG_CONSTANTS = "bcast_bg_constants";
+
+ /**
+ * Broadcast dispatch tuning parameters specific to specific "offline" broadcasts.
+ *
+ * This is encoded as a key=value list, separated by commas. Ex: "foo=1,bar=true".
+ * See {@link #BROADCAST_FG_CONSTANTS} for the list of supported keys.
+ *
+ * @hide
+ */
+ public static final String BROADCAST_OFFLOAD_CONSTANTS = "bcast_offload_constants";
+
+ /**
* Whether or not App Standby feature is enabled by system. This controls throttling of apps
* based on usage patterns and predictions. Platform will turn on this feature if both this
* flag and {@link #ADAPTIVE_BATTERY_MANAGEMENT_ENABLED} is on.
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
new file mode 100644
index 000000000000..7026d2b1389c
--- /dev/null
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view;
+
+import static android.view.InsetsState.TYPE_IME;
+
+import android.os.Parcel;
+import android.text.TextUtils;
+import android.view.SurfaceControl.Transaction;
+import android.view.WindowInsets.Type;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Arrays;
+import java.util.function.Supplier;
+
+/**
+ * Controls the visibility and animations of IME window insets source.
+ * @hide
+ */
+public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
+ private EditorInfo mFocusedEditor;
+ private EditorInfo mPreRenderedEditor;
+ /**
+ * Determines if IME would be shown next time IME is pre-rendered for currently focused
+ * editor {@link #mFocusedEditor} if {@link #isServedEditorRendered} is {@code true}.
+ */
+ private boolean mShowOnNextImeRender;
+ private boolean mHasWindowFocus;
+
+ public ImeInsetsSourceConsumer(
+ InsetsState state, Supplier<Transaction> transactionSupplier,
+ InsetsController controller) {
+ super(TYPE_IME, state, transactionSupplier, controller);
+ }
+
+ public void onPreRendered(EditorInfo info) {
+ mPreRenderedEditor = info;
+ if (mShowOnNextImeRender) {
+ mShowOnNextImeRender = false;
+ if (isServedEditorRendered()) {
+ applyImeVisibility(true /* setVisible */);
+ }
+ }
+ }
+
+ public void onServedEditorChanged(EditorInfo info) {
+ if (isDummyOrEmptyEditor(info)) {
+ mShowOnNextImeRender = false;
+ }
+ mFocusedEditor = info;
+ }
+
+ public void applyImeVisibility(boolean setVisible) {
+ if (!mHasWindowFocus) {
+ // App window doesn't have focus, any visibility changes would be no-op.
+ return;
+ }
+
+ if (setVisible) {
+ mController.show(Type.IME);
+ } else {
+ mController.hide(Type.IME);
+ }
+ }
+
+ @Override
+ public void onWindowFocusGained() {
+ mHasWindowFocus = true;
+ getImm().registerImeConsumer(this);
+ }
+
+ @Override
+ public void onWindowFocusLost() {
+ mHasWindowFocus = false;
+ }
+
+ private boolean isDummyOrEmptyEditor(EditorInfo info) {
+ // TODO(b/123044812): Handle dummy input gracefully in IME Insets API
+ return info == null || (info.fieldId <= 0 && info.inputType <= 0);
+ }
+
+ private boolean isServedEditorRendered() {
+ if (mFocusedEditor == null || mPreRenderedEditor == null
+ || isDummyOrEmptyEditor(mFocusedEditor)
+ || isDummyOrEmptyEditor(mPreRenderedEditor)) {
+ // No view is focused or ready.
+ return false;
+ }
+ return areEditorsSimilar(mFocusedEditor, mPreRenderedEditor);
+ }
+
+ @VisibleForTesting
+ public static boolean areEditorsSimilar(EditorInfo info1, EditorInfo info2) {
+ // We don't need to compare EditorInfo.fieldId (View#id) since that shouldn't change
+ // IME views.
+ boolean areOptionsSimilar =
+ info1.imeOptions == info2.imeOptions
+ && info1.inputType == info2.inputType
+ && TextUtils.equals(info1.packageName, info2.packageName);
+ areOptionsSimilar &= info1.privateImeOptions != null
+ ? info1.privateImeOptions.equals(info2.privateImeOptions) : true;
+
+ if (!areOptionsSimilar) {
+ return false;
+ }
+
+ // compare bundle extras.
+ if ((info1.extras == null && info2.extras == null) || info1.extras == info2.extras) {
+ return true;
+ }
+ if ((info1.extras == null && info2.extras != null)
+ || (info1.extras == null && info2.extras != null)) {
+ return false;
+ }
+ if (info1.extras.hashCode() == info2.extras.hashCode()
+ || info1.extras.equals(info1)) {
+ return true;
+ }
+ if (info1.extras.size() != info2.extras.size()) {
+ return false;
+ }
+ if (info1.extras.toString().equals(info2.extras.toString())) {
+ return true;
+ }
+
+ // Compare bytes
+ Parcel parcel1 = Parcel.obtain();
+ info1.extras.writeToParcel(parcel1, 0);
+ parcel1.setDataPosition(0);
+ Parcel parcel2 = Parcel.obtain();
+ info2.extras.writeToParcel(parcel2, 0);
+ parcel2.setDataPosition(0);
+
+ return Arrays.equals(parcel1.createByteArray(), parcel2.createByteArray());
+ }
+
+ private InputMethodManager getImm() {
+ return mController.getViewRoot().mDisplayContext.getSystemService(InputMethodManager.class);
+ }
+}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 2142c36f8803..4f9ecd575326 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.view.InsetsState.TYPE_IME;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
@@ -262,7 +264,7 @@ public class InsetsController implements WindowInsetsController {
if (controller != null) {
return controller;
}
- controller = new InsetsSourceConsumer(type, mState, Transaction::new, this);
+ controller = createConsumerOfType(type);
mSourceConsumers.put(type, controller);
return controller;
}
@@ -274,6 +276,32 @@ public class InsetsController implements WindowInsetsController {
}
/**
+ * Called when current window gains focus.
+ */
+ public void onWindowFocusGained() {
+ getSourceConsumer(TYPE_IME).onWindowFocusGained();
+ }
+
+ /**
+ * Called when current window loses focus.
+ */
+ public void onWindowFocusLost() {
+ getSourceConsumer(TYPE_IME).onWindowFocusLost();
+ }
+
+ ViewRootImpl getViewRoot() {
+ return mViewRoot;
+ }
+
+ private InsetsSourceConsumer createConsumerOfType(int type) {
+ if (type == TYPE_IME) {
+ return new ImeInsetsSourceConsumer(mState, Transaction::new, this);
+ } else {
+ return new InsetsSourceConsumer(type, mState, Transaction::new, this);
+ }
+ }
+
+ /**
* Sends the local visibility state back to window manager.
*/
private void sendStateToWindowManager() {
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 7937cb69b80e..f48318cd7d0a 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -30,12 +30,12 @@ import java.util.function.Supplier;
*/
public class InsetsSourceConsumer {
+ protected final InsetsController mController;
+ protected boolean mVisible;
private final Supplier<Transaction> mTransactionSupplier;
private final @InternalInsetType int mType;
private final InsetsState mState;
- private final InsetsController mController;
private @Nullable InsetsSourceControl mSourceControl;
- private boolean mVisible;
public InsetsSourceConsumer(@InternalInsetType int type, InsetsState state,
Supplier<Transaction> transactionSupplier, InsetsController controller) {
@@ -43,7 +43,7 @@ public class InsetsSourceConsumer {
mState = state;
mTransactionSupplier = transactionSupplier;
mController = controller;
- mVisible = InsetsState.getDefaultVisibly(type);
+ mVisible = InsetsState.getDefaultVisibility(type);
}
public void setControl(@Nullable InsetsSourceControl control) {
@@ -76,6 +76,16 @@ public class InsetsSourceConsumer {
setVisible(false);
}
+ /**
+ * Called when current window gains focus
+ */
+ public void onWindowFocusGained() {}
+
+ /**
+ * Called when current window loses focus.
+ */
+ public void onWindowFocusLost() {}
+
boolean applyLocalVisibilityOverride() {
// If we don't have control, we are not able to change the visibility.
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index a6af1a296faf..4f809fe6d54a 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -278,7 +278,7 @@ public class InsetsState implements Parcelable {
}
}
- public static boolean getDefaultVisibly(@InsetType int type) {
+ public static boolean getDefaultVisibility(@InsetType int type) {
switch (type) {
case TYPE_TOP_BAR:
case TYPE_SIDE_BAR_1:
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 45e6c50d9ada..ecbec652fcf0 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -492,7 +492,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
if (mBackgroundControl == null) {
return;
}
- if ((mSurfaceFlags & PixelFormat.OPAQUE) == 0) {
+ if ((mSurfaceFlags & PixelFormat.OPAQUE) != 0) {
mBackgroundControl.show();
mBackgroundControl.setLayer(Integer.MIN_VALUE);
} else {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a031b704627f..f47eb10efd29 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2802,6 +2802,11 @@ public final class ViewRootImpl implements ViewParent,
hasWindowFocus = mUpcomingWindowFocus;
inTouchMode = mUpcomingInTouchMode;
}
+ if (hasWindowFocus) {
+ mInsetsController.onWindowFocusGained();
+ } else {
+ mInsetsController.onWindowFocusLost();
+ }
if (mAdded) {
profileRendering(hasWindowFocus);
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index b9017b365f90..d7f1b9f2c3e1 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -156,8 +156,7 @@ public final class ContentCaptureManager {
// Wait for system server to return the component name.
final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
mHandler.sendMessage(obtainMessage(
- ContentCaptureManager::handleReceiverServiceComponentName,
- this, mContext.getUserId(), resultReceiver));
+ ContentCaptureManager::handleGetComponentName, this, resultReceiver));
try {
return resultReceiver.getParcelableResult();
@@ -198,7 +197,7 @@ public final class ContentCaptureManager {
Preconditions.checkNotNull(request);
try {
- mService.removeUserData(mContext.getUserId(), request);
+ mService.removeUserData(request);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
@@ -227,9 +226,9 @@ public final class ContentCaptureManager {
/** Retrieves the component name of the target content capture service through system_server. */
- private void handleReceiverServiceComponentName(int userId, IResultReceiver resultReceiver) {
+ private void handleGetComponentName(@NonNull IResultReceiver resultReceiver) {
try {
- mService.getReceiverServiceComponentName(userId, resultReceiver);
+ mService.getServiceComponentName(resultReceiver);
} catch (RemoteException e) {
Log.w(TAG, "Unable to retrieve service component name: " + e);
}
diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
index 51aea162f1db..56ed8bfab9ad 100644
--- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl
+++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
@@ -34,21 +34,21 @@ import java.util.List;
*/
oneway interface IContentCaptureManager {
/**
- * Starts a new session for the provided {@code userId} running as part of the
+ * Starts a new session for the calling user running as part of the
* app's activity identified by {@code activityToken}/{@code componentName}.
*
* @param sessionId Unique session id as provided by the app.
* @param flags Meta flags that enable or disable content capture (see
* {@link IContentCaptureContext#flags}).
*/
- void startSession(int userId, IBinder activityToken, in ComponentName componentName,
+ void startSession(IBinder activityToken, in ComponentName componentName,
String sessionId, int flags, in IResultReceiver result);
/**
- * Marks the end of a session for the provided {@code userId} identified by
+ * Marks the end of a session for the calling user identified by
* the corresponding {@code startSession}'s {@code sessionId}.
*/
- void finishSession(int userId, String sessionId);
+ void finishSession(String sessionId);
/**
* Returns the content capture service's component name (if enabled and
@@ -56,10 +56,10 @@ oneway interface IContentCaptureManager {
* @param Receiver of the content capture service's @{code ComponentName}
* provided {@code Bundle} with key "{@code EXTRA}".
*/
- void getReceiverServiceComponentName(int userId, in IResultReceiver result);
+ void getServiceComponentName(in IResultReceiver result);
/**
- * Requests the removal of user data for the provided {@code userId}.
+ * Requests the removal of user data for the calling user.
*/
- void removeUserData(int userId, in UserDataRemovalRequest request);
+ void removeUserData(in UserDataRemovalRequest request);
}
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 9e99c88da3ca..83dbf2d5bb58 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -211,8 +211,8 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
try {
if (mSystemServerInterface == null) return;
- mSystemServerInterface.startSession(mContext.getUserId(), mApplicationToken,
- componentName, mId, flags, new IResultReceiver.Stub() {
+ mSystemServerInterface.startSession(mApplicationToken, componentName, mId, flags,
+ new IResultReceiver.Stub() {
@Override
public void send(int resultCode, Bundle resultData) {
IBinder binder = null;
@@ -473,7 +473,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
try {
if (mSystemServerInterface == null) return;
- mSystemServerInterface.finishSession(mContext.getUserId(), mId);
+ mSystemServerInterface.finishSession(mId);
} catch (RemoteException e) {
Log.e(TAG, "Error destroying system-service session " + mId + " for "
+ getDebugState() + ": " + e);
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 0cb1800996c9..7fee3ef29a09 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -55,6 +55,7 @@ import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.SparseArray;
import android.view.Display;
+import android.view.ImeInsetsSourceConsumer;
import android.view.InputChannel;
import android.view.InputEvent;
import android.view.InputEventSender;
@@ -441,6 +442,13 @@ public final class InputMethodManager {
*/
private int mRequestUpdateCursorAnchorInfoMonitorMode = REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE;
+ /**
+ * When {@link ViewRootImpl#sNewInsetsMode} is set to
+ * >= {@link ViewRootImpl#NEW_INSETS_MODE_IME}, {@link ImeInsetsSourceConsumer} applies the
+ * IME visibility and listens for other state changes.
+ */
+ private ImeInsetsSourceConsumer mImeInsetsConsumer;
+
final Pool<PendingEvent> mPendingEventPool = new SimplePool<>(20);
final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20);
@@ -454,6 +462,8 @@ public final class InputMethodManager {
static final int MSG_TIMEOUT_INPUT_EVENT = 6;
static final int MSG_FLUSH_INPUT_EVENT = 7;
static final int MSG_REPORT_FULLSCREEN_MODE = 10;
+ static final int MSG_REPORT_PRE_RENDERED = 15;
+ static final int MSG_APPLY_IME_VISIBILITY = 20;
private static boolean isAutofillUIShowing(View servedView) {
AutofillManager afm = servedView.getContext().getSystemService(AutofillManager.class);
@@ -650,6 +660,23 @@ public final class InputMethodManager {
}
return;
}
+ case MSG_REPORT_PRE_RENDERED: {
+ synchronized (mH) {
+ if (mImeInsetsConsumer != null) {
+ mImeInsetsConsumer.onPreRendered((EditorInfo) msg.obj);
+ }
+ }
+ return;
+
+ }
+ case MSG_APPLY_IME_VISIBILITY: {
+ synchronized (mH) {
+ if (mImeInsetsConsumer != null) {
+ mImeInsetsConsumer.applyImeVisibility(msg.arg1 != 0);
+ }
+ }
+ return;
+ }
}
}
}
@@ -729,6 +756,18 @@ public final class InputMethodManager {
.sendToTarget();
}
+ @Override
+ public void reportPreRendered(EditorInfo info) {
+ mH.obtainMessage(MSG_REPORT_PRE_RENDERED, 0, 0, info)
+ .sendToTarget();
+ }
+
+ @Override
+ public void applyImeVisibility(boolean setVisible) {
+ mH.obtainMessage(MSG_APPLY_IME_VISIBILITY, setVisible ? 1 : 0, 0)
+ .sendToTarget();
+ }
+
};
final InputConnection mDummyInputConnection = new BaseInputConnection(this, false);
@@ -1515,6 +1554,7 @@ public final class InputMethodManager {
// Hook 'em up and let 'er rip.
mCurrentTextBoxAttribute = tba;
+ maybeCallServedViewChangedLocked(tba);
mServedConnecting = false;
if (mServedInputConnectionWrapper != null) {
mServedInputConnectionWrapper.deactivate();
@@ -1730,6 +1770,10 @@ public final class InputMethodManager {
mCurrentTextBoxAttribute = null;
mCompletions = null;
mServedConnecting = true;
+ // servedView has changed and it's not editable.
+ if (!mServedView.onCheckIsTextEditor()) {
+ maybeCallServedViewChangedLocked(null);
+ }
}
if (ic != null) {
@@ -1828,6 +1872,21 @@ public final class InputMethodManager {
}
/**
+ * Register for IME state callbacks and applying visibility in
+ * {@link android.view.ImeInsetsSourceConsumer}.
+ * @hide
+ */
+ public void registerImeConsumer(@NonNull ImeInsetsSourceConsumer imeInsetsConsumer) {
+ if (imeInsetsConsumer == null) {
+ throw new IllegalStateException("ImeInsetsSourceConsumer cannot be null.");
+ }
+
+ synchronized (mH) {
+ mImeInsetsConsumer = imeInsetsConsumer;
+ }
+ }
+
+ /**
* Report the current selection range.
*
* <p><strong>Editor authors</strong>, you need to call this method whenever
@@ -2705,6 +2764,12 @@ public final class InputMethodManager {
}
}
+ private void maybeCallServedViewChangedLocked(EditorInfo tba) {
+ if (mImeInsetsConsumer != null) {
+ mImeInsetsConsumer.onServedEditorChanged(tba);
+ }
+ }
+
void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
final Printer p = new PrintWriterPrinter(fout);
p.println("Input method client state for " + this + ":");
diff --git a/core/java/android/view/inputmethod/InputMethodSystemProperty.java b/core/java/android/view/inputmethod/InputMethodSystemProperty.java
index 57ed7f923e88..7c79d44e7d31 100644
--- a/core/java/android/view/inputmethod/InputMethodSystemProperty.java
+++ b/core/java/android/view/inputmethod/InputMethodSystemProperty.java
@@ -93,8 +93,14 @@ public class InputMethodSystemProperty {
* {@code true} when per-profile IME is enabled.
* @hide
*/
- public static final boolean PER_PROFILE_IME_ENABLED = MULTI_CLIENT_IME_ENABLED
- || Build.IS_DEBUGGABLE && SystemProperties.getBoolean(
- PROP_DEBUG_PER_PROFILE_IME, false);
-
+ public static final boolean PER_PROFILE_IME_ENABLED;
+ static {
+ if (MULTI_CLIENT_IME_ENABLED) {
+ PER_PROFILE_IME_ENABLED = true;
+ } else if (Build.IS_DEBUGGABLE) {
+ PER_PROFILE_IME_ENABLED = SystemProperties.getBoolean(PROP_DEBUG_PER_PROFILE_IME, true);
+ } else {
+ PER_PROFILE_IME_ENABLED = true;
+ }
+ }
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 803462d59fad..299801a1b8fc 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -22,6 +22,8 @@ import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppPredictionManager;
import android.app.prediction.AppPredictor;
import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.AppTargetId;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -111,6 +113,7 @@ public class ChooserActivity extends ResolverActivity {
private static final boolean USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS = false;
// TODO(b/123088566) Share these in a better way.
private static final String APP_PREDICTION_SHARE_UI_SURFACE = "share";
+ public static final String LAUNCH_LOCATON_DIRECT_SHARE = "direct_share";
private static final int APP_PREDICTION_SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20;
public static final String APP_PREDICTION_INTENT_FILTER_KEY = "intent_filter";
private AppPredictor mAppPredictor;
@@ -787,9 +790,11 @@ public class ChooserActivity extends ResolverActivity {
// Do nothing. We'll send the voice stuff ourselves.
}
- // TODO(b/123377860) Send clicked ShortcutInfo to mAppPredictor
void updateModelAndChooserCounts(TargetInfo info) {
if (info != null) {
+ if (USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) {
+ sendClickToAppPredictor(info);
+ }
final ResolveInfo ri = info.getResolveInfo();
Intent targetIntent = getTargetIntent();
if (ri != null && ri.activityInfo != null && targetIntent != null) {
@@ -802,13 +807,39 @@ public class ChooserActivity extends ResolverActivity {
Log.d(TAG, "ResolveInfo Package is " + ri.activityInfo.packageName);
Log.d(TAG, "Action to be updated is " + targetIntent.getAction());
}
- } else if(DEBUG) {
+ } else if (DEBUG) {
Log.d(TAG, "Can not log Chooser Counts of null ResovleInfo");
}
}
mIsSuccessfullySelected = true;
}
+ private void sendClickToAppPredictor(TargetInfo targetInfo) {
+ if (!(targetInfo instanceof ChooserTargetInfo)) {
+ return;
+ }
+ ChooserTarget chooserTarget = ((ChooserTargetInfo) targetInfo).getChooserTarget();
+ ComponentName componentName = chooserTarget.getComponentName();
+ Bundle extras = chooserTarget.getIntentExtras();
+ if (extras == null) {
+ return;
+ }
+ String shortcutId = extras.getString(Intent.EXTRA_SHORTCUT_ID);
+ if (shortcutId == null) {
+ return;
+ }
+ mAppPredictor.notifyAppTargetEvent(
+ new AppTargetEvent.Builder(
+ new AppTarget(
+ new AppTargetId(shortcutId),
+ componentName.getPackageName(),
+ componentName.getClassName(),
+ getUser()),
+ AppTargetEvent.ACTION_LAUNCH
+ ).setLaunchLocation(LAUNCH_LOCATON_DIRECT_SHARE)
+ .build());
+ }
+
void onRefinementResult(TargetInfo selectedTarget, Intent matchingIntent) {
if (mRefinementResultReceiver != null) {
mRefinementResultReceiver.destroy();
@@ -1128,6 +1159,10 @@ public class ChooserActivity extends ResolverActivity {
return mBadgeContentDescription;
}
+ public ChooserTarget getChooserTarget() {
+ return mChooserTarget;
+ }
+
@Override
public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) {
return new ChooserTargetInfo(this, fillInIntent, flags);
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
index d0272e08b53a..e27ff0076054 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
package com.android.internal.inputmethod;
import android.net.Uri;
+import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.inputmethod.IInputContentUriToken;
@@ -39,4 +40,6 @@ interface IInputMethodPrivilegedOperations {
boolean switchToNextInputMethod(boolean onlyCurrentIme);
boolean shouldOfferSwitchingToNextInputMethod();
void notifyUserAction();
+ void reportPreRendered(in EditorInfo info);
+ void applyImeVisibility(boolean setVisible);
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 8978496073e5..d42c607b98bf 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -23,6 +23,7 @@ import android.net.Uri;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.annotations.GuardedBy;
@@ -347,4 +348,40 @@ public final class InputMethodPrivilegedOperations {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Calls {@link IInputMethodPrivilegedOperations#reportPreRendered(info)}.
+ *
+ * @param info {@link EditorInfo} of the currently rendered {@link TextView}.
+ */
+ @AnyThread
+ public void reportPreRendered(EditorInfo info) {
+ final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+ if (ops == null) {
+ return;
+ }
+ try {
+ ops.reportPreRendered(info);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Calls {@link IInputMethodPrivilegedOperations#applyImeVisibility(boolean)}.
+ *
+ * @param setVisible {@code true} to set IME visible, else hidden.
+ */
+ @AnyThread
+ public void applyImeVisibility(boolean setVisible) {
+ final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+ if (ops == null) {
+ return;
+ }
+ try {
+ ops.applyImeVisibility(setVisible);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl
index 17b2bc46de36..2cfdaaa5ada6 100644
--- a/core/java/com/android/internal/view/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/view/IInputMethodClient.aidl
@@ -16,6 +16,8 @@
package com.android.internal.view;
+import android.view.inputmethod.EditorInfo;
+
import com.android.internal.view.InputBindResult;
/**
@@ -27,4 +29,6 @@ oneway interface IInputMethodClient {
void onUnbindMethod(int sequence, int unbindReason);
void setActive(boolean active, boolean fullscreen);
void reportFullscreenMode(boolean fullscreen);
+ void reportPreRendered(in EditorInfo info);
+ void applyImeVisibility(boolean setVisible);
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 0466aaaee9fd..9afd5d0bff03 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -166,6 +166,7 @@ cc_library_shared {
"android_media_AudioRecord.cpp",
"android_media_AudioSystem.cpp",
"android_media_AudioTrack.cpp",
+ "android_media_AudioAttributes.cpp",
"android_media_DeviceCallback.cpp",
"android_media_JetPlayer.cpp",
"android_media_MediaMetricsJNI.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index f458299468b1..c90071a0de44 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -107,6 +107,7 @@ extern int register_android_media_AudioEffectDescriptor(JNIEnv *env);
extern int register_android_media_AudioRecord(JNIEnv *env);
extern int register_android_media_AudioSystem(JNIEnv *env);
extern int register_android_media_AudioTrack(JNIEnv *env);
+extern int register_android_media_AudioAttributes(JNIEnv *env);
extern int register_android_media_MicrophoneInfo(JNIEnv *env);
extern int register_android_media_JetPlayer(JNIEnv *env);
extern int register_android_media_ToneGenerator(JNIEnv *env);
@@ -1470,6 +1471,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_media_AudioSystem),
REG_JNI(register_android_media_AudioRecord),
REG_JNI(register_android_media_AudioTrack),
+ REG_JNI(register_android_media_AudioAttributes),
REG_JNI(register_android_media_JetPlayer),
REG_JNI(register_android_media_MicrophoneInfo),
REG_JNI(register_android_media_RemoteDisplay),
diff --git a/core/jni/android_hardware_HardwareBuffer.cpp b/core/jni/android_hardware_HardwareBuffer.cpp
index 8174a4184c83..2d1fec819e97 100644
--- a/core/jni/android_hardware_HardwareBuffer.cpp
+++ b/core/jni/android_hardware_HardwareBuffer.cpp
@@ -104,6 +104,21 @@ static jlong android_hardware_HardwareBuffer_getNativeFinalizer(JNIEnv* env, job
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyWrapper));
}
+static jboolean android_hardware_HardwareBuffer_isSupported(JNIEnv* env, jobject clazz,
+ jint width, jint height, jint format, jint layers, jlong usage) {
+
+ AHardwareBuffer_Desc desc;
+ desc.width = width;
+ desc.height = height;
+ desc.format = format;
+ desc.layers = layers;
+ desc.usage = usage;
+ desc.stride = 0;
+ desc.rfu0 = 0;
+ desc.rfu1 = 0;
+ return AHardwareBuffer_isSupported(&desc);
+}
+
//----------------------------------------------------------------------------
// Accessors
// ----------------------------------------------------------------------------
@@ -234,6 +249,8 @@ static const JNINativeMethod gMethods[] = {
(void*) android_hardware_HardwareBuffer_write },
{ "nReadHardwareBufferFromParcel", "(Landroid/os/Parcel;)J",
(void*) android_hardware_HardwareBuffer_read },
+ { "nIsSupported", "(IIIIJ)Z",
+ (void*) android_hardware_HardwareBuffer_isSupported },
// --------------- @FastNative ----------------------
{ "nGetWidth", "(J)I", (void*) android_hardware_HardwareBuffer_getWidth },
diff --git a/core/jni/android_media_AudioAttributes.cpp b/core/jni/android_media_AudioAttributes.cpp
new file mode 100644
index 000000000000..bfe61626c1a2
--- /dev/null
+++ b/core/jni/android_media_AudioAttributes.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+
+#define LOG_TAG "AudioAttributes-JNI"
+
+#include <inttypes.h>
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include "core_jni_helpers.h"
+
+#include <utils/Log.h>
+#include <vector>
+
+#include <nativehelper/ScopedLocalRef.h>
+#include <nativehelper/ScopedUtfChars.h>
+
+#include "android_media_AudioAttributes.h"
+#include "android_media_AudioErrors.h"
+
+// ----------------------------------------------------------------------------
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+static const char* const kClassPathName = "android/media/AudioAttributes";
+
+static jclass gAudioAttributesClass;
+static struct {
+ jfieldID mUsage; // AudioAttributes.mUsage
+ jfieldID mSource; // AudioAttributes.mSource
+ jfieldID mContentType; // AudioAttributes.mContentType
+ jfieldID mFlags; // AudioAttributes.mFlags
+ jfieldID mFormattedTags; // AudioAttributes.mFormattedTags
+} gAudioAttributesFields;
+
+static jclass gAudioAttributesBuilderClass;
+static jmethodID gAudioAttributesBuilderCstor;
+static struct {
+ jmethodID build;
+ jmethodID setUsage;
+ jmethodID setInternalCapturePreset;
+ jmethodID setContentType;
+ jmethodID setFlags;
+ jmethodID addTag;
+} gAudioAttributesBuilderMethods;
+
+
+static jint nativeAudioAttributesFromJavaAudioAttributes(
+ JNIEnv* env, jobject jAudioAttributes, audio_attributes_t *aa)
+{
+ if (env == nullptr) {
+ return AUDIO_JAVA_DEAD_OBJECT;
+ }
+ if (jAudioAttributes == nullptr) {
+ ALOGE("Invalid AudioAttributes java object");
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+ if (!env->IsInstanceOf(jAudioAttributes, gAudioAttributesClass)) {
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+ const jstring jtags =
+ (jstring) env->GetObjectField(jAudioAttributes, gAudioAttributesFields.mFormattedTags);
+ if (jtags == nullptr) {
+ return AUDIO_JAVA_NO_INIT;
+ }
+ const char* tags = env->GetStringUTFChars(jtags, NULL);
+ // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
+ strncpy(aa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
+ env->ReleaseStringUTFChars(jtags, tags);
+
+ // Record ?
+ aa->source = (audio_source_t) env->GetIntField(jAudioAttributes,
+ gAudioAttributesFields.mSource);
+ // Track ?
+ aa->usage = (audio_usage_t) env->GetIntField(jAudioAttributes, gAudioAttributesFields.mUsage);
+
+ aa->content_type =
+ (audio_content_type_t) env->GetIntField(jAudioAttributes,
+ gAudioAttributesFields.mContentType);
+
+ aa->flags = (audio_flags_mask_t)env->GetIntField(jAudioAttributes,
+ gAudioAttributesFields.mFlags);
+
+ ALOGV("AudioAttributes for usage=%d content=%d source=%d tags=%s flags=%08x tags=%s",
+ aa->usage, aa->content_type, aa->source, aa->tags, aa->flags, aa->tags);
+ return (jint)AUDIO_JAVA_SUCCESS;
+}
+
+static jint nativeAudioAttributesToJavaAudioAttributes(
+ JNIEnv* env, jobject *jAudioAttributes, const audio_attributes_t &attributes)
+{
+ ScopedLocalRef<jobject> jAttributeBuilder(env, env->NewObject(gAudioAttributesBuilderClass,
+ gAudioAttributesBuilderCstor));
+ if (jAttributeBuilder.get() == nullptr) {
+ return (jint)AUDIO_JAVA_ERROR;
+ }
+ env->CallObjectMethod(jAttributeBuilder.get(),
+ gAudioAttributesBuilderMethods.setUsage,
+ attributes.usage);
+ env->CallObjectMethod(jAttributeBuilder.get(),
+ gAudioAttributesBuilderMethods.setInternalCapturePreset,
+ attributes.source);
+ env->CallObjectMethod(jAttributeBuilder.get(),
+ gAudioAttributesBuilderMethods.setContentType,
+ attributes.content_type);
+ env->CallObjectMethod(jAttributeBuilder.get(),
+ gAudioAttributesBuilderMethods.setFlags,
+ attributes.flags);
+ env->CallObjectMethod(jAttributeBuilder.get(),
+ gAudioAttributesBuilderMethods.addTag,
+ env->NewStringUTF(attributes.tags));
+
+ *jAudioAttributes = env->CallObjectMethod(jAttributeBuilder.get(),
+ gAudioAttributesBuilderMethods.build);
+ return (jint)AUDIO_JAVA_SUCCESS;
+}
+
+// ----------------------------------------------------------------------------
+JNIAudioAttributeHelper::UniqueAaPtr JNIAudioAttributeHelper::makeUnique()
+{
+ audio_attributes_t *aa = new (calloc(1, sizeof(audio_attributes_t)))
+ audio_attributes_t{AUDIO_ATTRIBUTES_INITIALIZER};
+ return UniqueAaPtr{aa, free};
+}
+
+jint JNIAudioAttributeHelper::nativeFromJava(JNIEnv* env, jobject jAudioAttributes,
+ audio_attributes_t *paa)
+{
+ return nativeAudioAttributesFromJavaAudioAttributes(env, jAudioAttributes, paa);
+}
+
+jint JNIAudioAttributeHelper::nativeToJava(
+ JNIEnv* env, jobject *jAudioAttributes, const audio_attributes_t &attributes)
+{
+ return nativeAudioAttributesToJavaAudioAttributes(env, jAudioAttributes, attributes);
+}
+
+jint JNIAudioAttributeHelper::getJavaArray(
+ JNIEnv* env, jobjectArray *jAudioAttributeArray, jint numAudioAttributes)
+{
+ *jAudioAttributeArray = env->NewObjectArray(numAudioAttributes, gAudioAttributesClass, NULL);
+ return jAudioAttributeArray == NULL? (jint)AUDIO_JAVA_ERROR : (jint)AUDIO_JAVA_SUCCESS;
+}
+
+/*
+ * JNI registration.
+ */
+static const JNINativeMethod gMethods[] = {
+ // n/a
+};
+
+int register_android_media_AudioAttributes(JNIEnv *env)
+{
+ jclass audioAttributesClass = FindClassOrDie(env, kClassPathName);
+ gAudioAttributesClass = MakeGlobalRefOrDie(env, audioAttributesClass);
+ gAudioAttributesFields.mUsage = GetFieldIDOrDie(env, audioAttributesClass, "mUsage", "I");
+ gAudioAttributesFields.mSource = GetFieldIDOrDie(env, audioAttributesClass, "mSource", "I");
+ gAudioAttributesFields.mContentType =
+ GetFieldIDOrDie(env, audioAttributesClass, "mContentType", "I");
+ gAudioAttributesFields.mFlags = GetFieldIDOrDie(env, audioAttributesClass, "mFlags", "I");
+ gAudioAttributesFields.mFormattedTags =
+ GetFieldIDOrDie(env, audioAttributesClass, "mFormattedTags", "Ljava/lang/String;");
+
+ jclass audioAttributesBuilderClass = FindClassOrDie(
+ env, "android/media/AudioAttributes$Builder");
+ gAudioAttributesBuilderClass = MakeGlobalRefOrDie(env, audioAttributesBuilderClass);
+ gAudioAttributesBuilderCstor = GetMethodIDOrDie(
+ env, audioAttributesBuilderClass, "<init>", "()V");
+ gAudioAttributesBuilderMethods.build = GetMethodIDOrDie(
+ env, audioAttributesBuilderClass, "build", "()Landroid/media/AudioAttributes;");
+ gAudioAttributesBuilderMethods.setUsage = GetMethodIDOrDie(
+ env, audioAttributesBuilderClass, "setUsage",
+ "(I)Landroid/media/AudioAttributes$Builder;");
+ gAudioAttributesBuilderMethods.setInternalCapturePreset = GetMethodIDOrDie(
+ env, audioAttributesBuilderClass, "setInternalCapturePreset",
+ "(I)Landroid/media/AudioAttributes$Builder;");
+ gAudioAttributesBuilderMethods.setContentType = GetMethodIDOrDie(
+ env, audioAttributesBuilderClass, "setContentType",
+ "(I)Landroid/media/AudioAttributes$Builder;");
+ gAudioAttributesBuilderMethods.setFlags = GetMethodIDOrDie(
+ env, audioAttributesBuilderClass, "setFlags",
+ "(I)Landroid/media/AudioAttributes$Builder;");
+ gAudioAttributesBuilderMethods.addTag = GetMethodIDOrDie(
+ env, audioAttributesBuilderClass, "addTag",
+ "(Ljava/lang/String;)Landroid/media/AudioAttributes$Builder;");
+
+ env->DeleteLocalRef(audioAttributesClass);
+
+ return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
+}
diff --git a/core/jni/android_media_AudioAttributes.h b/core/jni/android_media_AudioAttributes.h
new file mode 100644
index 000000000000..c55835222086
--- /dev/null
+++ b/core/jni/android_media_AudioAttributes.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "jni.h"
+
+#include <memory.h>
+
+#include <system/audio.h>
+
+namespace android {
+
+class JNIAudioAttributeHelper
+{
+public:
+ using UniqueAaPtr = std::unique_ptr<audio_attributes_t, decltype(free)*>;
+
+ /**
+ * @brief makeUnique helper to prevent leak
+ * @return a unique ptr of 0-initialized native audio attributes structure
+ */
+ static UniqueAaPtr makeUnique();
+
+ /**
+ * @brief nativeFromJava Gets the underlying AudioAttributes from an AudioAttributes Java
+ * object.
+ * @param env
+ * @param jAudioAttributes JAVA AudioAttribute object
+ * @param paa native AudioAttribute pointer
+ * @return AUDIO_JAVA_SUCCESS on success, error code otherwise
+ */
+ static jint nativeFromJava(
+ JNIEnv* env, jobject jAudioAttributes, audio_attributes_t *attributes);
+
+ /**
+ * @brief nativeToJava AudioAttributes Java object from a native AudioAttributes.
+ * @param env
+ * @param jAudioAttributes JAVA AudioAttribute object
+ * @param attributes native AudioAttribute
+ * @return AUDIO_JAVA_SUCCESS on success, error code otherwise
+ */
+ static jint nativeToJava(
+ JNIEnv* env, jobject *jAudioAttributes, const audio_attributes_t &attributes);
+
+ /**
+ * @brief getJavaArray: creates an array of JAVA AudioAttributes objects
+ * @param env
+ * @param jAudioAttributeArray
+ * @param numAudioAttributes
+ * @return Array of AudioAttributes objects
+ */
+ static jint getJavaArray(
+ JNIEnv* env, jobjectArray *jAudioAttributeArray, jint numAudioAttributes);
+};
+
+}; // namespace android
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 67c306413bb2..02dffdc2ca25 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -35,6 +35,7 @@
#include "android_media_DeviceCallback.h"
#include "android_media_MediaMetricsJNI.h"
#include "android_media_MicrophoneInfo.h"
+#include "android_media_AudioAttributes.h"
// ----------------------------------------------------------------------------
@@ -42,7 +43,6 @@ using namespace android;
// ----------------------------------------------------------------------------
static const char* const kClassPathName = "android/media/AudioRecord";
-static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
static jclass gArrayListClass;
static struct {
@@ -56,12 +56,6 @@ struct audio_record_fields_t {
jfieldID nativeCallbackCookie; // provides access to the AudioRecord callback data
jfieldID nativeDeviceCallback; // provides access to the JNIDeviceCallback instance
};
-struct audio_attributes_fields_t {
- jfieldID fieldRecSource; // AudioAttributes.mSource
- jfieldID fieldFlags; // AudioAttributes.mFlags
- jfieldID fieldFormattedTags;// AudioAttributes.mFormattedTags
-};
-static audio_attributes_fields_t javaAudioAttrFields;
static audio_record_fields_t javaAudioRecordFields;
static struct {
jfieldID fieldFramePosition; // AudioTimestamp.framePosition
@@ -213,7 +207,6 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
nSession = NULL;
- audio_attributes_t *paa = NULL;
sp<AudioRecord> lpRecorder = 0;
audiorecord_callback_cookie *lpCallbackData = NULL;
@@ -275,15 +268,11 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str()));
// read the AudioAttributes values
- paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
- const jstring jtags =
- (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
- const char* tags = env->GetStringUTFChars(jtags, NULL);
- // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
- strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
- env->ReleaseStringUTFChars(jtags, tags);
- paa->source = (audio_source_t) env->GetIntField(jaa, javaAudioAttrFields.fieldRecSource);
- paa->flags = (audio_flags_mask_t)env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
+ auto paa = JNIAudioAttributeHelper::makeUnique();
+ jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get());
+ if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
+ return jStatus;
+ }
ALOGV("AudioRecord_setup for source=%d tags=%s flags=%08x", paa->source, paa->tags, paa->flags);
audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE;
@@ -311,7 +300,7 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
AudioRecord::TRANSFER_DEFAULT,
flags,
-1, -1, // default uid, pid
- paa);
+ paa.get());
if (status != NO_ERROR) {
ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.",
@@ -368,19 +357,10 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
// of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData);
- if (paa != NULL) {
- // audio attributes were copied in AudioRecord creation
- free(paa);
- paa = NULL;
- }
-
return (jint) AUDIO_JAVA_SUCCESS;
// failure:
native_init_failure:
- if (paa != NULL) {
- free(paa);
- }
env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
delete lpCallbackData;
@@ -972,13 +952,6 @@ int register_android_media_AudioRecord(JNIEnv *env)
javaAudioRecordFields.nativeDeviceCallback = GetFieldIDOrDie(env,
audioRecordClass, JAVA_NATIVEDEVICECALLBACK_FIELD_NAME, "J");
- // Get the AudioAttributes class and fields
- jclass audioAttrClass = FindClassOrDie(env, kAudioAttributesClassPathName);
- javaAudioAttrFields.fieldRecSource = GetFieldIDOrDie(env, audioAttrClass, "mSource", "I");
- javaAudioAttrFields.fieldFlags = GetFieldIDOrDie(env, audioAttrClass, "mFlags", "I");
- javaAudioAttrFields.fieldFormattedTags = GetFieldIDOrDie(env,
- audioAttrClass, "mFormattedTags", "Ljava/lang/String;");
-
// Get the RecordTimestamp class and fields
jclass audioTimestampClass = FindClassOrDie(env, "android/media/AudioTimestamp");
javaAudioTimestampFields.fieldFramePosition =
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 0d8ede794b73..34abcd889644 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -36,6 +36,7 @@
#include "android_media_AudioFormat.h"
#include "android_media_AudioErrors.h"
#include "android_media_MicrophoneInfo.h"
+#include "android_media_AudioAttributes.h"
// ----------------------------------------------------------------------------
@@ -153,15 +154,6 @@ static struct {
jfieldID mRule;
} gAudioMixMatchCriterionFields;
-static jclass gAudioAttributesClass;
-static struct {
- jfieldID mUsage;
- jfieldID mSource;
- jfieldID mContentType;
- jfieldID mFlags;
- jfieldID mFormattedTags;
-} gAudioAttributesFields;
-
static const char* const kEventHandlerClassPathName =
"android/media/AudioPortEventHandler";
static struct {
@@ -705,27 +697,6 @@ static void convertAudioGainConfigToNative(JNIEnv *env,
env->DeleteLocalRef(jValues);
}
-static jint convertAudioAttributesToNative(JNIEnv *env,
- audio_attributes_t *nAudioAttributes,
- const jobject jAudioAttributes)
-{
- nAudioAttributes->usage = (audio_usage_t)env->GetIntField(jAudioAttributes,
- gAudioAttributesFields.mUsage);
- nAudioAttributes->source = (audio_source_t)env->GetIntField(jAudioAttributes,
- gAudioAttributesFields.mSource);
- nAudioAttributes->content_type = (audio_content_type_t)env->GetIntField(jAudioAttributes,
- gAudioAttributesFields.mContentType);
- nAudioAttributes->flags = env->GetIntField(jAudioAttributes,
- gAudioAttributesFields.mFlags);
- const jstring jtags = (jstring)env->GetObjectField(jAudioAttributes,
- gAudioAttributesFields.mFormattedTags);
- const char *tags = env->GetStringUTFChars(jtags, NULL);
- strncpy(nAudioAttributes->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
- env->ReleaseStringUTFChars(jtags, tags);
- return (jint)AUDIO_JAVA_SUCCESS;
-}
-
-
static jint convertAudioPortConfigToNative(JNIEnv *env,
struct audio_port_config *nAudioPortConfig,
const jobject jAudioPortConfig,
@@ -1705,22 +1676,19 @@ android_media_AudioSystem_startAudioSource(JNIEnv *env, jobject clazz,
if (!env->IsInstanceOf(jAudioPortConfig, gAudioPortConfigClass)) {
return AUDIO_JAVA_BAD_VALUE;
}
- if (!env->IsInstanceOf(jAudioAttributes, gAudioAttributesClass)) {
- return AUDIO_JAVA_BAD_VALUE;
- }
struct audio_port_config nAudioPortConfig = {};
jint jStatus = convertAudioPortConfigToNativeWithDevicePort(env,
&nAudioPortConfig, jAudioPortConfig, false);
if (jStatus != AUDIO_JAVA_SUCCESS) {
return jStatus;
}
- audio_attributes_t nAudioAttributes = {};
- jStatus = convertAudioAttributesToNative(env, &nAudioAttributes, jAudioAttributes);
- if (jStatus != AUDIO_JAVA_SUCCESS) {
+ auto paa = JNIAudioAttributeHelper::makeUnique();
+ jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jAudioAttributes, paa.get());
+ if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
return jStatus;
}
audio_port_handle_t handle;
- status_t status = AudioSystem::startAudioSource(&nAudioPortConfig, &nAudioAttributes, &handle);
+ status_t status = AudioSystem::startAudioSource(&nAudioPortConfig, paa.get(), &handle);
ALOGV("AudioSystem::startAudioSource() returned %d handle %d", status, handle);
return handle > 0 ? handle : nativeToJavaStatus(status);
}
@@ -1833,12 +1801,16 @@ static jint convertAudioMixToNative(JNIEnv *env,
case RULE_MATCH_ATTRIBUTE_USAGE:
case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET: {
jobject jAttributes = env->GetObjectField(jCriterion, gAudioMixMatchCriterionFields.mAttr);
+
+ auto paa = JNIAudioAttributeHelper::makeUnique();
+ jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jAttributes, paa.get());
+ if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
+ return jStatus;
+ }
if (match_rule == RULE_MATCH_ATTRIBUTE_USAGE) {
- nCriterion.mValue.mUsage = (audio_usage_t)env->GetIntField(jAttributes,
- gAudioAttributesFields.mUsage);
+ nCriterion.mValue.mUsage = paa->usage;
} else {
- nCriterion.mValue.mSource = (audio_source_t)env->GetIntField(jAttributes,
- gAudioAttributesFields.mSource);
+ nCriterion.mValue.mSource = paa->source;
}
env->DeleteLocalRef(jAttributes);
}
@@ -2395,17 +2367,6 @@ int register_android_media_AudioSystem(JNIEnv *env)
"I");
gAudioMixMatchCriterionFields.mRule = GetFieldIDOrDie(env, audioMixMatchCriterionClass, "mRule",
"I");
-
- jclass audioAttributesClass = FindClassOrDie(env, "android/media/AudioAttributes");
- gAudioAttributesClass = MakeGlobalRefOrDie(env, audioAttributesClass);
- gAudioAttributesFields.mUsage = GetFieldIDOrDie(env, audioAttributesClass, "mUsage", "I");
- gAudioAttributesFields.mSource = GetFieldIDOrDie(env, audioAttributesClass, "mSource", "I");
- gAudioAttributesFields.mContentType = GetFieldIDOrDie(env,
- audioAttributesClass, "mContentType", "I");
- gAudioAttributesFields.mFlags = GetFieldIDOrDie(env, audioAttributesClass, "mFlags", "I");
- gAudioAttributesFields.mFormattedTags = GetFieldIDOrDie(env,
- audioAttributesClass, "mFormattedTags", "Ljava/lang/String;");
-
// AudioTrackRoutingProxy methods
gClsAudioTrackRoutingProxy =
android::FindClassOrDie(env, "android/media/AudioTrackRoutingProxy");
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index a0d99ec95b55..f9f28dabc694 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -37,6 +37,7 @@
#include "android_media_PlaybackParams.h"
#include "android_media_DeviceCallback.h"
#include "android_media_VolumeShaper.h"
+#include "android_media_AudioAttributes.h"
#include <cinttypes>
@@ -48,7 +49,6 @@ using ::android::media::VolumeShaper;
// ----------------------------------------------------------------------------
static const char* const kClassPathName = "android/media/AudioTrack";
-static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
struct audio_track_fields_t {
// these fields provide access from C++ to the...
@@ -57,14 +57,7 @@ struct audio_track_fields_t {
jfieldID jniData; // stores in Java additional resources used by the native AudioTrack
jfieldID fieldStreamType; // ... mStreamType field in the AudioTrack Java object
};
-struct audio_attributes_fields_t {
- jfieldID fieldUsage; // AudioAttributes.mUsage
- jfieldID fieldContentType; // AudioAttributes.mContentType
- jfieldID fieldFlags; // AudioAttributes.mFlags
- jfieldID fieldFormattedTags;// AudioAttributes.mFormattedTags
-};
static audio_track_fields_t javaAudioTrackFields;
-static audio_attributes_fields_t javaAudioAttrFields;
static PlaybackParams::fields_t gPlaybackParamsFields;
static VolumeShaperHelper::fields_t gVolumeShaperFields;
@@ -249,8 +242,6 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job
AudioTrackJniStorage* lpJniStorage = NULL;
- audio_attributes_t *paa = NULL;
-
jclass clazz = env->GetObjectClass(thiz);
if (clazz == NULL) {
ALOGE("Can't find %s when setting up callback.", kClassPathName);
@@ -304,18 +295,11 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job
lpTrack = new AudioTrack();
// read the AudioAttributes values
- paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
- const jstring jtags =
- (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
- const char* tags = env->GetStringUTFChars(jtags, NULL);
- // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
- strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
- env->ReleaseStringUTFChars(jtags, tags);
- paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage);
- paa->content_type =
- (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);
- paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
-
+ auto paa = JNIAudioAttributeHelper::makeUnique();
+ jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get());
+ if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
+ return jStatus;
+ }
ALOGV("AudioTrack_setup for usage=%d content=%d flags=0x%#x tags=%s",
paa->usage, paa->content_type, paa->flags, paa->tags);
@@ -357,7 +341,7 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job
offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK : AudioTrack::TRANSFER_SYNC,
offload ? &offloadInfo : NULL,
-1, -1, // default uid, pid values
- paa);
+ paa.get());
break;
@@ -384,7 +368,7 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job
AudioTrack::TRANSFER_SHARED,
NULL, // default offloadInfo
-1, -1, // default uid, pid values
- paa);
+ paa.get());
break;
default:
@@ -452,20 +436,11 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job
// since we had audio attributes, the stream type was derived from them during the
// creation of the native AudioTrack: push the same value to the Java object
env->SetIntField(thiz, javaAudioTrackFields.fieldStreamType, (jint) lpTrack->streamType());
- if (paa != NULL) {
- // audio attributes were copied in AudioTrack creation
- free(paa);
- paa = NULL;
- }
-
return (jint) AUDIO_JAVA_SUCCESS;
// failures:
native_init_failure:
- if (paa != NULL) {
- free(paa);
- }
if (nSession != NULL) {
env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
}
@@ -1470,17 +1445,6 @@ int register_android_media_AudioTrack(JNIEnv *env)
env->DeleteLocalRef(audioTrackClass);
- // Get the AudioAttributes class and fields
- jclass audioAttrClass = FindClassOrDie(env, kAudioAttributesClassPathName);
- javaAudioAttrFields.fieldUsage = GetFieldIDOrDie(env, audioAttrClass, "mUsage", "I");
- javaAudioAttrFields.fieldContentType = GetFieldIDOrDie(env,
- audioAttrClass, "mContentType", "I");
- javaAudioAttrFields.fieldFlags = GetFieldIDOrDie(env, audioAttrClass, "mFlags", "I");
- javaAudioAttrFields.fieldFormattedTags = GetFieldIDOrDie(env,
- audioAttrClass, "mFormattedTags", "Ljava/lang/String;");
-
- env->DeleteLocalRef(audioAttrClass);
-
// initialize PlaybackParams field info
gPlaybackParamsFields.init(env);
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index b8139a712fd7..f9407f43af9f 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -131,6 +131,7 @@ int android_view_Surface_mapPublicFormatToHalFormat(PublicFormat f) {
switch(f) {
case PublicFormat::JPEG:
case PublicFormat::DEPTH_POINT_CLOUD:
+ case PublicFormat::DEPTH_JPEG:
return HAL_PIXEL_FORMAT_BLOB;
case PublicFormat::DEPTH16:
return HAL_PIXEL_FORMAT_Y16;
@@ -161,6 +162,8 @@ android_dataspace android_view_Surface_mapPublicFormatToHalDataspace(
case PublicFormat::NV21:
case PublicFormat::YV12:
return HAL_DATASPACE_V0_JFIF;
+ case PublicFormat::DEPTH_JPEG:
+ return static_cast<android_dataspace> (HAL_DATASPACE_DYNAMIC_DEPTH);
default:
// Most formats map to UNKNOWN
return HAL_DATASPACE_UNKNOWN;
@@ -223,8 +226,12 @@ PublicFormat android_view_Surface_mapHalFormatDataspaceToPublicFormat(
case HAL_DATASPACE_V0_JFIF:
return PublicFormat::JPEG;
default:
- // Assume otherwise-marked blobs are also JPEG
- return PublicFormat::JPEG;
+ if (dataSpace == static_cast<android_dataspace>(HAL_DATASPACE_DYNAMIC_DEPTH)) {
+ return PublicFormat::DEPTH_JPEG;
+ } else {
+ // Assume otherwise-marked blobs are also JPEG
+ return PublicFormat::JPEG;
+ }
}
break;
case HAL_PIXEL_FORMAT_BGRA_8888:
diff --git a/core/jni/com_android_internal_os_AtomicDirectory.cpp b/core/jni/com_android_internal_os_AtomicDirectory.cpp
index 50b2288a614a..76b0fc167264 100644
--- a/core/jni/com_android_internal_os_AtomicDirectory.cpp
+++ b/core/jni/com_android_internal_os_AtomicDirectory.cpp
@@ -29,7 +29,7 @@ static jint com_android_internal_os_AtomicDirectory_getDirectoryFd(JNIEnv* env,
return -1;
}
int fd;
- if ((fd = TEMP_FAILURE_RETRY(open(path8.c_str(), O_DIRECTORY | O_RDONLY))) == -1) {
+ if ((fd = TEMP_FAILURE_RETRY(open(path8.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC))) == -1) {
ALOGE("Cannot open directory %s, error: %s\n", path8.c_str(), strerror(errno));
return -1;
}
diff --git a/core/jni/include/android_runtime/android_view_Surface.h b/core/jni/include/android_runtime/android_view_Surface.h
index 36487b3b40b3..984e942207c1 100644
--- a/core/jni/include/android_runtime/android_view_Surface.h
+++ b/core/jni/include/android_runtime/android_view_Surface.h
@@ -57,7 +57,8 @@ enum class PublicFormat {
YV12 = 0x32315659,
Y8 = 0x20203859, // @hide
Y16 = 0x20363159, // @hide
- DEPTH16 = 0x44363159
+ DEPTH16 = 0x44363159,
+ DEPTH_JPEG = 0x69656963,
};
/* Gets the underlying ANativeWindow for a Surface. */
diff --git a/core/proto/android/app/job/enums.proto b/core/proto/android/app/job/enums.proto
index bba832880669..f702b3e37bda 100644
--- a/core/proto/android/app/job/enums.proto
+++ b/core/proto/android/app/job/enums.proto
@@ -18,6 +18,9 @@ syntax = "proto2";
package android.app.job;
+// This file is for JobScheduler enums inside the app directory. If you're
+// adding enums for system-server-side code, use the file in
+// frameworks/base/core/proto/android/server/job.
option java_outer_classname = "JobProtoEnums";
option java_multiple_files = true;
diff --git a/core/proto/android/bluetooth/hci/enums.proto b/core/proto/android/bluetooth/hci/enums.proto
index e1d96bbce68a..9cf5ea78090d 100644
--- a/core/proto/android/bluetooth/hci/enums.proto
+++ b/core/proto/android/bluetooth/hci/enums.proto
@@ -351,7 +351,7 @@ enum EventEnum {
EVT_COMMAND_COMPLETE = 0x0E;
EVT_COMMAND_STATUS = 0x0F;
EVT_HARDWARE_ERROR = 0x10;
- EVT_FLUSH_OCCURED = 0x11;
+ EVT_FLUSH_OCCURRED = 0x11;
EVT_ROLE_CHANGE = 0x12;
EVT_NUM_COMPL_DATA_PKTS = 0x13;
EVT_MODE_CHANGE = 0x14;
diff --git a/core/proto/android/server/job/enums.proto b/core/proto/android/server/job/enums.proto
new file mode 100644
index 000000000000..50fc0310ad99
--- /dev/null
+++ b/core/proto/android/server/job/enums.proto
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package com.android.server.job;
+
+// This file is for JobScheduler enums inside the server directory. If you're
+// adding enums for app-side code, use the file in
+// frameworks/base/core/proto/android/app/job.
+option java_outer_classname = "JobServerProtoEnums";
+option java_multiple_files = true;
+
+// Set of constraints that a job potentially needs satisfied before it can run.
+// Defined in
+// frameworks/base/services/core/java/com/android/server/job/controllers/JobStatus.java
+enum ConstraintEnum {
+ CONSTRAINT_UNKNOWN = 0;
+ CONSTRAINT_CHARGING = 1;
+ CONSTRAINT_BATTERY_NOT_LOW = 2;
+ CONSTRAINT_STORAGE_NOT_LOW = 3;
+ CONSTRAINT_TIMING_DELAY = 4;
+ CONSTRAINT_DEADLINE = 5;
+ CONSTRAINT_IDLE = 6;
+ CONSTRAINT_CONNECTIVITY = 7;
+ CONSTRAINT_CONTENT_TRIGGER = 8;
+ CONSTRAINT_DEVICE_NOT_DOZING = 9;
+ CONSTRAINT_WITHIN_QUOTA = 10;
+ CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 11;
+}
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 188769d930d1..6c9d13a572ee 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -29,6 +29,7 @@ import "frameworks/base/core/proto/android/net/networkrequest.proto";
import "frameworks/base/core/proto/android/os/bundle.proto";
import "frameworks/base/core/proto/android/os/persistablebundle.proto";
import "frameworks/base/core/proto/android/server/forceappstandbytracker.proto";
+import "frameworks/base/core/proto/android/server/job/enums.proto";
import "frameworks/base/libs/incident/proto/android/privacy.proto";
// Next tag: 21
@@ -757,21 +758,9 @@ message JobStatusDumpProto {
}
optional JobInfo job_info = 6;
- enum Constraint {
- CONSTRAINT_CHARGING = 1;
- CONSTRAINT_BATTERY_NOT_LOW = 2;
- CONSTRAINT_STORAGE_NOT_LOW = 3;
- CONSTRAINT_TIMING_DELAY = 4;
- CONSTRAINT_DEADLINE = 5;
- CONSTRAINT_IDLE = 6;
- CONSTRAINT_CONNECTIVITY = 7;
- CONSTRAINT_CONTENT_TRIGGER = 8;
- CONSTRAINT_DEVICE_NOT_DOZING = 9;
- CONSTRAINT_WITHIN_QUOTA = 10;
- }
- repeated Constraint required_constraints = 7;
- repeated Constraint satisfied_constraints = 8;
- repeated Constraint unsatisfied_constraints = 9;
+ repeated ConstraintEnum required_constraints = 7;
+ repeated ConstraintEnum satisfied_constraints = 8;
+ repeated ConstraintEnum unsatisfied_constraints = 9;
optional bool is_doze_whitelisted = 10;
optional bool is_uid_active = 26;
diff --git a/core/res/res/values-night/themes_device_defaults.xml b/core/res/res/values-night/themes_device_defaults.xml
index 43b3552ff8d4..949c12e7d1da 100644
--- a/core/res/res/values-night/themes_device_defaults.xml
+++ b/core/res/res/values-night/themes_device_defaults.xml
@@ -51,6 +51,10 @@ easier.
-->
<!-- DeviceDefault theme for a window that should look like the Settings app. -->
<style name="Theme.DeviceDefault.Settings" parent="Theme.DeviceDefault">
+ <item name="windowLightStatusBar">false</item>
+ <item name="navigationBarColor">@android:color/black</item>
+ <item name="windowLightNavigationBar">false</item>
+
<item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
<item name="colorBackground">@color/primary_dark_device_default_settings</item>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index bd7f8527fc6f..2a29f8336adc 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -132,6 +132,9 @@ public class SettingsBackupTest {
Settings.Global.AUTOMATIC_POWER_SAVER_MODE,
Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED,
Settings.Global.BATTERY_CHARGING_STATE_UPDATE_DELAY,
+ Settings.Global.BROADCAST_BG_CONSTANTS,
+ Settings.Global.BROADCAST_FG_CONSTANTS,
+ Settings.Global.BROADCAST_OFFLOAD_CONSTANTS,
Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD,
Settings.Global.BATTERY_DISCHARGE_THRESHOLD,
Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS,
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
new file mode 100644
index 000000000000..b07cb99f35c5
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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;
+
+import static android.view.ImeInsetsSourceConsumer.areEditorsSimilar;
+import static android.view.InsetsState.TYPE_IME;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.content.Context;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl.Transaction;
+import android.view.WindowManager.BadTokenException;
+import android.view.WindowManager.LayoutParams;
+import android.view.inputmethod.EditorInfo;
+import android.widget.TextView;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+@Presubmit
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@RunWith(AndroidJUnit4.class)
+public class ImeInsetsSourceConsumerTest {
+
+ Context mContext = InstrumentationRegistry.getTargetContext();
+ ImeInsetsSourceConsumer mImeConsumer;
+ InsetsController mController;
+ SurfaceControl mLeash;
+
+ @Before
+ public void setup() {
+ mLeash = new SurfaceControl.Builder(new SurfaceSession())
+ .setName("testSurface")
+ .build();
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ ViewRootImpl viewRootImpl = new ViewRootImpl(mContext, mContext.getDisplay());
+ try {
+ viewRootImpl.setView(new TextView(mContext), new LayoutParams(), null);
+ } catch (BadTokenException e) {
+ // activity isn't running, we will ignore BadTokenException.
+ }
+ mController = new InsetsController(viewRootImpl);
+ final Rect rect = new Rect(5, 5, 5, 5);
+ mController.calculateInsets(
+ false,
+ false,
+ new DisplayCutout(
+ Insets.of(10, 10, 10, 10), rect, rect, rect, rect),
+ rect, rect);
+ mImeConsumer = new ImeInsetsSourceConsumer(
+ new InsetsState(), Transaction::new, mController);
+ });
+ }
+
+ @Test
+ public void testImeVisibility() {
+ final InsetsSourceControl ime = new InsetsSourceControl(TYPE_IME, mLeash);
+ mController.onControlsChanged(new InsetsSourceControl[] { ime });
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ // test if setVisibility can show IME
+ mImeConsumer.onWindowFocusGained();
+ mImeConsumer.applyImeVisibility(true);
+ mController.cancelExistingAnimation();
+ assertTrue(mController.getSourceConsumer(ime.getType()).isVisible());
+
+ // test if setVisibility can hide IME
+ mImeConsumer.applyImeVisibility(false);
+ mController.cancelExistingAnimation();
+ assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ });
+ }
+
+ @Test
+ public void testAreEditorsSimilar() {
+ EditorInfo info1 = new EditorInfo();
+ info1.privateImeOptions = "dummy";
+ EditorInfo info2 = new EditorInfo();
+
+ assertFalse(areEditorsSimilar(info1, info2));
+
+ info1.privateImeOptions = null;
+ assertTrue(areEditorsSimilar(info1, info2));
+
+ info1.inputType = info2.inputType = 3;
+ info1.imeOptions = info2.imeOptions = 0x4;
+ info1.packageName = info2.packageName = "dummy.package";
+ assertTrue(areEditorsSimilar(info1, info2));
+
+ Bundle extras1 = new Bundle();
+ extras1.putByteArray("key1", "value1".getBytes());
+ extras1.putChar("key2", 'c');
+ Bundle extras2 = new Bundle();
+ extras2.putByteArray("key1", "value1".getBytes());
+ extras2.putChar("key2", 'c');
+ info1.extras = extras1;
+ info2.extras = extras2;
+ assertTrue(areEditorsSimilar(info1, info2));
+
+ Bundle extraBundle = new Bundle();
+ ArrayList<Integer> list = new ArrayList<>();
+ list.add(2);
+ list.add(5);
+ extraBundle.putByteArray("key1", "value1".getBytes());
+ extraBundle.putChar("key2", 'c');
+ extraBundle.putIntegerArrayList("key3", list);
+
+ extras1.putAll(extraBundle);
+ extras2.putAll(extraBundle);
+ assertTrue(areEditorsSimilar(info1, info2));
+
+ extras2.putChar("key2", 'd');
+ assertFalse(areEditorsSimilar(info1, info2));
+
+ extras2.putChar("key2", 'c');
+ extras2.putInt("key4", 1);
+ assertFalse(areEditorsSimilar(info1, info2));
+ }
+}
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 2db2f5f2a4fd..03af67df13d0 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -155,10 +155,10 @@ public class InsetsStateTest {
@Test
public void testGetDefaultVisibility() {
- assertTrue(InsetsState.getDefaultVisibly(TYPE_TOP_BAR));
- assertTrue(InsetsState.getDefaultVisibly(TYPE_SIDE_BAR_1));
- assertTrue(InsetsState.getDefaultVisibly(TYPE_SIDE_BAR_2));
- assertTrue(InsetsState.getDefaultVisibly(TYPE_SIDE_BAR_3));
- assertFalse(InsetsState.getDefaultVisibly(TYPE_IME));
+ assertTrue(InsetsState.getDefaultVisibility(TYPE_TOP_BAR));
+ assertTrue(InsetsState.getDefaultVisibility(TYPE_SIDE_BAR_1));
+ assertTrue(InsetsState.getDefaultVisibility(TYPE_SIDE_BAR_2));
+ assertTrue(InsetsState.getDefaultVisibility(TYPE_SIDE_BAR_3));
+ assertFalse(InsetsState.getDefaultVisibility(TYPE_IME));
}
}
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index c580c46cc888..0787d8518fa5 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -183,6 +183,14 @@ public class ImageFormat {
public static final int JPEG = 0x100;
/**
+ * Depth augmented compressed JPEG format.
+ *
+ * <p>JPEG compressed main image along with XMP embedded depth metadata
+ * following ISO 16684-1:2011(E).</p>
+ */
+ public static final int DEPTH_JPEG = 0x69656963;
+
+ /**
* <p>Multi-plane Android YUV 420 format</p>
*
* <p>This format is a generic YCbCr format, capable of describing any 4:2:0
@@ -787,6 +795,7 @@ public class ImageFormat {
case PRIVATE:
case RAW_DEPTH:
case Y8:
+ case DEPTH_JPEG:
return true;
}
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index 072fe7321826..2ae28f507e0d 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -16,12 +16,6 @@
package android.security;
-import android.annotation.UnsupportedAppUsage;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Log;
-
import com.android.org.bouncycastle.util.io.pem.PemObject;
import com.android.org.bouncycastle.util.io.pem.PemReader;
import com.android.org.bouncycastle.util.io.pem.PemWriter;
@@ -34,7 +28,6 @@ import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
-import java.security.KeyPair;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
@@ -53,8 +46,6 @@ public class Credentials {
public static final String INSTALL_AS_USER_ACTION = "android.credentials.INSTALL_AS_USER";
- public static final String UNLOCK_ACTION = "com.android.credentials.UNLOCK";
-
/** Key prefix for CA certificates. */
public static final String CA_CERTIFICATE = "CACERT_";
@@ -171,58 +162,6 @@ public class Credentials {
}
}
- private static Credentials singleton;
-
- @UnsupportedAppUsage
- public static Credentials getInstance() {
- if (singleton == null) {
- singleton = new Credentials();
- }
- return singleton;
- }
-
- @UnsupportedAppUsage
- public void unlock(Context context) {
- try {
- Intent intent = new Intent(UNLOCK_ACTION);
- context.startActivity(intent);
- } catch (ActivityNotFoundException e) {
- Log.w(LOGTAG, e.toString());
- }
- }
-
- public void install(Context context) {
- try {
- Intent intent = KeyChain.createInstallIntent();
- context.startActivity(intent);
- } catch (ActivityNotFoundException e) {
- Log.w(LOGTAG, e.toString());
- }
- }
-
- @UnsupportedAppUsage
- public void install(Context context, KeyPair pair) {
- try {
- Intent intent = KeyChain.createInstallIntent();
- intent.putExtra(EXTRA_PRIVATE_KEY, pair.getPrivate().getEncoded());
- intent.putExtra(EXTRA_PUBLIC_KEY, pair.getPublic().getEncoded());
- context.startActivity(intent);
- } catch (ActivityNotFoundException e) {
- Log.w(LOGTAG, e.toString());
- }
- }
-
- @UnsupportedAppUsage
- public void install(Context context, String type, byte[] value) {
- try {
- Intent intent = KeyChain.createInstallIntent();
- intent.putExtra(type, value);
- context.startActivity(intent);
- } catch (ActivityNotFoundException e) {
- Log.w(LOGTAG, e.toString());
- }
- }
-
/**
* Delete all types (private key, user certificate, CA certificate) for a
* particular {@code alias}. All three can exist for any given alias.
diff --git a/media/apex/java/android/media/MediaPlayer2.java b/media/apex/java/android/media/MediaPlayer2.java
index a2feec2e9faf..6a90c176c2df 100644
--- a/media/apex/java/android/media/MediaPlayer2.java
+++ b/media/apex/java/android/media/MediaPlayer2.java
@@ -4482,6 +4482,7 @@ public class MediaPlayer2 implements AutoCloseable
};
// Modular DRM
+ private final Map<UUID, MediaDrm> mDrmObjs = new HashMap<>();
private class DrmHandle {
static final int PROVISION_TIMEOUT_MS = 60000;
@@ -4594,7 +4595,13 @@ public class MediaPlayer2 implements AutoCloseable
Log.v(TAG, "prepareDrm_createDrmStep: UUID: " + uuid);
try {
- mDrmObj = new MediaDrm(uuid);
+ mDrmObj = mDrmObjs.computeIfAbsent(uuid, scheme -> {
+ try {
+ return new MediaDrm(scheme);
+ } catch (UnsupportedSchemeException e) {
+ throw new IllegalArgumentException(e);
+ }
+ });
Log.v(TAG, "prepareDrm_createDrmStep: Created mDrmObj=" + mDrmObj);
} catch (Exception e) { // UnsupportedSchemeException
Log.e(TAG, "prepareDrm_createDrmStep: MediaDrm failed with " + e);
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 9ac147b59ee5..60ef1d93191a 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -17,12 +17,10 @@
package android.media;
import android.graphics.ImageFormat;
-import android.graphics.PixelFormat;
import android.hardware.HardwareBuffer;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.util.Log;
import android.view.Surface;
import dalvik.system.VMRuntime;
@@ -74,12 +72,6 @@ public class ImageReader implements AutoCloseable {
private static final int ACQUIRE_MAX_IMAGES = 2;
/**
- * Invalid consumer buffer usage flag. This usage flag will be ignored
- * by the {@code ImageReader} instance is constructed with this value.
- */
- private static final long BUFFER_USAGE_UNKNOWN = 0;
-
- /**
* <p>
* Create a new reader for images of the desired size and format.
* </p>
@@ -129,7 +121,10 @@ public class ImageReader implements AutoCloseable {
* @see Image
*/
public static ImageReader newInstance(int width, int height, int format, int maxImages) {
- return new ImageReader(width, height, format, maxImages, BUFFER_USAGE_UNKNOWN);
+ // If the format is private don't default to USAGE_CPU_READ_OFTEN since it may not
+ // work, and is inscrutable anyway
+ return new ImageReader(width, height, format, maxImages,
+ format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN);
}
/**
@@ -207,18 +202,23 @@ public class ImageReader implements AutoCloseable {
* obtained by the user, one of them has to be released before a new Image will
* become available for access through {@link #acquireLatestImage()} or
* {@link #acquireNextImage()}. Must be greater than 0.
- * @param usage The intended usage of the images produced by this ImageReader. It needs
- * to be one of the Usage defined by {@link HardwareBuffer}, or an
- * {@link IllegalArgumentException} will be thrown.
+ * @param usage The intended usage of the images produced by this ImageReader. See the usages
+ * on {@link HardwareBuffer} for a list of valid usage bits. See also
+ * {@link HardwareBuffer#isSupported(int, int, int, int, long)} for checking
+ * if a combination is supported. If it's not supported this will throw
+ * an {@link IllegalArgumentException}.
* @see Image
* @see HardwareBuffer
*/
public static ImageReader newInstance(int width, int height, int format, int maxImages,
long usage) {
- if (!isFormatUsageCombinationAllowed(format, usage)) {
- throw new IllegalArgumentException("Format usage combination is not supported:"
- + " format = " + format + ", usage = " + usage);
- }
+ // TODO: Check this - can't do it just yet because format support is different
+ // Unify formats! The only reliable way to validate usage is to just try it and see.
+
+// if (!HardwareBuffer.isSupported(width, height, format, 1, usage)) {
+// throw new IllegalArgumentException("The given format=" + Integer.toHexString(format)
+// + " & usage=" + Long.toHexString(usage) + " is not supported");
+// }
return new ImageReader(width, height, format, maxImages, usage);
}
@@ -716,19 +716,6 @@ public class ImageReader implements AutoCloseable {
return si.getReader() == this;
}
- private static boolean isFormatUsageCombinationAllowed(int format, long usage) {
- if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) {
- return false;
- }
-
- // Valid usage needs to be provided.
- if (usage == BUFFER_USAGE_UNKNOWN) {
- return false;
- }
-
- return true;
- }
-
/**
* Called from Native code when an Event happens.
*
@@ -833,6 +820,7 @@ public class ImageReader implements AutoCloseable {
case ImageFormat.JPEG:
case ImageFormat.DEPTH_POINT_CLOUD:
case ImageFormat.RAW_PRIVATE:
+ case ImageFormat.DEPTH_JPEG:
width = ImageReader.this.getWidth();
break;
default:
@@ -849,6 +837,7 @@ public class ImageReader implements AutoCloseable {
case ImageFormat.JPEG:
case ImageFormat.DEPTH_POINT_CLOUD:
case ImageFormat.RAW_PRIVATE:
+ case ImageFormat.DEPTH_JPEG:
height = ImageReader.this.getHeight();
break;
default:
diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java
index 2a0e04ebf051..b77a884d3412 100644
--- a/media/java/android/media/ImageUtils.java
+++ b/media/java/android/media/ImageUtils.java
@@ -63,6 +63,7 @@ class ImageUtils {
case ImageFormat.DEPTH16:
case ImageFormat.DEPTH_POINT_CLOUD:
case ImageFormat.RAW_DEPTH:
+ case ImageFormat.DEPTH_JPEG:
return 1;
case ImageFormat.PRIVATE:
return 0;
@@ -192,6 +193,7 @@ class ImageUtils {
// 10x compression from RGB_888
case ImageFormat.JPEG:
case ImageFormat.DEPTH_POINT_CLOUD:
+ case ImageFormat.DEPTH_JPEG:
estimatedBytePerPixel = 0.3;
break;
case ImageFormat.Y8:
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 21c58b377e94..09b755912c43 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -20,9 +20,9 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.StringDef;
import android.annotation.SystemApi;
-import android.annotation.SdkConstant.SdkConstantType;
import android.app.Activity;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -1680,6 +1680,7 @@ public final class TvContract {
TYPE_ISDB_T,
TYPE_ISDB_TB,
TYPE_ISDB_S,
+ TYPE_ISDB_S3,
TYPE_ISDB_C,
TYPE_1SEG,
TYPE_DTMB,
@@ -1821,6 +1822,13 @@ public final class TvContract {
public static final String TYPE_ISDB_S = "TYPE_ISDB_S";
/**
+ * The channel type for ISDB-S3 (satellite).
+ *
+ * @see #COLUMN_TYPE
+ */
+ public static final String TYPE_ISDB_S3 = "TYPE_ISDB_S3";
+
+ /**
* The channel type for ISDB-C (cable).
*
* @see #COLUMN_TYPE
@@ -2026,6 +2034,7 @@ public final class TvContract {
* {@link #TYPE_DVB_T2},
* {@link #TYPE_ISDB_C},
* {@link #TYPE_ISDB_S},
+ * {@link #TYPE_ISDB_S3},
* {@link #TYPE_ISDB_T},
* {@link #TYPE_ISDB_TB},
* {@link #TYPE_NTSC},
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 852d2962ee66..0fb4d2a5e446 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -94,26 +94,14 @@ cc_library_shared {
],
shared_libs: [
- // MediaCas
- "android.hardware.cas@1.0",
- "android.hardware.cas.native@1.0",
- "android.hidl.allocator@1.0",
- "libhidlbase",
- "libhidlmemory",
-
- "libmediametrics",
- "libbinder", // Used by JWakeLock and MediaMetrics.
-
- "libutils", // Have to use shared lib to make libandroid_runtime behave correctly.
- // Otherwise, AndroidRuntime::getJNIEnv() will return NULL.
-
- // NDK or NDK-compliant
+ // NDK or LLNDK or NDK-compliant
"libandroid",
"libbinder_ndk",
"libmediandk",
+ "libmediametrics",
"libnativehelper_compat_libc++",
"liblog",
- "libz",
+ "libvndksupport",
],
header_libs: [
@@ -122,6 +110,16 @@ cc_library_shared {
],
static_libs: [
+ // MediaCas
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "libhidlbase",
+ "libhidlmemory",
+ "libhidltransport",
+ "libhwbinder",
+ "libbinderthreadstate",
+
+ // MediaPlayer2 implementation
"libbase",
"libcrypto",
"libcutils",
@@ -131,10 +129,11 @@ cc_library_shared {
"libmediaplayer2-protos",
"libmediandk_utils",
"libmediautils",
+ "libprocessgroup",
"libprotobuf-cpp-lite",
"libstagefright",
"libstagefright_esds",
- "libstagefright_foundation",
+ "libstagefright_foundation_without_imemory",
"libstagefright_httplive",
"libstagefright_id3",
"libstagefright_mpeg2support",
@@ -142,6 +141,7 @@ cc_library_shared {
"libstagefright_player2",
"libstagefright_rtsp_player2",
"libstagefright_timedtext2",
+ "libutils",
"libmedia2_jni_core",
],
@@ -161,6 +161,7 @@ cc_library_shared {
"-Wno-error=deprecated-declarations",
"-Wunused",
"-Wunreachable-code",
+ "-fvisibility=hidden",
],
ldflags: ["-Wl,--exclude-libs=ALL,-error-limit=0"],
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 417a427b6c7c..7168b2dadf92 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -379,24 +379,9 @@ static void ImageReader_init(JNIEnv* env, jobject thiz, jobject weakThiz, jint w
String8 consumerName = String8::format("ImageReader-%dx%df%xm%d-%d-%d",
width, height, format, maxImages, getpid(),
createProcessUniqueId());
- uint32_t consumerUsage = GRALLOC_USAGE_SW_READ_OFTEN;
- bool needUsageOverride = ndkUsage != CONSUMER_BUFFER_USAGE_UNKNOWN;
- uint64_t outProducerUsage = 0;
- uint64_t outConsumerUsage = 0;
- android_hardware_HardwareBuffer_convertToGrallocUsageBits(&outProducerUsage, &outConsumerUsage,
- ndkUsage, 0);
-
- if (isFormatOpaque(nativeFormat)) {
- // Use the SW_READ_NEVER usage to tell producer that this format is not for preview or video
- // encoding. The only possibility will be ZSL output.
- consumerUsage = GRALLOC_USAGE_SW_READ_NEVER;
- if (needUsageOverride) {
- consumerUsage = android_convertGralloc1To0Usage(0, outConsumerUsage);
- }
- } else if (needUsageOverride) {
- ALOGW("Consumer usage override for non-opaque format is not implemented yet, "
- "ignore the provided usage from the application");
- }
+ uint64_t consumerUsage =
+ android_hardware_HardwareBuffer_convertToGrallocUsageBits(ndkUsage);
+
bufferConsumer = new BufferItemConsumer(gbConsumer, consumerUsage, maxImages,
/*controlledByApp*/true);
if (bufferConsumer == nullptr) {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
index 5ab50925a268..0340cec7432d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
@@ -857,7 +857,7 @@ public class CameraTestUtils extends Assert {
// JPEG doesn't have pixelstride and rowstride, treat it as 1D buffer.
// Same goes for DEPTH_POINT_CLOUD
if (format == ImageFormat.JPEG || format == ImageFormat.DEPTH_POINT_CLOUD ||
- format == ImageFormat.RAW_PRIVATE) {
+ format == ImageFormat.DEPTH_JPEG || format == ImageFormat.RAW_PRIVATE) {
buffer = planes[0].getBuffer();
assertNotNull("Fail to get jpeg or depth ByteBuffer", buffer);
data = new byte[buffer.remaining()];
@@ -940,6 +940,7 @@ public class CameraTestUtils extends Assert {
case ImageFormat.RAW_PRIVATE:
case ImageFormat.DEPTH16:
case ImageFormat.DEPTH_POINT_CLOUD:
+ case ImageFormat.DEPTH_JPEG:
assertEquals("JPEG/RAW/depth Images should have one plane", 1, planes.length);
break;
default:
@@ -1363,6 +1364,9 @@ public class CameraTestUtils extends Assert {
case ImageFormat.RAW_PRIVATE:
validateRawPrivateData(data, width, height, image.getTimestamp(), filePath);
break;
+ case ImageFormat.DEPTH_JPEG:
+ validateDepthJpegData(data, width, height, format, image.getTimestamp(), filePath);
+ break;
default:
throw new UnsupportedOperationException("Unsupported format for validation: "
+ format);
@@ -1528,6 +1532,23 @@ public class CameraTestUtils extends Assert {
}
+ private static void validateDepthJpegData(byte[] depthData, int width, int height, int format,
+ long ts, String filePath) {
+
+ if (VERBOSE) Log.v(TAG, "Validating depth jpeg data");
+
+ // Can't validate size since it is variable
+
+ if (DEBUG && filePath != null) {
+ String fileName =
+ filePath + "/" + width + "x" + height + "_" + ts / 1e6 + ".jpg";
+ dumpFile(fileName, depthData);
+ }
+
+ return;
+
+ }
+
public static <T> T getValueNotNull(CaptureResult result, CaptureResult.Key<T> key) {
if (result == null) {
throw new IllegalArgumentException("Result must not be null");
diff --git a/packages/SystemUI/res/drawable/smart_reply_button_background.xml b/packages/SystemUI/res/drawable/smart_reply_button_background.xml
index 31119a90a788..5652b49818fc 100644
--- a/packages/SystemUI/res/drawable/smart_reply_button_background.xml
+++ b/packages/SystemUI/res/drawable/smart_reply_button_background.xml
@@ -25,7 +25,7 @@
android:insetRight="0dp"
android:insetBottom="8dp">
<shape android:shape="rectangle">
- <corners android:radius="8dp" />
+ <corners android:radius="@dimen/smart_reply_button_corner_radius" />
<stroke android:width="@dimen/smart_reply_button_stroke_width"
android:color="@color/smart_reply_button_stroke" />
<solid android:color="@color/smart_reply_button_background"/>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a14259eca45e..d435c67364ba 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -888,8 +888,10 @@
<dimen name="smart_reply_button_stroke_width">1dp</dimen>
<dimen name="smart_reply_button_font_size">14sp</dimen>
<dimen name="smart_reply_button_line_spacing_extra">6sp</dimen> <!-- Total line height 20sp. -->
- <dimen name="smart_action_button_icon_size">24dp</dimen>
- <dimen name="smart_action_button_icon_padding">10dp</dimen>
+ <!-- Corner radius = half of min_height to create rounded sides. -->
+ <dimen name="smart_reply_button_corner_radius">24dp</dimen>
+ <dimen name="smart_action_button_icon_size">18dp</dimen>
+ <dimen name="smart_action_button_icon_padding">8dp</dimen>
<!-- A reasonable upper bound for the height of the smart reply button. The measuring code
needs to start with a guess for the maximum size. Currently two-line smart reply buttons
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 190bd7ae91a2..ffa5de8de2b7 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2305,8 +2305,8 @@
<item quantity="other"><xliff:g id="num_apps" example="3">%1$d</xliff:g> applications are using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</item>
</plurals>
- <!-- Action on Ongoing Privacy Dialog to dismiss [CHAR LIMIT=10]-->
- <string name="ongoing_privacy_dialog_cancel">Cancel</string>
+ <!-- Action for accepting the Ongoing privacy dialog [CHAR LIMIT=10]-->
+ <string name="ongoing_privacy_dialog_ok">Got it</string>
<!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=20]-->
<string name="ongoing_privacy_dialog_open_settings">View details</string>
@@ -2337,6 +2337,7 @@
<item quantity="one"><xliff:g id="num_apps" example="1">%d</xliff:g> other app</item>
<item quantity="other"><xliff:g id="num_apps" example="3">%d</xliff:g> other apps</item>
</plurals>
+
<!-- Text for the quick setting tile for sensor privacy [CHAR LIMIT=30] -->
<string name="sensor_privacy_mode">Sensors off</string>
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index f14495bb959d..4527f73ab118 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -40,6 +40,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricSourceType;
+import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Bundle;
@@ -750,7 +751,14 @@ public class KeyguardViewMediator extends SystemUI {
mDeviceInteractive = mPM.isInteractive();
- mLockSounds = new SoundPool(1, AudioManager.STREAM_SYSTEM, 0);
+ mLockSounds = new SoundPool.Builder()
+ .setMaxStreams(1)
+ .setAudioAttributes(
+ new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ .build())
+ .build();
String soundPath = Settings.Global.getString(cr, Settings.Global.LOCK_SOUND);
if (soundPath != null) {
mLockSoundId = mLockSounds.load(soundPath, 1);
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
index 26c6d501f2cb..db5c244d95b2 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
@@ -20,6 +20,7 @@ import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.content.res.ColorStateList
+import android.os.UserHandle
import android.util.IconDrawableFactory
import android.view.Gravity
import android.view.LayoutInflater
@@ -48,6 +49,7 @@ class OngoingPrivacyDialog constructor(
R.dimen.ongoing_appops_dialog_icon_margin)
private val MAX_ITEMS = context.resources.getInteger(R.integer.ongoing_appops_dialog_max_apps)
private val iconFactory = IconDrawableFactory.newInstance(context, true)
+ private var dismissDialog: (() -> Unit)? = null
init {
val a = context.theme.obtainStyledAttributes(
@@ -58,8 +60,8 @@ class OngoingPrivacyDialog constructor(
fun createDialog(): Dialog {
val builder = AlertDialog.Builder(context).apply {
- setNegativeButton(R.string.ongoing_privacy_dialog_cancel, null)
- setPositiveButton(R.string.ongoing_privacy_dialog_open_settings,
+ setPositiveButton(R.string.ongoing_privacy_dialog_ok, null)
+ setNeutralButton(R.string.ongoing_privacy_dialog_open_settings,
object : DialogInterface.OnClickListener {
val intent = Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE).putExtra(
Intent.EXTRA_DURATION_MILLIS, TimeUnit.MINUTES.toMillis(1))
@@ -72,7 +74,9 @@ class OngoingPrivacyDialog constructor(
})
}
builder.setView(getContentView())
- return builder.create()
+ val dialog = builder.create()
+ dismissDialog = dialog::dismiss
+ return dialog
}
fun getContentView(): View {
@@ -116,6 +120,7 @@ class OngoingPrivacyDialog constructor(
return contentView
}
+ @Suppress("DEPRECATION")
private fun addAppItem(
itemList: LinearLayout,
app: PrivacyApplication,
@@ -152,6 +157,16 @@ class OngoingPrivacyDialog constructor(
} else {
icons.visibility = View.GONE
}
+ item.setOnClickListener(object : View.OnClickListener {
+ val intent = Intent(Intent.ACTION_REVIEW_APP_PERMISSION_USAGE)
+ .putExtra(Intent.EXTRA_PACKAGE_NAME, app.packageName)
+ .putExtra(Intent.EXTRA_USER, UserHandle.getUserHandleForUid(app.uid))
+ override fun onClick(v: View?) {
+ Dependency.get(ActivityStarter::class.java)
+ .postStartActivityDismissingKeyguard(intent, 0)
+ dismissDialog?.invoke()
+ }
+ })
itemList.addView(item)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
index 9252167d02bd..dbe87d1d53c6 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
@@ -37,7 +37,7 @@ data class PrivacyItem(
val application: PrivacyApplication
)
-data class PrivacyApplication(val packageName: String, val context: Context)
+data class PrivacyApplication(val packageName: String, val uid: Int, val context: Context)
: Comparable<PrivacyApplication> {
override fun compareTo(other: PrivacyApplication): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index 2339fae2d1ee..f1c3bf299eea 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -59,7 +59,8 @@ class PrivacyItemController @Inject constructor(val context: Context) {
@Suppress("DEPRECATION")
private val uiHandler = Dependency.get(Dependency.MAIN_HANDLER)
private var listening = false
- val systemApp = PrivacyApplication(context.getString(R.string.device_services), context)
+ val systemApp =
+ PrivacyApplication(context.getString(R.string.device_services), SYSTEM_UID, context)
private val callbacks = mutableListOf<WeakReference<Callback>>()
private val notifyChanges = Runnable {
@@ -162,7 +163,7 @@ class PrivacyItemController @Inject constructor(val context: Context) {
else -> return null
}
if (appOpItem.uid == SYSTEM_UID) return PrivacyItem(type, systemApp)
- val app = PrivacyApplication(appOpItem.packageName, context)
+ val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid, context)
return PrivacyItem(type, app)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 35b7ef42177d..5e5241910e7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -39,7 +39,6 @@ import com.android.systemui.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -221,6 +220,11 @@ public class NotificationLogger implements StateListener {
}
@Override
+ public void onEntryReinflated(NotificationEntry entry) {
+ mExpansionStateLogger.onEntryReinflated(entry.key);
+ }
+
+ @Override
public void onInflationError(
StatusBarNotification notification,
Exception exception) {
@@ -468,6 +472,13 @@ public class NotificationLogger implements StateListener {
mLoggedExpansionState.remove(key);
}
+ @VisibleForTesting
+ void onEntryReinflated(String key) {
+ // When the notification is updated, we should consider the notification as not
+ // yet logged.
+ mLoggedExpansionState.remove(key);
+ }
+
private State getState(String key) {
State state = mExpansionStates.get(key);
if (state == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index c10837165c0f..711b08e45117 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -448,6 +448,11 @@ public class NotificationPanelView extends PanelView implements
false);
addView(mKeyguardStatusView, index);
+ // Re-associate the clock container with the keyguard clock switch.
+ mBigClockContainer.removeAllViews();
+ KeyguardClockSwitch keyguardClockSwitch = findViewById(R.id.keyguard_clock_container);
+ keyguardClockSwitch.setBigClockContainer(mBigClockContainer);
+
// Update keyguard bottom area
index = indexOfChild(mKeyguardBottomArea);
removeView(mKeyguardBottomArea);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index c4f027f2abea..07c6587c56e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -479,13 +479,20 @@ public class SmartReplyView extends ViewGroup {
remeasureButtonsIfNecessary(accumulatedMeasures.mButtonPaddingHorizontal,
accumulatedMeasures.mMaxChildHeight);
+ int buttonHeight = Math.max(getSuggestedMinimumHeight(), mPaddingTop
+ + accumulatedMeasures.mMaxChildHeight + mPaddingBottom);
+
+ // Set the corner radius to half the button height to make the side of the buttons look like
+ // a semicircle.
+ for (View smartSuggestionButton : smartSuggestions) {
+ setCornerRadius((Button) smartSuggestionButton, ((float) buttonHeight) / 2);
+ }
+
setMeasuredDimension(
resolveSize(Math.max(getSuggestedMinimumWidth(),
accumulatedMeasures.mMeasuredWidth),
widthMeasureSpec),
- resolveSize(Math.max(getSuggestedMinimumHeight(), mPaddingTop
- + accumulatedMeasures.mMaxChildHeight + mPaddingBottom),
- heightMeasureSpec));
+ resolveSize(buttonHeight, heightMeasureSpec));
}
/**
@@ -806,6 +813,23 @@ public class SmartReplyView extends ViewGroup {
button.setTextColor(textColor);
}
+ private void setCornerRadius(Button button, float radius) {
+ Drawable drawable = button.getBackground();
+ if (drawable instanceof RippleDrawable) {
+ // Mutate in case other notifications are using this drawable.
+ drawable = drawable.mutate();
+ RippleDrawable ripple = (RippleDrawable) drawable;
+ Drawable inset = ripple.getDrawable(0);
+ if (inset instanceof InsetDrawable) {
+ Drawable background = ((InsetDrawable) inset).getDrawable();
+ if (background instanceof GradientDrawable) {
+ GradientDrawable gradientDrawable = (GradientDrawable) background;
+ gradientDrawable.setCornerRadius(radius);
+ }
+ }
+ }
+ }
+
private ActivityStarter getActivityStarter() {
if (mActivityStarter == null) {
mActivityStarter = Dependency.get(ActivityStarter.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt
index d3b3dae2c1cd..f163b88357cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt
@@ -27,16 +27,20 @@ import org.junit.runner.RunWith
@SmallTest
class PrivacyDialogBuilderTest : SysuiTestCase() {
+ companion object {
+ val TEST_UID = 1
+ }
+
@Test
fun testGenerateAppsList() {
val bar2 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
- "Bar", context))
+ "Bar", TEST_UID, context))
val bar3 = PrivacyItem(Privacy.TYPE_LOCATION, PrivacyApplication(
- "Bar", context))
+ "Bar", TEST_UID, context))
val foo0 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
- "Foo", context))
+ "Foo", TEST_UID, context))
val baz1 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
- "Baz", context))
+ "Baz", TEST_UID, context))
val items = listOf(bar2, foo0, baz1, bar3)
@@ -55,11 +59,14 @@ class PrivacyDialogBuilderTest : SysuiTestCase() {
@Test
fun testOrder() {
// We want location to always go last, so it will go in the "+ other apps"
- val appCamera = PrivacyItem(PrivacyType.TYPE_CAMERA, PrivacyApplication("Camera", context))
+ val appCamera = PrivacyItem(PrivacyType.TYPE_CAMERA,
+ PrivacyApplication("Camera", TEST_UID, context))
val appMicrophone =
- PrivacyItem(PrivacyType.TYPE_MICROPHONE, PrivacyApplication("Microphone", context))
+ PrivacyItem(PrivacyType.TYPE_MICROPHONE,
+ PrivacyApplication("Microphone", TEST_UID, context))
val appLocation =
- PrivacyItem(PrivacyType.TYPE_LOCATION, PrivacyApplication("Location", context))
+ PrivacyItem(PrivacyType.TYPE_LOCATION,
+ PrivacyApplication("Location", TEST_UID, context))
val items = listOf(appLocation, appMicrophone, appCamera)
val textBuilder = PrivacyDialogBuilder(context, items)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java
index 5ff9d1490f3a..f1d900332607 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java
@@ -15,10 +15,10 @@
*/
package com.android.systemui.statusbar.notification.logging;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.os.RemoteException;
@@ -31,6 +31,7 @@ import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.UiOffloadThread;
+import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import org.junit.Before;
import org.junit.Test;
@@ -155,6 +156,27 @@ public class ExpansionStateLoggerTest extends SysuiTestCase {
NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN.toMetricsEventEnum());
}
+ @Test
+ public void testOnEntryReinflated() throws RemoteException {
+ mLogger.onExpansionChanged(NOTIFICATION_KEY, true, true,
+ NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN);
+ mLogger.onVisibilityChanged(
+ Collections.singletonList(createNotificationVisibility(NOTIFICATION_KEY, true)),
+ Collections.emptyList());
+ waitForUiOffloadThread();
+ verify(mBarService).onNotificationExpansionChanged(
+ NOTIFICATION_KEY, true, true, ExpandableViewState.LOCATION_UNKNOWN);
+
+ mLogger.onEntryReinflated(NOTIFICATION_KEY);
+ mLogger.onVisibilityChanged(
+ Collections.singletonList(createNotificationVisibility(NOTIFICATION_KEY, true)),
+ Collections.emptyList());
+ waitForUiOffloadThread();
+ // onNotificationExpansionChanged is called the second time.
+ verify(mBarService, times(2)).onNotificationExpansionChanged(
+ NOTIFICATION_KEY, true, true, ExpandableViewState.LOCATION_UNKNOWN);
+ }
+
private NotificationVisibility createNotificationVisibility(String key, boolean visibility) {
return createNotificationVisibility(key, visibility,
NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN);
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 844096d9d717..6108aaa860d5 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -177,12 +177,13 @@ public final class ContentCaptureManagerService extends
final class ContentCaptureManagerServiceStub extends IContentCaptureManager.Stub {
@Override
- public void startSession(@UserIdInt int userId, @NonNull IBinder activityToken,
+ public void startSession(@NonNull IBinder activityToken,
@NonNull ComponentName componentName, @NonNull String sessionId, int flags,
@NonNull IResultReceiver result) {
Preconditions.checkNotNull(activityToken);
Preconditions.checkNotNull(componentName);
Preconditions.checkNotNull(sessionId);
+ final int userId = UserHandle.getCallingUserId();
// TODO(b/111276913): refactor getTaskIdForActivity() to also return ComponentName,
// so we don't pass it on startSession (same for Autofill)
@@ -199,8 +200,9 @@ public final class ContentCaptureManagerService extends
}
@Override
- public void finishSession(@UserIdInt int userId, @NonNull String sessionId) {
+ public void finishSession(@NonNull String sessionId) {
Preconditions.checkNotNull(sessionId);
+ final int userId = UserHandle.getCallingUserId();
synchronized (mLock) {
final ContentCapturePerUserService service = getServiceForUserLocked(userId);
@@ -209,15 +211,16 @@ public final class ContentCaptureManagerService extends
}
@Override
- public void getReceiverServiceComponentName(@UserIdInt int userId,
- IResultReceiver receiver) {
+ public void getServiceComponentName(@NonNull IResultReceiver result) {
+ final int userId = UserHandle.getCallingUserId();
ComponentName connectedServiceComponentName;
synchronized (mLock) {
final ContentCapturePerUserService service = getServiceForUserLocked(userId);
connectedServiceComponentName = service.getServiceComponentName();
}
try {
- receiver.send(0, SyncResultReceiver.bundleFor(connectedServiceComponentName));
+ result.send(/* resultCode= */ 0,
+ SyncResultReceiver.bundleFor(connectedServiceComponentName));
} catch (RemoteException e) {
// Ignore exception as we need to be resilient against app behavior.
Slog.w(TAG, "Unable to send service component name: " + e);
@@ -225,8 +228,9 @@ public final class ContentCaptureManagerService extends
}
@Override
- public void removeUserData(@UserIdInt int userId, @NonNull UserDataRemovalRequest request) {
+ public void removeUserData(@NonNull UserDataRemovalRequest request) {
Preconditions.checkNotNull(request);
+ final int userId = UserHandle.getCallingUserId();
synchronized (mLock) {
final ContentCapturePerUserService service = getServiceForUserLocked(userId);
service.removeUserDataLocked(request);
diff --git a/services/core/java/com/android/server/AlarmManagerInternal.java b/services/core/java/com/android/server/AlarmManagerInternal.java
index dbff957a0da7..275661084aa3 100644
--- a/services/core/java/com/android/server/AlarmManagerInternal.java
+++ b/services/core/java/com/android/server/AlarmManagerInternal.java
@@ -17,5 +17,15 @@
package com.android.server;
public interface AlarmManagerInternal {
- void removeAlarmsForUid(int uid);
+ // Some other components in the system server need to know about
+ // broadcast alarms currently in flight
+ public interface InFlightListener {
+ /** There is now an alarm pending delivery to the given app */
+ void broadcastAlarmPending(int recipientUid);
+ /** A broadcast alarm targeted to the given app has completed delivery */
+ void broadcastAlarmComplete(int recipientUid);
+ }
+
+ public void removeAlarmsForUid(int uid);
+ public void registerInFlightListener(InFlightListener callback);
}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index e3dcb7d331cf..10b532700d1b 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -197,6 +197,8 @@ class AlarmManagerService extends SystemService {
PowerManager.WakeLock mWakeLock;
ArrayList<Alarm> mPendingNonWakeupAlarms = new ArrayList<>();
ArrayList<InFlight> mInFlight = new ArrayList<>();
+ private final ArrayList<AlarmManagerInternal.InFlightListener> mInFlightListeners =
+ new ArrayList<>();
AlarmHandler mHandler;
AppWakeupHistory mAppWakeupHistory;
ClockReceiver mClockReceiver;
@@ -1315,6 +1317,10 @@ class AlarmManagerService extends SystemService {
mAlarmType = alarm.type;
}
+ boolean isBroadcast() {
+ return mPendingIntent != null && mPendingIntent.isBroadcast();
+ }
+
@Override
public String toString() {
return "InFlight{"
@@ -1354,6 +1360,20 @@ class AlarmManagerService extends SystemService {
}
}
+ private void notifyBroadcastAlarmPendingLocked(int uid) {
+ final int numListeners = mInFlightListeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ mInFlightListeners.get(i).broadcastAlarmPending(uid);
+ }
+ }
+
+ private void notifyBroadcastAlarmCompleteLocked(int uid) {
+ final int numListeners = mInFlightListeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ mInFlightListeners.get(i).broadcastAlarmComplete(uid);
+ }
+ }
+
static final class FilterStats {
final BroadcastStats mBroadcastStats;
final String mTag;
@@ -1976,6 +1996,13 @@ class AlarmManagerService extends SystemService {
removeLocked(uid);
}
}
+
+ @Override
+ public void registerInFlightListener(InFlightListener callback) {
+ synchronized (mLock) {
+ mInFlightListeners.add(callback);
+ }
+ }
}
/**
@@ -4426,7 +4453,11 @@ class AlarmManagerService extends SystemService {
private InFlight removeLocked(PendingIntent pi, Intent intent) {
for (int i = 0; i < mInFlight.size(); i++) {
- if (mInFlight.get(i).mPendingIntent == pi) {
+ final InFlight inflight = mInFlight.get(i);
+ if (inflight.mPendingIntent == pi) {
+ if (pi.isBroadcast()) {
+ notifyBroadcastAlarmCompleteLocked(inflight.mUid);
+ }
return mInFlight.remove(i);
}
}
@@ -4649,6 +4680,9 @@ class AlarmManagerService extends SystemService {
final InFlight inflight = new InFlight(AlarmManagerService.this, alarm, nowELAPSED);
mInFlight.add(inflight);
mBroadcastRefCount++;
+ if (inflight.isBroadcast()) {
+ notifyBroadcastAlarmPendingLocked(alarm.uid);
+ }
if (allowWhileIdle) {
// Record the last time this uid handled an ALLOW_WHILE_IDLE alarm.
mLastAllowWhileIdleDispatch.put(alarm.creatorUid, nowELAPSED);
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index cec825fb9c00..cecd55a325d3 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -802,10 +802,7 @@ class StorageManagerService extends IStorageManager.Stub
// For now, simply clone property when it changes
DeviceConfig.addOnPropertyChangedListener(DeviceConfig.Storage.NAMESPACE,
mContext.getMainExecutor(), (namespace, name, value) -> {
- if (DeviceConfig.Storage.ISOLATED_STORAGE_ENABLED.equals(name)) {
- Settings.Global.putString(mResolver,
- Settings.Global.ISOLATED_STORAGE_REMOTE, value);
- }
+ refreshIsolatedStorageSettings();
});
refreshIsolatedStorageSettings();
}
@@ -840,6 +837,12 @@ class StorageManagerService extends IStorageManager.Stub
}
private void refreshIsolatedStorageSettings() {
+ // Always copy value from newer DeviceConfig location
+ Settings.Global.putString(mResolver,
+ Settings.Global.ISOLATED_STORAGE_REMOTE,
+ DeviceConfig.getProperty(DeviceConfig.Storage.NAMESPACE,
+ DeviceConfig.Storage.ISOLATED_STORAGE_ENABLED));
+
final int local = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ISOLATED_STORAGE_LOCAL, 0);
final int remote = Settings.Global.getInt(mContext.getContentResolver(),
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 0aaea2f62de0..e42666c6a637 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -48,6 +48,7 @@ class ActivityManagerDebugConfig {
static final boolean DEBUG_BROADCAST = DEBUG_ALL || false;
static final boolean DEBUG_BROADCAST_BACKGROUND = DEBUG_BROADCAST || false;
static final boolean DEBUG_BROADCAST_LIGHT = DEBUG_BROADCAST || false;
+ static final boolean DEBUG_BROADCAST_DEFERRAL = DEBUG_BROADCAST || false;
static final boolean DEBUG_LRU = DEBUG_ALL || false;
static final boolean DEBUG_MU = DEBUG_ALL || false;
static final boolean DEBUG_NETWORK = DEBUG_ALL || false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index eb643b670fcd..d9f3c02e4292 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -224,7 +224,6 @@ import android.hardware.display.DisplayManagerInternal;
import android.location.LocationManager;
import android.media.audiofx.AudioEffect;
import android.net.Proxy;
-import android.net.ProxyInfo;
import android.net.Uri;
import android.os.AppZygote;
import android.os.BatteryStats;
@@ -2090,6 +2089,8 @@ public class ActivityManagerService extends IActivityManager.Stub
if (phase == PHASE_SYSTEM_SERVICES_READY) {
mService.mBatteryStatsService.systemServicesReady();
mService.mServices.systemServicesReady();
+ } else if (phase == PHASE_ACTIVITY_MANAGER_READY) {
+ mService.startBroadcastObservers();
}
}
@@ -2266,12 +2267,27 @@ public class ActivityManagerService extends IActivityManager.Stub
mProcessList.init(this, activeUids);
mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids);
+ // Broadcast policy parameters
+ final BroadcastConstants foreConstants = new BroadcastConstants(
+ Settings.Global.BROADCAST_FG_CONSTANTS);
+ foreConstants.TIMEOUT = BROADCAST_FG_TIMEOUT;
+
+ final BroadcastConstants backConstants = new BroadcastConstants(
+ Settings.Global.BROADCAST_BG_CONSTANTS);
+ backConstants.TIMEOUT = BROADCAST_BG_TIMEOUT;
+
+ final BroadcastConstants offloadConstants = new BroadcastConstants(
+ Settings.Global.BROADCAST_OFFLOAD_CONSTANTS);
+ offloadConstants.TIMEOUT = BROADCAST_BG_TIMEOUT;
+ // by default, no "slow" policy in this queue
+ offloadConstants.SLOW_TIME = Integer.MAX_VALUE;
+
mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
- "foreground", BROADCAST_FG_TIMEOUT, false);
+ "foreground", foreConstants, false);
mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
- "background", BROADCAST_BG_TIMEOUT, true);
+ "background", backConstants, true);
mOffloadBroadcastQueue = new BroadcastQueue(this, mHandler,
- "offload", BROADCAST_BG_TIMEOUT, true);
+ "offload", offloadConstants, true);
mBroadcastQueues[0] = mFgBroadcastQueue;
mBroadcastQueues[1] = mBgBroadcastQueue;
mBroadcastQueues[2] = mOffloadBroadcastQueue;
@@ -5226,6 +5242,15 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
+ public boolean isIntentSenderABroadcast(IIntentSender pendingResult) {
+ if (pendingResult instanceof PendingIntentRecord) {
+ final PendingIntentRecord res = (PendingIntentRecord) pendingResult;
+ return res.key.type == ActivityManager.INTENT_SENDER_BROADCAST;
+ }
+ return false;
+ }
+
+ @Override
public Intent getIntentForIntentSender(IIntentSender pendingResult) {
enforceCallingPermission(Manifest.permission.GET_INTENT_SENDER_INTENT,
"getIntentForIntentSender()");
@@ -7211,7 +7236,6 @@ public class ActivityManagerService extends IActivityManager.Stub
synchronized (this) {
mSystemProvidersInstalled = true;
}
-
mConstants.start(mContext.getContentResolver());
mCoreSettingsObserver = new CoreSettingsObserver(this);
mActivityTaskManager.installSystemProviders();
@@ -8711,6 +8735,12 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ private void startBroadcastObservers() {
+ for (BroadcastQueue queue : mBroadcastQueues) {
+ queue.start(mContext.getContentResolver());
+ }
+ }
+
private void updateForceBackgroundCheck(boolean enabled) {
synchronized (this) {
if (mForceBackgroundCheck != enabled) {
@@ -14909,10 +14939,7 @@ public class ActivityManagerService extends IActivityManager.Stub
resultData, resultExtras, ordered, sticky, false, userId,
allowBackgroundActivityStarts);
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r
- + ": prev had " + queue.mOrderedBroadcasts.size());
- if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,
- "Enqueueing broadcast " + r.intent.getAction());
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r);
final BroadcastRecord oldRecord =
replacePending ? queue.replaceOrderedBroadcastLocked(r) : null;
@@ -15788,13 +15815,12 @@ public class ActivityManagerService extends IActivityManager.Stub
* Returns true if things are idle enough to perform GCs.
*/
private final boolean canGcNowLocked() {
- boolean processingBroadcasts = false;
for (BroadcastQueue q : mBroadcastQueues) {
- if (q.mParallelBroadcasts.size() != 0 || q.mOrderedBroadcasts.size() != 0) {
- processingBroadcasts = true;
+ if (!q.mParallelBroadcasts.isEmpty() || !q.mDispatcher.isEmpty()) {
+ return false;
}
}
- return !processingBroadcasts && mAtmInternal.canGcNow();
+ return mAtmInternal.canGcNow();
}
/**
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
new file mode 100644
index 000000000000..820caf12ac84
--- /dev/null
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.provider.Settings;
+import android.util.KeyValueListParser;
+import android.util.Slog;
+import android.util.TimeUtils;
+
+import java.io.PrintWriter;
+
+/**
+ * Tunable parameters for broadcast dispatch policy
+ */
+public class BroadcastConstants {
+ private static final String TAG = "BroadcastConstants";
+
+ // Value element names within the Settings record
+ static final String KEY_TIMEOUT = "bcast_timeout";
+ static final String KEY_SLOW_TIME = "bcast_slow_time";
+ static final String KEY_DEFERRAL = "bcast_deferral";
+ static final String KEY_DEFERRAL_DECAY_FACTOR = "bcast_deferral_decay_factor";
+ static final String KEY_DEFERRAL_FLOOR = "bcast_deferral_floor";
+
+ // All time intervals are in milliseconds
+ private static final long DEFAULT_TIMEOUT = 10_000;
+ private static final long DEFAULT_SLOW_TIME = 5_000;
+ private static final long DEFAULT_DEFERRAL = 5_000;
+ private static final float DEFAULT_DEFERRAL_DECAY_FACTOR = 0.75f;
+ private static final long DEFAULT_DEFERRAL_FLOOR = 0;
+
+ // All time constants are in milliseconds
+
+ // Timeout period for this broadcast queue
+ public long TIMEOUT = DEFAULT_TIMEOUT;
+ // Handling time above which we declare that a broadcast recipient was "slow". Any
+ // value <= zero is interpreted as disabling broadcast deferral policy entirely.
+ public long SLOW_TIME = DEFAULT_SLOW_TIME;
+ // How long to initially defer broadcasts, if an app is slow to handle one
+ public long DEFERRAL = DEFAULT_DEFERRAL;
+ // Decay factor for successive broadcasts' deferral time
+ public float DEFERRAL_DECAY_FACTOR = DEFAULT_DEFERRAL_DECAY_FACTOR;
+ // Minimum that the deferral time can decay to until the backlog fully clears
+ public long DEFERRAL_FLOOR = DEFAULT_DEFERRAL_FLOOR;
+
+ // Settings override tracking for this instance
+ private String mSettingsKey;
+ private SettingsObserver mSettingsObserver;
+ private ContentResolver mResolver;
+ private final KeyValueListParser mParser = new KeyValueListParser(',');
+
+ class SettingsObserver extends ContentObserver {
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ updateConstants();
+ }
+ }
+
+ // A given constants instance is configured to observe specific keys from which
+ // that instance's values are drawn.
+ public BroadcastConstants(String settingsKey) {
+ mSettingsKey = settingsKey;
+ }
+
+ /**
+ * Spin up the observer lazily, since it can only happen once the settings provider
+ * has been brought into service
+ */
+ public void startObserving(Handler handler, ContentResolver resolver) {
+ mResolver = resolver;
+
+ mSettingsObserver = new SettingsObserver(handler);
+ mResolver.registerContentObserver(Settings.Global.getUriFor(mSettingsKey),
+ false, mSettingsObserver);
+
+ updateConstants();
+ }
+
+ private void updateConstants() {
+ synchronized (mParser) {
+ try {
+ mParser.setString(Settings.Global.getString(mResolver, mSettingsKey));
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Bad broadcast settings in key '" + mSettingsKey + "'", e);
+ return;
+ }
+
+ // Unspecified fields retain their current value rather than revert to default
+ TIMEOUT = mParser.getLong(KEY_TIMEOUT, TIMEOUT);
+ SLOW_TIME = mParser.getLong(KEY_SLOW_TIME, SLOW_TIME);
+ DEFERRAL = mParser.getLong(KEY_DEFERRAL, DEFERRAL);
+ DEFERRAL_DECAY_FACTOR = mParser.getFloat(KEY_DEFERRAL_DECAY_FACTOR,
+ DEFERRAL_DECAY_FACTOR);
+ DEFERRAL_FLOOR = mParser.getLong(KEY_DEFERRAL_FLOOR, DEFERRAL_FLOOR);
+ }
+ }
+
+ /**
+ * Standard dumpsys support; invoked from BroadcastQueue dump
+ */
+ public void dump(PrintWriter pw) {
+ synchronized (mParser) {
+ pw.println();
+ pw.print(" Broadcast parameters (key=");
+ pw.print(mSettingsKey);
+ pw.print(", observing=");
+ pw.print(mSettingsObserver != null);
+ pw.println("):");
+
+ pw.print(" "); pw.print(KEY_TIMEOUT); pw.print(" = ");
+ TimeUtils.formatDuration(TIMEOUT, pw);
+ pw.println();
+
+ pw.print(" "); pw.print(KEY_SLOW_TIME); pw.print(" = ");
+ TimeUtils.formatDuration(SLOW_TIME, pw);
+ pw.println();
+
+ pw.print(" "); pw.print(KEY_DEFERRAL); pw.print(" = ");
+ TimeUtils.formatDuration(DEFERRAL, pw);
+ pw.println();
+
+ pw.print(" "); pw.print(KEY_DEFERRAL_DECAY_FACTOR); pw.print(" = ");
+ pw.println(DEFERRAL_DECAY_FACTOR);
+
+ pw.print(" "); pw.print(KEY_DEFERRAL_FLOOR); pw.print(" = ");
+ TimeUtils.formatDuration(DEFERRAL_FLOOR, pw);
+ pw.println();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/BroadcastDispatcher.java b/services/core/java/com/android/server/am/BroadcastDispatcher.java
new file mode 100644
index 000000000000..0d46379112aa
--- /dev/null
+++ b/services/core/java/com/android/server/am/BroadcastDispatcher.java
@@ -0,0 +1,687 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_DEFERRAL;
+
+import android.content.Intent;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.SparseIntArray;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.AlarmManagerInternal;
+import com.android.server.LocalServices;
+
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * Manages ordered broadcast delivery, applying policy to mitigate the effects of
+ * slow receivers.
+ */
+public class BroadcastDispatcher {
+ private static final String TAG = "BroadcastDispatcher";
+
+ // Deferred broadcasts to one app; times are all uptime time base like
+ // other broadcast-related timekeeping
+ static class Deferrals {
+ final int uid;
+ long deferredAt; // when we started deferring
+ long deferredBy; // how long did we defer by last time?
+ long deferUntil; // when does the next element become deliverable?
+ int alarmCount;
+
+ final ArrayList<BroadcastRecord> broadcasts;
+
+ Deferrals(int uid, long now, long backoff, int count) {
+ this.uid = uid;
+ this.deferredAt = now;
+ this.deferredBy = backoff;
+ this.deferUntil = now + backoff;
+ this.alarmCount = count;
+ broadcasts = new ArrayList<>();
+ }
+
+ void add(BroadcastRecord br) {
+ broadcasts.add(br);
+ }
+
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ for (BroadcastRecord br : broadcasts) {
+ br.writeToProto(proto, fieldId);
+ }
+ }
+
+ void dumpLocked(Dumper d) {
+ for (BroadcastRecord br : broadcasts) {
+ d.dump(br);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("Deferrals{uid=");
+ sb.append(uid);
+ sb.append(", deferUntil=");
+ sb.append(deferUntil);
+ sb.append(", #broadcasts=");
+ sb.append(broadcasts.size());
+ sb.append("}");
+ return sb.toString();
+ }
+ }
+
+ // Carrying dump formatting state across multiple concatenated datasets
+ class Dumper {
+ final PrintWriter mPw;
+ final String mQueueName;
+ final String mDumpPackage;
+ final SimpleDateFormat mSdf;
+ boolean mPrinted;
+ boolean mNeedSep;
+ String mHeading;
+ String mLabel;
+ int mOrdinal;
+
+ Dumper(PrintWriter pw, String queueName, String dumpPackage, SimpleDateFormat sdf) {
+ mPw = pw;
+ mQueueName = queueName;
+ mDumpPackage = dumpPackage;
+ mSdf = sdf;
+
+ mPrinted = false;
+ mNeedSep = true;
+ }
+
+ void setHeading(String heading) {
+ mHeading = heading;
+ mPrinted = false;
+ }
+
+ void setLabel(String label) {
+ //" Active Ordered Broadcast " + mQueueName + " #" + i + ":"
+ mLabel = " " + label + " " + mQueueName + " #";
+ mOrdinal = 0;
+ }
+
+ boolean didPrint() {
+ return mPrinted;
+ }
+
+ void dump(BroadcastRecord br) {
+ if (mDumpPackage == null || mDumpPackage.equals(br.callerPackage)) {
+ if (!mPrinted) {
+ if (mNeedSep) {
+ mPw.println();
+ }
+ mPrinted = true;
+ mNeedSep = true;
+ mPw.println(" " + mHeading + " [" + mQueueName + "]:");
+ }
+ mPw.println(mLabel + mOrdinal + ":");
+ mOrdinal++;
+
+ br.dump(mPw, " ", mSdf);
+ }
+ }
+ }
+
+ private final Object mLock;
+ private final BroadcastQueue mQueue;
+ private final BroadcastConstants mConstants;
+ private final Handler mHandler;
+ private AlarmManagerInternal mAlarm;
+
+ // Current alarm targets; mapping uid -> in-flight alarm count
+ final SparseIntArray mAlarmUids = new SparseIntArray();
+ final AlarmManagerInternal.InFlightListener mAlarmListener =
+ new AlarmManagerInternal.InFlightListener() {
+ @Override
+ public void broadcastAlarmPending(final int recipientUid) {
+ synchronized (mLock) {
+ final int newCount = mAlarmUids.get(recipientUid, 0) + 1;
+ mAlarmUids.put(recipientUid, newCount);
+ // any deferred broadcasts to this app now get fast-tracked
+ final int numEntries = mDeferredBroadcasts.size();
+ for (int i = 0; i < numEntries; i++) {
+ if (recipientUid == mDeferredBroadcasts.get(i).uid) {
+ Deferrals d = mDeferredBroadcasts.remove(i);
+ mAlarmBroadcasts.add(d);
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void broadcastAlarmComplete(final int recipientUid) {
+ synchronized (mLock) {
+ final int newCount = mAlarmUids.get(recipientUid, 0) - 1;
+ if (newCount >= 0) {
+ mAlarmUids.put(recipientUid, newCount);
+ } else {
+ Slog.wtf(TAG, "Undercount of broadcast alarms in flight for " + recipientUid);
+ mAlarmUids.put(recipientUid, 0);
+ }
+
+ // No longer an alarm target, so resume ordinary deferral policy
+ if (newCount <= 0) {
+ final int numEntries = mAlarmBroadcasts.size();
+ for (int i = 0; i < numEntries; i++) {
+ if (recipientUid == mAlarmBroadcasts.get(i).uid) {
+ Deferrals d = mAlarmBroadcasts.remove(i);
+ insertLocked(mDeferredBroadcasts, d);
+ break;
+ }
+ }
+ }
+ }
+ }
+ };
+
+ // Queue recheck operation used to tickle broadcast delivery when appropriate
+ final Runnable mScheduleRunnable = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mLock) {
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.v(TAG, "Deferral recheck of pending broadcasts");
+ }
+ mQueue.scheduleBroadcastsLocked();
+ mRecheckScheduled = false;
+ }
+ }
+ };
+ private boolean mRecheckScheduled = false;
+
+ // Usual issuance-order outbound queue
+ private final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<>();
+ // General deferrals not holding up alarms
+ private final ArrayList<Deferrals> mDeferredBroadcasts = new ArrayList<>();
+ // Deferrals that *are* holding up alarms; ordered by alarm dispatch time
+ private final ArrayList<Deferrals> mAlarmBroadcasts = new ArrayList<>();
+
+ // Next outbound broadcast, established by getNextBroadcastLocked()
+ private BroadcastRecord mCurrentBroadcast;
+
+ /**
+ * Constructed & sharing a lock with its associated BroadcastQueue instance
+ */
+ public BroadcastDispatcher(BroadcastQueue queue, BroadcastConstants constants,
+ Handler handler, Object lock) {
+ mQueue = queue;
+ mConstants = constants;
+ mHandler = handler;
+ mLock = lock;
+ }
+
+ /**
+ * Spin up the integration with the alarm manager service; done lazily to manage
+ * service availability ordering during boot.
+ */
+ public void start() {
+ // Set up broadcast alarm tracking
+ mAlarm = LocalServices.getService(AlarmManagerInternal.class);
+ mAlarm.registerInFlightListener(mAlarmListener);
+ }
+
+ /**
+ * Standard contents-are-empty check
+ */
+ public boolean isEmpty() {
+ synchronized (mLock) {
+ return mCurrentBroadcast == null
+ && mOrderedBroadcasts.isEmpty()
+ && mDeferredBroadcasts.isEmpty()
+ && mAlarmBroadcasts.isEmpty();
+ }
+ }
+
+ /**
+ * Not quite the traditional size() measurement; includes any in-process but
+ * not yet retired active outbound broadcast.
+ */
+ public int totalUndelivered() {
+ synchronized (mLock) {
+ return mAlarmBroadcasts.size()
+ + mDeferredBroadcasts.size()
+ + mOrderedBroadcasts.size()
+ + (mCurrentBroadcast == null ? 0 : 1);
+ }
+ }
+
+ // ----------------------------------
+ // BroadcastQueue operation support
+
+ void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
+ mOrderedBroadcasts.add(r);
+ }
+
+ // Returns the now-replaced broadcast record, or null if none
+ BroadcastRecord replaceBroadcastLocked(BroadcastRecord r, String typeForLogging) {
+ // Simple case, in the ordinary queue.
+ BroadcastRecord old = replaceBroadcastLocked(mOrderedBroadcasts, r, typeForLogging);
+
+ // If we didn't find it, less-simple: in a deferral queue?
+ if (old == null) {
+ old = replaceDeferredBroadcastLocked(mAlarmBroadcasts, r, typeForLogging);
+ }
+ if (old == null) {
+ old = replaceDeferredBroadcastLocked(mDeferredBroadcasts, r, typeForLogging);
+ }
+ return old;
+ }
+
+ private BroadcastRecord replaceDeferredBroadcastLocked(ArrayList<Deferrals> list,
+ BroadcastRecord r, String typeForLogging) {
+ BroadcastRecord old;
+ final int numEntries = list.size();
+ for (int i = 0; i < numEntries; i++) {
+ final Deferrals d = list.get(i);
+ old = replaceBroadcastLocked(d.broadcasts, r, typeForLogging);
+ if (old != null) {
+ return old;
+ }
+ }
+ return null;
+ }
+
+ private BroadcastRecord replaceBroadcastLocked(ArrayList<BroadcastRecord> list,
+ BroadcastRecord r, String typeForLogging) {
+ BroadcastRecord old;
+ final Intent intent = r.intent;
+ // Any in-flight broadcast has already been popped, and cannot be replaced.
+ // (This preserves existing behavior of the replacement API)
+ for (int i = list.size() - 1; i >= 0; i++) {
+ old = list.get(i);
+ if (old.userId == r.userId && intent.filterEquals(old.intent)) {
+ if (DEBUG_BROADCAST) {
+ Slog.v(TAG, "***** Replacing " + typeForLogging
+ + " [" + mQueue.mQueueName + "]: " + intent);
+ }
+ // Clone deferral state too if any
+ r.deferred = old.deferred;
+ list.set(i, r);
+ return old;
+ }
+ }
+ return null;
+ }
+
+ boolean cleanupDisabledPackageReceiversLocked(final String packageName,
+ Set<String> filterByClasses, final int userId, final boolean doit) {
+ // Note: fast short circuits when 'doit' is false, as soon as we hit any
+ // "yes we would do something" circumstance
+ boolean didSomething = cleanupBroadcastListDisabledReceiversLocked(mOrderedBroadcasts,
+ packageName, filterByClasses, userId, doit);
+ if (doit || !didSomething) {
+ didSomething |= cleanupDeferralsListDisabledReceiversLocked(mAlarmBroadcasts,
+ packageName, filterByClasses, userId, doit);
+ }
+ if (doit || !didSomething) {
+ didSomething |= cleanupDeferralsListDisabledReceiversLocked(mDeferredBroadcasts,
+ packageName, filterByClasses, userId, doit);
+ }
+ if ((doit || !didSomething) && mCurrentBroadcast != null) {
+ didSomething |= mCurrentBroadcast.cleanupDisabledPackageReceiversLocked(
+ packageName, filterByClasses, userId, doit);
+ }
+
+ return didSomething;
+ }
+
+ private boolean cleanupDeferralsListDisabledReceiversLocked(ArrayList<Deferrals> list,
+ final String packageName, Set<String> filterByClasses, final int userId,
+ final boolean doit) {
+ boolean didSomething = false;
+ for (Deferrals d : list) {
+ didSomething = cleanupBroadcastListDisabledReceiversLocked(d.broadcasts,
+ packageName, filterByClasses, userId, doit);
+ if (!doit && didSomething) {
+ return true;
+ }
+ }
+ return didSomething;
+ }
+
+ private boolean cleanupBroadcastListDisabledReceiversLocked(ArrayList<BroadcastRecord> list,
+ final String packageName, Set<String> filterByClasses, final int userId,
+ final boolean doit) {
+ boolean didSomething = false;
+ for (BroadcastRecord br : list) {
+ didSomething |= br.cleanupDisabledPackageReceiversLocked(packageName,
+ filterByClasses, userId, doit);
+ if (!doit && didSomething) {
+ return true;
+ }
+ }
+ return didSomething;
+ }
+
+ /**
+ * Standard proto dump entry point
+ */
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ if (mCurrentBroadcast != null) {
+ mCurrentBroadcast.writeToProto(proto, fieldId);
+ }
+ for (Deferrals d : mAlarmBroadcasts) {
+ d.writeToProto(proto, fieldId);
+ }
+ for (BroadcastRecord br : mOrderedBroadcasts) {
+ br.writeToProto(proto, fieldId);
+ }
+ for (Deferrals d : mDeferredBroadcasts) {
+ d.writeToProto(proto, fieldId);
+ }
+ }
+
+ // ----------------------------------
+ // Dispatch & deferral management
+
+ public BroadcastRecord getActiveBroadcastLocked() {
+ return mCurrentBroadcast;
+ }
+
+ /**
+ * If there is a deferred broadcast that is being sent to an alarm target, return
+ * that one. If there's no deferred alarm target broadcast but there is one
+ * that has reached the end of its deferral, return that.
+ *
+ * This stages the broadcast internally until it is retired, and returns that
+ * staged record if this is called repeatedly, until retireBroadcast(r) is called.
+ */
+ public BroadcastRecord getNextBroadcastLocked(final long now) {
+ if (mCurrentBroadcast != null) {
+ return mCurrentBroadcast;
+ }
+
+ BroadcastRecord next = null;
+ if (!mAlarmBroadcasts.isEmpty()) {
+ next = popLocked(mAlarmBroadcasts);
+ if (DEBUG_BROADCAST_DEFERRAL && next != null) {
+ Slog.i(TAG, "Next broadcast from alarm targets: " + next);
+ }
+ }
+
+ if (next == null && !mDeferredBroadcasts.isEmpty()) {
+ for (int i = 0; i < mDeferredBroadcasts.size(); i++) {
+ Deferrals d = mDeferredBroadcasts.get(i);
+ if (now < d.deferUntil) {
+ // No more deferrals due
+ break;
+ }
+
+ if (d.broadcasts.size() > 0) {
+ next = d.broadcasts.remove(0);
+ // apply deferral-interval decay policy and move this uid's
+ // deferred broadcasts down in the delivery queue accordingly
+ mDeferredBroadcasts.remove(i); // already 'd'
+ d.deferredBy = calculateDeferral(d.deferredBy);
+ d.deferUntil += d.deferredBy;
+ insertLocked(mDeferredBroadcasts, d);
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG, "Next broadcast from deferrals " + next
+ + ", deferUntil now " + d.deferUntil);
+ }
+ break;
+ }
+ }
+ }
+
+ if (next == null && !mOrderedBroadcasts.isEmpty()) {
+ next = mOrderedBroadcasts.remove(0);
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG, "Next broadcast from main queue: " + next);
+ }
+ }
+
+ mCurrentBroadcast = next;
+ return next;
+ }
+
+ /**
+ * Called after the broadcast queue finishes processing the currently
+ * active broadcast (obtained by calling getNextBroadcastLocked()).
+ */
+ public void retireBroadcastLocked(final BroadcastRecord r) {
+ // ERROR if 'r' is not the active broadcast
+ if (r != mCurrentBroadcast) {
+ Slog.wtf(TAG, "Retiring broadcast " + r
+ + " doesn't match current outgoing " + mCurrentBroadcast);
+ }
+ mCurrentBroadcast = null;
+ }
+
+ /**
+ * Called prior to broadcast dispatch to check whether the intended
+ * recipient is currently subject to deferral policy.
+ */
+ public boolean isDeferringLocked(final int uid) {
+ Deferrals d = findUidLocked(uid);
+ if (d != null && d.broadcasts.isEmpty()) {
+ // once we've caught up with deferred broadcasts to this uid
+ // and time has advanced sufficiently that we wouldn't be
+ // deferring newly-enqueued ones, we're back to normal policy.
+ if (SystemClock.uptimeMillis() >= d.deferUntil) {
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG, "No longer deferring broadcasts to uid " + d.uid);
+ }
+ removeDeferral(d);
+ return false;
+ }
+ }
+ return (d != null);
+ }
+
+ /**
+ * Defer broadcasts for the given app. If 'br' is non-null, this also makes
+ * sure that broadcast record is enqueued as the next upcoming broadcast for
+ * the app.
+ */
+ public void startDeferring(final int uid) {
+ synchronized (mLock) {
+ Deferrals d = findUidLocked(uid);
+
+ // If we're not yet tracking this app, set up that bookkeeping
+ if (d == null) {
+ // Start a new deferral
+ final long now = SystemClock.uptimeMillis();
+ d = new Deferrals(uid,
+ now,
+ mConstants.DEFERRAL,
+ mAlarmUids.get(uid, 0));
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG, "Now deferring broadcasts to " + uid
+ + " until " + d.deferUntil);
+ }
+ // where it goes depends on whether it is coming into an alarm-related situation
+ if (d.alarmCount == 0) {
+ // common case, put it in the ordinary priority queue
+ insertLocked(mDeferredBroadcasts, d);
+ scheduleDeferralCheckLocked(true);
+ } else {
+ // alarm-related: strict order-encountered
+ mAlarmBroadcasts.add(d);
+ }
+ } else {
+ // We're already deferring, but something was slow again. Reset the
+ // deferral decay progression.
+ d.deferredBy = mConstants.DEFERRAL;
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG, "Uid " + uid + " slow again, deferral interval reset to "
+ + d.deferredBy);
+ }
+ }
+ }
+ }
+
+ /**
+ * Key entry point when a broadcast about to be delivered is instead
+ * set aside for deferred delivery
+ */
+ public void addDeferredBroadcast(final int uid, BroadcastRecord br) {
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG, "Enqueuing deferred broadcast " + br);
+ }
+ synchronized (mLock) {
+ Deferrals d = findUidLocked(uid);
+ if (d == null) {
+ Slog.wtf(TAG, "Adding deferred broadcast but not tracking " + uid);
+ } else {
+ if (br == null) {
+ Slog.wtf(TAG, "Deferring null broadcast to " + uid);
+ } else {
+ br.deferred = true;
+ d.add(br);
+ }
+ }
+ }
+ }
+
+ /**
+ * When there are deferred broadcasts, we need to make sure to recheck the
+ * dispatch queue when they come due. Alarm-sensitive deferrals get dispatched
+ * aggressively, so we only need to use the ordinary deferrals timing to figure
+ * out when to recheck.
+ */
+ public void scheduleDeferralCheckLocked(boolean force) {
+ if ((force || !mRecheckScheduled) && !mDeferredBroadcasts.isEmpty()) {
+ final Deferrals d = mDeferredBroadcasts.get(0);
+ if (!d.broadcasts.isEmpty()) {
+ mHandler.removeCallbacks(mScheduleRunnable);
+ mHandler.postAtTime(mScheduleRunnable, d.deferUntil);
+ mRecheckScheduled = true;
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG, "Scheduling deferred broadcast recheck at " + d.deferUntil);
+ }
+ }
+ }
+ }
+
+ // ----------------------------------
+
+ /**
+ * If broadcasts to this uid are being deferred, find the deferrals record about it.
+ * @return null if this uid's broadcasts are not being deferred
+ */
+ private Deferrals findUidLocked(final int uid) {
+ // The common case is that they it isn't also an alarm target...
+ Deferrals d = findUidLocked(uid, mDeferredBroadcasts);
+ // ...but if not there, also check alarm-prioritized deferrals
+ if (d == null) {
+ d = findUidLocked(uid, mAlarmBroadcasts);
+ }
+ return d;
+ }
+
+ /**
+ * Remove the given deferral record from whichever queue it might be in at present
+ * @return true if the deferral was in fact found, false if this made no changes
+ */
+ private boolean removeDeferral(Deferrals d) {
+ boolean didRemove = mDeferredBroadcasts.remove(d);
+ if (!didRemove) {
+ didRemove = mAlarmBroadcasts.remove(d);
+ }
+ return didRemove;
+ }
+
+ /**
+ * Find the deferrals record for the given uid in the given list
+ */
+ private static Deferrals findUidLocked(final int uid, ArrayList<Deferrals> list) {
+ final int numElements = list.size();
+ for (int i = 0; i < numElements; i++) {
+ Deferrals d = list.get(i);
+ if (uid == d.uid) {
+ return d;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Pop the next broadcast record from the head of the given deferrals list,
+ * if one exists.
+ */
+ private static BroadcastRecord popLocked(ArrayList<Deferrals> list) {
+ final Deferrals d = list.get(0);
+ return d.broadcasts.isEmpty() ? null : d.broadcasts.remove(0);
+ }
+
+ /**
+ * Insert the given Deferrals into the priority queue, sorted by defer-until milestone
+ */
+ private static void insertLocked(ArrayList<Deferrals> list, Deferrals d) {
+ // Simple linear search is appropriate here because we expect to
+ // have very few entries in the deferral lists (i.e. very few badly-
+ // behaving apps currently facing deferral)
+ int i;
+ final int numElements = list.size();
+ for (i = 0; i < numElements; i++) {
+ if (d.deferUntil < list.get(i).deferUntil) {
+ break;
+ }
+ }
+ list.add(i, d);
+ }
+
+ /**
+ * Calculate a new deferral time based on the previous time. This should decay
+ * toward zero, though a small nonzero floor is an option.
+ */
+ private long calculateDeferral(long previous) {
+ return Math.max(mConstants.DEFERRAL_FLOOR,
+ (long) (previous * mConstants.DEFERRAL_DECAY_FACTOR));
+ }
+
+ // ----------------------------------
+
+ boolean dumpLocked(PrintWriter pw, String dumpPackage, String queueName,
+ SimpleDateFormat sdf) {
+ final Dumper dumper = new Dumper(pw, queueName, dumpPackage, sdf);
+ boolean printed = false;
+
+ dumper.setHeading("Active ordered broadcasts");
+ dumper.setLabel("Active Ordered Broadcast");
+ for (Deferrals d : mAlarmBroadcasts) {
+ d.dumpLocked(dumper);
+ }
+ printed |= dumper.didPrint();
+
+ for (BroadcastRecord br : mOrderedBroadcasts) {
+ dumper.dump(br);
+ }
+ printed |= dumper.didPrint();
+
+ dumper.setHeading("Deferred ordered broadcasts");
+ dumper.setLabel("Deferred Ordered Broadcast");
+ for (Deferrals d : mDeferredBroadcasts) {
+ d.dumpLocked(dumper);
+ }
+ printed |= dumper.didPrint();
+
+ return printed;
+ }
+}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index cdf6e0ede865..64a36ef66f12 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -24,6 +24,7 @@ import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.app.PendingIntent;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
@@ -45,6 +46,7 @@ import android.os.Trace;
import android.os.UserHandle;
import android.util.EventLog;
import android.util.Slog;
+import android.util.SparseIntArray;
import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -75,14 +77,15 @@ public final class BroadcastQueue {
final ActivityManagerService mService;
/**
- * Recognizable moniker for this queue
+ * Behavioral parameters such as timeouts and deferral policy, tracking Settings
+ * for runtime configurability
*/
- final String mQueueName;
+ final BroadcastConstants mConstants;
/**
- * Timeout period for this queue's broadcasts
+ * Recognizable moniker for this queue
*/
- final long mTimeoutPeriod;
+ final String mQueueName;
/**
* If true, we can delay broadcasts while waiting services to finish in the previous
@@ -100,13 +103,18 @@ public final class BroadcastQueue {
final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<>();
/**
- * List of all active broadcasts that are to be executed one at a time.
- * The object at the top of the list is the currently activity broadcasts;
- * those after it are waiting for the top to finish. As with parallel
- * broadcasts, separate background- and foreground-priority queues are
- * maintained.
+ * Tracking of the ordered broadcast queue, including deferral policy and alarm
+ * prioritization.
*/
- final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<>();
+ final BroadcastDispatcher mDispatcher;
+
+ /**
+ * Refcounting for completion callbacks of split/deferred broadcasts. The key
+ * is an opaque integer token assigned lazily when a broadcast is first split
+ * into multiple BroadcastRecord objects.
+ */
+ final SparseIntArray mSplitRefcounts = new SparseIntArray();
+ private int mNextToken = 0;
/**
* Historical data of past broadcasts, for debugging. This is a ring buffer
@@ -173,7 +181,8 @@ public final class BroadcastQueue {
switch (msg.what) {
case BROADCAST_INTENT_MSG: {
if (DEBUG_BROADCAST) Slog.v(
- TAG_BROADCAST, "Received BROADCAST_INTENT_MSG");
+ TAG_BROADCAST, "Received BROADCAST_INTENT_MSG ["
+ + mQueueName + "]");
processNextBroadcast(true);
} break;
case BROADCAST_TIMEOUT_MSG: {
@@ -201,12 +210,19 @@ public final class BroadcastQueue {
}
BroadcastQueue(ActivityManagerService service, Handler handler,
- String name, long timeoutPeriod, boolean allowDelayBehindServices) {
+ String name, BroadcastConstants constants, boolean allowDelayBehindServices) {
mService = service;
mHandler = new BroadcastHandler(handler.getLooper());
mQueueName = name;
- mTimeoutPeriod = timeoutPeriod;
mDelayBehindServices = allowDelayBehindServices;
+
+ mConstants = constants;
+ mDispatcher = new BroadcastDispatcher(this, mConstants, mHandler, mService);
+ }
+
+ void start(ContentResolver resolver) {
+ mDispatcher.start();
+ mConstants.startObserving(mHandler, resolver);
}
@Override
@@ -224,7 +240,7 @@ public final class BroadcastQueue {
}
public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
- mOrderedBroadcasts.add(r);
+ mDispatcher.enqueueOrderedBroadcastLocked(r);
enqueueBroadcastHelper(r);
}
@@ -255,7 +271,7 @@ public final class BroadcastQueue {
* the old one.
*/
public final BroadcastRecord replaceOrderedBroadcastLocked(BroadcastRecord r) {
- return replaceBroadcastLocked(mOrderedBroadcasts, r, "ORDERED");
+ return mDispatcher.replaceBroadcastLocked(r, "ORDERED");
}
private BroadcastRecord replaceBroadcastLocked(ArrayList<BroadcastRecord> queue,
@@ -365,14 +381,17 @@ public final class BroadcastQueue {
}
}
+ // Skip the current receiver, if any, that is in flight to the given process
public void skipCurrentReceiverLocked(ProcessRecord app) {
BroadcastRecord r = null;
- if (mOrderedBroadcasts.size() > 0) {
- BroadcastRecord br = mOrderedBroadcasts.get(0);
- if (br.curApp == app) {
- r = br;
- }
+ final BroadcastRecord curActive = mDispatcher.getActiveBroadcastLocked();
+ if (curActive != null && curActive.curApp == app) {
+ // confirmed: the current active broadcast is to the given app
+ r = curActive;
}
+
+ // If the current active broadcast isn't this BUT we're waiting for
+ // mPendingBroadcast to spin up the target app, that's what we use.
if (r == null && mPendingBroadcast != null && mPendingBroadcast.curApp == app) {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"[" + mQueueName + "] skip & discard pending app " + r);
@@ -404,20 +423,29 @@ public final class BroadcastQueue {
}
public BroadcastRecord getMatchingOrderedReceiver(IBinder receiver) {
- if (mOrderedBroadcasts.size() > 0) {
- final BroadcastRecord r = mOrderedBroadcasts.get(0);
- if (r != null && r.receiver == receiver) {
- return r;
- }
+ BroadcastRecord br = mDispatcher.getActiveBroadcastLocked();
+ if (br != null && br.receiver == receiver) {
+ return br;
}
return null;
}
+ // > 0 only, no worry about "eventual" recycling
+ private int nextSplitTokenLocked() {
+ int next = mNextToken + 1;
+ if (next <= 0) {
+ next = 1;
+ }
+ mNextToken = next;
+ return next;
+ }
+
public boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) {
final int state = r.state;
final ActivityInfo receiver = r.curReceiver;
final long finishTime = SystemClock.uptimeMillis();
+ final long elapsed = finishTime - r.receiverTime;
r.state = BroadcastRecord.IDLE;
if (state == BroadcastRecord.IDLE) {
Slog.w(TAG, "finishReceiver [" + mQueueName + "] called but state is IDLE");
@@ -428,8 +456,22 @@ public final class BroadcastQueue {
// If we're abandoning this broadcast before any receivers were actually spun up,
// nextReceiver is zero; in which case time-to-process bookkeeping doesn't apply.
if (r.nextReceiver > 0) {
- r.duration[r.nextReceiver - 1] = finishTime - r.receiverTime;
+ r.duration[r.nextReceiver - 1] = elapsed;
+ }
+
+ // if this receiver was slow, impose deferral policy on the app. This will kick in
+ // when processNextBroadcastLocked() next finds this uid as a receiver identity.
+ if (mConstants.SLOW_TIME > 0 && elapsed > mConstants.SLOW_TIME) {
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG, "Broadcast receiver was slow: " + receiver + " br=" + r);
+ }
+ if (r.curApp != null) {
+ mDispatcher.startDeferring(r.curApp.uid);
+ } else {
+ Slog.d(TAG, "finish receiver curApp is null? " + r);
+ }
}
+
r.receiver = null;
r.intent.setComponent(null);
if (r.curApp != null && r.curApp.curReceivers.contains(r)) {
@@ -452,9 +494,10 @@ public final class BroadcastQueue {
r.resultAbort = false;
}
+ // If we want to wait behind services *AND* we're finishing the head/
+ // active broadcast on its queue
if (waitForServices && r.curComponent != null && r.queue.mDelayBehindServices
- && r.queue.mOrderedBroadcasts.size() > 0
- && r.queue.mOrderedBroadcasts.get(0) == r) {
+ && r.queue.mDispatcher.getActiveBroadcastLocked() == r) {
ActivityInfo nextReceiver;
if (r.nextReceiver < r.receivers.size()) {
Object obj = r.receivers.get(r.nextReceiver);
@@ -488,8 +531,8 @@ public final class BroadcastQueue {
}
public void backgroundServicesFinishedLocked(int userId) {
- if (mOrderedBroadcasts.size() > 0) {
- BroadcastRecord br = mOrderedBroadcasts.get(0);
+ BroadcastRecord br = mDispatcher.getActiveBroadcastLocked();
+ if (br != null) {
if (br.userId == userId && br.state == BroadcastRecord.WAITING_SERVICES) {
Slog.i(TAG, "Resuming delayed broadcast");
br.curComponent = null;
@@ -861,7 +904,7 @@ public final class BroadcastQueue {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast ["
+ mQueueName + "]: "
+ mParallelBroadcasts.size() + " parallel broadcasts, "
- + mOrderedBroadcasts.size() + " ordered broadcasts");
+ + mDispatcher.totalUndelivered() + " ordered broadcasts");
mService.updateCpuStats();
@@ -937,8 +980,12 @@ public final class BroadcastQueue {
boolean looped = false;
do {
- if (mOrderedBroadcasts.size() == 0) {
- // No more broadcasts pending, so all done!
+ final long now = SystemClock.uptimeMillis();
+ r = mDispatcher.getNextBroadcastLocked(now);
+
+ if (r == null) {
+ // No more broadcasts are deliverable right now, so all done!
+ mDispatcher.scheduleDeferralCheckLocked(false);
mService.scheduleAppGcsLocked();
if (looped) {
// If we had finished the last ordered broadcast, then
@@ -954,7 +1001,7 @@ public final class BroadcastQueue {
return;
}
- r = mOrderedBroadcasts.get(0);
+
boolean forceReceive = false;
// Ensure that even if something goes awry with the timeout
@@ -967,9 +1014,8 @@ public final class BroadcastQueue {
// significant amounts of time.
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (mService.mProcessesReady && r.dispatchTime > 0) {
- long now = SystemClock.uptimeMillis();
if ((numReceivers > 0) &&
- (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
+ (now > r.dispatchTime + (2 * mConstants.TIMEOUT * numReceivers))) {
Slog.w(TAG, "Hung broadcast ["
+ mQueueName + "] discarded after timeout failure:"
+ " now=" + now
@@ -993,27 +1039,53 @@ public final class BroadcastQueue {
return;
}
+ // Is the current broadcast is done for any reason?
if (r.receivers == null || r.nextReceiver >= numReceivers
|| r.resultAbort || forceReceive) {
- // No more receivers for this broadcast! Send the final
- // result if requested...
+ // Send the final result if requested
if (r.resultTo != null) {
- try {
- if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,
- "Finishing broadcast [" + mQueueName + "] "
- + r.intent.getAction() + " app=" + r.callerApp);
- performReceiveLocked(r.callerApp, r.resultTo,
- new Intent(r.intent), r.resultCode,
- r.resultData, r.resultExtras, false, false, r.userId);
- // Set this to null so that the reference
- // (local and remote) isn't kept in the mBroadcastHistory.
- r.resultTo = null;
- } catch (RemoteException e) {
- r.resultTo = null;
- Slog.w(TAG, "Failure ["
- + mQueueName + "] sending broadcast result of "
- + r.intent, e);
-
+ boolean sendResult = true;
+
+ // if this was part of a split/deferral complex, update the refcount and only
+ // send the completion when we clear all of them
+ if (r.splitToken != 0) {
+ int newCount = mSplitRefcounts.get(r.splitToken) - 1;
+ if (newCount == 0) {
+ // done! clear out this record's bookkeeping and deliver
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG, "Sending broadcast completion for split token "
+ + r.splitToken);
+ }
+ mSplitRefcounts.delete(r.splitToken);
+ } else {
+ // still have some split broadcast records in flight; update refcount
+ // and hold off on the callback
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG, "Result refcount " + newCount + " for split token "
+ + r.splitToken + " - not sending completion yet");
+ }
+ sendResult = false;
+ mSplitRefcounts.put(r.splitToken, newCount);
+ }
+ }
+ if (sendResult) {
+ try {
+ if (DEBUG_BROADCAST) {
+ Slog.i(TAG_BROADCAST, "Finishing broadcast [" + mQueueName + "] "
+ + r.intent.getAction() + " app=" + r.callerApp);
+ }
+ performReceiveLocked(r.callerApp, r.resultTo,
+ new Intent(r.intent), r.resultCode,
+ r.resultData, r.resultExtras, false, false, r.userId);
+ // Set this to null so that the reference
+ // (local and remote) isn't kept in the mBroadcastHistory.
+ r.resultTo = null;
+ } catch (RemoteException e) {
+ r.resultTo = null;
+ Slog.w(TAG, "Failure ["
+ + mQueueName + "] sending broadcast result of "
+ + r.intent, e);
+ }
}
}
@@ -1031,11 +1103,76 @@ public final class BroadcastQueue {
mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,
r.manifestCount, r.manifestSkipCount, r.finishTime-r.dispatchTime);
}
- mOrderedBroadcasts.remove(0);
+ mDispatcher.retireBroadcastLocked(r);
r = null;
looped = true;
continue;
}
+
+ // Check whether the next receiver is under deferral policy, and handle that
+ // accordingly. If the current broadcast was already part of deferred-delivery
+ // tracking, we know that it must now be deliverable as-is without re-deferral.
+ if (!r.deferred) {
+ final int receiverUid = r.getReceiverUid(r.receivers.get(r.nextReceiver));
+ if (mDispatcher.isDeferringLocked(receiverUid)) {
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG_BROADCAST, "Next receiver in " + r + " uid " + receiverUid
+ + " at " + r.nextReceiver + " is under deferral");
+ }
+ // If this is the only (remaining) receiver in the broadcast, "splitting"
+ // doesn't make sense -- just defer it as-is and retire it as the
+ // currently active outgoing broadcast.
+ BroadcastRecord defer;
+ if (r.nextReceiver + 1 == numReceivers) {
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG, "Sole receiver of " + r
+ + " is under deferral; setting aside and proceeding");
+ }
+ defer = r;
+ mDispatcher.retireBroadcastLocked(r);
+ } else {
+ // Nontrivial case; split out 'uid's receivers to a new broadcast record
+ // and defer that, then loop and pick up continuing delivery of the current
+ // record (now absent those receivers).
+
+ // The split operation is guaranteed to match at least at 'nextReceiver'
+ defer = r.splitRecipientsLocked(receiverUid, r.nextReceiver);
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG_BROADCAST, "Post split:");
+ Slog.i(TAG_BROADCAST, "Original broadcast receivers:");
+ for (int i = 0; i < r.receivers.size(); i++) {
+ Slog.i(TAG_BROADCAST, " " + r.receivers.get(i));
+ }
+ Slog.i(TAG_BROADCAST, "Split receivers:");
+ for (int i = 0; i < defer.receivers.size(); i++) {
+ Slog.i(TAG_BROADCAST, " " + defer.receivers.get(i));
+ }
+ }
+ // Track completion refcount as well if relevant
+ if (r.resultTo != null) {
+ int token = r.splitToken;
+ if (token == 0) {
+ // first split of this record; refcount for 'r' and 'deferred'
+ r.splitToken = defer.splitToken = nextSplitTokenLocked();
+ mSplitRefcounts.put(r.splitToken, 2);
+ } else {
+ // new split from an already-refcounted situation; increment count
+ final int curCount = mSplitRefcounts.get(token);
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ if (curCount == 0) {
+ Slog.wtf(TAG, "Split refcount is zero with token for " + r);
+ }
+ }
+ mSplitRefcounts.put(token, curCount + 1);
+ }
+ }
+ }
+ mDispatcher.addDeferredBroadcast(receiverUid, defer);
+ r = null;
+ looped = true;
+ continue;
+ }
+ }
} while (r == null);
// Get the next receiver...
@@ -1066,7 +1203,7 @@ public final class BroadcastQueue {
+ mQueueName + "] " + r);
}
if (! mPendingBroadcastTimeoutMessage) {
- long timeoutTime = r.receiverTime + mTimeoutPeriod;
+ long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Submitting BROADCAST_TIMEOUT_MSG ["
+ mQueueName + "] for " + r + " at " + timeoutTime);
@@ -1474,12 +1611,12 @@ public final class BroadcastQueue {
mPendingBroadcastTimeoutMessage = false;
}
- if (mOrderedBroadcasts.size() == 0) {
+ if (mDispatcher.isEmpty() || mDispatcher.getActiveBroadcastLocked() == null) {
return;
}
long now = SystemClock.uptimeMillis();
- BroadcastRecord r = mOrderedBroadcasts.get(0);
+ BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
if (fromMsg) {
if (!mService.mProcessesReady) {
// Only process broadcast timeouts if the system is ready. That way
@@ -1488,7 +1625,7 @@ public final class BroadcastQueue {
return;
}
- long timeoutTime = r.receiverTime + mTimeoutPeriod;
+ long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
if (timeoutTime > now) {
// We can observe premature timeouts because we do not cancel and reset the
// broadcast timeout message after each receiver finishes. Instead, we set up
@@ -1503,16 +1640,15 @@ public final class BroadcastQueue {
}
}
- BroadcastRecord br = mOrderedBroadcasts.get(0);
- if (br.state == BroadcastRecord.WAITING_SERVICES) {
+ if (r.state == BroadcastRecord.WAITING_SERVICES) {
// In this case the broadcast had already finished, but we had decided to wait
// for started services to finish as well before going on. So if we have actually
// waited long enough time timeout the broadcast, let's give up on the whole thing
// and just move on to the next.
- Slog.i(TAG, "Waited long enough for: " + (br.curComponent != null
- ? br.curComponent.flattenToShortString() : "(null)"));
- br.curComponent = null;
- br.state = BroadcastRecord.IDLE;
+ Slog.i(TAG, "Waited long enough for: " + (r.curComponent != null
+ ? r.curComponent.flattenToShortString() : "(null)"));
+ r.curComponent = null;
+ r.state = BroadcastRecord.IDLE;
processNextBroadcast(false);
return;
}
@@ -1619,13 +1755,8 @@ public final class BroadcastQueue {
}
}
- for (int i = mOrderedBroadcasts.size() - 1; i >= 0; i--) {
- didSomething |= mOrderedBroadcasts.get(i).cleanupDisabledPackageReceiversLocked(
- packageName, filterByClasses, userId, doit);
- if (!doit && didSomething) {
- return true;
- }
- }
+ didSomething |= mDispatcher.cleanupDisabledPackageReceiversLocked(packageName,
+ filterByClasses, userId, doit);
return didSomething;
}
@@ -1665,7 +1796,7 @@ public final class BroadcastQueue {
}
final boolean isIdle() {
- return mParallelBroadcasts.isEmpty() && mOrderedBroadcasts.isEmpty()
+ return mParallelBroadcasts.isEmpty() && mDispatcher.isEmpty()
&& (mPendingBroadcast == null);
}
@@ -1677,10 +1808,7 @@ public final class BroadcastQueue {
for (int i = N - 1; i >= 0; i--) {
mParallelBroadcasts.get(i).writeToProto(proto, BroadcastQueueProto.PARALLEL_BROADCASTS);
}
- N = mOrderedBroadcasts.size();
- for (int i = N - 1; i >= 0; i--) {
- mOrderedBroadcasts.get(i).writeToProto(proto, BroadcastQueueProto.ORDERED_BROADCASTS);
- }
+ mDispatcher.writeToProto(proto, BroadcastQueueProto.ORDERED_BROADCASTS);
if (mPendingBroadcast != null) {
mPendingBroadcast.writeToProto(proto, BroadcastQueueProto.PENDING_BROADCAST);
}
@@ -1721,7 +1849,7 @@ public final class BroadcastQueue {
final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage, boolean needSep) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
- if (mParallelBroadcasts.size() > 0 || mOrderedBroadcasts.size() > 0
+ if (!mParallelBroadcasts.isEmpty() || !mDispatcher.isEmpty()
|| mPendingBroadcast != null) {
boolean printed = false;
for (int i = mParallelBroadcasts.size() - 1; i >= 0; i--) {
@@ -1740,29 +1868,12 @@ public final class BroadcastQueue {
pw.println(" Active Broadcast " + mQueueName + " #" + i + ":");
br.dump(pw, " ", sdf);
}
- printed = false;
- needSep = true;
- for (int i = mOrderedBroadcasts.size() - 1; i >= 0; i--) {
- BroadcastRecord br = mOrderedBroadcasts.get(i);
- if (dumpPackage != null && !dumpPackage.equals(br.callerPackage)) {
- continue;
- }
- if (!printed) {
- if (needSep) {
- pw.println();
- }
- needSep = true;
- printed = true;
- pw.println(" Active ordered broadcasts [" + mQueueName + "]:");
- }
- pw.println(" Active Ordered Broadcast " + mQueueName + " #" + i + ":");
- mOrderedBroadcasts.get(i).dump(pw, " ", sdf);
- }
+
+ mDispatcher.dumpLocked(pw, dumpPackage, mQueueName, sdf);
+
if (dumpPackage == null || (mPendingBroadcast != null
&& dumpPackage.equals(mPendingBroadcast.callerPackage))) {
- if (needSep) {
- pw.println();
- }
+ pw.println();
pw.println(" Pending broadcast [" + mQueueName + "]:");
if (mPendingBroadcast != null) {
mPendingBroadcast.dump(pw, " ", sdf);
@@ -1773,6 +1884,8 @@ public final class BroadcastQueue {
}
}
+ mConstants.dump(pw);
+
int i;
boolean printed = false;
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 9e799f6f14f3..d9e03f8f908f 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -36,10 +36,12 @@ import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* An active intent broadcast.
@@ -64,6 +66,9 @@ final class BroadcastRecord extends Binder {
final int[] delivery; // delivery state of each receiver
final long[] duration; // duration a receiver took to process broadcast
IIntentReceiver resultTo; // who receives final result if non-null
+ boolean deferred;
+ int splitCount; // refcount for result callback, when split
+ int splitToken; // identifier for cross-BroadcastRecord refcount
long enqueueClockTime; // the clock time the broadcast was enqueued
long dispatchTime; // when dispatch started on this set of receivers
long dispatchClockTime; // the clock time the dispatch started
@@ -106,6 +111,9 @@ final class BroadcastRecord extends Binder {
ComponentName curComponent; // the receiver class that is currently running.
ActivityInfo curReceiver; // info about the receiver that is currently running.
+ // Private refcount-management bookkeeping; start > 0
+ static AtomicInteger sNextToken = new AtomicInteger(1);
+
void dump(PrintWriter pw, String prefix, SimpleDateFormat sdf) {
final long now = SystemClock.uptimeMillis();
@@ -304,6 +312,52 @@ final class BroadcastRecord extends Binder {
allowBackgroundActivityStarts = from.allowBackgroundActivityStarts;
}
+ /**
+ * Split off a new BroadcastRecord that clones this one, but contains only the
+ * recipient records for the current (just-finished) receiver's app, starting
+ * after the just-finished receiver [i.e. at r.nextReceiver]. Returns null
+ * if there are no matching subsequent receivers in this BroadcastRecord.
+ */
+ BroadcastRecord splitRecipientsLocked(int slowAppUid, int startingAt) {
+ // Do we actually have any matching receivers down the line...?
+ ArrayList splitReceivers = null;
+ for (int i = startingAt; i < receivers.size(); ) {
+ Object o = receivers.get(i);
+ if (getReceiverUid(o) == slowAppUid) {
+ if (splitReceivers == null) {
+ splitReceivers = new ArrayList<>();
+ }
+ splitReceivers.add(o);
+ receivers.remove(i);
+ break;
+ } else {
+ i++;
+ }
+ }
+
+ // No later receivers in the same app, so we have no more to do
+ if (splitReceivers == null) {
+ return null;
+ }
+
+ // build a new BroadcastRecord around that single-target list
+ BroadcastRecord split = new BroadcastRecord(queue, intent, callerApp,
+ callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
+ requiredPermissions, appOp, options, splitReceivers, resultTo, resultCode,
+ resultData, resultExtras, ordered, sticky, initialSticky, userId,
+ allowBackgroundActivityStarts);
+
+ return split;
+ }
+
+ int getReceiverUid(Object receiver) {
+ if (receiver instanceof BroadcastFilter) {
+ return ((BroadcastFilter) receiver).owningUid;
+ } else /* if (receiver instanceof ResolveInfo) */ {
+ return ((ResolveInfo) receiver).activityInfo.applicationInfo.uid;
+ }
+ }
+
public BroadcastRecord maybeStripForHistory() {
if (!intent.canStripForHistory()) {
return this;
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index b71a7517ab12..5b469fe2dc7a 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -16,6 +16,10 @@
package com.android.server.attention;
+import static android.provider.DeviceConfig.AttentionManagerService.NAMESPACE;
+import static android.provider.DeviceConfig.AttentionManagerService.PROPERTY_COMPONENT_NAME;
+import static android.provider.DeviceConfig.AttentionManagerService.PROPERTY_SERVICE_ENABLED;
+
import android.Manifest;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -40,6 +44,7 @@ import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.DeviceConfig;
import android.service.attention.AttentionService;
import android.service.attention.AttentionService.AttentionFailureCodes;
import android.service.attention.IAttentionCallback;
@@ -66,6 +71,9 @@ import java.io.PrintWriter;
public class AttentionManagerService extends SystemService {
private static final String LOG_TAG = "AttentionManagerService";
+ /** Default value in absence of {@link DeviceConfig} override. */
+ private static final boolean DEFAULT_SERVICE_ENABLED = true;
+
/** Service will unbind if connection is not used for that amount of time. */
private static final long CONNECTION_TTL_MILLIS = 60_000;
@@ -105,7 +113,7 @@ public class AttentionManagerService extends SystemService {
super.onBootPhase(phase);
if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
mComponentName = resolveAttentionService(mContext);
- if (mComponentName != null) {
+ if (isAttentionServiceSupported()) {
// If the service is supported we want to keep receiving the screen off events.
mContext.registerReceiver(new ScreenStateReceiver(),
new IntentFilter(Intent.ACTION_SCREEN_OFF));
@@ -117,7 +125,12 @@ public class AttentionManagerService extends SystemService {
* Returns {@code true} if attention service is supported on this device.
*/
public boolean isAttentionServiceSupported() {
- return mComponentName != null;
+ return mComponentName != null && isServiceEnabled();
+ }
+
+ private boolean isServiceEnabled() {
+ final String enabled = DeviceConfig.getProperty(NAMESPACE, PROPERTY_SERVICE_ENABLED);
+ return enabled == null ? DEFAULT_SERVICE_ENABLED : "true".equals(enabled);
}
/**
@@ -266,8 +279,9 @@ public class AttentionManagerService extends SystemService {
* system.
*/
private static ComponentName resolveAttentionService(Context context) {
- // TODO(b/111939367): add a flag to turn on/off.
- final String componentNameString = context.getString(
+ final String flag = DeviceConfig.getProperty(NAMESPACE, PROPERTY_COMPONENT_NAME);
+
+ final String componentNameString = flag != null ? flag : context.getString(
R.string.config_defaultAttentionService);
if (TextUtils.isEmpty(componentNameString)) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 4db541c29448..99bed732446c 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -207,6 +207,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
static final int MSG_SET_ACTIVE = 3020;
static final int MSG_SET_INTERACTIVE = 3030;
static final int MSG_REPORT_FULLSCREEN_MODE = 3045;
+ static final int MSG_REPORT_PRE_RENDERED = 3060;
+ static final int MSG_APPLY_IME_VISIBILITY = 3070;
static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
@@ -3327,6 +3329,32 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ @BinderThread
+ private void reportPreRendered(IBinder token, EditorInfo info) {
+ synchronized (mMethodMap) {
+ if (!calledWithValidTokenLocked(token)) {
+ return;
+ }
+ if (mCurClient != null && mCurClient.client != null) {
+ executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
+ MSG_REPORT_PRE_RENDERED, info, mCurClient));
+ }
+ }
+ }
+
+ @BinderThread
+ private void applyImeVisibility(IBinder token, boolean setVisible) {
+ synchronized (mMethodMap) {
+ if (!calledWithValidTokenLocked(token)) {
+ return;
+ }
+ if (mCurClient != null && mCurClient.client != null) {
+ executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
+ MSG_APPLY_IME_VISIBILITY, setVisible ? 1 : 0, mCurClient));
+ }
+ }
+ }
+
private void setInputMethodWithSubtypeIdLocked(IBinder token, String id, int subtypeId) {
if (token == null) {
if (mContext.checkCallingOrSelfPermission(
@@ -3580,6 +3608,32 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
return true;
}
+ case MSG_REPORT_PRE_RENDERED: {
+ args = (SomeArgs) msg.obj;
+ final EditorInfo info = (EditorInfo) args.arg1;
+ final ClientState clientState = (ClientState) args.arg2;
+ try {
+ clientState.client.reportPreRendered(info);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Got RemoteException sending "
+ + "reportPreRendered(" + info + ") notification to pid="
+ + clientState.pid + " uid=" + clientState.uid);
+ }
+ args.recycle();
+ return true;
+ }
+ case MSG_APPLY_IME_VISIBILITY: {
+ final boolean setVisible = msg.arg1 != 0;
+ final ClientState clientState = (ClientState) msg.obj;
+ try {
+ clientState.client.applyImeVisibility(setVisible);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Got RemoteException sending "
+ + "applyImeVisibility(" + setVisible + ") notification to pid="
+ + clientState.pid + " uid=" + clientState.uid);
+ }
+ return true;
+ }
// --------------------------------------------------------------
case MSG_HARD_KEYBOARD_SWITCH_CHANGED:
@@ -4756,5 +4810,17 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
public void notifyUserAction() {
mImms.notifyUserAction(mToken);
}
+
+ @BinderThread
+ @Override
+ public void reportPreRendered(EditorInfo info) {
+ mImms.reportPreRendered(mToken, info);
+ }
+
+ @BinderThread
+ @Override
+ public void applyImeVisibility(boolean setVisible) {
+ mImms.applyImeVisibility(mToken, setVisible);
+ }
}
}
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 82bfa511507f..ed1a6d0beabf 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -33,6 +33,7 @@ import android.text.format.Time;
import android.util.ArraySet;
import android.util.Pair;
import android.util.Slog;
+import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -40,6 +41,7 @@ import com.android.server.LocalServices;
import com.android.server.job.GrantedUriPermissions;
import com.android.server.job.JobSchedulerInternal;
import com.android.server.job.JobSchedulerService;
+import com.android.server.job.JobServerProtoEnums;
import com.android.server.job.JobStatusDumpProto;
import com.android.server.job.JobStatusShortInfoProto;
@@ -80,6 +82,28 @@ public final class JobStatus {
static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24;
static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1<<22;
+ /**
+ * The constraints that we want to log to statsd.
+ *
+ * Constraints that can be inferred from other atoms have been excluded to avoid logging too
+ * much information and to reduce redundancy:
+ *
+ * * CONSTRAINT_CHARGING can be inferred with PluggedStateChanged (Atom #32)
+ * * CONSTRAINT_BATTERY_NOT_LOW can be inferred with BatteryLevelChanged (Atom #30)
+ * * CONSTRAINT_CONNECTIVITY can be partially inferred with ConnectivityStateChanged
+ * (Atom #98) and BatterySaverModeStateChanged (Atom #20).
+ * * CONSTRAINT_DEVICE_NOT_DOZING can be mostly inferred with DeviceIdleModeStateChanged
+ * (Atom #21)
+ * * CONSTRAINT_BACKGROUND_NOT_RESTRICTED can be inferred with BatterySaverModeStateChanged
+ * (Atom #20)
+ */
+ private static final int STATSD_CONSTRAINTS_TO_LOG = CONSTRAINT_CONTENT_TRIGGER
+ | CONSTRAINT_DEADLINE
+ | CONSTRAINT_IDLE
+ | CONSTRAINT_STORAGE_NOT_LOW
+ | CONSTRAINT_TIMING_DELAY
+ | CONSTRAINT_WITHIN_QUOTA;
+
// Soft override: ignore constraints like time that don't affect API availability
public static final int OVERRIDE_SOFT = 1;
// Full override: ignore all constraints including API-affecting like connectivity
@@ -976,6 +1000,12 @@ public final class JobStatus {
}
satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0);
mSatisfiedConstraintsOfInterest = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
+ if ((STATSD_CONSTRAINTS_TO_LOG & constraint) != 0) {
+ StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED,
+ sourceUid, null, getBatteryName(), getProtoConstraint(constraint),
+ state ? StatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED__STATE__SATISFIED
+ : StatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED__STATE__UNSATISFIED);
+ }
return true;
}
@@ -1228,37 +1258,70 @@ public final class JobStatus {
}
}
+ /** Returns a {@link JobServerProtoEnums.Constraint} enum value for the given constraint. */
+ private int getProtoConstraint(int constraint) {
+ switch (constraint) {
+ case CONSTRAINT_BACKGROUND_NOT_RESTRICTED:
+ return JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED;
+ case CONSTRAINT_BATTERY_NOT_LOW:
+ return JobServerProtoEnums.CONSTRAINT_BATTERY_NOT_LOW;
+ case CONSTRAINT_CHARGING:
+ return JobServerProtoEnums.CONSTRAINT_CHARGING;
+ case CONSTRAINT_CONNECTIVITY:
+ return JobServerProtoEnums.CONSTRAINT_CONNECTIVITY;
+ case CONSTRAINT_CONTENT_TRIGGER:
+ return JobServerProtoEnums.CONSTRAINT_CONTENT_TRIGGER;
+ case CONSTRAINT_DEADLINE:
+ return JobServerProtoEnums.CONSTRAINT_DEADLINE;
+ case CONSTRAINT_DEVICE_NOT_DOZING:
+ return JobServerProtoEnums.CONSTRAINT_DEVICE_NOT_DOZING;
+ case CONSTRAINT_IDLE:
+ return JobServerProtoEnums.CONSTRAINT_IDLE;
+ case CONSTRAINT_STORAGE_NOT_LOW:
+ return JobServerProtoEnums.CONSTRAINT_STORAGE_NOT_LOW;
+ case CONSTRAINT_TIMING_DELAY:
+ return JobServerProtoEnums.CONSTRAINT_TIMING_DELAY;
+ case CONSTRAINT_WITHIN_QUOTA:
+ return JobServerProtoEnums.CONSTRAINT_WITHIN_QUOTA;
+ default:
+ return JobServerProtoEnums.CONSTRAINT_UNKNOWN;
+ }
+ }
+
/** Writes constraints to the given repeating proto field. */
void dumpConstraints(ProtoOutputStream proto, long fieldId, int constraints) {
if ((constraints & CONSTRAINT_CHARGING) != 0) {
- proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_CHARGING);
+ proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CHARGING);
}
if ((constraints & CONSTRAINT_BATTERY_NOT_LOW) != 0) {
- proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_BATTERY_NOT_LOW);
+ proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_BATTERY_NOT_LOW);
}
if ((constraints & CONSTRAINT_STORAGE_NOT_LOW) != 0) {
- proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_STORAGE_NOT_LOW);
+ proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_STORAGE_NOT_LOW);
}
if ((constraints & CONSTRAINT_TIMING_DELAY) != 0) {
- proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_TIMING_DELAY);
+ proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_TIMING_DELAY);
}
if ((constraints & CONSTRAINT_DEADLINE) != 0) {
- proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_DEADLINE);
+ proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_DEADLINE);
}
if ((constraints & CONSTRAINT_IDLE) != 0) {
- proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_IDLE);
+ proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_IDLE);
}
if ((constraints & CONSTRAINT_CONNECTIVITY) != 0) {
- proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_CONNECTIVITY);
+ proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CONNECTIVITY);
}
if ((constraints & CONSTRAINT_CONTENT_TRIGGER) != 0) {
- proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_CONTENT_TRIGGER);
+ proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CONTENT_TRIGGER);
}
if ((constraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0) {
- proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_DEVICE_NOT_DOZING);
+ proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_DEVICE_NOT_DOZING);
}
if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) {
- proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_WITHIN_QUOTA);
+ proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_WITHIN_QUOTA);
+ }
+ if ((constraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
+ proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 102318262798..2d89bc7c5613 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -544,6 +544,16 @@ class ActivityMetricsLogger {
mHandler.obtainMessage(MSG_CHECK_VISIBILITY, args).sendToTarget();
}
+ private boolean hasVisibleNonFinishingActivity(TaskRecord t) {
+ for (int i = t.mActivities.size() - 1; i >= 0; --i) {
+ final ActivityRecord r = t.mActivities.get(i);
+ if (r.visible && !r.finishing) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private void checkVisibility(TaskRecord t, ActivityRecord r) {
synchronized (mSupervisor.mService.mGlobalLock) {
@@ -552,7 +562,7 @@ class ActivityMetricsLogger {
// If we have an active transition that's waiting on a certain activity that will be
// invisible now, we'll never get onWindowsDrawn, so abort the transition if necessary.
- if (info != null && !t.isVisible()) {
+ if (info != null && !hasVisibleNonFinishingActivity(t)) {
if (DEBUG_METRICS) Slog.i(TAG, "notifyVisibilityChanged to invisible"
+ " activity=" + r);
logAppTransitionCancel(info);
@@ -841,13 +851,11 @@ class ActivityMetricsLogger {
Log.i(TAG, sb.toString());
}
- void logActivityStart(Intent intent, WindowProcessController callerApp, ActivityRecord r,
+ void logAbortedBgActivityStart(Intent intent, WindowProcessController callerApp,
int callingUid, String callingPackage, int callingUidProcState,
boolean callingUidHasAnyVisibleWindow,
int realCallingUid, int realCallingUidProcState,
boolean realCallingUidHasAnyVisibleWindow,
- int targetUid, String targetPackage, int targetUidProcState,
- boolean targetUidHasAnyVisibleWindow, String targetWhitelistTag,
boolean comingFromPendingIntent) {
final long nowElapsed = SystemClock.elapsedRealtime();
@@ -865,13 +873,6 @@ class ActivityMetricsLogger {
processStateAmToProto(realCallingUidProcState));
builder.addTaggedData(FIELD_REAL_CALLING_UID_HAS_ANY_VISIBLE_WINDOW,
realCallingUidHasAnyVisibleWindow ? 1 : 0);
- builder.addTaggedData(FIELD_TARGET_UID, targetUid);
- builder.addTaggedData(FIELD_TARGET_PACKAGE_NAME, targetPackage);
- builder.addTaggedData(FIELD_TARGET_UID_PROC_STATE,
- processStateAmToProto(targetUidProcState));
- builder.addTaggedData(FIELD_TARGET_UID_HAS_ANY_VISIBLE_WINDOW,
- targetUidHasAnyVisibleWindow ? 1 : 0);
- builder.addTaggedData(FIELD_TARGET_WHITELIST_TAG, targetWhitelistTag);
builder.addTaggedData(FIELD_COMING_FROM_PENDING_INTENT, comingFromPendingIntent ? 1 : 0);
if (intent != null) {
builder.addTaggedData(FIELD_INTENT_ACTION, intent.getAction());
@@ -904,35 +905,6 @@ class ActivityMetricsLogger {
(nowUptime - callerApp.getWhenUnimportant()));
}
}
- if (r != null) {
- builder.addTaggedData(FIELD_TARGET_SHORT_COMPONENT_NAME, r.shortComponentName);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_LAUNCH_MODE, r.info.launchMode);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_TARGET_ACTIVITY, r.info.targetActivity);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_FLAGS, r.info.flags);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_REAL_ACTIVITY,
- r.mActivityComponent.toShortString());
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_SHORT_COMPONENT_NAME, r.shortComponentName);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_PROCESS_NAME, r.processName);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_FULLSCREEN, r.fullscreen ? 1 : 0);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_NO_DISPLAY, r.noDisplay ? 1 : 0);
- if (r.lastVisibleTime != 0) {
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_VISIBLE,
- (nowUptime - r.lastVisibleTime));
- }
- if (r.resultTo != null) {
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_PKG_NAME,
- r.resultTo.packageName);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_SHORT_COMPONENT_NAME,
- r.resultTo.shortComponentName);
- }
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE, r.visible ? 1 : 0);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE_IGNORING_KEYGUARD,
- r.visibleIgnoringKeyguard ? 1 : 0);
- if (r.lastLaunchTime != 0) {
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_LAUNCH,
- (nowUptime - r.lastLaunchTime));
- }
- }
mMetricsLogger.write(builder);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index b7c35c083174..8f39f3d6e463 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -161,6 +161,7 @@ import android.app.servertransaction.NewIntentItem;
import android.app.servertransaction.PauseActivityItem;
import android.app.servertransaction.PipModeChangeItem;
import android.app.servertransaction.ResumeActivityItem;
+import android.app.servertransaction.TopResumedActivityChangeItem;
import android.app.servertransaction.WindowVisibilityItem;
import android.app.usage.UsageEvents.Event;
import android.content.ComponentName;
@@ -692,6 +693,26 @@ final class ActivityRecord extends ConfigurationContainer {
}
}
+ void scheduleTopResumedActivityChanged(boolean onTop) {
+ if (!attachedToProcess()) {
+ if (DEBUG_CONFIGURATION) {
+ Slog.w(TAG, "Can't report activity position update - client not running"
+ + ", activityRecord=" + this);
+ }
+ return;
+ }
+ try {
+ if (DEBUG_CONFIGURATION) {
+ Slog.v(TAG, "Sending position change to " + this + ", onTop: " + onTop);
+ }
+
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+ TopResumedActivityChangeItem.obtain(onTop));
+ } catch (RemoteException e) {
+ // If process died, whatever.
+ }
+ }
+
void updateMultiWindowMode() {
if (task == null || task.getStack() == null || !attachedToProcess()) {
return;
@@ -3099,6 +3120,7 @@ final class ActivityRecord extends ConfigurationContainer {
transaction.addCallback(callbackItem);
transaction.setLifecycleStateRequest(lifecycleItem);
mAtmService.getLifecycleManager().scheduleTransaction(transaction);
+ mRootActivityContainer.updateTopResumedActivityIfNeeded();
// Note: don't need to call pauseIfSleepingLocked() here, because the caller will only
// request resume if this activity is currently resumed, which implies we aren't
// sleeping.
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index c97e4e8a9bc0..549567a182bb 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -2524,6 +2524,7 @@ class ActivityStack extends ConfigurationContainer {
// Protect against recursion.
mInResumeTopActivity = true;
result = resumeTopActivityInnerLocked(prev, options);
+ mRootActivityContainer.updateTopResumedActivityIfNeeded();
// When resuming the top activity, it may be necessary to pause the top activity (for
// example, returning to the lock screen. We suppress the normal pause logic in
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index a83ef34f1cac..c8a150beecfa 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -840,6 +840,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
// Schedule transaction.
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
+ mRootActivityContainer.updateTopResumedActivityIfNeeded();
if ((proc.mInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0
&& mService.mHasHeavyWeightFeature) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index b0e581134a95..95a6f71fc44b 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -749,9 +749,15 @@ class ActivityStarter {
boolean abortBackgroundStart = false;
if (!abort) {
- abortBackgroundStart = shouldAbortBackgroundActivityStart(callingUid, callingPid,
- callingPackage, realCallingUid, callerApp, originatingPendingIntent,
- allowBackgroundActivityStart, intent);
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "shouldAbortBackgroundActivityStart");
+ abortBackgroundStart = shouldAbortBackgroundActivityStart(callingUid, callingPid,
+ callingPackage, realCallingUid, callerApp, originatingPendingIntent,
+ allowBackgroundActivityStart, intent);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
abort |= (abortBackgroundStart && !mService.isBackgroundActivityStartsEnabled());
// TODO: remove this toast after feature development is done
if (abortBackgroundStart) {
@@ -903,12 +909,6 @@ class ActivityStarter {
mService.onStartActivitySetDidAppSwitch();
mController.doPendingActivityLaunches(false);
- // maybe log to TRON, but only if we haven't already in shouldAbortBackgroundActivityStart()
- if (!abortBackgroundStart) {
- maybeLogActivityStart(callingUid, callingPackage, realCallingUid, intent, callerApp, r,
- originatingPendingIntent, false /*abortedStart*/);
- }
-
return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
true /* doResume */, checkedOptions, inTask, outActivity);
}
@@ -927,17 +927,31 @@ class ActivityStarter {
return false;
}
// don't abort if the callingUid is in the foreground or is a persistent system process
- final boolean isCallingUidForeground = mService.isUidForeground(callingUid);
- final boolean isCallingUidPersistentSystemProcess = isUidPersistentSystemProcess(
- callingUid);
+ final int callingUidProcState = mService.getUidStateLocked(callingUid);
+ final boolean callingUidHasAnyVisibleWindow =
+ mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(callingUid);
+ final boolean isCallingUidForeground = callingUidHasAnyVisibleWindow
+ || callingUidProcState == ActivityManager.PROCESS_STATE_TOP;
+ final boolean isCallingUidPersistentSystemProcess = (callingUid == Process.SYSTEM_UID)
+ || callingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
if (isCallingUidForeground || isCallingUidPersistentSystemProcess) {
return false;
}
// take realCallingUid into consideration
- final boolean isRealCallingUidForeground = mService.isUidForeground(
- realCallingUid);
- final boolean isRealCallingUidPersistentSystemProcess = isUidPersistentSystemProcess(
- realCallingUid);
+ final int realCallingUidProcState = (callingUid == realCallingUid)
+ ? callingUidProcState
+ : mService.getUidStateLocked(realCallingUid);
+ final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid)
+ ? callingUidHasAnyVisibleWindow
+ : mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(realCallingUid);
+ final boolean isRealCallingUidForeground = (callingUid == realCallingUid)
+ ? isCallingUidForeground
+ : realCallingUidHasAnyVisibleWindow
+ || realCallingUidProcState == ActivityManager.PROCESS_STATE_TOP;
+ final boolean isRealCallingUidPersistentSystemProcess = (callingUid == realCallingUid)
+ ? isCallingUidPersistentSystemProcess
+ : (realCallingUid == Process.SYSTEM_UID)
+ || realCallingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
if (realCallingUid != callingUid) {
// don't abort if the realCallingUid is in the foreground and callingUid isn't
if (isRealCallingUidForeground) {
@@ -975,61 +989,14 @@ class ActivityStarter {
+ "; isBgStartWhitelisted: " + allowBackgroundActivityStart
+ "; intent: " + intent
+ "]");
- maybeLogActivityStart(callingUid, callingPackage, realCallingUid, intent, callerApp,
- null /*r*/, originatingPendingIntent, true /*abortedStart*/);
- return true;
- }
-
- /** Returns true if uid is in a persistent state. */
- private boolean isUidPersistentSystemProcess(int uid) {
- return (uid == Process.SYSTEM_UID)
- || (mService.getUidStateLocked(uid) <= ActivityManager.PROCESS_STATE_PERSISTENT_UI);
- }
-
- private void maybeLogActivityStart(int callingUid, String callingPackage, int realCallingUid,
- Intent intent, WindowProcessController callerApp, ActivityRecord r,
- PendingIntentRecord originatingPendingIntent, boolean abortedStart) {
- boolean callerAppHasForegroundActivity =
- callerApp != null && callerApp.hasForegroundActivities();
- if (!mService.isActivityStartsLoggingEnabled() || callerAppHasForegroundActivity
- || (!abortedStart && r == null)) {
- // skip logging in this case
- return;
- }
-
- try {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "logActivityStart");
- final int callingUidProcState = mService.getUidStateLocked(callingUid);
- final boolean callingUidHasAnyVisibleWindow =
- mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(callingUid);
- final int realCallingUidProcState = (callingUid == realCallingUid)
- ? callingUidProcState
- : mService.getUidStateLocked(realCallingUid);
- final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid)
- ? callingUidHasAnyVisibleWindow
- : mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(
- realCallingUid);
- final String targetPackage = (r != null) ? r.packageName : null;
- final int targetUid = (r!= null) ? ((r.appInfo != null) ? r.appInfo.uid : -1) : -1;
- final int targetUidProcState = mService.getUidStateLocked(targetUid);
- final boolean targetUidHasAnyVisibleWindow = (targetUid != -1)
- ? mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(targetUid)
- : false;
- final String targetWhitelistTag = (targetUid != -1)
- ? mService.getPendingTempWhitelistTagForUidLocked(targetUid)
- : null;
-
- mSupervisor.getActivityMetricsLogger().logActivityStart(intent, callerApp, r,
- callingUid, callingPackage, callingUidProcState,
- callingUidHasAnyVisibleWindow,
- realCallingUid, realCallingUidProcState,
- realCallingUidHasAnyVisibleWindow,
- targetUid, targetPackage, targetUidProcState,
- targetUidHasAnyVisibleWindow, targetWhitelistTag,
+ // log aborted activity start to TRON
+ if (mService.isActivityStartsLoggingEnabled()) {
+ mSupervisor.getActivityMetricsLogger().logAbortedBgActivityStart(intent, callerApp,
+ callingUid, callingPackage, callingUidProcState, callingUidHasAnyVisibleWindow,
+ realCallingUid, realCallingUidProcState, realCallingUidHasAnyVisibleWindow,
(originatingPendingIntent != null));
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
+ return true;
}
/**
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index e79820338c55..66666e681e7a 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -69,7 +69,7 @@ class InsetsSourceProvider {
InsetsSourceProvider(InsetsSource source, InsetsStateController stateController,
DisplayContent displayContent) {
- mClientVisible = InsetsState.getDefaultVisibly(source.getType());
+ mClientVisible = InsetsState.getDefaultVisibility(source.getType());
mSource = source;
mDisplayContent = displayContent;
mStateController = stateController;
@@ -153,6 +153,7 @@ class InsetsSourceProvider {
return;
}
mAdapter = new ControlAdapter();
+ setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
mWin.startAnimation(mDisplayContent.getPendingTransaction(), mAdapter,
!mClientVisible /* hidden */);
mControllingWin = target;
@@ -219,7 +220,7 @@ class InsetsSourceProvider {
public void onAnimationCancelled(SurfaceControl animationLeash) {
if (mAdapter == this) {
mStateController.notifyControlRevoked(mControllingWin, InsetsSourceProvider.this);
- setClientVisible(InsetsState.getDefaultVisibly(mSource.getType()));
+ setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
mControl = null;
mControllingWin = null;
mAdapter = null;
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index e95ac5c43315..624fdc2e168e 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -175,6 +175,12 @@ class RootActivityContainer extends ConfigurationContainer
private ActivityDisplay mDefaultDisplay;
private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>();
+ /**
+ * Cached value of the topmost resumed activity in the system. Updated when new activity is
+ * resumed.
+ */
+ private ActivityRecord mTopResumedActivity;
+
/** The current user */
int mCurrentUser;
/** Stack id of the front stack when user switched, indexed by userId. */
@@ -1145,6 +1151,23 @@ class RootActivityContainer extends ConfigurationContainer
return result;
}
+ void updateTopResumedActivityIfNeeded() {
+ final ActivityRecord prevTopActivity = mTopResumedActivity;
+ final ActivityStack topStack = getTopDisplayFocusedStack();
+ if (topStack == null || topStack.mResumedActivity == prevTopActivity) {
+ return;
+ }
+ // Clear previous top state
+ if (prevTopActivity != null) {
+ prevTopActivity.scheduleTopResumedActivityChanged(false /* onTop */);
+ }
+ // Update the current top activity
+ mTopResumedActivity = topStack.mResumedActivity;
+ if (mTopResumedActivity != null) {
+ mTopResumedActivity.scheduleTopResumedActivityChanged(true /* onTop */);
+ }
+ }
+
void applySleepTokens(boolean applyToStacks) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
// Set the sleeping state of the display.
@@ -1404,6 +1427,7 @@ class RootActivityContainer extends ConfigurationContainer
mActivityDisplays.remove(display);
mActivityDisplays.add(position, display);
}
+ updateTopResumedActivityIfNeeded();
}
@VisibleForTesting
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 056568a0de64..ace965b8b42c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -523,9 +523,8 @@ public class ActivityStarterTests extends ActivityTestsBase {
starter.setReason("testActivityStartsLogging_noLoggingWhenDisabled").execute();
// verify logging wasn't done
- verify(mActivityMetricsLogger, never()).logActivityStart(any(), any(), any(), anyInt(),
- any(), anyInt(), anyBoolean(), anyInt(), anyInt(), anyBoolean(), anyInt(), any(),
- anyInt(), anyBoolean(), any(), anyBoolean());
+ verify(mActivityMetricsLogger, never()).logAbortedBgActivityStart(any(), any(), anyInt(),
+ any(), anyInt(), anyBoolean(), anyInt(), anyInt(), anyBoolean(), anyBoolean());
}
/**
@@ -546,10 +545,9 @@ public class ActivityStarterTests extends ActivityTestsBase {
starter.setReason("testActivityStartsLogging_logsWhenEnabled").execute();
// verify the above activity start was logged
- verify(mActivityMetricsLogger, times(1)).logActivityStart(any(), any(), any(),
+ verify(mActivityMetricsLogger, times(1)).logAbortedBgActivityStart(any(), any(),
eq(FAKE_CALLING_UID), eq(FAKE_CALLING_PACKAGE), anyInt(), anyBoolean(),
- eq(FAKE_REAL_CALLING_UID), anyInt(), anyBoolean(), anyInt(),
- any(), anyInt(), anyBoolean(), any(), eq(false));
+ eq(FAKE_REAL_CALLING_UID), anyInt(), anyBoolean(), eq(false));
}
/**
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index bf9bf9a1ba8f..44756307a4b3 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1631,8 +1631,9 @@ public class ServiceState implements Parcelable {
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- public static boolean bearerBitmapHasCdma(int radioTechnologyBitmap) {
- return (RIL_RADIO_CDMA_TECHNOLOGY_BITMASK & radioTechnologyBitmap) != 0;
+ public static boolean bearerBitmapHasCdma(int networkTypeBitmask) {
+ return (RIL_RADIO_CDMA_TECHNOLOGY_BITMASK
+ & convertNetworkTypeBitmaskToBearerBitmask(networkTypeBitmask)) != 0;
}
/** @hide */
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 98fc7251e9a8..80ee0a72799e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -10134,4 +10134,26 @@ public class TelephonyManager {
}
return ret;
}
+
+ /**
+ * Broadcast intent action for network country code changes.
+ *
+ * <p>
+ * The {@link #EXTRA_NETWORK_COUNTRY} extra indicates the country code of the current
+ * network returned by {@link #getNetworkCountryIso()}.
+ *
+ * @see #EXTRA_NETWORK_COUNTRY
+ * @see #getNetworkCountryIso()
+ */
+ public static final String ACTION_NETWORK_COUNTRY_CHANGED =
+ "android.telephony.action.NETWORK_COUNTRY_CHANGED";
+
+ /**
+ * The extra used with an {@link #ACTION_NETWORK_COUNTRY_CHANGED} to specify the
+ * the country code in ISO 3166 format.
+ * <p class="note">
+ * Retrieve with {@link android.content.Intent#getStringExtra(String)}.
+ */
+ public static final String EXTRA_NETWORK_COUNTRY =
+ "android.telephony.extra.NETWORK_COUNTRY";
}
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index 294c79ba57a2..3d2fe5fec14a 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -23,6 +23,7 @@ import android.annotation.SystemApi;
import android.net.LinkAddress;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.data.ApnSetting.ProtocolType;
import java.net.InetAddress;
import java.util.ArrayList;
@@ -40,7 +41,7 @@ public final class DataCallResponse implements Parcelable {
private final int mSuggestedRetryTime;
private final int mCid;
private final int mActive;
- private final String mType;
+ private final int mProtocolType;
private final String mIfname;
private final List<LinkAddress> mAddresses;
private final List<InetAddress> mDnses;
@@ -53,8 +54,8 @@ public final class DataCallResponse implements Parcelable {
* @param suggestedRetryTime The suggested data retry time in milliseconds.
* @param cid The unique id of the data connection.
* @param active Data connection active status. 0 = inactive, 1 = dormant, 2 = active.
- * @param type The connection protocol, should be one of the PDP_type values in TS 27.007
- * section 10.1.1. For example, "IP", "IPV6", "IPV4V6", or "PPP".
+ * @param protocolType The connection protocol, should be one of the PDP_type values in 3GPP
+ * TS 27.007 section 10.1.1. For example, "IP", "IPV6", "IPV4V6", or "PPP".
* @param ifname The network interface name.
* @param addresses A list of addresses with optional "/" prefix length, e.g.,
* "192.0.1.3" or "192.0.1.11/16 2001:db8::1/64". Typically 1 IPv4 or 1 IPv6 or
@@ -71,7 +72,7 @@ public final class DataCallResponse implements Parcelable {
* either not sent a value or sent an invalid value.
*/
public DataCallResponse(int status, int suggestedRetryTime, int cid, int active,
- @Nullable String type, @Nullable String ifname,
+ @ProtocolType int protocolType, @Nullable String ifname,
@Nullable List<LinkAddress> addresses,
@Nullable List<InetAddress> dnses,
@Nullable List<InetAddress> gateways,
@@ -80,7 +81,7 @@ public final class DataCallResponse implements Parcelable {
mSuggestedRetryTime = suggestedRetryTime;
mCid = cid;
mActive = active;
- mType = (type == null) ? "" : type;
+ mProtocolType = protocolType;
mIfname = (ifname == null) ? "" : ifname;
mAddresses = (addresses == null) ? new ArrayList<>() : addresses;
mDnses = (dnses == null) ? new ArrayList<>() : dnses;
@@ -94,7 +95,7 @@ public final class DataCallResponse implements Parcelable {
mSuggestedRetryTime = source.readInt();
mCid = source.readInt();
mActive = source.readInt();
- mType = source.readString();
+ mProtocolType = source.readInt();
mIfname = source.readString();
mAddresses = new ArrayList<>();
source.readList(mAddresses, LinkAddress.class.getClassLoader());
@@ -128,11 +129,10 @@ public final class DataCallResponse implements Parcelable {
public int getActive() { return mActive; }
/**
- * @return The connection protocol, should be one of the PDP_type values in TS 27.007 section
- * 10.1.1. For example, "IP", "IPV6", "IPV4V6", or "PPP".
+ * @return The connection protocol type.
*/
- @NonNull
- public String getType() { return mType; }
+ @ProtocolType
+ public int getProtocolType() { return mProtocolType; }
/**
* @return The network interface name.
@@ -181,7 +181,7 @@ public final class DataCallResponse implements Parcelable {
.append(" retry=").append(mSuggestedRetryTime)
.append(" cid=").append(mCid)
.append(" active=").append(mActive)
- .append(" type=").append(mType)
+ .append(" protocolType=").append(mProtocolType)
.append(" ifname=").append(mIfname)
.append(" addresses=").append(mAddresses)
.append(" dnses=").append(mDnses)
@@ -205,7 +205,7 @@ public final class DataCallResponse implements Parcelable {
&& this.mSuggestedRetryTime == other.mSuggestedRetryTime
&& this.mCid == other.mCid
&& this.mActive == other.mActive
- && this.mType.equals(other.mType)
+ && this.mProtocolType == other.mProtocolType
&& this.mIfname.equals(other.mIfname)
&& mAddresses.size() == other.mAddresses.size()
&& mAddresses.containsAll(other.mAddresses)
@@ -220,8 +220,8 @@ public final class DataCallResponse implements Parcelable {
@Override
public int hashCode() {
- return Objects.hash(mStatus, mSuggestedRetryTime, mCid, mActive, mType, mIfname, mAddresses,
- mDnses, mGateways, mPcscfs, mMtu);
+ return Objects.hash(mStatus, mSuggestedRetryTime, mCid, mActive, mProtocolType, mIfname,
+ mAddresses, mDnses, mGateways, mPcscfs, mMtu);
}
@Override
@@ -235,7 +235,7 @@ public final class DataCallResponse implements Parcelable {
dest.writeInt(mSuggestedRetryTime);
dest.writeInt(mCid);
dest.writeInt(mActive);
- dest.writeString(mType);
+ dest.writeInt(mProtocolType);
dest.writeString(mIfname);
dest.writeList(mAddresses);
dest.writeList(mDnses);
diff --git a/telephony/java/android/telephony/data/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java
index da4822cc1d14..1d196f9b2885 100644
--- a/telephony/java/android/telephony/data/DataProfile.java
+++ b/telephony/java/android/telephony/data/DataProfile.java
@@ -16,14 +16,23 @@
package android.telephony.data;
+import static android.telephony.data.ApnSetting.ProtocolType;
+
+import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.TelephonyManager.NetworkTypeBitMask;
+import android.telephony.data.ApnSetting.ApnType;
+import android.telephony.data.ApnSetting.AuthType;
import android.text.TextUtils;
import com.android.internal.telephony.RILConstants;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Description of a mobile data profile used for establishing
* data connections.
@@ -32,24 +41,39 @@ import com.android.internal.telephony.RILConstants;
*/
@SystemApi
public final class DataProfile implements Parcelable {
-
- // The types indicating the data profile is used on GSM (3GPP) or CDMA (3GPP2) network.
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"TYPE_"},
+ value = {
+ TYPE_COMMON,
+ TYPE_3GPP,
+ TYPE_3GPP2})
+ public @interface DataProfileType {}
+
+ /** Common data profile */
public static final int TYPE_COMMON = 0;
+
+ /** 3GPP type data profile */
public static final int TYPE_3GPP = 1;
+
+ /** 3GPP2 type data profile */
public static final int TYPE_3GPP2 = 2;
private final int mProfileId;
private final String mApn;
- private final String mProtocol;
+ @ProtocolType
+ private final int mProtocolType;
+ @AuthType
private final int mAuthType;
private final String mUserName;
private final String mPassword;
+ @DataProfileType
private final int mType;
private final int mMaxConnsTime;
@@ -60,10 +84,13 @@ public final class DataProfile implements Parcelable {
private final boolean mEnabled;
+ @ApnType
private final int mSupportedApnTypesBitmap;
- private final String mRoamingProtocol;
+ @ProtocolType
+ private final int mRoamingProtocolType;
+ @NetworkTypeBitMask
private final int mBearerBitmap;
private final int mMtu;
@@ -73,14 +100,14 @@ public final class DataProfile implements Parcelable {
private final boolean mPreferred;
/** @hide */
- public DataProfile(int profileId, String apn, String protocol, int authType, String userName,
- String password, int type, int maxConnsTime, int maxConns, int waitTime,
- boolean enabled, int supportedApnTypesBitmap, String roamingProtocol,
- int bearerBitmap, int mtu, boolean persistent, boolean preferred) {
-
+ public DataProfile(int profileId, String apn, @ProtocolType int protocolType, int authType,
+ String userName, String password, int type, int maxConnsTime, int maxConns,
+ int waitTime, boolean enabled, @ApnType int supportedApnTypesBitmap,
+ @ProtocolType int roamingProtocolType, @NetworkTypeBitMask int bearerBitmap,
+ int mtu, boolean persistent, boolean preferred) {
this.mProfileId = profileId;
this.mApn = apn;
- this.mProtocol = protocol;
+ this.mProtocolType = protocolType;
if (authType == -1) {
authType = TextUtils.isEmpty(userName) ? RILConstants.SETUP_DATA_AUTH_NONE
: RILConstants.SETUP_DATA_AUTH_PAP_CHAP;
@@ -95,7 +122,7 @@ public final class DataProfile implements Parcelable {
this.mEnabled = enabled;
this.mSupportedApnTypesBitmap = supportedApnTypesBitmap;
- this.mRoamingProtocol = roamingProtocol;
+ this.mRoamingProtocolType = roamingProtocolType;
this.mBearerBitmap = bearerBitmap;
this.mMtu = mtu;
this.mPersistent = persistent;
@@ -106,7 +133,7 @@ public final class DataProfile implements Parcelable {
public DataProfile(Parcel source) {
mProfileId = source.readInt();
mApn = source.readString();
- mProtocol = source.readString();
+ mProtocolType = source.readInt();
mAuthType = source.readInt();
mUserName = source.readString();
mPassword = source.readString();
@@ -116,7 +143,7 @@ public final class DataProfile implements Parcelable {
mWaitTime = source.readInt();
mEnabled = source.readBoolean();
mSupportedApnTypesBitmap = source.readInt();
- mRoamingProtocol = source.readString();
+ mRoamingProtocolType = source.readInt();
mBearerBitmap = source.readInt();
mMtu = source.readInt();
mPersistent = source.readBoolean();
@@ -134,16 +161,14 @@ public final class DataProfile implements Parcelable {
public String getApn() { return mApn; }
/**
- * @return The connection protocol, should be one of the PDP_type values in TS 27.007 section
- * 10.1.1. For example, "IP", "IPV6", "IPV4V6", or "PPP".
+ * @return The connection protocol defined in 3GPP TS 27.007 section 10.1.1.
*/
- public String getProtocol() { return mProtocol; }
+ public @ProtocolType int getProtocol() { return mProtocolType; }
/**
- * @return The authentication protocol used for this PDP context
- * (None: 0, PAP: 1, CHAP: 2, PAP&CHAP: 3)
+ * @return The authentication protocol used for this PDP context.
*/
- public int getAuthType() { return mAuthType; }
+ public @AuthType int getAuthType() { return mAuthType; }
/**
* @return The username for APN. Can be null.
@@ -156,9 +181,9 @@ public final class DataProfile implements Parcelable {
public String getPassword() { return mPassword; }
/**
- * @return The profile type. Could be one of TYPE_COMMON, TYPE_3GPP, or TYPE_3GPP2.
+ * @return The profile type.
*/
- public int getType() { return mType; }
+ public @DataProfileType int getType() { return mType; }
/**
* @return The period in seconds to limit the maximum connections.
@@ -183,20 +208,19 @@ public final class DataProfile implements Parcelable {
public boolean isEnabled() { return mEnabled; }
/**
- * @return The supported APN types bitmap. See RIL_ApnTypes for the value of each bit.
+ * @return The supported APN types bitmap.
*/
- public int getSupportedApnTypesBitmap() { return mSupportedApnTypesBitmap; }
+ public @ApnType int getSupportedApnTypesBitmap() { return mSupportedApnTypesBitmap; }
/**
- * @return The connection protocol on roaming network, should be one of the PDP_type values in
- * TS 27.007 section 10.1.1. For example, "IP", "IPV6", "IPV4V6", or "PPP".
+ * @return The connection protocol on roaming network defined in 3GPP TS 27.007 section 10.1.1.
*/
- public String getRoamingProtocol() { return mRoamingProtocol; }
+ public @ProtocolType int getRoamingProtocol() { return mRoamingProtocolType; }
/**
- * @return The bearer bitmap. See RIL_RadioAccessFamily for the value of each bit.
+ * @return The bearer bitmap indicating the applicable networks for this data profile.
*/
- public int getBearerBitmap() { return mBearerBitmap; }
+ public @NetworkTypeBitMask int getBearerBitmap() { return mBearerBitmap; }
/**
* @return The maximum transmission unit (MTU) size in bytes.
@@ -222,12 +246,12 @@ public final class DataProfile implements Parcelable {
@Override
public String toString() {
- return "DataProfile=" + mProfileId + "/" + mProtocol + "/" + mAuthType
+ return "DataProfile=" + mProfileId + "/" + mProtocolType + "/" + mAuthType
+ "/" + (Build.IS_USER ? "***/***/***" :
(mApn + "/" + mUserName + "/" + mPassword)) + "/" + mType + "/"
+ mMaxConnsTime + "/" + mMaxConns + "/"
+ mWaitTime + "/" + mEnabled + "/" + mSupportedApnTypesBitmap + "/"
- + mRoamingProtocol + "/" + mBearerBitmap + "/" + mMtu + "/" + mPersistent + "/"
+ + mRoamingProtocolType + "/" + mBearerBitmap + "/" + mMtu + "/" + mPersistent + "/"
+ mPreferred;
}
@@ -242,7 +266,7 @@ public final class DataProfile implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mProfileId);
dest.writeString(mApn);
- dest.writeString(mProtocol);
+ dest.writeInt(mProtocolType);
dest.writeInt(mAuthType);
dest.writeString(mUserName);
dest.writeString(mPassword);
@@ -252,7 +276,7 @@ public final class DataProfile implements Parcelable {
dest.writeInt(mWaitTime);
dest.writeBoolean(mEnabled);
dest.writeInt(mSupportedApnTypesBitmap);
- dest.writeString(mRoamingProtocol);
+ dest.writeInt(mRoamingProtocolType);
dest.writeInt(mBearerBitmap);
dest.writeInt(mMtu);
dest.writeBoolean(mPersistent);
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 930003462110..d9b206f49dd5 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -268,31 +268,11 @@ public interface RILConstants {
int LTE_ON_CDMA_FALSE = 0;
int LTE_ON_CDMA_TRUE = 1;
- int CDM_TTY_MODE_DISABLED = 0;
- int CDM_TTY_MODE_ENABLED = 1;
-
- int CDM_TTY_FULL_MODE = 1;
- int CDM_TTY_HCO_MODE = 2;
- int CDM_TTY_VCO_MODE = 3;
-
- /* Setup a packet data connection. See ril.h RIL_REQUEST_SETUP_DATA_CALL */
- int SETUP_DATA_TECH_CDMA = 0;
- int SETUP_DATA_TECH_GSM = 1;
-
int SETUP_DATA_AUTH_NONE = 0;
int SETUP_DATA_AUTH_PAP = 1;
int SETUP_DATA_AUTH_CHAP = 2;
int SETUP_DATA_AUTH_PAP_CHAP = 3;
- String SETUP_DATA_PROTOCOL_IP = "IP";
- String SETUP_DATA_PROTOCOL_IPV6 = "IPV6";
- String SETUP_DATA_PROTOCOL_IPV4V6 = "IPV4V6";
-
- /* NV config radio reset types. */
- int NV_CONFIG_RELOAD_RESET = 1;
- int NV_CONFIG_ERASE_RESET = 2;
- int NV_CONFIG_FACTORY_RESET = 3;
-
/* LCE service related constants. */
int LCE_NOT_AVAILABLE = -1;
int LCE_STOPPED = 0;
diff --git a/tests/ActivityTests/AndroidManifest.xml b/tests/ActivityTests/AndroidManifest.xml
index b381cbfbd0a9..0b3bd70ba5c9 100644
--- a/tests/ActivityTests/AndroidManifest.xml
+++ b/tests/ActivityTests/AndroidManifest.xml
@@ -79,6 +79,7 @@
android:singleUser="true" android:exported="true" />
<receiver android:name="TrackTimeReceiver" />
<receiver android:name="AlarmSpamReceiver" />
+ <receiver android:name="SlowReceiver" />
<activity android:name="DisableScreenshotsActivity"
android:label="DisableScreenshots"
android:theme="@style/DisableScreenshots">
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
index 0f4960887a33..2581e083cc64 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -16,21 +16,21 @@
package com.google.android.test.activity;
-import java.util.ArrayList;
-import java.util.List;
-
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.PendingIntent;
-import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProviderClient;
+import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.pm.UserInfo;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
@@ -42,21 +42,18 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
-import android.graphics.Bitmap;
import android.provider.Settings;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-import android.widget.ScrollView;
-import android.widget.Toast;
+import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
-import android.content.Context;
-import android.content.pm.UserInfo;
-import android.content.res.Configuration;
-import android.util.Log;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import android.widget.Toast;
+import java.util.ArrayList;
+import java.util.List;
public class ActivityTestMain extends Activity {
static final String TAG = "ActivityTest";
@@ -73,8 +70,13 @@ public class ActivityTestMain extends Activity {
ServiceConnection mIsolatedConnection;
+ static final String SLOW_RECEIVER_ACTION = "com.google.android.test.activity.SLOW_ACTION";
+ static final String SLOW_RECEIVER_EXTRA = "slow_ordinal";
+
static final int MSG_SPAM = 1;
static final int MSG_SPAM_ALARM = 2;
+ static final int MSG_SLOW_RECEIVER = 3;
+ static final int MSG_SLOW_ALARM_RECEIVER = 4;
final Handler mHandler = new Handler() {
@Override
@@ -100,11 +102,58 @@ public class ActivityTestMain extends Activity {
mAlarm.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME, when+(30*1000), pi);
scheduleSpamAlarm(30*1000);
} break;
+ case MSG_SLOW_RECEIVER: {
+ // Several back to back, to illustrate dispatch policy
+ Intent intent = new Intent(ActivityTestMain.this, SlowReceiver.class);
+ intent.setAction(SLOW_RECEIVER_ACTION);
+ intent.putExtra(SLOW_RECEIVER_EXTRA, 1);
+ sendOrderedBroadcast(intent, null, mSlowReceiverCompletion, mHandler,
+ Activity.RESULT_OK, null, null);
+ intent.putExtra(SLOW_RECEIVER_EXTRA, 2);
+ sendOrderedBroadcast(intent, null, mSlowReceiverCompletion, mHandler,
+ Activity.RESULT_OK, null, null);
+ intent.putExtra(SLOW_RECEIVER_EXTRA, 3);
+ sendOrderedBroadcast(intent, null, mSlowReceiverCompletion, mHandler,
+ Activity.RESULT_OK, null, null);
+ } break;
+ case MSG_SLOW_ALARM_RECEIVER: {
+ // Several back to back, to illustrate dispatch policy
+ Intent intent = new Intent(ActivityTestMain.this, SlowReceiver.class);
+ intent.setAction(SLOW_RECEIVER_ACTION);
+ intent.putExtra(SLOW_RECEIVER_EXTRA, 1);
+ sendOrderedBroadcast(intent, null, mSlowReceiverCompletion, mHandler,
+ Activity.RESULT_OK, null, null);
+ intent.putExtra(SLOW_RECEIVER_EXTRA, 2);
+ sendOrderedBroadcast(intent, null, mSlowReceiverCompletion, mHandler,
+ Activity.RESULT_OK, null, null);
+ intent.putExtra(SLOW_RECEIVER_EXTRA, 3);
+ sendOrderedBroadcast(intent, null, mSlowReceiverCompletion, mHandler,
+ Activity.RESULT_OK, null, null);
+
+ // Also send a broadcast alarm to evaluate the alarm fast-forward policy
+ intent.putExtra(SLOW_RECEIVER_EXTRA, 4);
+ PendingIntent pi = PendingIntent.getBroadcast(ActivityTestMain.this, 1, intent, 0);
+ AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
+ long now = SystemClock.elapsedRealtime();
+ Log.i(TAG, "Setting alarm for now + 5 seconds");
+ am.setExact(AlarmManager.ELAPSED_REALTIME, now + 5_000, pi);
+ } break;
}
super.handleMessage(msg);
}
};
+ final BroadcastReceiver mSlowReceiverCompletion = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int extra = intent.getIntExtra(SLOW_RECEIVER_EXTRA, -1);
+ final String msg = "Slow receiver " + extra + " completed";
+ Toast.makeText(ActivityTestMain.this, msg, Toast.LENGTH_LONG)
+ .show();
+ Log.i(TAG, msg);
+ }
+ };
+
class BroadcastResultReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -387,6 +436,18 @@ public class ActivityTestMain extends Activity {
return true;
}
});
+ menu.add("Slow receiver").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override public boolean onMenuItemClick(MenuItem item) {
+ scheduleSlowReceiver();
+ return true;
+ }
+ });
+ menu.add("Slow alarm receiver").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override public boolean onMenuItemClick(MenuItem item) {
+ scheduleSlowAlarmReceiver();
+ return true;
+ }
+ });
menu.add("Spam!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override public boolean onMenuItemClick(MenuItem item) {
scheduleSpam(false);
@@ -469,6 +530,7 @@ public class ActivityTestMain extends Activity {
protected void onStop() {
super.onStop();
mHandler.removeMessages(MSG_SPAM_ALARM);
+ mHandler.removeMessages(MSG_SLOW_RECEIVER);
for (ServiceConnection conn : mConnections) {
unbindService(conn);
}
@@ -544,6 +606,16 @@ public class ActivityTestMain extends Activity {
mHandler.sendMessageDelayed(msg, delay);
}
+ void scheduleSlowReceiver() {
+ mHandler.removeMessages(MSG_SLOW_RECEIVER);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SLOW_RECEIVER), 500);
+ }
+
+ void scheduleSlowAlarmReceiver() {
+ mHandler.removeMessages(MSG_SLOW_ALARM_RECEIVER);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SLOW_ALARM_RECEIVER), 500);
+ }
+
private View scrollWrap(View view) {
ScrollView scroller = new ScrollView(this);
scroller.addView(view, new ScrollView.LayoutParams(ScrollView.LayoutParams.MATCH_PARENT,
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/SlowReceiver.java b/tests/ActivityTests/src/com/google/android/test/activity/SlowReceiver.java
new file mode 100644
index 000000000000..0437a289741c
--- /dev/null
+++ b/tests/ActivityTests/src/com/google/android/test/activity/SlowReceiver.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.activity;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.SystemClock;
+import android.util.Log;
+
+public class SlowReceiver extends BroadcastReceiver {
+ private static final String TAG = "SlowReceiver";
+ private static final long RECEIVER_DELAY = 6_000;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int extra = intent.getIntExtra(ActivityTestMain.SLOW_RECEIVER_EXTRA, -1);
+ if (extra == 1) {
+ Log.i(TAG, "Received broadcast 1; delaying return by " + RECEIVER_DELAY + " ms");
+ long now = SystemClock.elapsedRealtime();
+ final long end = now + RECEIVER_DELAY;
+ while (now < end) {
+ try {
+ Thread.sleep(end - now);
+ } catch (InterruptedException e) { }
+ now = SystemClock.elapsedRealtime();
+ }
+ } else {
+ Log.i(TAG, "Extra parameter not 1, returning immediately");
+ }
+ Log.i(TAG, "Returning from onReceive()");
+ }
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java
index fc4cd01a5a2a..2690ee87f5b2 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java
@@ -16,8 +16,12 @@
package android.processor.view.inspector;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.stream.Collectors;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
@@ -102,6 +106,83 @@ final class AnnotationUtils {
}
/**
+ * Get a typed list of values for an annotation array property by name.
+ *
+ * The returned list will be empty if the value was left at the default.
+ *
+ * @param propertyName The name of the property to search for
+ * @param valueClass The expected class of the property value
+ * @param element The element the annotation is on, used for exceptions
+ * @param annotationMirror An annotation mirror to search for the property
+ * @param <T> The type of the value
+ * @return A list containing the requested types
+ */
+ <T> List<T> typedArrayValuesByName(
+ String propertyName,
+ Class<T> valueClass,
+ Element element,
+ AnnotationMirror annotationMirror) {
+ return untypedArrayValuesByName(propertyName, element, annotationMirror)
+ .stream()
+ .map(annotationValue -> {
+ final Object value = annotationValue.getValue();
+
+ if (value == null) {
+ throw new ProcessingException(
+ "Unexpected null in array.",
+ element,
+ annotationMirror,
+ annotationValue);
+ }
+
+ if (valueClass.isAssignableFrom(value.getClass())) {
+ return valueClass.cast(value);
+ } else {
+ throw new ProcessingException(
+ String.format(
+ "Expected array entry to have type %s, but got %s.",
+ valueClass.getCanonicalName(),
+ value.getClass().getCanonicalName()),
+ element,
+ annotationMirror,
+ annotationValue);
+ }
+ })
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Get a list of values for an annotation array property by name.
+ *
+ * @param propertyName The name of the property to search for
+ * @param element The element the annotation is on, used for exceptions
+ * @param annotationMirror An annotation mirror to search for the property
+ * @return A list of annotation values, empty list if none found
+ */
+ List<AnnotationValue> untypedArrayValuesByName(
+ String propertyName,
+ Element element,
+ AnnotationMirror annotationMirror) {
+ return typedValueByName(propertyName, List.class, element, annotationMirror)
+ .map(untypedValues -> {
+ List<AnnotationValue> typedValues = new ArrayList<>(untypedValues.size());
+
+ for (Object untypedValue : untypedValues) {
+ if (untypedValue instanceof AnnotationValue) {
+ typedValues.add((AnnotationValue) untypedValue);
+ } else {
+ throw new ProcessingException(
+ "Unable to convert array entry to AnnotationValue",
+ element,
+ annotationMirror);
+ }
+ }
+
+ return typedValues;
+ }).orElseGet(Collections::emptyList);
+ }
+
+ /**
* Get the typed value of an annotation property by name.
*
* The returned optional will be empty if the value was left at the default, or if the value
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
index f1ebb87fed4d..6f588935c44c 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
@@ -19,7 +19,9 @@ package android.processor.view.inspector;
import com.squareup.javapoet.ClassName;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -92,6 +94,8 @@ public final class InspectableClassModel {
private final Type mType;
private boolean mAttributeIdInferrableFromR = true;
private int mAttributeId = 0;
+ private List<IntEnumEntry> mIntEnumEntries;
+ private List<IntFlagEntry> mIntFlagEntries;
public Property(String name, String getter, Type type) {
mName = Objects.requireNonNull(name, "Name must not be null");
@@ -133,6 +137,40 @@ public final class InspectableClassModel {
return mType;
}
+ /**
+ * Get the mapping for an {@code int} enumeration, if present.
+ *
+ * @return A list of mapping entries, empty if absent
+ */
+ public List<IntEnumEntry> getIntEnumEntries() {
+ if (mIntEnumEntries != null) {
+ return mIntEnumEntries;
+ } else {
+ return Collections.emptyList();
+ }
+ }
+
+ public void setIntEnumEntries(List<IntEnumEntry> intEnumEntries) {
+ mIntEnumEntries = intEnumEntries;
+ }
+
+ /**
+ * Get the mapping of {@code int} flags, if present.
+ *
+ * @return A list of mapping entries, empty if absent
+ */
+ public List<IntFlagEntry> getIntFlagEntries() {
+ if (mIntFlagEntries != null) {
+ return mIntFlagEntries;
+ } else {
+ return Collections.emptyList();
+ }
+ }
+
+ public void setIntFlagEntries(List<IntFlagEntry> intFlagEntries) {
+ mIntFlagEntries = intFlagEntries;
+ }
+
public enum Type {
/** Primitive or boxed {@code boolean} */
BOOLEAN,
@@ -181,6 +219,7 @@ public final class InspectableClassModel {
* An enumeration packed into an {@code int}.
*
* @see android.view.inspector.IntEnumMapping
+ * @see IntEnumEntry
*/
INT_ENUM,
@@ -188,8 +227,74 @@ public final class InspectableClassModel {
* Non-exclusive or partially-exclusive flags packed into an {@code int}.
*
* @see android.view.inspector.IntFlagMapping
+ * @see IntFlagEntry
*/
INT_FLAG
}
}
+
+ /**
+ * Model one entry in a int enum mapping.
+ *
+ * @see android.view.inspector.IntEnumMapping
+ */
+ public static final class IntEnumEntry {
+ private final String mName;
+ private final int mValue;
+
+ public IntEnumEntry(String name, int value) {
+ mName = Objects.requireNonNull(name, "Name must not be null");
+ mValue = value;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public int getValue() {
+ return mValue;
+ }
+ }
+
+ /**
+ * Model one entry in an int flag mapping.
+ *
+ * @see android.view.inspector.IntFlagMapping
+ */
+ public static final class IntFlagEntry {
+ private final String mName;
+ private final int mTarget;
+ private final int mMask;
+
+ public IntFlagEntry(String name, int target, int mask) {
+ mName = Objects.requireNonNull(name, "Name must not be null");
+ mTarget = target;
+ mMask = mask;
+ }
+
+ public IntFlagEntry(String name, int target) {
+ this(name, target, target);
+ }
+
+ /**
+ * Determine if this entry has a bitmask.
+ *
+ * @return True if the bitmask and target are different, false otherwise
+ */
+ public boolean hasMask() {
+ return mTarget != mMask;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public int getTarget() {
+ return mTarget;
+ }
+
+ public int getMask() {
+ return mMask;
+ }
+ }
}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java
index f666be7a2a61..42ae890640cb 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java
@@ -16,13 +16,19 @@
package android.processor.view.inspector;
+import android.processor.view.inspector.InspectableClassModel.IntEnumEntry;
+import android.processor.view.inspector.InspectableClassModel.IntFlagEntry;
import android.processor.view.inspector.InspectableClassModel.Property;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
@@ -63,6 +69,7 @@ public final class InspectablePropertyProcessor implements ModelProcessor {
/**
* Set of android and androidx annotation qualified names for colors packed into {@code long}.
+ *
* @see android.annotation.ColorLong
*/
private static final String[] COLOR_LONG_ANNOTATION_NAMES = {
@@ -71,7 +78,7 @@ public final class InspectablePropertyProcessor implements ModelProcessor {
/**
* @param annotationQualifiedName The qualified name of the annotation to process
- * @param processingEnv The processing environment from the parent processor
+ * @param processingEnv The processing environment from the parent processor
*/
public InspectablePropertyProcessor(
String annotationQualifiedName,
@@ -109,8 +116,8 @@ public final class InspectablePropertyProcessor implements ModelProcessor {
* Check that an element is shaped like a getter.
*
* @param element An element that hopefully represents a getter
- * @throws ProcessingException if the element isn't a getter
* @return An {@link ExecutableElement} that represents a getter method.
+ * @throws ProcessingException if the element isn't a getter
*/
private ExecutableElement ensureGetter(Element element) {
if (element.getKind() != ElementKind.METHOD) {
@@ -144,7 +151,7 @@ public final class InspectablePropertyProcessor implements ModelProcessor {
throw new ProcessingException(
String.format(
"Expected a getter method to take no parameters, "
- + "but got %d parameters.",
+ + "but got %d parameters.",
method.getParameters().size()),
element);
}
@@ -167,10 +174,10 @@ public final class InspectablePropertyProcessor implements ModelProcessor {
/**
* Build a {@link Property} from a getter and an inspectable property annotation.
*
- * @param getter An element representing the getter to build from
+ * @param getter An element representing the getter to build from
* @param annotation A mirror of an inspectable property-shaped annotation
- * @throws ProcessingException If the supplied data is invalid and a property cannot be modeled
* @return A property for the getter and annotation
+ * @throws ProcessingException If the supplied data is invalid and a property cannot be modeled
*/
private Property buildProperty(ExecutableElement getter, AnnotationMirror annotation) {
final String name = mAnnotationUtils
@@ -190,16 +197,25 @@ public final class InspectablePropertyProcessor implements ModelProcessor {
.typedValueByName("attributeId", Integer.class, getter, annotation)
.ifPresent(property::setAttributeId);
+ switch (property.getType()) {
+ case INT_ENUM:
+ property.setIntEnumEntries(processEnumMapping(getter, annotation));
+ break;
+ case INT_FLAG:
+ property.setIntFlagEntries(processFlagMapping(getter, annotation));
+ break;
+ }
+
return property;
}
/**
* Determine the property type from the annotation, return type, or context clues.
*
- * @param getter An element representing the getter to build from
+ * @param getter An element representing the getter to build from
* @param annotation A mirror of an inspectable property-shaped annotation
* @return The resolved property type
- * @throws ProcessingException If the property type cannot be resolved
+ * @throws ProcessingException If the property type cannot be resolved or is invalid
* @see android.view.inspector.InspectableProperty#valueType()
*/
private Property.Type determinePropertyType(
@@ -213,10 +229,62 @@ public final class InspectablePropertyProcessor implements ModelProcessor {
final Property.Type returnType = convertReturnTypeToPropertyType(getter);
+ final boolean hasColor = hasColorAnnotation(getter);
+ final Optional<AnnotationValue> enumMapping =
+ mAnnotationUtils.valueByName("enumMapping", annotation);
+ final Optional<AnnotationValue> flagMapping =
+ mAnnotationUtils.valueByName("flagMapping", annotation);
+
+ if (returnType != Property.Type.INT) {
+ enumMapping.ifPresent(value -> {
+ throw new ProcessingException(
+ String.format(
+ "Can only use enumMapping on int types, got %s.",
+ returnType.toString().toLowerCase()),
+ getter,
+ annotation,
+ value);
+ });
+ flagMapping.ifPresent(value -> {
+ throw new ProcessingException(
+ String.format(
+ "Can only use flagMapping on int types, got %s.",
+ returnType.toString().toLowerCase()),
+ getter,
+ annotation,
+ value);
+ });
+ }
+
switch (valueType) {
case "INFERRED":
- if (hasColorAnnotation(getter)) {
+ if (hasColor) {
+ enumMapping.ifPresent(value -> {
+ throw new ProcessingException(
+ "Cannot use enumMapping on a color type.",
+ getter,
+ annotation,
+ value);
+ });
+ flagMapping.ifPresent(value -> {
+ throw new ProcessingException(
+ "Cannot use flagMapping on a color type.",
+ getter,
+ annotation,
+ value);
+ });
return Property.Type.COLOR;
+ } else if (enumMapping.isPresent()) {
+ flagMapping.ifPresent(value -> {
+ throw new ProcessingException(
+ "Cannot use flagMapping and enumMapping simultaneously.",
+ getter,
+ annotation,
+ value);
+ });
+ return Property.Type.INT_ENUM;
+ } else if (flagMapping.isPresent()) {
+ return Property.Type.INT_FLAG;
} else {
return returnType;
}
@@ -235,17 +303,14 @@ public final class InspectablePropertyProcessor implements ModelProcessor {
annotation);
}
case "GRAVITY":
- if (returnType == Property.Type.INT) {
- return Property.Type.GRAVITY;
- } else {
- throw new ProcessingException(
- String.format("Gravity must be an integer, got %s", returnType),
- getter,
- annotation);
- }
+ requirePackedIntToReturnInt("Gravity", returnType, getter, annotation);
+ return Property.Type.GRAVITY;
case "INT_ENUM":
+ requirePackedIntToReturnInt("IntEnum", returnType, getter, annotation);
+ return Property.Type.INT_ENUM;
case "INT_FLAG":
- throw new ProcessingException("Not implemented", getter, annotation);
+ requirePackedIntToReturnInt("IntFlag", returnType, getter, annotation);
+ return Property.Type.INT_FLAG;
default:
throw new ProcessingException(
String.format("Unknown value type enumeration value: %s", valueType),
@@ -258,8 +323,8 @@ public final class InspectablePropertyProcessor implements ModelProcessor {
* Get a property type from the return type of a getter.
*
* @param getter The getter to extract the return type of
- * @throws ProcessingException If the return type is not a primitive or an object
* @return The property type returned by the getter
+ * @throws ProcessingException If the return type is not a primitive or an object
*/
private Property.Type convertReturnTypeToPropertyType(ExecutableElement getter) {
final TypeMirror returnType = getter.getReturnType();
@@ -295,6 +360,31 @@ public final class InspectablePropertyProcessor implements ModelProcessor {
}
/**
+ * Require that a value type packed into an integer be on a getter that returns an int.
+ *
+ * @param typeName The name of the type to use in the exception
+ * @param returnType The return type of the getter to check
+ * @param getter The getter, to use in the exception
+ * @param annotation The annotation, to use in the exception
+ * @throws ProcessingException If the return type is not an int
+ */
+ private static void requirePackedIntToReturnInt(
+ String typeName,
+ Property.Type returnType,
+ ExecutableElement getter,
+ AnnotationMirror annotation) {
+ if (returnType != Property.Type.INT) {
+ throw new ProcessingException(
+ String.format(
+ "%s can only be defined on a method that returns int, got %s.",
+ typeName,
+ returnType.toString().toLowerCase()),
+ getter,
+ annotation);
+ }
+ }
+
+ /**
* Determine if a getter is annotated with color annotation matching its return type.
*
* Note that an {@code int} return value annotated with {@link android.annotation.ColorLong} is
@@ -303,7 +393,6 @@ public final class InspectablePropertyProcessor implements ModelProcessor {
*
* @param getter The getter to query
* @return True if the getter has a color annotation, false otherwise
- *
*/
private boolean hasColorAnnotation(ExecutableElement getter) {
switch (unboxType(getter.getReturnType())) {
@@ -353,6 +442,117 @@ public final class InspectablePropertyProcessor implements ModelProcessor {
}
/**
+ * Build a model of an {@code int} enumeration mapping from annotation values.
+ *
+ * This method only handles the one-to-one mapping of mirrors of
+ * {@link android.view.inspector.InspectableProperty.EnumMap} annotations into
+ * {@link IntEnumEntry} objects. Further validation should be handled elsewhere
+ *
+ * @see android.view.inspector.IntEnumMapping
+ * @see android.view.inspector.InspectableProperty#enumMapping()
+ * @param getter The getter of the property, used for exceptions
+ * @param annotation The {@link android.view.inspector.InspectableProperty} annotation to
+ * extract enum mapping values from.
+ * @return A list of int enum entries, in the order specified in source
+ * @throws ProcessingException if mapping doesn't exist or is invalid
+ */
+ private List<IntEnumEntry> processEnumMapping(
+ ExecutableElement getter,
+ AnnotationMirror annotation) {
+ List<AnnotationMirror> enumAnnotations = mAnnotationUtils.typedArrayValuesByName(
+ "enumMapping", AnnotationMirror.class, getter, annotation);
+ List<IntEnumEntry> enumEntries = new ArrayList<>(enumAnnotations.size());
+
+ if (enumAnnotations.isEmpty()) {
+ throw new ProcessingException(
+ "Encountered an empty array for enumMapping", getter, annotation);
+ }
+
+ for (AnnotationMirror enumAnnotation : enumAnnotations) {
+ final String name = mAnnotationUtils.typedValueByName(
+ "name", String.class, getter, enumAnnotation)
+ .orElseThrow(() -> {
+ throw new ProcessingException(
+ "Name is required for @EnumMap",
+ getter,
+ enumAnnotation);
+ });
+
+ final int value = mAnnotationUtils.typedValueByName(
+ "value", Integer.class, getter, enumAnnotation)
+ .orElseThrow(() -> {
+ throw new ProcessingException(
+ "Value is required for @EnumMap",
+ getter,
+ enumAnnotation);
+ });
+
+ enumEntries.add(new IntEnumEntry(name, value));
+ }
+
+ return enumEntries;
+ }
+
+ /**
+ * Build a model of an {@code int} flag mapping from annotation values.
+ *
+ * This method only handles the one-to-one mapping of mirrors of
+ * {@link android.view.inspector.InspectableProperty.FlagMap} annotations into
+ * {@link IntFlagEntry} objects. Further validation should be handled elsewhere
+ *
+ * @see android.view.inspector.IntFlagMapping
+ * @see android.view.inspector.InspectableProperty#flagMapping()
+ * @param getter The getter of the property, used for exceptions
+ * @param annotation The {@link android.view.inspector.InspectableProperty} annotation to
+ * extract flag mapping values from.
+ * @return A list of int flags entries, in the order specified in source
+ * @throws ProcessingException if mapping doesn't exist or is invalid
+ */
+ private List<IntFlagEntry> processFlagMapping(
+ ExecutableElement getter,
+ AnnotationMirror annotation) {
+ List<AnnotationMirror> flagAnnotations = mAnnotationUtils.typedArrayValuesByName(
+ "flagMapping", AnnotationMirror.class, getter, annotation);
+ List<IntFlagEntry> flagEntries = new ArrayList<>(flagAnnotations.size());
+
+ if (flagAnnotations.isEmpty()) {
+ throw new ProcessingException(
+ "Encountered an empty array for flagMapping", getter, annotation);
+ }
+
+ for (AnnotationMirror flagAnnotation : flagAnnotations) {
+ final String name = mAnnotationUtils.typedValueByName(
+ "name", String.class, getter, flagAnnotation)
+ .orElseThrow(() -> {
+ throw new ProcessingException(
+ "Name is required for @FlagMap",
+ getter,
+ flagAnnotation);
+ });
+
+ final int target = mAnnotationUtils.typedValueByName(
+ "target", Integer.class, getter, flagAnnotation)
+ .orElseThrow(() -> {
+ throw new ProcessingException(
+ "Target is required for @FlagMap",
+ getter,
+ flagAnnotation);
+ });
+
+ final Optional<Integer> mask = mAnnotationUtils.typedValueByName(
+ "mask", Integer.class, getter, flagAnnotation);
+
+ if (mask.isPresent()) {
+ flagEntries.add(new IntFlagEntry(name, target, mask.get()));
+ } else {
+ flagEntries.add(new IntFlagEntry(name, target));
+ }
+ }
+
+ return flagEntries;
+ }
+
+ /**
* Determine if a {@link TypeMirror} is a boxed or unboxed boolean.
*
* @param type The type mirror to check
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
index dd4d8f54fb68..7b04645e9f44 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
@@ -16,6 +16,8 @@
package android.processor.view.inspector;
+import android.processor.view.inspector.InspectableClassModel.IntEnumEntry;
+import android.processor.view.inspector.InspectableClassModel.IntFlagEntry;
import android.processor.view.inspector.InspectableClassModel.Property;
import com.squareup.javapoet.ClassName;
@@ -69,6 +71,18 @@ public final class InspectionCompanionGenerator {
"android.view.inspector", "PropertyReader");
/**
+ * The class name of {@link android.view.inspector.IntEnumMapping}.
+ */
+ private static final ClassName INT_ENUM_MAPPING = ClassName.get(
+ "android.view.inspector", "IntEnumMapping");
+
+ /**
+ * The class name of {@link android.view.inspector.IntFlagMapping}.
+ */
+ private static final ClassName INT_FLAG_MAPPING = ClassName.get(
+ "android.view.inspector", "IntFlagMapping");
+
+ /**
* The {@code mPropertiesMapped} field.
*/
private static final FieldSpec M_PROPERTIES_MAPPED = FieldSpec
@@ -248,13 +262,13 @@ public final class InspectionCompanionGenerator {
final MethodSpec.Builder builder = MethodSpec.methodBuilder("readProperties")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
- .addParameter(model.getClassName(), "inspectable")
+ .addParameter(model.getClassName(), "node")
.addParameter(PROPERTY_READER, "propertyReader")
.addCode(generatePropertyMapInitializationCheck());
for (PropertyIdField propertyIdField : propertyIdFields) {
builder.addStatement(
- "propertyReader.read$L($N, inspectable.$L())",
+ "propertyReader.read$L($N, node.$L())",
methodSuffixForPropertyType(propertyIdField.mProperty.getType()),
propertyIdField.mFieldSpec,
propertyIdField.mProperty.getGetter());
@@ -286,21 +300,22 @@ public final class InspectionCompanionGenerator {
if (property.getAttributeId() == ID_NULL) {
builder.add("$L", ID_NULL);
} else {
- builder.add("$L", String.format("0x%08x", property.getAttributeId()));
+ builder.add("$L", hexLiteral(property.getAttributeId()));
}
}
switch (property.getType()) {
case INT_ENUM:
- throw new RuntimeException("IntEnumMapping generation not implemented");
+ builder.add(",$W");
+ builder.add(generateIntEnumMappingBuilder(property.getIntEnumEntries()));
+ break;
case INT_FLAG:
- throw new RuntimeException("IntFlagMapping generation not implemented");
- default:
- builder.add(")");
+ builder.add(",$W");
+ builder.add(generateIntFlagMappingBuilder(property.getIntFlagEntries()));
break;
}
- return builder.build();
+ return builder.add(")").build();
}
/**
@@ -327,6 +342,56 @@ public final class InspectionCompanionGenerator {
}
/**
+ * Generate an invocation of {@link android.view.inspector.IntEnumMapping.Builder}.
+ *
+ * <pre>
+ * new IntEnumMapping.Builder()
+ * .addValue("ONE", 1)
+ * .build()
+ * </pre>
+ *
+ * @return A codeblock containing the an int enum mapping builder
+ */
+ private CodeBlock generateIntEnumMappingBuilder(List<IntEnumEntry> intEnumEntries) {
+ final ArrayList<IntEnumEntry> sortedEntries = new ArrayList<>(intEnumEntries);
+ sortedEntries.sort(Comparator.comparing(IntEnumEntry::getValue));
+
+ final CodeBlock.Builder builder = CodeBlock.builder()
+ .add("new $T()$>", INT_ENUM_MAPPING.nestedClass("Builder"));
+
+ for (IntEnumEntry entry : sortedEntries) {
+ builder.add("\n.addValue($S, $L)", entry.getName(), entry.getValue());
+ }
+
+ return builder.add("\n.build()$<").build();
+ }
+
+ private CodeBlock generateIntFlagMappingBuilder(List<IntFlagEntry> intFlagEntries) {
+ final ArrayList<IntFlagEntry> sortedEntries = new ArrayList<>(intFlagEntries);
+ sortedEntries.sort(Comparator.comparing(IntFlagEntry::getName));
+
+ final CodeBlock.Builder builder = CodeBlock.builder()
+ .add("new $T()$>", INT_FLAG_MAPPING.nestedClass("Builder"));
+
+ for (IntFlagEntry entry : sortedEntries) {
+ if (entry.hasMask()) {
+ builder.add(
+ "\n.addFlag($S, $L, $L)",
+ entry.getName(),
+ hexLiteral(entry.getTarget()),
+ hexLiteral(entry.getMask()));
+ } else {
+ builder.add(
+ "\n.addFlag($S, $L)",
+ entry.getName(),
+ hexLiteral(entry.getTarget()));
+ }
+ }
+
+ return builder.add("\n.build()$<").build();
+ }
+
+ /**
* Generate the final class name for the inspection companion from the model's class name.
*
* The generated class is added to the same package as the source class. If the class in the
@@ -385,6 +450,10 @@ public final class InspectionCompanionGenerator {
}
}
+ private static String hexLiteral(int value) {
+ return String.format("0x%08x", value);
+ }
+
/**
* Value class that holds a {@link Property} and a {@link FieldSpec} for that property.
*/
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java
index 455f5b08e49e..01d94307f871 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java
@@ -32,6 +32,7 @@ import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
@@ -118,6 +119,12 @@ public final class PlatformInspectableProcessor extends AbstractProcessor {
break;
}
+ final Set<Modifier> classModifiers = classElement.get().getModifiers();
+
+ if (classModifiers.contains(Modifier.PRIVATE)) {
+ fail("Enclosing class cannot be private", element);
+ }
+
final InspectableClassModel model = modelMap.computeIfAbsent(
classElement.get().getQualifiedName().toString(),
k -> new InspectableClassModel(ClassName.get(classElement.get())));
diff --git a/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
index b0775dc77f45..f6d8bb0939db 100644
--- a/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
+++ b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
@@ -16,11 +16,13 @@
package android.processor.view.inspector;
-import android.processor.view.inspector.InspectableClassModel.Property;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.fail;
+import android.processor.view.inspector.InspectableClassModel.IntEnumEntry;
+import android.processor.view.inspector.InspectableClassModel.IntFlagEntry;
+import android.processor.view.inspector.InspectableClassModel.Property;
import com.google.common.base.Charsets;
import com.google.common.io.Resources;
@@ -31,6 +33,7 @@ import org.junit.Test;
import java.io.IOException;
import java.net.URL;
+import java.util.Arrays;
import java.util.Optional;
/**
@@ -40,7 +43,7 @@ public class InspectionCompanionGeneratorTest {
private static final String RESOURCE_PATH_TEMPLATE =
"android/processor/view/inspector/InspectionCompanionGeneratorTest/%s.java.txt";
private static final ClassName TEST_CLASS_NAME =
- ClassName.get("com.android.inspectable", "TestInspectable");
+ ClassName.get("com.android.node", "TestNode");
private InspectableClassModel mModel;
private InspectionCompanionGenerator mGenerator;
@@ -59,7 +62,7 @@ public class InspectionCompanionGeneratorTest {
@Test
public void testNestedClass() {
mModel = new InspectableClassModel(
- ClassName.get("com.android.inspectable", "Outer", "Inner"));
+ ClassName.get("com.android.node", "Outer", "Inner"));
assertGeneratedFileEquals("NestedClass");
}
@@ -105,6 +108,42 @@ public class InspectionCompanionGeneratorTest {
assertGeneratedFileEquals("SuppliedAttributeId");
}
+ @Test
+ public void testIntEnum() {
+ final Property property = new Property(
+ "intEnumProperty",
+ "getIntEnumProperty",
+ Property.Type.INT_ENUM);
+
+ property.setIntEnumEntries(Arrays.asList(
+ new IntEnumEntry("THREE", 3),
+ new IntEnumEntry("TWO", 2),
+ new IntEnumEntry("ONE", 1)));
+
+ mModel.putProperty(property);
+
+ assertGeneratedFileEquals("IntEnum");
+ }
+
+ @Test
+ public void testIntFlag() {
+ final Property property = new Property(
+ "intFlag",
+ "getIntFlag",
+ Property.Type.INT_FLAG);
+
+ property.setAttributeIdInferrableFromR(false);
+ property.setIntFlagEntries(Arrays.asList(
+ new IntFlagEntry("TURBO", 0x1, 0x3),
+ new IntFlagEntry("OVERDRIVE", 0x2, 0x3),
+ new IntFlagEntry("WARP", 0x4)
+ ));
+
+ mModel.putProperty(property);
+
+ assertGeneratedFileEquals("IntFlag");
+ }
+
private Property addProperty(String name, String getter, Property.Type type) {
final Property property = new Property(name, getter, type);
mModel.putProperty(property);
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/IntEnum.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/IntEnum.java.txt
new file mode 100644
index 000000000000..764aa8bfbd63
--- /dev/null
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/IntEnum.java.txt
@@ -0,0 +1,45 @@
+package com.android.node;
+
+import android.R;
+import android.view.inspector.InspectionCompanion;
+import android.view.inspector.IntEnumMapping;
+import android.view.inspector.PropertyMapper;
+import android.view.inspector.PropertyReader;
+import java.lang.Override;
+
+/**
+ * Inspection companion for {@link TestNode}.
+ *
+ * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator}
+ * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
+ */
+public final class TestNode$$InspectionCompanion implements InspectionCompanion<TestNode> {
+ /**
+ * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped.
+ */
+ private boolean mPropertiesMapped = false;
+
+ /**
+ * Property ID of {@code intEnumProperty}.
+ */
+ private int mIntEnumPropertyId;
+
+ @Override
+ public void mapProperties(PropertyMapper propertyMapper) {
+ mIntEnumPropertyId = propertyMapper.mapIntEnum("intEnumProperty", R.attr.intEnumProperty,
+ new IntEnumMapping.Builder()
+ .addValue("ONE", 1)
+ .addValue("TWO", 2)
+ .addValue("THREE", 3)
+ .build());
+ mPropertiesMapped = true;
+ }
+
+ @Override
+ public void readProperties(TestNode node, PropertyReader propertyReader) {
+ if (!mPropertiesMapped) {
+ throw new InspectionCompanion.UninitializedPropertyMapException();
+ }
+ propertyReader.readIntEnum(mIntEnumPropertyId, node.getIntEnumProperty());
+ }
+}
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/IntFlag.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/IntFlag.java.txt
new file mode 100644
index 000000000000..75f281314965
--- /dev/null
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/IntFlag.java.txt
@@ -0,0 +1,43 @@
+package com.android.node;
+
+import android.view.inspector.InspectionCompanion;
+import android.view.inspector.IntFlagMapping;
+import android.view.inspector.PropertyMapper;
+import android.view.inspector.PropertyReader;
+import java.lang.Override;
+
+/**
+ * Inspection companion for {@link TestNode}.
+ *
+ * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator}
+ * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
+ */
+public final class TestNode$$InspectionCompanion implements InspectionCompanion<TestNode> {
+ /**
+ * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped.
+ */
+ private boolean mPropertiesMapped = false;
+
+ /**
+ * Property ID of {@code intFlag}.
+ */
+ private int mIntFlagId;
+
+ @Override
+ public void mapProperties(PropertyMapper propertyMapper) {
+ mIntFlagId = propertyMapper.mapIntFlag("intFlag", 0, new IntFlagMapping.Builder()
+ .addFlag("OVERDRIVE", 0x00000002, 0x00000003)
+ .addFlag("TURBO", 0x00000001, 0x00000003)
+ .addFlag("WARP", 0x00000004)
+ .build());
+ mPropertiesMapped = true;
+ }
+
+ @Override
+ public void readProperties(TestNode node, PropertyReader propertyReader) {
+ if (!mPropertiesMapped) {
+ throw new InspectionCompanion.UninitializedPropertyMapException();
+ }
+ propertyReader.readIntFlag(mIntFlagId, node.getIntFlag());
+ }
+}
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt
index 2fc242c6cf4c..0cac462fba51 100644
--- a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt
@@ -1,4 +1,4 @@
-package com.android.inspectable;
+package com.android.node;
import android.view.inspector.InspectionCompanion;
import android.view.inspector.PropertyMapper;
@@ -23,7 +23,7 @@ public final class Outer$Inner$$InspectionCompanion implements InspectionCompani
}
@Override
- public void readProperties(Outer.Inner inspectable, PropertyReader propertyReader) {
+ public void readProperties(Outer.Inner node, PropertyReader propertyReader) {
if (!mPropertiesMapped) {
throw new InspectionCompanion.UninitializedPropertyMapException();
}
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt
index 23d0f7807aa5..ce0f867d5332 100644
--- a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NoAttributeId.java.txt
@@ -1,4 +1,4 @@
-package com.android.inspectable;
+package com.android.node;
import android.view.inspector.InspectionCompanion;
import android.view.inspector.PropertyMapper;
@@ -6,12 +6,12 @@ import android.view.inspector.PropertyReader;
import java.lang.Override;
/**
- * Inspection companion for {@link TestInspectable}.
+ * Inspection companion for {@link TestNode}.
*
* Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator}
* on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
*/
-public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> {
+public final class TestNode$$InspectionCompanion implements InspectionCompanion<TestNode> {
/**
* Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped.
*/
@@ -29,10 +29,10 @@ public final class TestInspectable$$InspectionCompanion implements InspectionCom
}
@Override
- public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) {
+ public void readProperties(TestNode node, PropertyReader propertyReader) {
if (!mPropertiesMapped) {
throw new InspectionCompanion.UninitializedPropertyMapException();
}
- propertyReader.readInt(mNoAttributePropertyId, inspectable.getNoAttributeProperty());
+ propertyReader.readInt(mNoAttributePropertyId, node.getNoAttributeProperty());
}
}
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt
index 11425482ce94..f7357fece2bf 100644
--- a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt
@@ -1,4 +1,4 @@
-package com.android.inspectable;
+package com.android.node;
import android.view.inspector.InspectionCompanion;
import android.view.inspector.PropertyMapper;
@@ -7,12 +7,12 @@ import java.lang.Override;
import java.lang.String;
/**
- * Inspection companion for {@link TestInspectable}.
+ * Inspection companion for {@link TestNode}.
*
* Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator}
* on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
*/
-public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> {
+public final class TestNode$$InspectionCompanion implements InspectionCompanion<TestNode> {
/**
* Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped.
*/
@@ -24,7 +24,7 @@ public final class TestInspectable$$InspectionCompanion implements InspectionCom
}
@Override
- public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) {
+ public void readProperties(TestNode node, PropertyReader propertyReader) {
if (!mPropertiesMapped) {
throw new InspectionCompanion.UninitializedPropertyMapException();
}
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt
index 57eb08041131..dfc1bce8afcc 100644
--- a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt
@@ -1,4 +1,4 @@
-package com.android.inspectable;
+package com.android.node;
import android.R;
import android.view.inspector.InspectionCompanion;
@@ -7,12 +7,12 @@ import android.view.inspector.PropertyReader;
import java.lang.Override;
/**
- * Inspection companion for {@link TestInspectable}.
+ * Inspection companion for {@link TestNode}.
*
* Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator}
* on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
*/
-public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> {
+public final class TestNode$$InspectionCompanion implements InspectionCompanion<TestNode> {
/**
* Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped.
*/
@@ -90,20 +90,20 @@ public final class TestInspectable$$InspectionCompanion implements InspectionCom
}
@Override
- public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) {
+ public void readProperties(TestNode node, PropertyReader propertyReader) {
if (!mPropertiesMapped) {
throw new InspectionCompanion.UninitializedPropertyMapException();
}
- propertyReader.readBoolean(mBooleanId, inspectable.getBoolean());
- propertyReader.readByte(mByteId, inspectable.getByte());
- propertyReader.readChar(mCharId, inspectable.getChar());
- propertyReader.readColor(mColorId, inspectable.getColor());
- propertyReader.readDouble(mDoubleId, inspectable.getDouble());
- propertyReader.readFloat(mFloatId, inspectable.getFloat());
- propertyReader.readGravity(mGravityId, inspectable.getGravity());
- propertyReader.readInt(mIntId, inspectable.getInt());
- propertyReader.readLong(mLongId, inspectable.getLong());
- propertyReader.readObject(mObjectId, inspectable.getObject());
- propertyReader.readShort(mShortId, inspectable.getShort());
+ propertyReader.readBoolean(mBooleanId, node.getBoolean());
+ propertyReader.readByte(mByteId, node.getByte());
+ propertyReader.readChar(mCharId, node.getChar());
+ propertyReader.readColor(mColorId, node.getColor());
+ propertyReader.readDouble(mDoubleId, node.getDouble());
+ propertyReader.readFloat(mFloatId, node.getFloat());
+ propertyReader.readGravity(mGravityId, node.getGravity());
+ propertyReader.readInt(mIntId, node.getInt());
+ propertyReader.readLong(mLongId, node.getLong());
+ propertyReader.readObject(mObjectId, node.getObject());
+ propertyReader.readShort(mShortId, node.getShort());
}
}
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SuppliedAttributeId.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SuppliedAttributeId.java.txt
index 6b6ce2157481..d72cdd533205 100644
--- a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SuppliedAttributeId.java.txt
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SuppliedAttributeId.java.txt
@@ -1,4 +1,4 @@
-package com.android.inspectable;
+package com.android.node;
import android.view.inspector.InspectionCompanion;
import android.view.inspector.PropertyMapper;
@@ -6,12 +6,12 @@ import android.view.inspector.PropertyReader;
import java.lang.Override;
/**
- * Inspection companion for {@link TestInspectable}.
+ * Inspection companion for {@link TestNode}.
*
* Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator}
* on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
*/
-public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> {
+public final class TestNode$$InspectionCompanion implements InspectionCompanion<TestNode> {
/**
* Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped.
*/
@@ -30,10 +30,10 @@ public final class TestInspectable$$InspectionCompanion implements InspectionCom
}
@Override
- public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) {
+ public void readProperties(TestNode node, PropertyReader propertyReader) {
if (!mPropertiesMapped) {
throw new InspectionCompanion.UninitializedPropertyMapException();
}
- propertyReader.readInt(mSuppliedAttributePropertyId, inspectable.getSuppliedAttributeProperty());
+ propertyReader.readInt(mSuppliedAttributePropertyId, node.getSuppliedAttributeProperty());
}
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 066823931832..e5733edd9803 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1221,7 +1221,6 @@ public class WifiManager {
* @param scanResults a list of scanResult that represents the BSSID
* @return List that consists of {@link WifiConfiguration} and corresponding scanResults per
* network type({@link #PASSPOINT_HOME_NETWORK} and {@link #PASSPOINT_ROAMING_NETWORK}).
- * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
* @hide
*/
@SystemApi
@@ -1264,7 +1263,6 @@ public class WifiManager {
*
* @param scanResults a list of ScanResult
* @return Map that consists {@link OsuProvider} and a list of matching {@link ScanResult}
- * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
* @hide
*/
@SystemApi
@@ -1291,7 +1289,6 @@ public class WifiManager {
*
* @param osuProviders a set of {@link OsuProvider}
* @return Map that consists of {@link OsuProvider} and matching {@link PasspointConfiguration}.
- * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
* @hide
*/
@SystemApi
@@ -1720,8 +1717,8 @@ public class WifiManager {
* FQDN, the new configuration will replace the existing configuration.
*
* @param config The Passpoint configuration to be added
- * @throws IllegalArgumentException if configuration is invalid
- * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
+ * @throws IllegalArgumentException if configuration is invalid or Passpoint is not enabled on
+ * the device.
*/
public void addOrUpdatePasspointConfiguration(PasspointConfiguration config) {
try {
@@ -1737,8 +1734,8 @@ public class WifiManager {
* Remove the Passpoint configuration identified by its FQDN (Fully Qualified Domain Name).
*
* @param fqdn The FQDN of the Passpoint configuration to be removed
- * @throws IllegalArgumentException if no configuration is associated with the given FQDN.
- * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
+ * @throws IllegalArgumentException if no configuration is associated with the given FQDN or
+ * Passpoint is not enabled on the device.
* @deprecated This is no longer supported.
*/
@Deprecated
@@ -1762,7 +1759,6 @@ public class WifiManager {
* An empty list will be returned when no configurations are installed.
*
* @return A list of {@link PasspointConfiguration}
- * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
* @deprecated This is no longer supported.
*/
@Deprecated